Page MenuHomeFreeBSD

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/contrib/wpa/hostapd/config_file.c b/contrib/wpa/hostapd/config_file.c
index 9bc1dc7756e9..daf3f37ad99e 100644
--- a/contrib/wpa/hostapd/config_file.c
+++ b/contrib/wpa/hostapd/config_file.c
@@ -1,4832 +1,4841 @@
/*
* hostapd / Configuration file parser
* Copyright (c) 2003-2018, 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"
#ifndef CONFIG_NATIVE_WINDOWS
#include <grp.h>
#endif /* CONFIG_NATIVE_WINDOWS */
#include "utils/common.h"
#include "utils/uuid.h"
+#include "utils/crc32.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "crypto/sha256.h"
#include "crypto/tls.h"
#include "drivers/driver.h"
#include "eap_server/eap.h"
#include "radius/radius_client.h"
#include "ap/wpa_auth.h"
#include "ap/ap_config.h"
#include "config_file.h"
#ifndef CONFIG_NO_VLAN
static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
const char *fname)
{
FILE *f;
char buf[128], *pos, *pos2, *pos3;
int line = 0, vlan_id;
struct hostapd_vlan *vlan;
f = fopen(fname, "r");
if (!f) {
wpa_printf(MSG_ERROR, "VLAN file '%s' not readable.", fname);
return -1;
}
while (fgets(buf, sizeof(buf), f)) {
line++;
if (buf[0] == '#')
continue;
pos = buf;
while (*pos != '\0') {
if (*pos == '\n') {
*pos = '\0';
break;
}
pos++;
}
if (buf[0] == '\0')
continue;
if (buf[0] == '*') {
vlan_id = VLAN_ID_WILDCARD;
pos = buf + 1;
} else {
vlan_id = strtol(buf, &pos, 10);
if (buf == pos || vlan_id < 1 ||
vlan_id > MAX_VLAN_ID) {
wpa_printf(MSG_ERROR, "Invalid VLAN ID at "
"line %d in '%s'", line, fname);
fclose(f);
return -1;
}
}
while (*pos == ' ' || *pos == '\t')
pos++;
pos2 = pos;
while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0')
pos2++;
if (*pos2 != '\0')
*(pos2++) = '\0';
if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) {
wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d "
"in '%s'", line, fname);
fclose(f);
return -1;
}
while (*pos2 == ' ' || *pos2 == '\t')
pos2++;
pos3 = pos2;
while (*pos3 != ' ' && *pos3 != '\t' && *pos3 != '\0')
pos3++;
*pos3 = '\0';
vlan = os_zalloc(sizeof(*vlan));
if (vlan == NULL) {
wpa_printf(MSG_ERROR, "Out of memory while reading "
"VLAN interfaces from '%s'", fname);
fclose(f);
return -1;
}
vlan->vlan_id = vlan_id;
vlan->vlan_desc.untagged = vlan_id;
vlan->vlan_desc.notempty = !!vlan_id;
os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname));
os_strlcpy(vlan->bridge, pos2, sizeof(vlan->bridge));
vlan->next = bss->vlan;
bss->vlan = vlan;
}
fclose(f);
return 0;
}
#endif /* CONFIG_NO_VLAN */
int hostapd_acl_comp(const void *a, const void *b)
{
const struct mac_acl_entry *aa = a;
const struct mac_acl_entry *bb = b;
return os_memcmp(aa->addr, bb->addr, sizeof(macaddr));
}
int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
int vlan_id, const u8 *addr)
{
struct mac_acl_entry *newacl;
newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
if (!newacl) {
wpa_printf(MSG_ERROR, "MAC list reallocation failed");
return -1;
}
*acl = newacl;
os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id));
(*acl)[*num].vlan_id.untagged = vlan_id;
(*acl)[*num].vlan_id.notempty = !!vlan_id;
(*num)++;
return 0;
}
void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
const u8 *addr)
{
int i = 0;
while (i < *num) {
if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
os_remove_in_array(*acl, *num, sizeof(**acl), i);
(*num)--;
} else {
i++;
}
}
}
static int hostapd_config_read_maclist(const char *fname,
struct mac_acl_entry **acl, int *num)
{
FILE *f;
char buf[128], *pos;
int line = 0;
u8 addr[ETH_ALEN];
int vlan_id;
f = fopen(fname, "r");
if (!f) {
wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname);
return -1;
}
while (fgets(buf, sizeof(buf), f)) {
int rem = 0;
line++;
if (buf[0] == '#')
continue;
pos = buf;
while (*pos != '\0') {
if (*pos == '\n') {
*pos = '\0';
break;
}
pos++;
}
if (buf[0] == '\0')
continue;
pos = buf;
if (buf[0] == '-') {
rem = 1;
pos++;
}
if (hwaddr_aton(pos, addr)) {
wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at "
"line %d in '%s'", pos, line, fname);
fclose(f);
return -1;
}
if (rem) {
hostapd_remove_acl_mac(acl, num, addr);
continue;
}
vlan_id = 0;
pos = buf;
while (*pos != '\0' && *pos != ' ' && *pos != '\t')
pos++;
while (*pos == ' ' || *pos == '\t')
pos++;
if (*pos != '\0')
vlan_id = atoi(pos);
if (hostapd_add_acl_maclist(acl, num, vlan_id, addr) < 0) {
fclose(f);
return -1;
}
}
fclose(f);
if (*acl)
qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
return 0;
}
#ifdef EAP_SERVER
static int hostapd_config_eap_user_salted(struct hostapd_eap_user *user,
const char *hash, size_t len,
char **pos, int line,
const char *fname)
{
char *pos2 = *pos;
while (*pos2 != '\0' && *pos2 != ' ' && *pos2 != '\t' && *pos2 != '#')
pos2++;
if (pos2 - *pos < (int) (2 * (len + 1))) { /* at least 1 byte of salt */
wpa_printf(MSG_ERROR,
"Invalid salted %s hash on line %d in '%s'",
hash, line, fname);
return -1;
}
user->password = os_malloc(len);
if (!user->password) {
wpa_printf(MSG_ERROR,
"Failed to allocate memory for salted %s hash",
hash);
return -1;
}
if (hexstr2bin(*pos, user->password, len) < 0) {
wpa_printf(MSG_ERROR,
"Invalid salted password on line %d in '%s'",
line, fname);
return -1;
}
user->password_len = len;
*pos += 2 * len;
user->salt_len = (pos2 - *pos) / 2;
user->salt = os_malloc(user->salt_len);
if (!user->salt) {
wpa_printf(MSG_ERROR,
"Failed to allocate memory for salted %s hash",
hash);
return -1;
}
if (hexstr2bin(*pos, user->salt, user->salt_len) < 0) {
wpa_printf(MSG_ERROR,
"Invalid salt for password on line %d in '%s'",
line, fname);
return -1;
}
*pos = pos2;
return 0;
}
static int hostapd_config_read_eap_user(const char *fname,
struct hostapd_bss_config *conf)
{
FILE *f;
char buf[512], *pos, *start, *pos2;
int line = 0, ret = 0, num_methods;
struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL;
if (os_strncmp(fname, "sqlite:", 7) == 0) {
#ifdef CONFIG_SQLITE
os_free(conf->eap_user_sqlite);
conf->eap_user_sqlite = os_strdup(fname + 7);
return 0;
#else /* CONFIG_SQLITE */
wpa_printf(MSG_ERROR,
"EAP user file in SQLite DB, but CONFIG_SQLITE was not enabled in the build.");
return -1;
#endif /* CONFIG_SQLITE */
}
f = fopen(fname, "r");
if (!f) {
wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", fname);
return -1;
}
/* Lines: "user" METHOD,METHOD2 "password" (password optional) */
while (fgets(buf, sizeof(buf), f)) {
line++;
if (buf[0] == '#')
continue;
pos = buf;
while (*pos != '\0') {
if (*pos == '\n') {
*pos = '\0';
break;
}
pos++;
}
if (buf[0] == '\0')
continue;
#ifndef CONFIG_NO_RADIUS
if (user && os_strncmp(buf, "radius_accept_attr=", 19) == 0) {
struct hostapd_radius_attr *attr, *a;
attr = hostapd_parse_radius_attr(buf + 19);
if (attr == NULL) {
wpa_printf(MSG_ERROR, "Invalid radius_accept_attr: %s",
buf + 19);
user = NULL; /* already in the BSS list */
goto failed;
}
if (user->accept_attr == NULL) {
user->accept_attr = attr;
} else {
a = user->accept_attr;
while (a->next)
a = a->next;
a->next = attr;
}
continue;
}
#endif /* CONFIG_NO_RADIUS */
user = NULL;
if (buf[0] != '"' && buf[0] != '*') {
wpa_printf(MSG_ERROR, "Invalid EAP identity (no \" in "
"start) on line %d in '%s'", line, fname);
goto failed;
}
user = os_zalloc(sizeof(*user));
if (user == NULL) {
wpa_printf(MSG_ERROR, "EAP user allocation failed");
goto failed;
}
user->force_version = -1;
if (buf[0] == '*') {
pos = buf;
} else {
pos = buf + 1;
start = pos;
while (*pos != '"' && *pos != '\0')
pos++;
if (*pos == '\0') {
wpa_printf(MSG_ERROR, "Invalid EAP identity "
"(no \" in end) on line %d in '%s'",
line, fname);
goto failed;
}
user->identity = os_memdup(start, pos - start);
if (user->identity == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate "
"memory for EAP identity");
goto failed;
}
user->identity_len = pos - start;
if (pos[0] == '"' && pos[1] == '*') {
user->wildcard_prefix = 1;
pos++;
}
}
pos++;
while (*pos == ' ' || *pos == '\t')
pos++;
if (*pos == '\0') {
wpa_printf(MSG_ERROR, "No EAP method on line %d in "
"'%s'", line, fname);
goto failed;
}
start = pos;
while (*pos != ' ' && *pos != '\t' && *pos != '\0')
pos++;
if (*pos == '\0') {
pos = NULL;
} else {
*pos = '\0';
pos++;
}
num_methods = 0;
while (*start) {
char *pos3 = os_strchr(start, ',');
if (pos3) {
*pos3++ = '\0';
}
user->methods[num_methods].method =
eap_server_get_type(
start,
&user->methods[num_methods].vendor);
if (user->methods[num_methods].vendor ==
EAP_VENDOR_IETF &&
user->methods[num_methods].method == EAP_TYPE_NONE)
{
if (os_strcmp(start, "TTLS-PAP") == 0) {
user->ttls_auth |= EAP_TTLS_AUTH_PAP;
goto skip_eap;
}
if (os_strcmp(start, "TTLS-CHAP") == 0) {
user->ttls_auth |= EAP_TTLS_AUTH_CHAP;
goto skip_eap;
}
if (os_strcmp(start, "TTLS-MSCHAP") == 0) {
user->ttls_auth |=
EAP_TTLS_AUTH_MSCHAP;
goto skip_eap;
}
if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) {
user->ttls_auth |=
EAP_TTLS_AUTH_MSCHAPV2;
goto skip_eap;
}
if (os_strcmp(start, "MACACL") == 0) {
user->macacl = 1;
goto skip_eap;
}
wpa_printf(MSG_ERROR, "Unsupported EAP type "
"'%s' on line %d in '%s'",
start, line, fname);
goto failed;
}
num_methods++;
if (num_methods >= EAP_MAX_METHODS)
break;
skip_eap:
if (pos3 == NULL)
break;
start = pos3;
}
if (num_methods == 0 && user->ttls_auth == 0 && !user->macacl) {
wpa_printf(MSG_ERROR, "No EAP types configured on "
"line %d in '%s'", line, fname);
goto failed;
}
if (pos == NULL)
goto done;
while (*pos == ' ' || *pos == '\t')
pos++;
if (*pos == '\0')
goto done;
if (os_strncmp(pos, "[ver=0]", 7) == 0) {
user->force_version = 0;
goto done;
}
if (os_strncmp(pos, "[ver=1]", 7) == 0) {
user->force_version = 1;
goto done;
}
if (os_strncmp(pos, "[2]", 3) == 0) {
user->phase2 = 1;
goto done;
}
if (*pos == '"') {
pos++;
start = pos;
while (*pos != '"' && *pos != '\0')
pos++;
if (*pos == '\0') {
wpa_printf(MSG_ERROR, "Invalid EAP password "
"(no \" in end) on line %d in '%s'",
line, fname);
goto failed;
}
user->password = os_memdup(start, pos - start);
if (user->password == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate "
"memory for EAP password");
goto failed;
}
user->password_len = pos - start;
pos++;
} else if (os_strncmp(pos, "hash:", 5) == 0) {
pos += 5;
pos2 = pos;
while (*pos2 != '\0' && *pos2 != ' ' &&
*pos2 != '\t' && *pos2 != '#')
pos2++;
if (pos2 - pos != 32) {
wpa_printf(MSG_ERROR, "Invalid password hash "
"on line %d in '%s'", line, fname);
goto failed;
}
user->password = os_malloc(16);
if (user->password == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate "
"memory for EAP password hash");
goto failed;
}
if (hexstr2bin(pos, user->password, 16) < 0) {
wpa_printf(MSG_ERROR, "Invalid hash password "
"on line %d in '%s'", line, fname);
goto failed;
}
user->password_len = 16;
user->password_hash = 1;
pos = pos2;
} else if (os_strncmp(pos, "ssha1:", 6) == 0) {
pos += 6;
if (hostapd_config_eap_user_salted(user, "sha1", 20,
&pos,
line, fname) < 0)
goto failed;
} else if (os_strncmp(pos, "ssha256:", 8) == 0) {
pos += 8;
if (hostapd_config_eap_user_salted(user, "sha256", 32,
&pos,
line, fname) < 0)
goto failed;
} else if (os_strncmp(pos, "ssha512:", 8) == 0) {
pos += 8;
if (hostapd_config_eap_user_salted(user, "sha512", 64,
&pos,
line, fname) < 0)
goto failed;
} else {
pos2 = pos;
while (*pos2 != '\0' && *pos2 != ' ' &&
*pos2 != '\t' && *pos2 != '#')
pos2++;
if ((pos2 - pos) & 1) {
wpa_printf(MSG_ERROR, "Invalid hex password "
"on line %d in '%s'", line, fname);
goto failed;
}
user->password = os_malloc((pos2 - pos) / 2);
if (user->password == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate "
"memory for EAP password");
goto failed;
}
if (hexstr2bin(pos, user->password,
(pos2 - pos) / 2) < 0) {
wpa_printf(MSG_ERROR, "Invalid hex password "
"on line %d in '%s'", line, fname);
goto failed;
}
user->password_len = (pos2 - pos) / 2;
pos = pos2;
}
while (*pos == ' ' || *pos == '\t')
pos++;
if (os_strncmp(pos, "[2]", 3) == 0) {
user->phase2 = 1;
}
done:
if (tail == NULL) {
tail = new_user = user;
} else {
tail->next = user;
tail = user;
}
continue;
failed:
if (user)
hostapd_config_free_eap_user(user);
ret = -1;
break;
}
fclose(f);
if (ret == 0) {
hostapd_config_free_eap_users(conf->eap_user);
conf->eap_user = new_user;
} else {
hostapd_config_free_eap_users(new_user);
}
return ret;
}
#endif /* EAP_SERVER */
#ifndef CONFIG_NO_RADIUS
static int
hostapd_config_read_radius_addr(struct hostapd_radius_server **server,
int *num_server, const char *val, int def_port,
struct hostapd_radius_server **curr_serv)
{
struct hostapd_radius_server *nserv;
int ret;
static int server_index = 1;
nserv = os_realloc_array(*server, *num_server + 1, sizeof(*nserv));
if (nserv == NULL)
return -1;
*server = nserv;
nserv = &nserv[*num_server];
(*num_server)++;
(*curr_serv) = nserv;
os_memset(nserv, 0, sizeof(*nserv));
nserv->port = def_port;
ret = hostapd_parse_ip_addr(val, &nserv->addr);
nserv->index = server_index++;
return ret;
}
static int hostapd_parse_das_client(struct hostapd_bss_config *bss, char *val)
{
char *secret;
secret = os_strchr(val, ' ');
if (secret == NULL)
return -1;
*secret++ = '\0';
if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr))
return -1;
os_free(bss->radius_das_shared_secret);
bss->radius_das_shared_secret = (u8 *) os_strdup(secret);
if (bss->radius_das_shared_secret == NULL)
return -1;
bss->radius_das_shared_secret_len = os_strlen(secret);
return 0;
}
#endif /* CONFIG_NO_RADIUS */
static int hostapd_config_parse_key_mgmt(int line, const char *value)
{
int val = 0, last;
char *start, *end, *buf;
buf = os_strdup(value);
if (buf == NULL)
return -1;
start = buf;
while (*start != '\0') {
while (*start == ' ' || *start == '\t')
start++;
if (*start == '\0')
break;
end = start;
while (*end != ' ' && *end != '\t' && *end != '\0')
end++;
last = *end == '\0';
*end = '\0';
if (os_strcmp(start, "WPA-PSK") == 0)
val |= WPA_KEY_MGMT_PSK;
else if (os_strcmp(start, "WPA-EAP") == 0)
val |= WPA_KEY_MGMT_IEEE8021X;
#ifdef CONFIG_IEEE80211R_AP
else if (os_strcmp(start, "FT-PSK") == 0)
val |= WPA_KEY_MGMT_FT_PSK;
else if (os_strcmp(start, "FT-EAP") == 0)
val |= WPA_KEY_MGMT_FT_IEEE8021X;
#ifdef CONFIG_SHA384
else if (os_strcmp(start, "FT-EAP-SHA384") == 0)
val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R_AP */
else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
val |= WPA_KEY_MGMT_PSK_SHA256;
else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
#ifdef CONFIG_SAE
else if (os_strcmp(start, "SAE") == 0)
val |= WPA_KEY_MGMT_SAE;
else if (os_strcmp(start, "FT-SAE") == 0)
val |= WPA_KEY_MGMT_FT_SAE;
#endif /* CONFIG_SAE */
#ifdef CONFIG_SUITEB
else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_SUITEB192
else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
#endif /* CONFIG_SUITEB192 */
#ifdef CONFIG_FILS
else if (os_strcmp(start, "FILS-SHA256") == 0)
val |= WPA_KEY_MGMT_FILS_SHA256;
else if (os_strcmp(start, "FILS-SHA384") == 0)
val |= WPA_KEY_MGMT_FILS_SHA384;
#ifdef CONFIG_IEEE80211R_AP
else if (os_strcmp(start, "FT-FILS-SHA256") == 0)
val |= WPA_KEY_MGMT_FT_FILS_SHA256;
else if (os_strcmp(start, "FT-FILS-SHA384") == 0)
val |= WPA_KEY_MGMT_FT_FILS_SHA384;
#endif /* CONFIG_IEEE80211R_AP */
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
else if (os_strcmp(start, "OWE") == 0)
val |= WPA_KEY_MGMT_OWE;
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
else if (os_strcmp(start, "DPP") == 0)
val |= WPA_KEY_MGMT_DPP;
#endif /* CONFIG_DPP */
#ifdef CONFIG_HS20
else if (os_strcmp(start, "OSEN") == 0)
val |= WPA_KEY_MGMT_OSEN;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_PASN
else if (os_strcmp(start, "PASN") == 0)
val |= WPA_KEY_MGMT_PASN;
#endif /* CONFIG_PASN */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
os_free(buf);
return -1;
}
if (last)
break;
start = end + 1;
}
os_free(buf);
if (val == 0) {
wpa_printf(MSG_ERROR, "Line %d: no key_mgmt values "
"configured.", line);
return -1;
}
return val;
}
static int hostapd_config_parse_cipher(int line, const char *value)
{
int val = wpa_parse_cipher(value);
if (val < 0) {
wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
line, value);
return -1;
}
if (val == 0) {
wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
line);
return -1;
}
return val;
}
#ifdef CONFIG_WEP
static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx,
char *val)
{
size_t len = os_strlen(val);
if (keyidx < 0 || keyidx > 3)
return -1;
if (len == 0) {
int i, set = 0;
bin_clear_free(wep->key[keyidx], wep->len[keyidx]);
wep->key[keyidx] = NULL;
wep->len[keyidx] = 0;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (wep->key[i])
set++;
}
if (!set)
wep->keys_set = 0;
return 0;
}
if (wep->key[keyidx] != NULL)
return -1;
if (val[0] == '"') {
if (len < 2 || val[len - 1] != '"')
return -1;
len -= 2;
wep->key[keyidx] = os_memdup(val + 1, len);
if (wep->key[keyidx] == NULL)
return -1;
wep->len[keyidx] = len;
} else {
if (len & 1)
return -1;
len /= 2;
wep->key[keyidx] = os_malloc(len);
if (wep->key[keyidx] == NULL)
return -1;
wep->len[keyidx] = len;
if (hexstr2bin(val, wep->key[keyidx], len) < 0)
return -1;
}
wep->keys_set++;
return 0;
}
#endif /* CONFIG_WEP */
static int hostapd_parse_chanlist(struct hostapd_config *conf, char *val)
{
char *pos;
/* for backwards compatibility, translate ' ' in conf str to ',' */
pos = val;
while (pos) {
pos = os_strchr(pos, ' ');
if (pos)
*pos++ = ',';
}
if (freq_range_list_parse(&conf->acs_ch_list, val))
return -1;
return 0;
}
static int hostapd_parse_intlist(int **int_list, char *val)
{
int *list;
int count;
char *pos, *end;
os_free(*int_list);
*int_list = NULL;
pos = val;
count = 0;
while (*pos != '\0') {
if (*pos == ' ')
count++;
pos++;
}
list = os_malloc(sizeof(int) * (count + 2));
if (list == NULL)
return -1;
pos = val;
count = 0;
while (*pos != '\0') {
end = os_strchr(pos, ' ');
if (end)
*end = '\0';
list[count++] = atoi(pos);
if (!end)
break;
pos = end + 1;
}
list[count] = -1;
*int_list = list;
return 0;
}
static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname)
{
struct hostapd_bss_config **all, *bss;
if (*ifname == '\0')
return -1;
all = os_realloc_array(conf->bss, conf->num_bss + 1,
sizeof(struct hostapd_bss_config *));
if (all == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate memory for "
"multi-BSS entry");
return -1;
}
conf->bss = all;
bss = os_zalloc(sizeof(*bss));
if (bss == NULL)
return -1;
bss->radius = os_zalloc(sizeof(*bss->radius));
if (bss->radius == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate memory for "
"multi-BSS RADIUS data");
os_free(bss);
return -1;
}
conf->bss[conf->num_bss++] = bss;
conf->last_bss = bss;
hostapd_config_defaults_bss(bss);
os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
os_memcpy(bss->ssid.vlan, bss->iface, IFNAMSIZ + 1);
return 0;
}
#ifdef CONFIG_IEEE80211R_AP
static int rkh_derive_key(const char *pos, u8 *key, size_t key_len)
{
u8 oldkey[16];
int ret;
if (!hexstr2bin(pos, key, key_len))
return 0;
/* Try to use old short key for backwards compatibility */
if (hexstr2bin(pos, oldkey, sizeof(oldkey)))
return -1;
ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0,
key, key_len);
os_memset(oldkey, 0, sizeof(oldkey));
return ret;
}
static int add_r0kh(struct hostapd_bss_config *bss, char *value)
{
struct ft_remote_r0kh *r0kh;
char *pos, *next;
r0kh = os_zalloc(sizeof(*r0kh));
if (r0kh == NULL)
return -1;
/* 02:01:02:03:04:05 a.example.com 000102030405060708090a0b0c0d0e0f */
pos = value;
next = os_strchr(pos, ' ');
if (next)
*next++ = '\0';
if (next == NULL || hwaddr_aton(pos, r0kh->addr)) {
wpa_printf(MSG_ERROR, "Invalid R0KH MAC address: '%s'", pos);
os_free(r0kh);
return -1;
}
pos = next;
next = os_strchr(pos, ' ');
if (next)
*next++ = '\0';
if (next == NULL || next - pos > FT_R0KH_ID_MAX_LEN) {
wpa_printf(MSG_ERROR, "Invalid R0KH-ID: '%s'", pos);
os_free(r0kh);
return -1;
}
r0kh->id_len = next - pos - 1;
os_memcpy(r0kh->id, pos, r0kh->id_len);
pos = next;
if (rkh_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) {
wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos);
os_free(r0kh);
return -1;
}
r0kh->next = bss->r0kh_list;
bss->r0kh_list = r0kh;
return 0;
}
static int add_r1kh(struct hostapd_bss_config *bss, char *value)
{
struct ft_remote_r1kh *r1kh;
char *pos, *next;
r1kh = os_zalloc(sizeof(*r1kh));
if (r1kh == NULL)
return -1;
/* 02:01:02:03:04:05 02:01:02:03:04:05
* 000102030405060708090a0b0c0d0e0f */
pos = value;
next = os_strchr(pos, ' ');
if (next)
*next++ = '\0';
if (next == NULL || hwaddr_aton(pos, r1kh->addr)) {
wpa_printf(MSG_ERROR, "Invalid R1KH MAC address: '%s'", pos);
os_free(r1kh);
return -1;
}
pos = next;
next = os_strchr(pos, ' ');
if (next)
*next++ = '\0';
if (next == NULL || hwaddr_aton(pos, r1kh->id)) {
wpa_printf(MSG_ERROR, "Invalid R1KH-ID: '%s'", pos);
os_free(r1kh);
return -1;
}
pos = next;
if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) {
wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos);
os_free(r1kh);
return -1;
}
r1kh->next = bss->r1kh_list;
bss->r1kh_list = r1kh;
return 0;
}
#endif /* CONFIG_IEEE80211R_AP */
static int hostapd_config_ht_capab(struct hostapd_config *conf,
const char *capab)
{
if (os_strstr(capab, "[LDPC]"))
conf->ht_capab |= HT_CAP_INFO_LDPC_CODING_CAP;
if (os_strstr(capab, "[HT40-]")) {
conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
conf->secondary_channel = -1;
}
if (os_strstr(capab, "[HT40+]")) {
conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
conf->secondary_channel = 1;
}
if (os_strstr(capab, "[HT40+]") && os_strstr(capab, "[HT40-]")) {
conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
conf->ht40_plus_minus_allowed = 1;
}
if (!os_strstr(capab, "[HT40+]") && !os_strstr(capab, "[HT40-]"))
conf->secondary_channel = 0;
if (os_strstr(capab, "[GF]"))
conf->ht_capab |= HT_CAP_INFO_GREEN_FIELD;
if (os_strstr(capab, "[SHORT-GI-20]"))
conf->ht_capab |= HT_CAP_INFO_SHORT_GI20MHZ;
if (os_strstr(capab, "[SHORT-GI-40]"))
conf->ht_capab |= HT_CAP_INFO_SHORT_GI40MHZ;
if (os_strstr(capab, "[TX-STBC]"))
conf->ht_capab |= HT_CAP_INFO_TX_STBC;
if (os_strstr(capab, "[RX-STBC1]")) {
conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK;
conf->ht_capab |= HT_CAP_INFO_RX_STBC_1;
}
if (os_strstr(capab, "[RX-STBC12]")) {
conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK;
conf->ht_capab |= HT_CAP_INFO_RX_STBC_12;
}
if (os_strstr(capab, "[RX-STBC123]")) {
conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK;
conf->ht_capab |= HT_CAP_INFO_RX_STBC_123;
}
if (os_strstr(capab, "[DELAYED-BA]"))
conf->ht_capab |= HT_CAP_INFO_DELAYED_BA;
if (os_strstr(capab, "[MAX-AMSDU-7935]"))
conf->ht_capab |= HT_CAP_INFO_MAX_AMSDU_SIZE;
if (os_strstr(capab, "[DSSS_CCK-40]"))
conf->ht_capab |= HT_CAP_INFO_DSSS_CCK40MHZ;
if (os_strstr(capab, "[40-INTOLERANT]"))
conf->ht_capab |= HT_CAP_INFO_40MHZ_INTOLERANT;
if (os_strstr(capab, "[LSIG-TXOP-PROT]"))
conf->ht_capab |= HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT;
return 0;
}
#ifdef CONFIG_IEEE80211AC
static int hostapd_config_vht_capab(struct hostapd_config *conf,
const char *capab)
{
if (os_strstr(capab, "[MAX-MPDU-7991]"))
conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_7991;
if (os_strstr(capab, "[MAX-MPDU-11454]"))
conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_11454;
if (os_strstr(capab, "[VHT160]"))
conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
if (os_strstr(capab, "[VHT160-80PLUS80]"))
conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
if (os_strstr(capab, "[RXLDPC]"))
conf->vht_capab |= VHT_CAP_RXLDPC;
if (os_strstr(capab, "[SHORT-GI-80]"))
conf->vht_capab |= VHT_CAP_SHORT_GI_80;
if (os_strstr(capab, "[SHORT-GI-160]"))
conf->vht_capab |= VHT_CAP_SHORT_GI_160;
if (os_strstr(capab, "[TX-STBC-2BY1]"))
conf->vht_capab |= VHT_CAP_TXSTBC;
if (os_strstr(capab, "[RX-STBC-1]"))
conf->vht_capab |= VHT_CAP_RXSTBC_1;
if (os_strstr(capab, "[RX-STBC-12]"))
conf->vht_capab |= VHT_CAP_RXSTBC_2;
if (os_strstr(capab, "[RX-STBC-123]"))
conf->vht_capab |= VHT_CAP_RXSTBC_3;
if (os_strstr(capab, "[RX-STBC-1234]"))
conf->vht_capab |= VHT_CAP_RXSTBC_4;
if (os_strstr(capab, "[SU-BEAMFORMER]"))
conf->vht_capab |= VHT_CAP_SU_BEAMFORMER_CAPABLE;
if (os_strstr(capab, "[SU-BEAMFORMEE]"))
conf->vht_capab |= VHT_CAP_SU_BEAMFORMEE_CAPABLE;
if (os_strstr(capab, "[BF-ANTENNA-2]") &&
(conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
if (os_strstr(capab, "[BF-ANTENNA-3]") &&
(conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
conf->vht_capab |= (2 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
if (os_strstr(capab, "[BF-ANTENNA-4]") &&
(conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
(conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
if (os_strstr(capab, "[SOUNDING-DIMENSION-3]") &&
(conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
conf->vht_capab |= (2 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
if (os_strstr(capab, "[SOUNDING-DIMENSION-4]") &&
(conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
conf->vht_capab |= (3 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
if (os_strstr(capab, "[MU-BEAMFORMER]"))
conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
if (os_strstr(capab, "[VHT-TXOP-PS]"))
conf->vht_capab |= VHT_CAP_VHT_TXOP_PS;
if (os_strstr(capab, "[HTC-VHT]"))
conf->vht_capab |= VHT_CAP_HTC_VHT;
if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP7]"))
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX;
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP6]"))
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6;
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP5]"))
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5;
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP4]"))
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4;
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP3]"))
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3;
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP2]"))
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2;
else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP1]"))
conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1;
if (os_strstr(capab, "[VHT-LINK-ADAPT2]") &&
(conf->vht_capab & VHT_CAP_HTC_VHT))
conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB;
if (os_strstr(capab, "[VHT-LINK-ADAPT3]") &&
(conf->vht_capab & VHT_CAP_HTC_VHT))
conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
if (os_strstr(capab, "[RX-ANTENNA-PATTERN]"))
conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN;
if (os_strstr(capab, "[TX-ANTENNA-PATTERN]"))
conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN;
return 0;
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
static u8 find_bit_offset(u8 val)
{
u8 res = 0;
for (; val; val >>= 1) {
if (val & 1)
break;
res++;
}
return res;
}
static u8 set_he_cap(int val, u8 mask)
{
return (u8) (mask & (val << find_bit_offset(mask)));
}
static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val)
{
int bitpos;
char *pos, *end;
os_memset(bitmap, 0, 8);
pos = val;
while (*pos != '\0') {
end = os_strchr(pos, ' ');
if (end)
*end = '\0';
bitpos = atoi(pos);
if (bitpos < 0 || bitpos > 64)
return -1;
bitmap[bitpos / 8] |= BIT(bitpos % 8);
if (!end)
break;
pos = end + 1;
}
return 0;
}
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_INTERWORKING
static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos,
int line)
{
size_t len = os_strlen(pos);
u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
struct hostapd_roaming_consortium *rc;
if ((len & 1) || len < 2 * 3 || len / 2 > MAX_ROAMING_CONSORTIUM_LEN ||
hexstr2bin(pos, oi, len / 2)) {
wpa_printf(MSG_ERROR, "Line %d: invalid roaming_consortium "
"'%s'", line, pos);
return -1;
}
len /= 2;
rc = os_realloc_array(bss->roaming_consortium,
bss->roaming_consortium_count + 1,
sizeof(struct hostapd_roaming_consortium));
if (rc == NULL)
return -1;
os_memcpy(rc[bss->roaming_consortium_count].oi, oi, len);
rc[bss->roaming_consortium_count].len = len;
bss->roaming_consortium = rc;
bss->roaming_consortium_count++;
return 0;
}
static int parse_lang_string(struct hostapd_lang_string **array,
unsigned int *count, char *pos)
{
char *sep, *str = NULL;
size_t clen, nlen, slen;
struct hostapd_lang_string *ls;
int ret = -1;
if (*pos == '"' || (*pos == 'P' && pos[1] == '"')) {
str = wpa_config_parse_string(pos, &slen);
if (!str)
return -1;
pos = str;
}
sep = os_strchr(pos, ':');
if (sep == NULL)
goto fail;
*sep++ = '\0';
clen = os_strlen(pos);
if (clen < 2 || clen > sizeof(ls->lang))
goto fail;
nlen = os_strlen(sep);
if (nlen > 252)
goto fail;
ls = os_realloc_array(*array, *count + 1,
sizeof(struct hostapd_lang_string));
if (ls == NULL)
goto fail;
*array = ls;
ls = &(*array)[*count];
(*count)++;
os_memset(ls->lang, 0, sizeof(ls->lang));
os_memcpy(ls->lang, pos, clen);
ls->name_len = nlen;
os_memcpy(ls->name, sep, nlen);
ret = 0;
fail:
os_free(str);
return ret;
}
static int parse_venue_name(struct hostapd_bss_config *bss, char *pos,
int line)
{
if (parse_lang_string(&bss->venue_name, &bss->venue_name_count, pos)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'",
line, pos);
return -1;
}
return 0;
}
static int parse_venue_url(struct hostapd_bss_config *bss, char *pos,
int line)
{
char *sep;
size_t nlen;
struct hostapd_venue_url *url;
int ret = -1;
sep = os_strchr(pos, ':');
if (!sep)
goto fail;
*sep++ = '\0';
nlen = os_strlen(sep);
if (nlen > 254)
goto fail;
url = os_realloc_array(bss->venue_url, bss->venue_url_count + 1,
sizeof(struct hostapd_venue_url));
if (!url)
goto fail;
bss->venue_url = url;
url = &bss->venue_url[bss->venue_url_count++];
url->venue_number = atoi(pos);
url->url_len = nlen;
os_memcpy(url->url, sep, nlen);
ret = 0;
fail:
if (ret)
wpa_printf(MSG_ERROR, "Line %d: Invalid venue_url '%s'",
line, pos);
return ret;
}
static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf,
int line)
{
size_t count;
char *pos;
u8 *info = NULL, *ipos;
/* format: <MCC1,MNC1>[;<MCC2,MNC2>][;...] */
count = 1;
for (pos = buf; *pos; pos++) {
if ((*pos < '0' || *pos > '9') && *pos != ';' && *pos != ',')
goto fail;
if (*pos == ';')
count++;
}
if (1 + count * 3 > 0x7f)
goto fail;
info = os_zalloc(2 + 3 + count * 3);
if (info == NULL)
return -1;
ipos = info;
*ipos++ = 0; /* GUD - Version 1 */
*ipos++ = 3 + count * 3; /* User Data Header Length (UDHL) */
*ipos++ = 0; /* PLMN List IEI */
/* ext(b8) | Length of PLMN List value contents(b7..1) */
*ipos++ = 1 + count * 3;
*ipos++ = count; /* Number of PLMNs */
pos = buf;
while (pos && *pos) {
char *mcc, *mnc;
size_t mnc_len;
mcc = pos;
mnc = os_strchr(pos, ',');
if (mnc == NULL)
goto fail;
*mnc++ = '\0';
pos = os_strchr(mnc, ';');
if (pos)
*pos++ = '\0';
mnc_len = os_strlen(mnc);
if (os_strlen(mcc) != 3 || (mnc_len != 2 && mnc_len != 3))
goto fail;
/* BC coded MCC,MNC */
/* MCC digit 2 | MCC digit 1 */
*ipos++ = ((mcc[1] - '0') << 4) | (mcc[0] - '0');
/* MNC digit 3 | MCC digit 3 */
*ipos++ = (((mnc_len == 2) ? 0xf0 : ((mnc[2] - '0') << 4))) |
(mcc[2] - '0');
/* MNC digit 2 | MNC digit 1 */
*ipos++ = ((mnc[1] - '0') << 4) | (mnc[0] - '0');
}
os_free(bss->anqp_3gpp_cell_net);
bss->anqp_3gpp_cell_net = info;
bss->anqp_3gpp_cell_net_len = 2 + 3 + 3 * count;
wpa_hexdump(MSG_MSGDUMP, "3GPP Cellular Network information",
bss->anqp_3gpp_cell_net, bss->anqp_3gpp_cell_net_len);
return 0;
fail:
wpa_printf(MSG_ERROR, "Line %d: Invalid anqp_3gpp_cell_net: %s",
line, buf);
os_free(info);
return -1;
}
static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line)
{
struct hostapd_nai_realm_data *realm;
size_t i, j, len;
int *offsets;
char *pos, *end, *rpos;
offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS,
sizeof(int));
if (offsets == NULL)
return -1;
for (i = 0; i < bss->nai_realm_count; i++) {
realm = &bss->nai_realm_data[i];
for (j = 0; j < MAX_NAI_REALMS; j++) {
offsets[i * MAX_NAI_REALMS + j] =
realm->realm[j] ?
realm->realm[j] - realm->realm_buf : -1;
}
}
realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1,
sizeof(struct hostapd_nai_realm_data));
if (realm == NULL) {
os_free(offsets);
return -1;
}
bss->nai_realm_data = realm;
/* patch the pointers after realloc */
for (i = 0; i < bss->nai_realm_count; i++) {
realm = &bss->nai_realm_data[i];
for (j = 0; j < MAX_NAI_REALMS; j++) {
int offs = offsets[i * MAX_NAI_REALMS + j];
if (offs >= 0)
realm->realm[j] = realm->realm_buf + offs;
else
realm->realm[j] = NULL;
}
}
os_free(offsets);
realm = &bss->nai_realm_data[bss->nai_realm_count];
os_memset(realm, 0, sizeof(*realm));
pos = buf;
realm->encoding = atoi(pos);
pos = os_strchr(pos, ',');
if (pos == NULL)
goto fail;
pos++;
end = os_strchr(pos, ',');
if (end) {
len = end - pos;
*end = '\0';
} else {
len = os_strlen(pos);
}
if (len > MAX_NAI_REALMLEN) {
wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d "
"characters)", (int) len, MAX_NAI_REALMLEN);
goto fail;
}
os_memcpy(realm->realm_buf, pos, len);
if (end)
pos = end + 1;
else
pos = NULL;
while (pos && *pos) {
struct hostapd_nai_realm_eap *eap;
if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) {
wpa_printf(MSG_ERROR, "Too many EAP methods");
goto fail;
}
eap = &realm->eap_method[realm->eap_method_count];
realm->eap_method_count++;
end = os_strchr(pos, ',');
if (end == NULL)
end = pos + os_strlen(pos);
eap->eap_method = atoi(pos);
for (;;) {
pos = os_strchr(pos, '[');
if (pos == NULL || pos > end)
break;
pos++;
if (eap->num_auths >= MAX_NAI_AUTH_TYPES) {
wpa_printf(MSG_ERROR, "Too many auth params");
goto fail;
}
eap->auth_id[eap->num_auths] = atoi(pos);
pos = os_strchr(pos, ':');
if (pos == NULL || pos > end)
goto fail;
pos++;
eap->auth_val[eap->num_auths] = atoi(pos);
pos = os_strchr(pos, ']');
if (pos == NULL || pos > end)
goto fail;
pos++;
eap->num_auths++;
}
if (*end != ',')
break;
pos = end + 1;
}
/* Split realm list into null terminated realms */
rpos = realm->realm_buf;
i = 0;
while (*rpos) {
if (i >= MAX_NAI_REALMS) {
wpa_printf(MSG_ERROR, "Too many realms");
goto fail;
}
realm->realm[i++] = rpos;
rpos = os_strchr(rpos, ';');
if (rpos == NULL)
break;
*rpos++ = '\0';
}
bss->nai_realm_count++;
return 0;
fail:
wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf);
return -1;
}
static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line)
{
char *delim;
u16 infoid;
size_t len;
struct wpabuf *payload;
struct anqp_element *elem;
delim = os_strchr(buf, ':');
if (!delim)
return -1;
delim++;
infoid = atoi(buf);
len = os_strlen(delim);
if (len & 1)
return -1;
len /= 2;
payload = wpabuf_alloc(len);
if (!payload)
return -1;
if (hexstr2bin(delim, wpabuf_put(payload, len), len) < 0) {
wpabuf_free(payload);
return -1;
}
dl_list_for_each(elem, &bss->anqp_elem, struct anqp_element, list) {
if (elem->infoid == infoid) {
/* Update existing entry */
wpabuf_free(elem->payload);
elem->payload = payload;
return 0;
}
}
/* Add a new entry */
elem = os_zalloc(sizeof(*elem));
if (!elem) {
wpabuf_free(payload);
return -1;
}
elem->infoid = infoid;
elem->payload = payload;
dl_list_add(&bss->anqp_elem, &elem->list);
return 0;
}
static int parse_qos_map_set(struct hostapd_bss_config *bss,
char *buf, int line)
{
u8 qos_map_set[16 + 2 * 21], count = 0;
char *pos = buf;
int val;
for (;;) {
if (count == sizeof(qos_map_set)) {
wpa_printf(MSG_ERROR, "Line %d: Too many qos_map_set "
"parameters '%s'", line, buf);
return -1;
}
val = atoi(pos);
if (val > 255 || val < 0) {
wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set "
"'%s'", line, buf);
return -1;
}
qos_map_set[count++] = val;
pos = os_strchr(pos, ',');
if (!pos)
break;
pos++;
}
if (count < 16 || count & 1) {
wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set '%s'",
line, buf);
return -1;
}
os_memcpy(bss->qos_map_set, qos_map_set, count);
bss->qos_map_set_len = count;
return 0;
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf,
int line)
{
u8 *conn_cap;
char *pos;
if (bss->hs20_connection_capability_len >= 0xfff0)
return -1;
conn_cap = os_realloc(bss->hs20_connection_capability,
bss->hs20_connection_capability_len + 4);
if (conn_cap == NULL)
return -1;
bss->hs20_connection_capability = conn_cap;
conn_cap += bss->hs20_connection_capability_len;
pos = buf;
conn_cap[0] = atoi(pos);
pos = os_strchr(pos, ':');
if (pos == NULL)
return -1;
pos++;
WPA_PUT_LE16(conn_cap + 1, atoi(pos));
pos = os_strchr(pos, ':');
if (pos == NULL)
return -1;
pos++;
conn_cap[3] = atoi(pos);
bss->hs20_connection_capability_len += 4;
return 0;
}
static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf,
int line)
{
u8 *wan_metrics;
char *pos;
/* <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD> */
wan_metrics = os_zalloc(13);
if (wan_metrics == NULL)
return -1;
pos = buf;
/* WAN Info */
if (hexstr2bin(pos, wan_metrics, 1) < 0)
goto fail;
pos += 2;
if (*pos != ':')
goto fail;
pos++;
/* Downlink Speed */
WPA_PUT_LE32(wan_metrics + 1, atoi(pos));
pos = os_strchr(pos, ':');
if (pos == NULL)
goto fail;
pos++;
/* Uplink Speed */
WPA_PUT_LE32(wan_metrics + 5, atoi(pos));
pos = os_strchr(pos, ':');
if (pos == NULL)
goto fail;
pos++;
/* Downlink Load */
wan_metrics[9] = atoi(pos);
pos = os_strchr(pos, ':');
if (pos == NULL)
goto fail;
pos++;
/* Uplink Load */
wan_metrics[10] = atoi(pos);
pos = os_strchr(pos, ':');
if (pos == NULL)
goto fail;
pos++;
/* LMD */
WPA_PUT_LE16(wan_metrics + 11, atoi(pos));
os_free(bss->hs20_wan_metrics);
bss->hs20_wan_metrics = wan_metrics;
return 0;
fail:
wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'",
line, buf);
os_free(wan_metrics);
return -1;
}
static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss,
char *pos, int line)
{
if (parse_lang_string(&bss->hs20_oper_friendly_name,
&bss->hs20_oper_friendly_name_count, pos)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid "
"hs20_oper_friendly_name '%s'", line, pos);
return -1;
}
return 0;
}
static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos)
{
struct hs20_icon *icon;
char *end;
icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1,
sizeof(struct hs20_icon));
if (icon == NULL)
return -1;
bss->hs20_icons = icon;
icon = &bss->hs20_icons[bss->hs20_icons_count];
os_memset(icon, 0, sizeof(*icon));
icon->width = atoi(pos);
pos = os_strchr(pos, ':');
if (pos == NULL)
return -1;
pos++;
icon->height = atoi(pos);
pos = os_strchr(pos, ':');
if (pos == NULL)
return -1;
pos++;
end = os_strchr(pos, ':');
if (end == NULL || end - pos > 3)
return -1;
os_memcpy(icon->language, pos, end - pos);
pos = end + 1;
end = os_strchr(pos, ':');
if (end == NULL || end - pos > 255)
return -1;
os_memcpy(icon->type, pos, end - pos);
pos = end + 1;
end = os_strchr(pos, ':');
if (end == NULL || end - pos > 255)
return -1;
os_memcpy(icon->name, pos, end - pos);
pos = end + 1;
if (os_strlen(pos) > 255)
return -1;
os_memcpy(icon->file, pos, os_strlen(pos));
bss->hs20_icons_count++;
return 0;
}
static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss,
char *pos, int line)
{
size_t slen;
char *str;
str = wpa_config_parse_string(pos, &slen);
if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) {
wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
os_free(str);
return -1;
}
os_memcpy(bss->osu_ssid, str, slen);
bss->osu_ssid_len = slen;
os_free(str);
return 0;
}
static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss,
char *pos, int line)
{
struct hs20_osu_provider *p;
p = os_realloc_array(bss->hs20_osu_providers,
bss->hs20_osu_providers_count + 1, sizeof(*p));
if (p == NULL)
return -1;
bss->hs20_osu_providers = p;
bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count];
bss->hs20_osu_providers_count++;
os_memset(bss->last_osu, 0, sizeof(*p));
bss->last_osu->server_uri = os_strdup(pos);
return 0;
}
static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss,
char *pos, int line)
{
if (bss->last_osu == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
if (parse_lang_string(&bss->last_osu->friendly_name,
&bss->last_osu->friendly_name_count, pos)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'",
line, pos);
return -1;
}
return 0;
}
static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
char *pos, int line)
{
if (bss->last_osu == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
os_free(bss->last_osu->osu_nai);
bss->last_osu->osu_nai = os_strdup(pos);
if (bss->last_osu->osu_nai == NULL)
return -1;
return 0;
}
static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss,
char *pos, int line)
{
if (bss->last_osu == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
os_free(bss->last_osu->osu_nai2);
bss->last_osu->osu_nai2 = os_strdup(pos);
if (bss->last_osu->osu_nai2 == NULL)
return -1;
bss->hs20_osu_providers_nai_count++;
return 0;
}
static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
int line)
{
if (bss->last_osu == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line);
return -1;
}
return 0;
}
static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos,
int line)
{
char **n;
struct hs20_osu_provider *p = bss->last_osu;
if (p == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *));
if (n == NULL)
return -1;
p->icons = n;
p->icons[p->icons_count] = os_strdup(pos);
if (p->icons[p->icons_count] == NULL)
return -1;
p->icons_count++;
return 0;
}
static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss,
char *pos, int line)
{
if (bss->last_osu == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
return -1;
}
if (parse_lang_string(&bss->last_osu->service_desc,
&bss->last_osu->service_desc_count, pos)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'",
line, pos);
return -1;
}
return 0;
}
static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos,
int line)
{
char **n;
n = os_realloc_array(bss->hs20_operator_icon,
bss->hs20_operator_icon_count + 1, sizeof(char *));
if (!n)
return -1;
bss->hs20_operator_icon = n;
bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos);
if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count])
return -1;
bss->hs20_operator_icon_count++;
return 0;
}
#endif /* CONFIG_HS20 */
#ifdef CONFIG_ACS
static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf,
char *pos)
{
struct acs_bias *bias = NULL, *tmp;
unsigned int num = 0;
char *end;
while (*pos) {
tmp = os_realloc_array(bias, num + 1, sizeof(*bias));
if (!tmp)
goto fail;
bias = tmp;
bias[num].channel = atoi(pos);
if (bias[num].channel <= 0)
goto fail;
pos = os_strchr(pos, ':');
if (!pos)
goto fail;
pos++;
bias[num].bias = strtod(pos, &end);
if (end == pos || bias[num].bias < 0.0)
goto fail;
pos = end;
if (*pos != ' ' && *pos != '\0')
goto fail;
num++;
}
os_free(conf->acs_chan_bias);
conf->acs_chan_bias = bias;
conf->num_acs_chan_bias = num;
return 0;
fail:
os_free(bias);
return -1;
}
#endif /* CONFIG_ACS */
static int parse_wpabuf_hex(int line, const char *name, struct wpabuf **buf,
const char *val)
{
struct wpabuf *elems;
if (val[0] == '\0') {
wpabuf_free(*buf);
*buf = NULL;
return 0;
}
elems = wpabuf_parse_bin(val);
if (!elems) {
wpa_printf(MSG_ERROR, "Line %d: Invalid %s '%s'",
line, name, val);
return -1;
}
wpabuf_free(*buf);
*buf = elems;
return 0;
}
#ifdef CONFIG_FILS
static int parse_fils_realm(struct hostapd_bss_config *bss, const char *val)
{
struct fils_realm *realm;
size_t len;
len = os_strlen(val);
realm = os_zalloc(sizeof(*realm) + len + 1);
if (!realm)
return -1;
os_memcpy(realm->realm, val, len);
if (fils_domain_name_hash(val, realm->hash) < 0) {
os_free(realm);
return -1;
}
dl_list_add_tail(&bss->fils_realms, &realm->list);
return 0;
}
#endif /* CONFIG_FILS */
#ifdef EAP_SERVER
static unsigned int parse_tls_flags(const char *val)
{
unsigned int flags = 0;
/* Disable TLS v1.3 by default for now to avoid interoperability issue.
* This can be enabled by default once the implementation has been fully
* completed and tested with other implementations. */
flags |= TLS_CONN_DISABLE_TLSv1_3;
if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]"))
flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
if (os_strstr(val, "[DISABLE-TIME-CHECKS]"))
flags |= TLS_CONN_DISABLE_TIME_CHECKS;
if (os_strstr(val, "[DISABLE-TLSv1.0]"))
flags |= TLS_CONN_DISABLE_TLSv1_0;
if (os_strstr(val, "[ENABLE-TLSv1.0]"))
flags |= TLS_CONN_ENABLE_TLSv1_0;
if (os_strstr(val, "[DISABLE-TLSv1.1]"))
flags |= TLS_CONN_DISABLE_TLSv1_1;
if (os_strstr(val, "[ENABLE-TLSv1.1]"))
flags |= TLS_CONN_ENABLE_TLSv1_1;
if (os_strstr(val, "[DISABLE-TLSv1.2]"))
flags |= TLS_CONN_DISABLE_TLSv1_2;
if (os_strstr(val, "[ENABLE-TLSv1.2]"))
flags |= TLS_CONN_ENABLE_TLSv1_2;
if (os_strstr(val, "[DISABLE-TLSv1.3]"))
flags |= TLS_CONN_DISABLE_TLSv1_3;
if (os_strstr(val, "[ENABLE-TLSv1.3]"))
flags &= ~TLS_CONN_DISABLE_TLSv1_3;
if (os_strstr(val, "[SUITEB]"))
flags |= TLS_CONN_SUITEB;
if (os_strstr(val, "[SUITEB-NO-ECDH]"))
flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB;
return flags;
}
#endif /* EAP_SERVER */
#ifdef CONFIG_AIRTIME_POLICY
static int add_airtime_weight(struct hostapd_bss_config *bss, char *value)
{
struct airtime_sta_weight *wt;
char *pos, *next;
wt = os_zalloc(sizeof(*wt));
if (!wt)
return -1;
/* 02:01:02:03:04:05 10 */
pos = value;
next = os_strchr(pos, ' ');
if (next)
*next++ = '\0';
if (!next || hwaddr_aton(pos, wt->addr)) {
wpa_printf(MSG_ERROR, "Invalid station address: '%s'", pos);
os_free(wt);
return -1;
}
pos = next;
wt->weight = atoi(pos);
if (!wt->weight) {
wpa_printf(MSG_ERROR, "Invalid weight: '%s'", pos);
os_free(wt);
return -1;
}
wt->next = bss->airtime_weight_list;
bss->airtime_weight_list = wt;
return 0;
}
#endif /* CONFIG_AIRTIME_POLICY */
#ifdef CONFIG_SAE
static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
{
struct sae_password_entry *pw;
const char *pos = val, *pos2, *end = NULL;
pw = os_zalloc(sizeof(*pw));
if (!pw)
return -1;
os_memset(pw->peer_addr, 0xff, ETH_ALEN); /* default to wildcard */
pos2 = os_strstr(pos, "|mac=");
if (pos2) {
end = pos2;
pos2 += 5;
if (hwaddr_aton(pos2, pw->peer_addr) < 0)
goto fail;
pos = pos2 + ETH_ALEN * 3 - 1;
}
pos2 = os_strstr(pos, "|vlanid=");
if (pos2) {
if (!end)
end = pos2;
pos2 += 8;
pw->vlan_id = atoi(pos2);
}
#ifdef CONFIG_SAE_PK
pos2 = os_strstr(pos, "|pk=");
if (pos2) {
const char *epos;
char *tmp;
if (!end)
end = pos2;
pos2 += 4;
epos = os_strchr(pos2, '|');
if (epos) {
tmp = os_malloc(epos - pos2 + 1);
if (!tmp)
goto fail;
os_memcpy(tmp, pos2, epos - pos2);
tmp[epos - pos2] = '\0';
} else {
tmp = os_strdup(pos2);
if (!tmp)
goto fail;
}
pw->pk = sae_parse_pk(tmp);
str_clear_free(tmp);
if (!pw->pk)
goto fail;
}
#endif /* CONFIG_SAE_PK */
pos2 = os_strstr(pos, "|id=");
if (pos2) {
if (!end)
end = pos2;
pos2 += 4;
pw->identifier = os_strdup(pos2);
if (!pw->identifier)
goto fail;
}
if (!end) {
pw->password = os_strdup(val);
if (!pw->password)
goto fail;
} else {
pw->password = os_malloc(end - val + 1);
if (!pw->password)
goto fail;
os_memcpy(pw->password, val, end - val);
pw->password[end - val] = '\0';
}
#ifdef CONFIG_SAE_PK
if (pw->pk &&
#ifdef CONFIG_TESTING_OPTIONS
!bss->sae_pk_password_check_skip &&
#endif /* CONFIG_TESTING_OPTIONS */
!sae_pk_valid_password(pw->password)) {
wpa_printf(MSG_INFO,
"Invalid SAE password for a SAE-PK sae_password entry");
goto fail;
}
#endif /* CONFIG_SAE_PK */
pw->next = bss->sae_passwords;
bss->sae_passwords = pw;
return 0;
fail:
str_clear_free(pw->password);
os_free(pw->identifier);
#ifdef CONFIG_SAE_PK
sae_deinit_pk(pw->pk);
#endif /* CONFIG_SAE_PK */
os_free(pw);
return -1;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_DPP2
static int hostapd_dpp_controller_parse(struct hostapd_bss_config *bss,
const char *pos)
{
struct dpp_controller_conf *conf;
char *val;
conf = os_zalloc(sizeof(*conf));
if (!conf)
return -1;
val = get_param(pos, "ipaddr=");
if (!val || hostapd_parse_ip_addr(val, &conf->ipaddr))
goto fail;
os_free(val);
val = get_param(pos, "pkhash=");
if (!val || os_strlen(val) != 2 * SHA256_MAC_LEN ||
hexstr2bin(val, conf->pkhash, SHA256_MAC_LEN) < 0)
goto fail;
os_free(val);
conf->next = bss->dpp_controller;
bss->dpp_controller = conf;
return 0;
fail:
os_free(val);
os_free(conf);
return -1;
}
#endif /* CONFIG_DPP2 */
static int get_hex_config(u8 *buf, size_t max_len, int line,
const char *field, const char *val)
{
size_t hlen = os_strlen(val), len = hlen / 2;
u8 tmp[EXT_CAPA_MAX_LEN];
os_memset(tmp, 0, EXT_CAPA_MAX_LEN);
if (hlen & 1 || len > EXT_CAPA_MAX_LEN || hexstr2bin(val, tmp, len)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid %s", line, field);
return -1;
}
os_memcpy(buf, tmp, EXT_CAPA_MAX_LEN);
return 0;
}
static int hostapd_config_fill(struct hostapd_config *conf,
struct hostapd_bss_config *bss,
const char *buf, char *pos, int line)
{
if (os_strcmp(buf, "interface") == 0) {
os_strlcpy(conf->bss[0]->iface, pos,
sizeof(conf->bss[0]->iface));
} else if (os_strcmp(buf, "bridge") == 0) {
os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
} else if (os_strcmp(buf, "vlan_bridge") == 0) {
os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
} else if (os_strcmp(buf, "wds_bridge") == 0) {
os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
} else if (os_strcmp(buf, "driver") == 0) {
int j;
const struct wpa_driver_ops *driver = NULL;
for (j = 0; wpa_drivers[j]; j++) {
if (os_strcmp(pos, wpa_drivers[j]->name) == 0) {
driver = wpa_drivers[j];
break;
}
}
if (!driver) {
wpa_printf(MSG_ERROR,
"Line %d: invalid/unknown driver '%s'",
line, pos);
return 1;
}
conf->driver = driver;
} else if (os_strcmp(buf, "driver_params") == 0) {
os_free(conf->driver_params);
conf->driver_params = os_strdup(pos);
} else if (os_strcmp(buf, "debug") == 0) {
wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' configuration variable is not used anymore",
line);
} else if (os_strcmp(buf, "logger_syslog_level") == 0) {
bss->logger_syslog_level = atoi(pos);
} else if (os_strcmp(buf, "logger_stdout_level") == 0) {
bss->logger_stdout_level = atoi(pos);
} else if (os_strcmp(buf, "logger_syslog") == 0) {
bss->logger_syslog = atoi(pos);
} else if (os_strcmp(buf, "logger_stdout") == 0) {
bss->logger_stdout = atoi(pos);
} else if (os_strcmp(buf, "dump_file") == 0) {
wpa_printf(MSG_INFO, "Line %d: DEPRECATED: 'dump_file' configuration variable is not used anymore",
line);
} else if (os_strcmp(buf, "ssid") == 0) {
- bss->ssid.ssid_len = os_strlen(pos);
- if (bss->ssid.ssid_len > SSID_MAX_LEN ||
- bss->ssid.ssid_len < 1) {
+ struct hostapd_ssid *ssid = &bss->ssid;
+
+ ssid->ssid_len = os_strlen(pos);
+ if (ssid->ssid_len > SSID_MAX_LEN || ssid->ssid_len < 1) {
wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
line, pos);
return 1;
}
- os_memcpy(bss->ssid.ssid, pos, bss->ssid.ssid_len);
- bss->ssid.ssid_set = 1;
+ os_memcpy(ssid->ssid, pos, ssid->ssid_len);
+ ssid->ssid_set = 1;
+ ssid->short_ssid = crc32(ssid->ssid, ssid->ssid_len);
} else if (os_strcmp(buf, "ssid2") == 0) {
+ struct hostapd_ssid *ssid = &bss->ssid;
size_t slen;
char *str = wpa_config_parse_string(pos, &slen);
if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) {
wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
line, pos);
os_free(str);
return 1;
}
- os_memcpy(bss->ssid.ssid, str, slen);
- bss->ssid.ssid_len = slen;
- bss->ssid.ssid_set = 1;
+ os_memcpy(ssid->ssid, str, slen);
+ ssid->ssid_len = slen;
+ ssid->ssid_set = 1;
+ ssid->short_ssid = crc32(ssid->ssid, ssid->ssid_len);
os_free(str);
} else if (os_strcmp(buf, "utf8_ssid") == 0) {
bss->ssid.utf8_ssid = atoi(pos) > 0;
} else if (os_strcmp(buf, "macaddr_acl") == 0) {
enum macaddr_acl acl = atoi(pos);
if (acl != ACCEPT_UNLESS_DENIED &&
acl != DENY_UNLESS_ACCEPTED &&
acl != USE_EXTERNAL_RADIUS_AUTH) {
wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d",
line, acl);
return 1;
}
bss->macaddr_acl = acl;
} else if (os_strcmp(buf, "accept_mac_file") == 0) {
if (hostapd_config_read_maclist(pos, &bss->accept_mac,
&bss->num_accept_mac)) {
wpa_printf(MSG_ERROR, "Line %d: Failed to read accept_mac_file '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "deny_mac_file") == 0) {
if (hostapd_config_read_maclist(pos, &bss->deny_mac,
&bss->num_deny_mac)) {
wpa_printf(MSG_ERROR, "Line %d: Failed to read deny_mac_file '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "wds_sta") == 0) {
bss->wds_sta = atoi(pos);
} else if (os_strcmp(buf, "start_disabled") == 0) {
bss->start_disabled = atoi(pos);
} else if (os_strcmp(buf, "ap_isolate") == 0) {
bss->isolate = atoi(pos);
} else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
bss->ap_max_inactivity = atoi(pos);
} else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
bss->skip_inactivity_poll = atoi(pos);
} else if (os_strcmp(buf, "country_code") == 0) {
if (pos[0] < 'A' || pos[0] > 'Z' ||
pos[1] < 'A' || pos[1] > 'Z') {
wpa_printf(MSG_ERROR,
"Line %d: Invalid country_code '%s'",
line, pos);
return 1;
}
os_memcpy(conf->country, pos, 2);
} else if (os_strcmp(buf, "country3") == 0) {
conf->country[2] = strtol(pos, NULL, 16);
} else if (os_strcmp(buf, "ieee80211d") == 0) {
conf->ieee80211d = atoi(pos);
} else if (os_strcmp(buf, "ieee80211h") == 0) {
conf->ieee80211h = atoi(pos);
} else if (os_strcmp(buf, "ieee8021x") == 0) {
bss->ieee802_1x = atoi(pos);
} else if (os_strcmp(buf, "eapol_version") == 0) {
int eapol_version = atoi(pos);
#ifdef CONFIG_MACSEC
int max_ver = 3;
#else /* CONFIG_MACSEC */
int max_ver = 2;
#endif /* CONFIG_MACSEC */
if (eapol_version < 1 || eapol_version > max_ver) {
wpa_printf(MSG_ERROR,
"Line %d: invalid EAPOL version (%d): '%s'.",
line, eapol_version, pos);
return 1;
}
bss->eapol_version = eapol_version;
wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version);
#ifdef EAP_SERVER
} else if (os_strcmp(buf, "eap_authenticator") == 0) {
bss->eap_server = atoi(pos);
wpa_printf(MSG_ERROR, "Line %d: obsolete eap_authenticator used; this has been renamed to eap_server", line);
} else if (os_strcmp(buf, "eap_server") == 0) {
bss->eap_server = atoi(pos);
} else if (os_strcmp(buf, "eap_user_file") == 0) {
if (hostapd_config_read_eap_user(pos, bss))
return 1;
} else if (os_strcmp(buf, "ca_cert") == 0) {
os_free(bss->ca_cert);
bss->ca_cert = os_strdup(pos);
} else if (os_strcmp(buf, "server_cert") == 0) {
os_free(bss->server_cert);
bss->server_cert = os_strdup(pos);
} else if (os_strcmp(buf, "server_cert2") == 0) {
os_free(bss->server_cert2);
bss->server_cert2 = os_strdup(pos);
} else if (os_strcmp(buf, "private_key") == 0) {
os_free(bss->private_key);
bss->private_key = os_strdup(pos);
} else if (os_strcmp(buf, "private_key2") == 0) {
os_free(bss->private_key2);
bss->private_key2 = os_strdup(pos);
} else if (os_strcmp(buf, "private_key_passwd") == 0) {
os_free(bss->private_key_passwd);
bss->private_key_passwd = os_strdup(pos);
} else if (os_strcmp(buf, "private_key_passwd2") == 0) {
os_free(bss->private_key_passwd2);
bss->private_key_passwd2 = os_strdup(pos);
} else if (os_strcmp(buf, "check_cert_subject") == 0) {
if (!pos[0]) {
wpa_printf(MSG_ERROR, "Line %d: unknown check_cert_subject '%s'",
line, pos);
return 1;
}
os_free(bss->check_cert_subject);
bss->check_cert_subject = os_strdup(pos);
if (!bss->check_cert_subject)
return 1;
} else if (os_strcmp(buf, "check_crl") == 0) {
bss->check_crl = atoi(pos);
} else if (os_strcmp(buf, "check_crl_strict") == 0) {
bss->check_crl_strict = atoi(pos);
} else if (os_strcmp(buf, "crl_reload_interval") == 0) {
bss->crl_reload_interval = atoi(pos);
} else if (os_strcmp(buf, "tls_session_lifetime") == 0) {
bss->tls_session_lifetime = atoi(pos);
} else if (os_strcmp(buf, "tls_flags") == 0) {
bss->tls_flags = parse_tls_flags(pos);
} else if (os_strcmp(buf, "max_auth_rounds") == 0) {
bss->max_auth_rounds = atoi(pos);
} else if (os_strcmp(buf, "max_auth_rounds_short") == 0) {
bss->max_auth_rounds_short = atoi(pos);
} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
os_free(bss->ocsp_stapling_response);
bss->ocsp_stapling_response = os_strdup(pos);
} else if (os_strcmp(buf, "ocsp_stapling_response_multi") == 0) {
os_free(bss->ocsp_stapling_response_multi);
bss->ocsp_stapling_response_multi = os_strdup(pos);
} else if (os_strcmp(buf, "dh_file") == 0) {
os_free(bss->dh_file);
bss->dh_file = os_strdup(pos);
} else if (os_strcmp(buf, "openssl_ciphers") == 0) {
os_free(bss->openssl_ciphers);
bss->openssl_ciphers = os_strdup(pos);
} else if (os_strcmp(buf, "openssl_ecdh_curves") == 0) {
os_free(bss->openssl_ecdh_curves);
bss->openssl_ecdh_curves = os_strdup(pos);
} else if (os_strcmp(buf, "fragment_size") == 0) {
bss->fragment_size = atoi(pos);
#ifdef EAP_SERVER_FAST
} else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) {
os_free(bss->pac_opaque_encr_key);
bss->pac_opaque_encr_key = os_malloc(16);
if (bss->pac_opaque_encr_key == NULL) {
wpa_printf(MSG_ERROR,
"Line %d: No memory for pac_opaque_encr_key",
line);
return 1;
} else if (hexstr2bin(pos, bss->pac_opaque_encr_key, 16)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid pac_opaque_encr_key",
line);
return 1;
}
} else if (os_strcmp(buf, "eap_fast_a_id") == 0) {
size_t idlen = os_strlen(pos);
if (idlen & 1) {
wpa_printf(MSG_ERROR, "Line %d: Invalid eap_fast_a_id",
line);
return 1;
}
os_free(bss->eap_fast_a_id);
bss->eap_fast_a_id = os_malloc(idlen / 2);
if (bss->eap_fast_a_id == NULL ||
hexstr2bin(pos, bss->eap_fast_a_id, idlen / 2)) {
wpa_printf(MSG_ERROR, "Line %d: Failed to parse eap_fast_a_id",
line);
os_free(bss->eap_fast_a_id);
bss->eap_fast_a_id = NULL;
return 1;
} else {
bss->eap_fast_a_id_len = idlen / 2;
}
} else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) {
os_free(bss->eap_fast_a_id_info);
bss->eap_fast_a_id_info = os_strdup(pos);
} else if (os_strcmp(buf, "eap_fast_prov") == 0) {
bss->eap_fast_prov = atoi(pos);
} else if (os_strcmp(buf, "pac_key_lifetime") == 0) {
bss->pac_key_lifetime = atoi(pos);
} else if (os_strcmp(buf, "pac_key_refresh_time") == 0) {
bss->pac_key_refresh_time = atoi(pos);
#endif /* EAP_SERVER_FAST */
#ifdef EAP_SERVER_TEAP
} else if (os_strcmp(buf, "eap_teap_auth") == 0) {
int val = atoi(pos);
if (val < 0 || val > 2) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid eap_teap_auth value",
line);
return 1;
}
bss->eap_teap_auth = val;
} else if (os_strcmp(buf, "eap_teap_pac_no_inner") == 0) {
bss->eap_teap_pac_no_inner = atoi(pos);
} else if (os_strcmp(buf, "eap_teap_separate_result") == 0) {
bss->eap_teap_separate_result = atoi(pos);
} else if (os_strcmp(buf, "eap_teap_id") == 0) {
bss->eap_teap_id = atoi(pos);
#endif /* EAP_SERVER_TEAP */
#ifdef EAP_SERVER_SIM
} else if (os_strcmp(buf, "eap_sim_db") == 0) {
os_free(bss->eap_sim_db);
bss->eap_sim_db = os_strdup(pos);
} else if (os_strcmp(buf, "eap_sim_db_timeout") == 0) {
bss->eap_sim_db_timeout = atoi(pos);
} else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
bss->eap_sim_aka_result_ind = atoi(pos);
} else if (os_strcmp(buf, "eap_sim_id") == 0) {
bss->eap_sim_id = atoi(pos);
#endif /* EAP_SERVER_SIM */
#ifdef EAP_SERVER_TNC
} else if (os_strcmp(buf, "tnc") == 0) {
bss->tnc = atoi(pos);
#endif /* EAP_SERVER_TNC */
#ifdef EAP_SERVER_PWD
} else if (os_strcmp(buf, "pwd_group") == 0) {
bss->pwd_group = atoi(pos);
#endif /* EAP_SERVER_PWD */
#ifdef CONFIG_ERP
} else if (os_strcmp(buf, "eap_server_erp") == 0) {
bss->eap_server_erp = atoi(pos);
#endif /* CONFIG_ERP */
#endif /* EAP_SERVER */
} else if (os_strcmp(buf, "eap_message") == 0) {
char *term;
os_free(bss->eap_req_id_text);
bss->eap_req_id_text = os_strdup(pos);
if (bss->eap_req_id_text == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Failed to allocate memory for eap_req_id_text",
line);
return 1;
}
bss->eap_req_id_text_len = os_strlen(bss->eap_req_id_text);
term = os_strstr(bss->eap_req_id_text, "\\0");
if (term) {
*term++ = '\0';
os_memmove(term, term + 1,
bss->eap_req_id_text_len -
(term - bss->eap_req_id_text) - 1);
bss->eap_req_id_text_len--;
}
} else if (os_strcmp(buf, "erp_send_reauth_start") == 0) {
bss->erp_send_reauth_start = atoi(pos);
} else if (os_strcmp(buf, "erp_domain") == 0) {
os_free(bss->erp_domain);
bss->erp_domain = os_strdup(pos);
#ifdef CONFIG_WEP
} else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
int val = atoi(pos);
if (val < 0 || val > 13) {
wpa_printf(MSG_ERROR,
"Line %d: invalid WEP key len %d (= %d bits)",
line, val, val * 8);
return 1;
}
bss->default_wep_key_len = val;
} else if (os_strcmp(buf, "wep_key_len_unicast") == 0) {
int val = atoi(pos);
if (val < 0 || val > 13) {
wpa_printf(MSG_ERROR,
"Line %d: invalid WEP key len %d (= %d bits)",
line, val, val * 8);
return 1;
}
bss->individual_wep_key_len = val;
} else if (os_strcmp(buf, "wep_rekey_period") == 0) {
bss->wep_rekeying_period = atoi(pos);
if (bss->wep_rekeying_period < 0) {
wpa_printf(MSG_ERROR, "Line %d: invalid period %d",
line, bss->wep_rekeying_period);
return 1;
}
#endif /* CONFIG_WEP */
} else if (os_strcmp(buf, "eap_reauth_period") == 0) {
bss->eap_reauth_period = atoi(pos);
if (bss->eap_reauth_period < 0) {
wpa_printf(MSG_ERROR, "Line %d: invalid period %d",
line, bss->eap_reauth_period);
return 1;
}
} else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) {
bss->eapol_key_index_workaround = atoi(pos);
#ifdef CONFIG_IAPP
} else if (os_strcmp(buf, "iapp_interface") == 0) {
wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
#endif /* CONFIG_IAPP */
} else if (os_strcmp(buf, "own_ip_addr") == 0) {
if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
wpa_printf(MSG_ERROR,
"Line %d: invalid IP address '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "nas_identifier") == 0) {
os_free(bss->nas_identifier);
bss->nas_identifier = os_strdup(pos);
#ifndef CONFIG_NO_RADIUS
} else if (os_strcmp(buf, "radius_client_addr") == 0) {
if (hostapd_parse_ip_addr(pos, &bss->radius->client_addr)) {
wpa_printf(MSG_ERROR,
"Line %d: invalid IP address '%s'",
line, pos);
return 1;
}
bss->radius->force_client_addr = 1;
} else if (os_strcmp(buf, "radius_client_dev") == 0) {
os_free(bss->radius->force_client_dev);
bss->radius->force_client_dev = os_strdup(pos);
} else if (os_strcmp(buf, "auth_server_addr") == 0) {
if (hostapd_config_read_radius_addr(
&bss->radius->auth_servers,
&bss->radius->num_auth_servers, pos, 1812,
&bss->radius->auth_server)) {
wpa_printf(MSG_ERROR,
"Line %d: invalid IP address '%s'",
line, pos);
return 1;
}
} else if (bss->radius->auth_server &&
os_strcmp(buf, "auth_server_addr_replace") == 0) {
if (hostapd_parse_ip_addr(pos,
&bss->radius->auth_server->addr)) {
wpa_printf(MSG_ERROR,
"Line %d: invalid IP address '%s'",
line, pos);
return 1;
}
} else if (bss->radius->auth_server &&
os_strcmp(buf, "auth_server_port") == 0) {
bss->radius->auth_server->port = atoi(pos);
} else if (bss->radius->auth_server &&
os_strcmp(buf, "auth_server_shared_secret") == 0) {
int len = os_strlen(pos);
if (len == 0) {
/* RFC 2865, Ch. 3 */
wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed",
line);
return 1;
}
os_free(bss->radius->auth_server->shared_secret);
bss->radius->auth_server->shared_secret = (u8 *) os_strdup(pos);
bss->radius->auth_server->shared_secret_len = len;
} else if (os_strcmp(buf, "acct_server_addr") == 0) {
if (hostapd_config_read_radius_addr(
&bss->radius->acct_servers,
&bss->radius->num_acct_servers, pos, 1813,
&bss->radius->acct_server)) {
wpa_printf(MSG_ERROR,
"Line %d: invalid IP address '%s'",
line, pos);
return 1;
}
} else if (bss->radius->acct_server &&
os_strcmp(buf, "acct_server_addr_replace") == 0) {
if (hostapd_parse_ip_addr(pos,
&bss->radius->acct_server->addr)) {
wpa_printf(MSG_ERROR,
"Line %d: invalid IP address '%s'",
line, pos);
return 1;
}
} else if (bss->radius->acct_server &&
os_strcmp(buf, "acct_server_port") == 0) {
bss->radius->acct_server->port = atoi(pos);
} else if (bss->radius->acct_server &&
os_strcmp(buf, "acct_server_shared_secret") == 0) {
int len = os_strlen(pos);
if (len == 0) {
/* RFC 2865, Ch. 3 */
wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed",
line);
return 1;
}
os_free(bss->radius->acct_server->shared_secret);
bss->radius->acct_server->shared_secret = (u8 *) os_strdup(pos);
bss->radius->acct_server->shared_secret_len = len;
} else if (os_strcmp(buf, "radius_retry_primary_interval") == 0) {
bss->radius->retry_primary_interval = atoi(pos);
} else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) {
bss->acct_interim_interval = atoi(pos);
} else if (os_strcmp(buf, "radius_request_cui") == 0) {
bss->radius_request_cui = atoi(pos);
} else if (os_strcmp(buf, "radius_auth_req_attr") == 0) {
struct hostapd_radius_attr *attr, *a;
attr = hostapd_parse_radius_attr(pos);
if (attr == NULL) {
wpa_printf(MSG_ERROR,
"Line %d: invalid radius_auth_req_attr",
line);
return 1;
} else if (bss->radius_auth_req_attr == NULL) {
bss->radius_auth_req_attr = attr;
} else {
a = bss->radius_auth_req_attr;
while (a->next)
a = a->next;
a->next = attr;
}
} else if (os_strcmp(buf, "radius_acct_req_attr") == 0) {
struct hostapd_radius_attr *attr, *a;
attr = hostapd_parse_radius_attr(pos);
if (attr == NULL) {
wpa_printf(MSG_ERROR,
"Line %d: invalid radius_acct_req_attr",
line);
return 1;
} else if (bss->radius_acct_req_attr == NULL) {
bss->radius_acct_req_attr = attr;
} else {
a = bss->radius_acct_req_attr;
while (a->next)
a = a->next;
a->next = attr;
}
} else if (os_strcmp(buf, "radius_req_attr_sqlite") == 0) {
os_free(bss->radius_req_attr_sqlite);
bss->radius_req_attr_sqlite = os_strdup(pos);
} else if (os_strcmp(buf, "radius_das_port") == 0) {
bss->radius_das_port = atoi(pos);
} else if (os_strcmp(buf, "radius_das_client") == 0) {
if (hostapd_parse_das_client(bss, pos) < 0) {
wpa_printf(MSG_ERROR, "Line %d: invalid DAS client",
line);
return 1;
}
} else if (os_strcmp(buf, "radius_das_time_window") == 0) {
bss->radius_das_time_window = atoi(pos);
} else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) {
bss->radius_das_require_event_timestamp = atoi(pos);
} else if (os_strcmp(buf, "radius_das_require_message_authenticator") ==
0) {
bss->radius_das_require_message_authenticator = atoi(pos);
#endif /* CONFIG_NO_RADIUS */
} else if (os_strcmp(buf, "auth_algs") == 0) {
bss->auth_algs = atoi(pos);
if (bss->auth_algs == 0) {
wpa_printf(MSG_ERROR, "Line %d: no authentication algorithms allowed",
line);
return 1;
}
} else if (os_strcmp(buf, "max_num_sta") == 0) {
bss->max_num_sta = atoi(pos);
if (bss->max_num_sta < 0 ||
bss->max_num_sta > MAX_STA_COUNT) {
wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
line, bss->max_num_sta, MAX_STA_COUNT);
return 1;
}
} else if (os_strcmp(buf, "wpa") == 0) {
bss->wpa = atoi(pos);
} else if (os_strcmp(buf, "extended_key_id") == 0) {
int val = atoi(pos);
if (val < 0 || val > 2) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid extended_key_id=%d; allowed range 0..2",
line, val);
return 1;
}
bss->extended_key_id = val;
} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
bss->wpa_group_rekey = atoi(pos);
bss->wpa_group_rekey_set = 1;
} else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
bss->wpa_strict_rekey = atoi(pos);
} else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
bss->wpa_gmk_rekey = atoi(pos);
} else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
bss->wpa_ptk_rekey = atoi(pos);
} else if (os_strcmp(buf, "wpa_deny_ptk0_rekey") == 0) {
bss->wpa_deny_ptk0_rekey = atoi(pos);
if (bss->wpa_deny_ptk0_rekey < 0 ||
bss->wpa_deny_ptk0_rekey > 2) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid wpa_deny_ptk0_rekey=%d; allowed range 0..2",
line, bss->wpa_deny_ptk0_rekey);
return 1;
}
} else if (os_strcmp(buf, "wpa_group_update_count") == 0) {
char *endp;
unsigned long val = strtoul(pos, &endp, 0);
if (*endp || val < 1 || val > (u32) -1) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid wpa_group_update_count=%lu; allowed range 1..4294967295",
line, val);
return 1;
}
bss->wpa_group_update_count = (u32) val;
} else if (os_strcmp(buf, "wpa_pairwise_update_count") == 0) {
char *endp;
unsigned long val = strtoul(pos, &endp, 0);
if (*endp || val < 1 || val > (u32) -1) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid wpa_pairwise_update_count=%lu; allowed range 1..4294967295",
line, val);
return 1;
}
bss->wpa_pairwise_update_count = (u32) val;
} else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) {
bss->wpa_disable_eapol_key_retries = atoi(pos);
} else if (os_strcmp(buf, "wpa_passphrase") == 0) {
int len = os_strlen(pos);
if (len < 8 || len > 63) {
wpa_printf(MSG_ERROR, "Line %d: invalid WPA passphrase length %d (expected 8..63)",
line, len);
return 1;
}
os_free(bss->ssid.wpa_passphrase);
bss->ssid.wpa_passphrase = os_strdup(pos);
if (bss->ssid.wpa_passphrase) {
hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
bss->ssid.wpa_passphrase_set = 1;
}
} else if (os_strcmp(buf, "wpa_psk") == 0) {
hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
if (bss->ssid.wpa_psk == NULL)
return 1;
if (hexstr2bin(pos, bss->ssid.wpa_psk->psk, PMK_LEN) ||
pos[PMK_LEN * 2] != '\0') {
wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
line, pos);
hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
return 1;
}
bss->ssid.wpa_psk->group = 1;
os_free(bss->ssid.wpa_passphrase);
bss->ssid.wpa_passphrase = NULL;
bss->ssid.wpa_psk_set = 1;
} else if (os_strcmp(buf, "wpa_psk_file") == 0) {
os_free(bss->ssid.wpa_psk_file);
bss->ssid.wpa_psk_file = os_strdup(pos);
if (!bss->ssid.wpa_psk_file) {
wpa_printf(MSG_ERROR, "Line %d: allocation failed",
line);
return 1;
}
} else if (os_strcmp(buf, "wpa_key_mgmt") == 0) {
bss->wpa_key_mgmt = hostapd_config_parse_key_mgmt(line, pos);
if (bss->wpa_key_mgmt == -1)
return 1;
} else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
bss->wpa_psk_radius = atoi(pos);
if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
wpa_printf(MSG_ERROR,
"Line %d: unknown wpa_psk_radius %d",
line, bss->wpa_psk_radius);
return 1;
}
} else if (os_strcmp(buf, "wpa_pairwise") == 0) {
bss->wpa_pairwise = hostapd_config_parse_cipher(line, pos);
if (bss->wpa_pairwise == -1 || bss->wpa_pairwise == 0)
return 1;
if (bss->wpa_pairwise &
(WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "rsn_pairwise") == 0) {
bss->rsn_pairwise = hostapd_config_parse_cipher(line, pos);
if (bss->rsn_pairwise == -1 || bss->rsn_pairwise == 0)
return 1;
if (bss->rsn_pairwise &
(WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "group_cipher") == 0) {
bss->group_cipher = hostapd_config_parse_cipher(line, pos);
if (bss->group_cipher == -1 || bss->group_cipher == 0)
return 1;
if (bss->group_cipher != WPA_CIPHER_TKIP &&
bss->group_cipher != WPA_CIPHER_CCMP &&
bss->group_cipher != WPA_CIPHER_GCMP &&
bss->group_cipher != WPA_CIPHER_GCMP_256 &&
bss->group_cipher != WPA_CIPHER_CCMP_256) {
wpa_printf(MSG_ERROR,
"Line %d: unsupported group cipher suite '%s'",
line, pos);
return 1;
}
#ifdef CONFIG_RSN_PREAUTH
} else if (os_strcmp(buf, "rsn_preauth") == 0) {
bss->rsn_preauth = atoi(pos);
} else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) {
os_free(bss->rsn_preauth_interfaces);
bss->rsn_preauth_interfaces = os_strdup(pos);
#endif /* CONFIG_RSN_PREAUTH */
} else if (os_strcmp(buf, "peerkey") == 0) {
wpa_printf(MSG_INFO,
"Line %d: Obsolete peerkey parameter ignored", line);
#ifdef CONFIG_IEEE80211R_AP
} else if (os_strcmp(buf, "mobility_domain") == 0) {
if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
hexstr2bin(pos, bss->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid mobility_domain '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "r1_key_holder") == 0) {
if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN ||
hexstr2bin(pos, bss->r1_key_holder, FT_R1KH_ID_LEN) != 0) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid r1_key_holder '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
/* DEPRECATED: Use ft_r0_key_lifetime instead. */
bss->r0_key_lifetime = atoi(pos) * 60;
} else if (os_strcmp(buf, "ft_r0_key_lifetime") == 0) {
bss->r0_key_lifetime = atoi(pos);
} else if (os_strcmp(buf, "r1_max_key_lifetime") == 0) {
bss->r1_max_key_lifetime = atoi(pos);
} else if (os_strcmp(buf, "reassociation_deadline") == 0) {
bss->reassociation_deadline = atoi(pos);
} else if (os_strcmp(buf, "rkh_pos_timeout") == 0) {
bss->rkh_pos_timeout = atoi(pos);
} else if (os_strcmp(buf, "rkh_neg_timeout") == 0) {
bss->rkh_neg_timeout = atoi(pos);
} else if (os_strcmp(buf, "rkh_pull_timeout") == 0) {
bss->rkh_pull_timeout = atoi(pos);
} else if (os_strcmp(buf, "rkh_pull_retries") == 0) {
bss->rkh_pull_retries = atoi(pos);
} else if (os_strcmp(buf, "r0kh") == 0) {
if (add_r0kh(bss, pos) < 0) {
wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "r1kh") == 0) {
if (add_r1kh(bss, pos) < 0) {
wpa_printf(MSG_DEBUG, "Line %d: Invalid r1kh '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "pmk_r1_push") == 0) {
bss->pmk_r1_push = atoi(pos);
} else if (os_strcmp(buf, "ft_over_ds") == 0) {
bss->ft_over_ds = atoi(pos);
} else if (os_strcmp(buf, "ft_psk_generate_local") == 0) {
bss->ft_psk_generate_local = atoi(pos);
#endif /* CONFIG_IEEE80211R_AP */
#ifndef CONFIG_NO_CTRL_IFACE
} else if (os_strcmp(buf, "ctrl_interface") == 0) {
os_free(bss->ctrl_interface);
bss->ctrl_interface = os_strdup(pos);
} else if (os_strcmp(buf, "ctrl_interface_group") == 0) {
#ifndef CONFIG_NATIVE_WINDOWS
struct group *grp;
char *endp;
const char *group = pos;
grp = getgrnam(group);
if (grp) {
bss->ctrl_interface_gid = grp->gr_gid;
bss->ctrl_interface_gid_set = 1;
wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d (from group name '%s')",
bss->ctrl_interface_gid, group);
return 0;
}
/* Group name not found - try to parse this as gid */
bss->ctrl_interface_gid = strtol(group, &endp, 10);
if (*group == '\0' || *endp != '\0') {
wpa_printf(MSG_DEBUG, "Line %d: Invalid group '%s'",
line, group);
return 1;
}
bss->ctrl_interface_gid_set = 1;
wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
bss->ctrl_interface_gid);
#endif /* CONFIG_NATIVE_WINDOWS */
#endif /* CONFIG_NO_CTRL_IFACE */
#ifdef RADIUS_SERVER
} else if (os_strcmp(buf, "radius_server_clients") == 0) {
os_free(bss->radius_server_clients);
bss->radius_server_clients = os_strdup(pos);
} else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
bss->radius_server_auth_port = atoi(pos);
} else if (os_strcmp(buf, "radius_server_acct_port") == 0) {
bss->radius_server_acct_port = atoi(pos);
} else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
bss->radius_server_ipv6 = atoi(pos);
#endif /* RADIUS_SERVER */
} else if (os_strcmp(buf, "use_pae_group_addr") == 0) {
bss->use_pae_group_addr = atoi(pos);
} else if (os_strcmp(buf, "hw_mode") == 0) {
if (os_strcmp(pos, "a") == 0)
conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
else if (os_strcmp(pos, "b") == 0)
conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
else if (os_strcmp(pos, "g") == 0)
conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
else if (os_strcmp(pos, "ad") == 0)
conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
else if (os_strcmp(pos, "any") == 0)
conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY;
else {
wpa_printf(MSG_ERROR, "Line %d: unknown hw_mode '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "wps_rf_bands") == 0) {
if (os_strcmp(pos, "ad") == 0)
bss->wps_rf_bands = WPS_RF_60GHZ;
else if (os_strcmp(pos, "a") == 0)
bss->wps_rf_bands = WPS_RF_50GHZ;
else if (os_strcmp(pos, "g") == 0 ||
os_strcmp(pos, "b") == 0)
bss->wps_rf_bands = WPS_RF_24GHZ;
else if (os_strcmp(pos, "ag") == 0 ||
os_strcmp(pos, "ga") == 0)
bss->wps_rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
else {
wpa_printf(MSG_ERROR,
"Line %d: unknown wps_rf_band '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "acs_exclude_dfs") == 0) {
conf->acs_exclude_dfs = atoi(pos);
} else if (os_strcmp(buf, "op_class") == 0) {
conf->op_class = atoi(pos);
} else if (os_strcmp(buf, "channel") == 0) {
if (os_strcmp(pos, "acs_survey") == 0) {
#ifndef CONFIG_ACS
wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled",
line);
return 1;
#else /* CONFIG_ACS */
conf->acs = 1;
conf->channel = 0;
#endif /* CONFIG_ACS */
} else {
conf->channel = atoi(pos);
conf->acs = conf->channel == 0;
}
} else if (os_strcmp(buf, "edmg_channel") == 0) {
conf->edmg_channel = atoi(pos);
} else if (os_strcmp(buf, "enable_edmg") == 0) {
conf->enable_edmg = atoi(pos);
} else if (os_strcmp(buf, "chanlist") == 0) {
if (hostapd_parse_chanlist(conf, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid channel list",
line);
return 1;
}
} else if (os_strcmp(buf, "freqlist") == 0) {
if (freq_range_list_parse(&conf->acs_freq_list, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid frequency list",
line);
return 1;
}
conf->acs_freq_list_present = 1;
} else if (os_strcmp(buf, "acs_exclude_6ghz_non_psc") == 0) {
conf->acs_exclude_6ghz_non_psc = atoi(pos);
} else if (os_strcmp(buf, "beacon_int") == 0) {
int val = atoi(pos);
/* MIB defines range as 1..65535, but very small values
* cause problems with the current implementation.
* Since it is unlikely that this small numbers are
* useful in real life scenarios, do not allow beacon
* period to be set below 10 TU. */
if (val < 10 || val > 65535) {
wpa_printf(MSG_ERROR,
"Line %d: invalid beacon_int %d (expected 10..65535)",
line, val);
return 1;
}
conf->beacon_int = val;
#ifdef CONFIG_ACS
} else if (os_strcmp(buf, "acs_num_scans") == 0) {
int val = atoi(pos);
if (val <= 0 || val > 100) {
wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)",
line, val);
return 1;
}
conf->acs_num_scans = val;
} else if (os_strcmp(buf, "acs_chan_bias") == 0) {
if (hostapd_config_parse_acs_chan_bias(conf, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid acs_chan_bias",
line);
return -1;
}
#endif /* CONFIG_ACS */
} else if (os_strcmp(buf, "dtim_period") == 0) {
int val = atoi(pos);
if (val < 1 || val > 255) {
wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d",
line, val);
return 1;
}
bss->dtim_period = val;
} else if (os_strcmp(buf, "bss_load_update_period") == 0) {
int val = atoi(pos);
if (val < 0 || val > 100) {
wpa_printf(MSG_ERROR,
"Line %d: invalid bss_load_update_period %d",
line, val);
return 1;
}
bss->bss_load_update_period = val;
} else if (os_strcmp(buf, "chan_util_avg_period") == 0) {
int val = atoi(pos);
if (val < 0) {
wpa_printf(MSG_ERROR,
"Line %d: invalid chan_util_avg_period",
line);
return 1;
}
bss->chan_util_avg_period = val;
} else if (os_strcmp(buf, "rts_threshold") == 0) {
conf->rts_threshold = atoi(pos);
if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) {
wpa_printf(MSG_ERROR,
"Line %d: invalid rts_threshold %d",
line, conf->rts_threshold);
return 1;
}
} else if (os_strcmp(buf, "fragm_threshold") == 0) {
conf->fragm_threshold = atoi(pos);
if (conf->fragm_threshold == -1) {
/* allow a value of -1 */
} else if (conf->fragm_threshold < 256 ||
conf->fragm_threshold > 2346) {
wpa_printf(MSG_ERROR,
"Line %d: invalid fragm_threshold %d",
line, conf->fragm_threshold);
return 1;
}
} else if (os_strcmp(buf, "send_probe_response") == 0) {
int val = atoi(pos);
if (val != 0 && val != 1) {
wpa_printf(MSG_ERROR, "Line %d: invalid send_probe_response %d (expected 0 or 1)",
line, val);
return 1;
}
bss->send_probe_response = val;
} else if (os_strcmp(buf, "supported_rates") == 0) {
if (hostapd_parse_intlist(&conf->supported_rates, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
line);
return 1;
}
} else if (os_strcmp(buf, "basic_rates") == 0) {
if (hostapd_parse_intlist(&conf->basic_rates, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
line);
return 1;
}
} else if (os_strcmp(buf, "beacon_rate") == 0) {
int val;
if (os_strncmp(pos, "ht:", 3) == 0) {
val = atoi(pos + 3);
if (val < 0 || val > 31) {
wpa_printf(MSG_ERROR,
"Line %d: invalid beacon_rate HT-MCS %d",
line, val);
return 1;
}
conf->rate_type = BEACON_RATE_HT;
conf->beacon_rate = val;
} else if (os_strncmp(pos, "vht:", 4) == 0) {
val = atoi(pos + 4);
if (val < 0 || val > 9) {
wpa_printf(MSG_ERROR,
"Line %d: invalid beacon_rate VHT-MCS %d",
line, val);
return 1;
}
conf->rate_type = BEACON_RATE_VHT;
conf->beacon_rate = val;
} else if (os_strncmp(pos, "he:", 3) == 0) {
val = atoi(pos + 3);
if (val < 0 || val > 11) {
wpa_printf(MSG_ERROR,
"Line %d: invalid beacon_rate HE-MCS %d",
line, val);
return 1;
}
conf->rate_type = BEACON_RATE_HE;
conf->beacon_rate = val;
} else {
val = atoi(pos);
if (val < 10 || val > 10000) {
wpa_printf(MSG_ERROR,
"Line %d: invalid legacy beacon_rate %d",
line, val);
return 1;
}
conf->rate_type = BEACON_RATE_LEGACY;
conf->beacon_rate = val;
}
} else if (os_strcmp(buf, "preamble") == 0) {
if (atoi(pos))
conf->preamble = SHORT_PREAMBLE;
else
conf->preamble = LONG_PREAMBLE;
} else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
bss->ignore_broadcast_ssid = atoi(pos);
} else if (os_strcmp(buf, "no_probe_resp_if_max_sta") == 0) {
bss->no_probe_resp_if_max_sta = atoi(pos);
#ifdef CONFIG_WEP
} else if (os_strcmp(buf, "wep_default_key") == 0) {
bss->ssid.wep.idx = atoi(pos);
if (bss->ssid.wep.idx > 3) {
wpa_printf(MSG_ERROR,
"Invalid wep_default_key index %d",
bss->ssid.wep.idx);
return 1;
}
} else if (os_strcmp(buf, "wep_key0") == 0 ||
os_strcmp(buf, "wep_key1") == 0 ||
os_strcmp(buf, "wep_key2") == 0 ||
os_strcmp(buf, "wep_key3") == 0) {
if (hostapd_config_read_wep(&bss->ssid.wep,
buf[7] - '0', pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid WEP key '%s'",
line, buf);
return 1;
}
#endif /* CONFIG_WEP */
#ifndef CONFIG_NO_VLAN
} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
bss->ssid.dynamic_vlan = atoi(pos);
} else if (os_strcmp(buf, "per_sta_vif") == 0) {
bss->ssid.per_sta_vif = atoi(pos);
} else if (os_strcmp(buf, "vlan_file") == 0) {
if (hostapd_config_read_vlan_file(bss, pos)) {
wpa_printf(MSG_ERROR, "Line %d: failed to read VLAN file '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "vlan_naming") == 0) {
bss->ssid.vlan_naming = atoi(pos);
if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END ||
bss->ssid.vlan_naming < 0) {
wpa_printf(MSG_ERROR,
"Line %d: invalid naming scheme %d",
line, bss->ssid.vlan_naming);
return 1;
}
#ifdef CONFIG_FULL_DYNAMIC_VLAN
} else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
os_free(bss->ssid.vlan_tagged_interface);
bss->ssid.vlan_tagged_interface = os_strdup(pos);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
#endif /* CONFIG_NO_VLAN */
} else if (os_strcmp(buf, "ap_table_max_size") == 0) {
conf->ap_table_max_size = atoi(pos);
} else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
conf->ap_table_expiration_time = atoi(pos);
} else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
if (hostapd_config_tx_queue(conf->tx_queue, buf, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid TX queue item",
line);
return 1;
}
} else if (os_strcmp(buf, "wme_enabled") == 0 ||
os_strcmp(buf, "wmm_enabled") == 0) {
bss->wmm_enabled = atoi(pos);
} else if (os_strcmp(buf, "uapsd_advertisement_enabled") == 0) {
bss->wmm_uapsd = atoi(pos);
} else if (os_strncmp(buf, "wme_ac_", 7) == 0 ||
os_strncmp(buf, "wmm_ac_", 7) == 0) {
if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid WMM ac item",
line);
return 1;
}
} else if (os_strcmp(buf, "bss") == 0) {
if (hostapd_config_bss(conf, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid bss item",
line);
return 1;
}
} else if (os_strcmp(buf, "bssid") == 0) {
if (hwaddr_aton(pos, bss->bssid)) {
wpa_printf(MSG_ERROR, "Line %d: invalid bssid item",
line);
return 1;
}
} else if (os_strcmp(buf, "use_driver_iface_addr") == 0) {
conf->use_driver_iface_addr = atoi(pos);
} else if (os_strcmp(buf, "ieee80211w") == 0) {
bss->ieee80211w = atoi(pos);
} else if (os_strcmp(buf, "group_mgmt_cipher") == 0) {
if (os_strcmp(pos, "AES-128-CMAC") == 0) {
bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
} else if (os_strcmp(pos, "BIP-GMAC-128") == 0) {
bss->group_mgmt_cipher = WPA_CIPHER_BIP_GMAC_128;
} else if (os_strcmp(pos, "BIP-GMAC-256") == 0) {
bss->group_mgmt_cipher = WPA_CIPHER_BIP_GMAC_256;
} else if (os_strcmp(pos, "BIP-CMAC-256") == 0) {
bss->group_mgmt_cipher = WPA_CIPHER_BIP_CMAC_256;
} else {
wpa_printf(MSG_ERROR, "Line %d: invalid group_mgmt_cipher: %s",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "beacon_prot") == 0) {
bss->beacon_prot = atoi(pos);
} else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) {
bss->assoc_sa_query_max_timeout = atoi(pos);
if (bss->assoc_sa_query_max_timeout == 0) {
wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_max_timeout",
line);
return 1;
}
} else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0) {
bss->assoc_sa_query_retry_timeout = atoi(pos);
if (bss->assoc_sa_query_retry_timeout == 0) {
wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_retry_timeout",
line);
return 1;
}
#ifdef CONFIG_OCV
} else if (os_strcmp(buf, "ocv") == 0) {
bss->ocv = atoi(pos);
if (bss->ocv && !bss->ieee80211w)
bss->ieee80211w = 1;
#endif /* CONFIG_OCV */
} else if (os_strcmp(buf, "ieee80211n") == 0) {
conf->ieee80211n = atoi(pos);
} else if (os_strcmp(buf, "ht_capab") == 0) {
if (hostapd_config_ht_capab(conf, pos) < 0) {
wpa_printf(MSG_ERROR, "Line %d: invalid ht_capab",
line);
return 1;
}
} else if (os_strcmp(buf, "require_ht") == 0) {
conf->require_ht = atoi(pos);
} else if (os_strcmp(buf, "obss_interval") == 0) {
conf->obss_interval = atoi(pos);
#ifdef CONFIG_IEEE80211AC
} else if (os_strcmp(buf, "ieee80211ac") == 0) {
conf->ieee80211ac = atoi(pos);
} else if (os_strcmp(buf, "vht_capab") == 0) {
if (hostapd_config_vht_capab(conf, pos) < 0) {
wpa_printf(MSG_ERROR, "Line %d: invalid vht_capab",
line);
return 1;
}
} else if (os_strcmp(buf, "require_vht") == 0) {
conf->require_vht = atoi(pos);
} else if (os_strcmp(buf, "vht_oper_chwidth") == 0) {
conf->vht_oper_chwidth = atoi(pos);
} else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0) {
conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
} else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) {
conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
} else if (os_strcmp(buf, "vendor_vht") == 0) {
bss->vendor_vht = atoi(pos);
} else if (os_strcmp(buf, "use_sta_nsts") == 0) {
bss->use_sta_nsts = atoi(pos);
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
} else if (os_strcmp(buf, "ieee80211ax") == 0) {
conf->ieee80211ax = atoi(pos);
} else if (os_strcmp(buf, "he_su_beamformer") == 0) {
conf->he_phy_capab.he_su_beamformer = atoi(pos);
} else if (os_strcmp(buf, "he_su_beamformee") == 0) {
conf->he_phy_capab.he_su_beamformee = atoi(pos);
} else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
conf->he_phy_capab.he_mu_beamformer = atoi(pos);
} else if (os_strcmp(buf, "he_bss_color") == 0) {
conf->he_op.he_bss_color = atoi(pos) & 0x3f;
conf->he_op.he_bss_color_disabled = 0;
} else if (os_strcmp(buf, "he_bss_color_partial") == 0) {
conf->he_op.he_bss_color_partial = atoi(pos);
} else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
conf->he_op.he_default_pe_duration = atoi(pos);
} else if (os_strcmp(buf, "he_twt_required") == 0) {
conf->he_op.he_twt_required = atoi(pos);
} else if (os_strcmp(buf, "he_twt_responder") == 0) {
conf->he_op.he_twt_responder = atoi(pos);
} else if (os_strcmp(buf, "he_rts_threshold") == 0) {
conf->he_op.he_rts_threshold = atoi(pos);
+ } else if (os_strcmp(buf, "he_er_su_disable") == 0) {
+ conf->he_op.he_er_su_disable = atoi(pos);
} else if (os_strcmp(buf, "he_basic_mcs_nss_set") == 0) {
conf->he_op.he_basic_mcs_nss_set = atoi(pos);
} else if (os_strcmp(buf, "he_mu_edca_qos_info_param_count") == 0) {
conf->he_mu_edca.he_qos_info |=
set_he_cap(atoi(pos), HE_QOS_INFO_EDCA_PARAM_SET_COUNT);
} else if (os_strcmp(buf, "he_mu_edca_qos_info_q_ack") == 0) {
conf->he_mu_edca.he_qos_info |=
set_he_cap(atoi(pos), HE_QOS_INFO_Q_ACK);
} else if (os_strcmp(buf, "he_mu_edca_qos_info_queue_request") == 0) {
conf->he_mu_edca.he_qos_info |=
set_he_cap(atoi(pos), HE_QOS_INFO_QUEUE_REQUEST);
} else if (os_strcmp(buf, "he_mu_edca_qos_info_txop_request") == 0) {
conf->he_mu_edca.he_qos_info |=
set_he_cap(atoi(pos), HE_QOS_INFO_TXOP_REQUEST);
} else if (os_strcmp(buf, "he_mu_edca_ac_be_aifsn") == 0) {
conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
} else if (os_strcmp(buf, "he_mu_edca_ac_be_acm") == 0) {
conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
} else if (os_strcmp(buf, "he_mu_edca_ac_be_aci") == 0) {
conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
} else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmin") == 0) {
conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
} else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmax") == 0) {
conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
} else if (os_strcmp(buf, "he_mu_edca_ac_be_timer") == 0) {
conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_TIMER_IDX] =
atoi(pos) & 0xff;
} else if (os_strcmp(buf, "he_mu_edca_ac_bk_aifsn") == 0) {
conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
} else if (os_strcmp(buf, "he_mu_edca_ac_bk_acm") == 0) {
conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
} else if (os_strcmp(buf, "he_mu_edca_ac_bk_aci") == 0) {
conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
} else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmin") == 0) {
conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
} else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmax") == 0) {
conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
} else if (os_strcmp(buf, "he_mu_edca_ac_bk_timer") == 0) {
conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_TIMER_IDX] =
atoi(pos) & 0xff;
} else if (os_strcmp(buf, "he_mu_edca_ac_vi_aifsn") == 0) {
conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
} else if (os_strcmp(buf, "he_mu_edca_ac_vi_acm") == 0) {
conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
} else if (os_strcmp(buf, "he_mu_edca_ac_vi_aci") == 0) {
conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
} else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmin") == 0) {
conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
} else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmax") == 0) {
conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
} else if (os_strcmp(buf, "he_mu_edca_ac_vi_timer") == 0) {
conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_TIMER_IDX] =
atoi(pos) & 0xff;
} else if (os_strcmp(buf, "he_mu_edca_ac_vo_aifsn") == 0) {
conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
} else if (os_strcmp(buf, "he_mu_edca_ac_vo_acm") == 0) {
conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
} else if (os_strcmp(buf, "he_mu_edca_ac_vo_aci") == 0) {
conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
} else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmin") == 0) {
conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
} else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmax") == 0) {
conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |=
set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
} else if (os_strcmp(buf, "he_mu_edca_ac_vo_timer") == 0) {
conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_TIMER_IDX] =
atoi(pos) & 0xff;
} else if (os_strcmp(buf, "he_spr_sr_control") == 0) {
conf->spr.sr_control = atoi(pos) & 0x1f;
} else if (os_strcmp(buf, "he_spr_non_srg_obss_pd_max_offset") == 0) {
conf->spr.non_srg_obss_pd_max_offset = atoi(pos);
} else if (os_strcmp(buf, "he_spr_srg_obss_pd_min_offset") == 0) {
conf->spr.srg_obss_pd_min_offset = atoi(pos);
} else if (os_strcmp(buf, "he_spr_srg_obss_pd_max_offset") == 0) {
conf->spr.srg_obss_pd_max_offset = atoi(pos);
} else if (os_strcmp(buf, "he_spr_srg_bss_colors") == 0) {
if (hostapd_parse_he_srg_bitmap(
conf->spr.srg_bss_color_bitmap, pos)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid srg bss colors list '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "he_spr_srg_partial_bssid") == 0) {
if (hostapd_parse_he_srg_bitmap(
conf->spr.srg_partial_bssid_bitmap, pos)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid srg partial bssid list '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "he_oper_chwidth") == 0) {
conf->he_oper_chwidth = atoi(pos);
} else if (os_strcmp(buf, "he_oper_centr_freq_seg0_idx") == 0) {
conf->he_oper_centr_freq_seg0_idx = atoi(pos);
} else if (os_strcmp(buf, "he_oper_centr_freq_seg1_idx") == 0) {
conf->he_oper_centr_freq_seg1_idx = atoi(pos);
} else if (os_strcmp(buf, "he_6ghz_max_mpdu") == 0) {
conf->he_6ghz_max_mpdu = atoi(pos);
} else if (os_strcmp(buf, "he_6ghz_max_ampdu_len_exp") == 0) {
conf->he_6ghz_max_ampdu_len_exp = atoi(pos);
} else if (os_strcmp(buf, "he_6ghz_rx_ant_pat") == 0) {
conf->he_6ghz_rx_ant_pat = atoi(pos);
} else if (os_strcmp(buf, "he_6ghz_tx_ant_pat") == 0) {
conf->he_6ghz_tx_ant_pat = atoi(pos);
} else if (os_strcmp(buf, "unsol_bcast_probe_resp_interval") == 0) {
int val = atoi(pos);
if (val < 0 || val > 20) {
wpa_printf(MSG_ERROR,
"Line %d: invalid unsol_bcast_probe_resp_interval value",
line);
return 1;
}
bss->unsol_bcast_probe_resp_interval = val;
#endif /* CONFIG_IEEE80211AX */
} else if (os_strcmp(buf, "max_listen_interval") == 0) {
bss->max_listen_interval = atoi(pos);
} else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
bss->disable_pmksa_caching = atoi(pos);
} else if (os_strcmp(buf, "okc") == 0) {
bss->okc = atoi(pos);
#ifdef CONFIG_WPS
} else if (os_strcmp(buf, "wps_state") == 0) {
bss->wps_state = atoi(pos);
if (bss->wps_state < 0 || bss->wps_state > 2) {
wpa_printf(MSG_ERROR, "Line %d: invalid wps_state",
line);
return 1;
}
} else if (os_strcmp(buf, "wps_independent") == 0) {
bss->wps_independent = atoi(pos);
} else if (os_strcmp(buf, "ap_setup_locked") == 0) {
bss->ap_setup_locked = atoi(pos);
} else if (os_strcmp(buf, "uuid") == 0) {
if (uuid_str2bin(pos, bss->uuid)) {
wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
return 1;
}
} else if (os_strcmp(buf, "wps_pin_requests") == 0) {
os_free(bss->wps_pin_requests);
bss->wps_pin_requests = os_strdup(pos);
} else if (os_strcmp(buf, "device_name") == 0) {
if (os_strlen(pos) > WPS_DEV_NAME_MAX_LEN) {
wpa_printf(MSG_ERROR, "Line %d: Too long "
"device_name", line);
return 1;
}
os_free(bss->device_name);
bss->device_name = os_strdup(pos);
} else if (os_strcmp(buf, "manufacturer") == 0) {
if (os_strlen(pos) > 64) {
wpa_printf(MSG_ERROR, "Line %d: Too long manufacturer",
line);
return 1;
}
os_free(bss->manufacturer);
bss->manufacturer = os_strdup(pos);
} else if (os_strcmp(buf, "model_name") == 0) {
if (os_strlen(pos) > 32) {
wpa_printf(MSG_ERROR, "Line %d: Too long model_name",
line);
return 1;
}
os_free(bss->model_name);
bss->model_name = os_strdup(pos);
} else if (os_strcmp(buf, "model_number") == 0) {
if (os_strlen(pos) > 32) {
wpa_printf(MSG_ERROR, "Line %d: Too long model_number",
line);
return 1;
}
os_free(bss->model_number);
bss->model_number = os_strdup(pos);
} else if (os_strcmp(buf, "serial_number") == 0) {
if (os_strlen(pos) > 32) {
wpa_printf(MSG_ERROR, "Line %d: Too long serial_number",
line);
return 1;
}
os_free(bss->serial_number);
bss->serial_number = os_strdup(pos);
} else if (os_strcmp(buf, "device_type") == 0) {
if (wps_dev_type_str2bin(pos, bss->device_type))
return 1;
} else if (os_strcmp(buf, "config_methods") == 0) {
os_free(bss->config_methods);
bss->config_methods = os_strdup(pos);
} else if (os_strcmp(buf, "os_version") == 0) {
if (hexstr2bin(pos, bss->os_version, 4)) {
wpa_printf(MSG_ERROR, "Line %d: invalid os_version",
line);
return 1;
}
} else if (os_strcmp(buf, "ap_pin") == 0) {
os_free(bss->ap_pin);
if (*pos == '\0')
bss->ap_pin = NULL;
else
bss->ap_pin = os_strdup(pos);
} else if (os_strcmp(buf, "skip_cred_build") == 0) {
bss->skip_cred_build = atoi(pos);
} else if (os_strcmp(buf, "extra_cred") == 0) {
os_free(bss->extra_cred);
bss->extra_cred = (u8 *) os_readfile(pos, &bss->extra_cred_len);
if (bss->extra_cred == NULL) {
wpa_printf(MSG_ERROR, "Line %d: could not read Credentials from '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "wps_cred_processing") == 0) {
bss->wps_cred_processing = atoi(pos);
} else if (os_strcmp(buf, "wps_cred_add_sae") == 0) {
bss->wps_cred_add_sae = atoi(pos);
} else if (os_strcmp(buf, "ap_settings") == 0) {
os_free(bss->ap_settings);
bss->ap_settings =
(u8 *) os_readfile(pos, &bss->ap_settings_len);
if (bss->ap_settings == NULL) {
wpa_printf(MSG_ERROR, "Line %d: could not read AP Settings from '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "multi_ap_backhaul_ssid") == 0) {
size_t slen;
char *str = wpa_config_parse_string(pos, &slen);
if (!str || slen < 1 || slen > SSID_MAX_LEN) {
wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
line, pos);
os_free(str);
return 1;
}
os_memcpy(bss->multi_ap_backhaul_ssid.ssid, str, slen);
bss->multi_ap_backhaul_ssid.ssid_len = slen;
bss->multi_ap_backhaul_ssid.ssid_set = 1;
os_free(str);
} else if (os_strcmp(buf, "multi_ap_backhaul_wpa_passphrase") == 0) {
int len = os_strlen(pos);
if (len < 8 || len > 63) {
wpa_printf(MSG_ERROR,
"Line %d: invalid WPA passphrase length %d (expected 8..63)",
line, len);
return 1;
}
os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase);
bss->multi_ap_backhaul_ssid.wpa_passphrase = os_strdup(pos);
if (bss->multi_ap_backhaul_ssid.wpa_passphrase) {
hostapd_config_clear_wpa_psk(
&bss->multi_ap_backhaul_ssid.wpa_psk);
bss->multi_ap_backhaul_ssid.wpa_passphrase_set = 1;
}
} else if (os_strcmp(buf, "multi_ap_backhaul_wpa_psk") == 0) {
hostapd_config_clear_wpa_psk(
&bss->multi_ap_backhaul_ssid.wpa_psk);
bss->multi_ap_backhaul_ssid.wpa_psk =
os_zalloc(sizeof(struct hostapd_wpa_psk));
if (!bss->multi_ap_backhaul_ssid.wpa_psk)
return 1;
if (hexstr2bin(pos, bss->multi_ap_backhaul_ssid.wpa_psk->psk,
PMK_LEN) ||
pos[PMK_LEN * 2] != '\0') {
wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
line, pos);
hostapd_config_clear_wpa_psk(
&bss->multi_ap_backhaul_ssid.wpa_psk);
return 1;
}
bss->multi_ap_backhaul_ssid.wpa_psk->group = 1;
os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase);
bss->multi_ap_backhaul_ssid.wpa_passphrase = NULL;
bss->multi_ap_backhaul_ssid.wpa_psk_set = 1;
} else if (os_strcmp(buf, "upnp_iface") == 0) {
os_free(bss->upnp_iface);
bss->upnp_iface = os_strdup(pos);
} else if (os_strcmp(buf, "friendly_name") == 0) {
os_free(bss->friendly_name);
bss->friendly_name = os_strdup(pos);
} else if (os_strcmp(buf, "manufacturer_url") == 0) {
os_free(bss->manufacturer_url);
bss->manufacturer_url = os_strdup(pos);
} else if (os_strcmp(buf, "model_description") == 0) {
os_free(bss->model_description);
bss->model_description = os_strdup(pos);
} else if (os_strcmp(buf, "model_url") == 0) {
os_free(bss->model_url);
bss->model_url = os_strdup(pos);
} else if (os_strcmp(buf, "upc") == 0) {
os_free(bss->upc);
bss->upc = os_strdup(pos);
} else if (os_strcmp(buf, "pbc_in_m1") == 0) {
bss->pbc_in_m1 = atoi(pos);
} else if (os_strcmp(buf, "server_id") == 0) {
os_free(bss->server_id);
bss->server_id = os_strdup(pos);
} else if (os_strcmp(buf, "wps_application_ext") == 0) {
wpabuf_free(bss->wps_application_ext);
bss->wps_application_ext = wpabuf_parse_bin(pos);
#ifdef CONFIG_WPS_NFC
} else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
bss->wps_nfc_dev_pw_id = atoi(pos);
if (bss->wps_nfc_dev_pw_id < 0x10 ||
bss->wps_nfc_dev_pw_id > 0xffff) {
wpa_printf(MSG_ERROR, "Line %d: Invalid wps_nfc_dev_pw_id value",
line);
return 1;
}
bss->wps_nfc_pw_from_config = 1;
} else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
wpabuf_free(bss->wps_nfc_dh_pubkey);
bss->wps_nfc_dh_pubkey = wpabuf_parse_bin(pos);
bss->wps_nfc_pw_from_config = 1;
} else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
wpabuf_free(bss->wps_nfc_dh_privkey);
bss->wps_nfc_dh_privkey = wpabuf_parse_bin(pos);
bss->wps_nfc_pw_from_config = 1;
} else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
wpabuf_free(bss->wps_nfc_dev_pw);
bss->wps_nfc_dev_pw = wpabuf_parse_bin(pos);
bss->wps_nfc_pw_from_config = 1;
#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P_MANAGER
} else if (os_strcmp(buf, "manage_p2p") == 0) {
if (atoi(pos))
bss->p2p |= P2P_MANAGE;
else
bss->p2p &= ~P2P_MANAGE;
} else if (os_strcmp(buf, "allow_cross_connection") == 0) {
if (atoi(pos))
bss->p2p |= P2P_ALLOW_CROSS_CONNECTION;
else
bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
#endif /* CONFIG_P2P_MANAGER */
} else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
bss->disassoc_low_ack = atoi(pos);
} else if (os_strcmp(buf, "tdls_prohibit") == 0) {
if (atoi(pos))
bss->tdls |= TDLS_PROHIBIT;
else
bss->tdls &= ~TDLS_PROHIBIT;
} else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) {
if (atoi(pos))
bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH;
else
bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH;
#ifdef CONFIG_RSN_TESTING
} else if (os_strcmp(buf, "rsn_testing") == 0) {
extern int rsn_testing;
rsn_testing = atoi(pos);
#endif /* CONFIG_RSN_TESTING */
} else if (os_strcmp(buf, "time_advertisement") == 0) {
bss->time_advertisement = atoi(pos);
} else if (os_strcmp(buf, "time_zone") == 0) {
size_t tz_len = os_strlen(pos);
if (tz_len < 4 || tz_len > 255) {
wpa_printf(MSG_DEBUG, "Line %d: invalid time_zone",
line);
return 1;
}
os_free(bss->time_zone);
bss->time_zone = os_strdup(pos);
if (bss->time_zone == NULL)
return 1;
#ifdef CONFIG_WNM_AP
} else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
bss->wnm_sleep_mode = atoi(pos);
} else if (os_strcmp(buf, "wnm_sleep_mode_no_keys") == 0) {
bss->wnm_sleep_mode_no_keys = atoi(pos);
} else if (os_strcmp(buf, "bss_transition") == 0) {
bss->bss_transition = atoi(pos);
#endif /* CONFIG_WNM_AP */
#ifdef CONFIG_INTERWORKING
} else if (os_strcmp(buf, "interworking") == 0) {
bss->interworking = atoi(pos);
} else if (os_strcmp(buf, "access_network_type") == 0) {
bss->access_network_type = atoi(pos);
if (bss->access_network_type < 0 ||
bss->access_network_type > 15) {
wpa_printf(MSG_ERROR,
"Line %d: invalid access_network_type",
line);
return 1;
}
} else if (os_strcmp(buf, "internet") == 0) {
bss->internet = atoi(pos);
} else if (os_strcmp(buf, "asra") == 0) {
bss->asra = atoi(pos);
} else if (os_strcmp(buf, "esr") == 0) {
bss->esr = atoi(pos);
} else if (os_strcmp(buf, "uesa") == 0) {
bss->uesa = atoi(pos);
} else if (os_strcmp(buf, "venue_group") == 0) {
bss->venue_group = atoi(pos);
bss->venue_info_set = 1;
} else if (os_strcmp(buf, "venue_type") == 0) {
bss->venue_type = atoi(pos);
bss->venue_info_set = 1;
} else if (os_strcmp(buf, "hessid") == 0) {
if (hwaddr_aton(pos, bss->hessid)) {
wpa_printf(MSG_ERROR, "Line %d: invalid hessid", line);
return 1;
}
} else if (os_strcmp(buf, "roaming_consortium") == 0) {
if (parse_roaming_consortium(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "venue_name") == 0) {
if (parse_venue_name(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "venue_url") == 0) {
if (parse_venue_url(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "network_auth_type") == 0) {
u8 auth_type;
u16 redirect_url_len;
if (hexstr2bin(pos, &auth_type, 1)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid network_auth_type '%s'",
line, pos);
return 1;
}
if (auth_type == 0 || auth_type == 2)
redirect_url_len = os_strlen(pos + 2);
else
redirect_url_len = 0;
os_free(bss->network_auth_type);
bss->network_auth_type = os_malloc(redirect_url_len + 3 + 1);
if (bss->network_auth_type == NULL)
return 1;
*bss->network_auth_type = auth_type;
WPA_PUT_LE16(bss->network_auth_type + 1, redirect_url_len);
if (redirect_url_len)
os_memcpy(bss->network_auth_type + 3, pos + 2,
redirect_url_len);
bss->network_auth_type_len = 3 + redirect_url_len;
} else if (os_strcmp(buf, "ipaddr_type_availability") == 0) {
if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid ipaddr_type_availability '%s'",
line, pos);
bss->ipaddr_type_configured = 0;
return 1;
}
bss->ipaddr_type_configured = 1;
} else if (os_strcmp(buf, "domain_name") == 0) {
int j, num_domains, domain_len, domain_list_len = 0;
char *tok_start, *tok_prev;
u8 *domain_list, *domain_ptr;
domain_list_len = os_strlen(pos) + 1;
domain_list = os_malloc(domain_list_len);
if (domain_list == NULL)
return 1;
domain_ptr = domain_list;
tok_prev = pos;
num_domains = 1;
while ((tok_prev = os_strchr(tok_prev, ','))) {
num_domains++;
tok_prev++;
}
tok_prev = pos;
for (j = 0; j < num_domains; j++) {
tok_start = os_strchr(tok_prev, ',');
if (tok_start) {
domain_len = tok_start - tok_prev;
*domain_ptr = domain_len;
os_memcpy(domain_ptr + 1, tok_prev, domain_len);
domain_ptr += domain_len + 1;
tok_prev = ++tok_start;
} else {
domain_len = os_strlen(tok_prev);
*domain_ptr = domain_len;
os_memcpy(domain_ptr + 1, tok_prev, domain_len);
domain_ptr += domain_len + 1;
}
}
os_free(bss->domain_name);
bss->domain_name = domain_list;
bss->domain_name_len = domain_list_len;
} else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
if (parse_3gpp_cell_net(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "nai_realm") == 0) {
if (parse_nai_realm(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "anqp_elem") == 0) {
if (parse_anqp_elem(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "gas_frag_limit") == 0) {
int val = atoi(pos);
if (val <= 0) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid gas_frag_limit '%s'",
line, pos);
return 1;
}
bss->gas_frag_limit = val;
} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
bss->gas_comeback_delay = atoi(pos);
} else if (os_strcmp(buf, "qos_map_set") == 0) {
if (parse_qos_map_set(bss, pos, line) < 0)
return 1;
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_RADIUS_TEST
} else if (os_strcmp(buf, "dump_msk_file") == 0) {
os_free(bss->dump_msk_file);
bss->dump_msk_file = os_strdup(pos);
#endif /* CONFIG_RADIUS_TEST */
#ifdef CONFIG_PROXYARP
} else if (os_strcmp(buf, "proxy_arp") == 0) {
bss->proxy_arp = atoi(pos);
#endif /* CONFIG_PROXYARP */
#ifdef CONFIG_HS20
} else if (os_strcmp(buf, "hs20") == 0) {
bss->hs20 = atoi(pos);
} else if (os_strcmp(buf, "hs20_release") == 0) {
int val = atoi(pos);
if (val < 1 || val > (HS20_VERSION >> 4) + 1) {
wpa_printf(MSG_ERROR,
"Line %d: Unsupported hs20_release: %s",
line, pos);
return 1;
}
bss->hs20_release = val;
} else if (os_strcmp(buf, "disable_dgaf") == 0) {
bss->disable_dgaf = atoi(pos);
} else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) {
bss->na_mcast_to_ucast = atoi(pos);
} else if (os_strcmp(buf, "osen") == 0) {
bss->osen = atoi(pos);
} else if (os_strcmp(buf, "anqp_domain_id") == 0) {
bss->anqp_domain_id = atoi(pos);
} else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
bss->hs20_deauth_req_timeout = atoi(pos);
} else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "hs20_wan_metrics") == 0) {
if (hs20_parse_wan_metrics(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "hs20_conn_capab") == 0) {
if (hs20_parse_conn_capab(bss, pos, line) < 0) {
return 1;
}
} else if (os_strcmp(buf, "hs20_operating_class") == 0) {
u8 *oper_class;
size_t oper_class_len;
oper_class_len = os_strlen(pos);
if (oper_class_len < 2 || (oper_class_len & 0x01)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid hs20_operating_class '%s'",
line, pos);
return 1;
}
oper_class_len /= 2;
oper_class = os_malloc(oper_class_len);
if (oper_class == NULL)
return 1;
if (hexstr2bin(pos, oper_class, oper_class_len)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid hs20_operating_class '%s'",
line, pos);
os_free(oper_class);
return 1;
}
os_free(bss->hs20_operating_class);
bss->hs20_operating_class = oper_class;
bss->hs20_operating_class_len = oper_class_len;
} else if (os_strcmp(buf, "hs20_icon") == 0) {
if (hs20_parse_icon(bss, pos) < 0) {
wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_icon '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "osu_ssid") == 0) {
if (hs20_parse_osu_ssid(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "osu_server_uri") == 0) {
if (hs20_parse_osu_server_uri(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "osu_friendly_name") == 0) {
if (hs20_parse_osu_friendly_name(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "osu_nai") == 0) {
if (hs20_parse_osu_nai(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "osu_nai2") == 0) {
if (hs20_parse_osu_nai2(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "osu_method_list") == 0) {
if (hs20_parse_osu_method_list(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "osu_icon") == 0) {
if (hs20_parse_osu_icon(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "osu_service_desc") == 0) {
if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "operator_icon") == 0) {
if (hs20_parse_operator_icon(bss, pos, line) < 0)
return 1;
} else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
os_free(bss->subscr_remediation_url);
bss->subscr_remediation_url = os_strdup(pos);
} else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
bss->subscr_remediation_method = atoi(pos);
} else if (os_strcmp(buf, "hs20_t_c_filename") == 0) {
os_free(bss->t_c_filename);
bss->t_c_filename = os_strdup(pos);
} else if (os_strcmp(buf, "hs20_t_c_timestamp") == 0) {
bss->t_c_timestamp = strtol(pos, NULL, 0);
} else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) {
os_free(bss->t_c_server_url);
bss->t_c_server_url = os_strdup(pos);
} else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) {
os_free(bss->hs20_sim_provisioning_url);
bss->hs20_sim_provisioning_url = os_strdup(pos);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
} else if (os_strcmp(buf, "mbo") == 0) {
bss->mbo_enabled = atoi(pos);
} else if (os_strcmp(buf, "mbo_cell_data_conn_pref") == 0) {
bss->mbo_cell_data_conn_pref = atoi(pos);
} else if (os_strcmp(buf, "oce") == 0) {
bss->oce = atoi(pos);
#endif /* CONFIG_MBO */
#ifdef CONFIG_TESTING_OPTIONS
#define PARSE_TEST_PROBABILITY(_val) \
} else if (os_strcmp(buf, #_val) == 0) { \
char *end; \
\
conf->_val = strtod(pos, &end); \
if (*end || conf->_val < 0.0 || \
conf->_val > 1.0) { \
wpa_printf(MSG_ERROR, \
"Line %d: Invalid value '%s'", \
line, pos); \
return 1; \
}
PARSE_TEST_PROBABILITY(ignore_probe_probability)
PARSE_TEST_PROBABILITY(ignore_auth_probability)
PARSE_TEST_PROBABILITY(ignore_assoc_probability)
PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
} else if (os_strcmp(buf, "ecsa_ie_only") == 0) {
conf->ecsa_ie_only = atoi(pos);
} else if (os_strcmp(buf, "bss_load_test") == 0) {
WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
pos = os_strchr(pos, ':');
if (pos == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Invalid bss_load_test",
line);
return 1;
}
pos++;
bss->bss_load_test[2] = atoi(pos);
pos = os_strchr(pos, ':');
if (pos == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Invalid bss_load_test",
line);
return 1;
}
pos++;
WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos));
bss->bss_load_test_set = 1;
} else if (os_strcmp(buf, "radio_measurements") == 0) {
/*
* DEPRECATED: This parameter will be removed in the future.
* Use rrm_neighbor_report instead.
*/
int val = atoi(pos);
if (val & BIT(0))
bss->radio_measurements[0] |=
WLAN_RRM_CAPS_NEIGHBOR_REPORT;
} else if (os_strcmp(buf, "own_ie_override") == 0) {
struct wpabuf *tmp;
size_t len = os_strlen(pos) / 2;
tmp = wpabuf_alloc(len);
if (!tmp)
return 1;
if (hexstr2bin(pos, wpabuf_put(tmp, len), len)) {
wpabuf_free(tmp);
wpa_printf(MSG_ERROR,
"Line %d: Invalid own_ie_override '%s'",
line, pos);
return 1;
}
wpabuf_free(bss->own_ie_override);
bss->own_ie_override = tmp;
} else if (os_strcmp(buf, "sae_reflection_attack") == 0) {
bss->sae_reflection_attack = atoi(pos);
} else if (os_strcmp(buf, "sae_commit_status") == 0) {
bss->sae_commit_status = atoi(pos);
} else if (os_strcmp(buf, "sae_pk_omit") == 0) {
bss->sae_pk_omit = atoi(pos);
} else if (os_strcmp(buf, "sae_pk_password_check_skip") == 0) {
bss->sae_pk_password_check_skip = atoi(pos);
} else if (os_strcmp(buf, "sae_commit_override") == 0) {
wpabuf_free(bss->sae_commit_override);
bss->sae_commit_override = wpabuf_parse_bin(pos);
} else if (os_strcmp(buf, "rsne_override_eapol") == 0) {
wpabuf_free(bss->rsne_override_eapol);
bss->rsne_override_eapol = wpabuf_parse_bin(pos);
} else if (os_strcmp(buf, "rsnxe_override_eapol") == 0) {
wpabuf_free(bss->rsnxe_override_eapol);
bss->rsnxe_override_eapol = wpabuf_parse_bin(pos);
} else if (os_strcmp(buf, "rsne_override_ft") == 0) {
wpabuf_free(bss->rsne_override_ft);
bss->rsne_override_ft = wpabuf_parse_bin(pos);
} else if (os_strcmp(buf, "rsnxe_override_ft") == 0) {
wpabuf_free(bss->rsnxe_override_ft);
bss->rsnxe_override_ft = wpabuf_parse_bin(pos);
} else if (os_strcmp(buf, "gtk_rsc_override") == 0) {
wpabuf_free(bss->gtk_rsc_override);
bss->gtk_rsc_override = wpabuf_parse_bin(pos);
} else if (os_strcmp(buf, "igtk_rsc_override") == 0) {
wpabuf_free(bss->igtk_rsc_override);
bss->igtk_rsc_override = wpabuf_parse_bin(pos);
} else if (os_strcmp(buf, "no_beacon_rsnxe") == 0) {
bss->no_beacon_rsnxe = atoi(pos);
} else if (os_strcmp(buf, "skip_prune_assoc") == 0) {
bss->skip_prune_assoc = atoi(pos);
} else if (os_strcmp(buf, "ft_rsnxe_used") == 0) {
bss->ft_rsnxe_used = atoi(pos);
} else if (os_strcmp(buf, "oci_freq_override_eapol_m3") == 0) {
bss->oci_freq_override_eapol_m3 = atoi(pos);
} else if (os_strcmp(buf, "oci_freq_override_eapol_g1") == 0) {
bss->oci_freq_override_eapol_g1 = atoi(pos);
} else if (os_strcmp(buf, "oci_freq_override_saquery_req") == 0) {
bss->oci_freq_override_saquery_req = atoi(pos);
} else if (os_strcmp(buf, "oci_freq_override_saquery_resp") == 0) {
bss->oci_freq_override_saquery_resp = atoi(pos);
} else if (os_strcmp(buf, "oci_freq_override_ft_assoc") == 0) {
bss->oci_freq_override_ft_assoc = atoi(pos);
} else if (os_strcmp(buf, "oci_freq_override_fils_assoc") == 0) {
bss->oci_freq_override_fils_assoc = atoi(pos);
} else if (os_strcmp(buf, "oci_freq_override_wnm_sleep") == 0) {
bss->oci_freq_override_wnm_sleep = atoi(pos);
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_SAE
} else if (os_strcmp(buf, "sae_password") == 0) {
if (parse_sae_password(bss, pos) < 0) {
wpa_printf(MSG_ERROR, "Line %d: Invalid sae_password",
line);
return 1;
}
#endif /* CONFIG_SAE */
} else if (os_strcmp(buf, "vendor_elements") == 0) {
if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
return 1;
} else if (os_strcmp(buf, "assocresp_elements") == 0) {
if (parse_wpabuf_hex(line, buf, &bss->assocresp_elements, pos))
return 1;
} else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0 ||
os_strcmp(buf, "anti_clogging_threshold") == 0) {
bss->anti_clogging_threshold = atoi(pos);
} else if (os_strcmp(buf, "sae_sync") == 0) {
bss->sae_sync = atoi(pos);
} else if (os_strcmp(buf, "sae_groups") == 0) {
if (hostapd_parse_intlist(&bss->sae_groups, pos)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid sae_groups value '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "sae_require_mfp") == 0) {
bss->sae_require_mfp = atoi(pos);
} else if (os_strcmp(buf, "sae_confirm_immediate") == 0) {
bss->sae_confirm_immediate = atoi(pos);
} else if (os_strcmp(buf, "sae_pwe") == 0) {
bss->sae_pwe = atoi(pos);
} else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
int val = atoi(pos);
if (val < 0 || val > 255) {
wpa_printf(MSG_ERROR, "Line %d: Invalid local_pwr_constraint %d (expected 0..255)",
line, val);
return 1;
}
conf->local_pwr_constraint = val;
} else if (os_strcmp(buf, "spectrum_mgmt_required") == 0) {
conf->spectrum_mgmt_required = atoi(pos);
} else if (os_strcmp(buf, "wowlan_triggers") == 0) {
os_free(bss->wowlan_triggers);
bss->wowlan_triggers = os_strdup(pos);
#ifdef CONFIG_FST
} else if (os_strcmp(buf, "fst_group_id") == 0) {
size_t len = os_strlen(pos);
if (!len || len >= sizeof(conf->fst_cfg.group_id)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid fst_group_id value '%s'",
line, pos);
return 1;
}
if (conf->fst_cfg.group_id[0]) {
wpa_printf(MSG_ERROR,
"Line %d: Duplicate fst_group value '%s'",
line, pos);
return 1;
}
os_strlcpy(conf->fst_cfg.group_id, pos,
sizeof(conf->fst_cfg.group_id));
} else if (os_strcmp(buf, "fst_priority") == 0) {
char *endp;
long int val;
if (!*pos) {
wpa_printf(MSG_ERROR,
"Line %d: fst_priority value not supplied (expected 1..%u)",
line, FST_MAX_PRIO_VALUE);
return -1;
}
val = strtol(pos, &endp, 0);
if (*endp || val < 1 || val > FST_MAX_PRIO_VALUE) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid fst_priority %ld (%s) (expected 1..%u)",
line, val, pos, FST_MAX_PRIO_VALUE);
return 1;
}
conf->fst_cfg.priority = (u8) val;
} else if (os_strcmp(buf, "fst_llt") == 0) {
char *endp;
long int val;
if (!*pos) {
wpa_printf(MSG_ERROR,
"Line %d: fst_llt value not supplied (expected 1..%u)",
line, FST_MAX_LLT_MS);
return -1;
}
val = strtol(pos, &endp, 0);
if (*endp || val < 1 ||
(unsigned long int) val > FST_MAX_LLT_MS) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)",
line, val, pos, FST_MAX_LLT_MS);
return 1;
}
conf->fst_cfg.llt = (u32) val;
#endif /* CONFIG_FST */
} else if (os_strcmp(buf, "track_sta_max_num") == 0) {
conf->track_sta_max_num = atoi(pos);
} else if (os_strcmp(buf, "track_sta_max_age") == 0) {
conf->track_sta_max_age = atoi(pos);
} else if (os_strcmp(buf, "no_probe_resp_if_seen_on") == 0) {
os_free(bss->no_probe_resp_if_seen_on);
bss->no_probe_resp_if_seen_on = os_strdup(pos);
} else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) {
os_free(bss->no_auth_if_seen_on);
bss->no_auth_if_seen_on = os_strdup(pos);
} else if (os_strcmp(buf, "lci") == 0) {
wpabuf_free(conf->lci);
conf->lci = wpabuf_parse_bin(pos);
if (conf->lci && wpabuf_len(conf->lci) == 0) {
wpabuf_free(conf->lci);
conf->lci = NULL;
}
} else if (os_strcmp(buf, "civic") == 0) {
wpabuf_free(conf->civic);
conf->civic = wpabuf_parse_bin(pos);
if (conf->civic && wpabuf_len(conf->civic) == 0) {
wpabuf_free(conf->civic);
conf->civic = NULL;
}
} else if (os_strcmp(buf, "rrm_neighbor_report") == 0) {
if (atoi(pos))
bss->radio_measurements[0] |=
WLAN_RRM_CAPS_NEIGHBOR_REPORT;
} else if (os_strcmp(buf, "rrm_beacon_report") == 0) {
if (atoi(pos))
bss->radio_measurements[0] |=
WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
} else if (os_strcmp(buf, "gas_address3") == 0) {
bss->gas_address3 = atoi(pos);
} else if (os_strcmp(buf, "stationary_ap") == 0) {
conf->stationary_ap = atoi(pos);
} else if (os_strcmp(buf, "ftm_responder") == 0) {
bss->ftm_responder = atoi(pos);
} else if (os_strcmp(buf, "ftm_initiator") == 0) {
bss->ftm_initiator = atoi(pos);
#ifdef CONFIG_FILS
} else if (os_strcmp(buf, "fils_cache_id") == 0) {
if (hexstr2bin(pos, bss->fils_cache_id, FILS_CACHE_ID_LEN)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid fils_cache_id '%s'",
line, pos);
return 1;
}
bss->fils_cache_id_set = 1;
} else if (os_strcmp(buf, "fils_realm") == 0) {
if (parse_fils_realm(bss, pos) < 0)
return 1;
} else if (os_strcmp(buf, "fils_dh_group") == 0) {
bss->fils_dh_group = atoi(pos);
} else if (os_strcmp(buf, "dhcp_server") == 0) {
if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
wpa_printf(MSG_ERROR,
"Line %d: invalid IP address '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "dhcp_rapid_commit_proxy") == 0) {
bss->dhcp_rapid_commit_proxy = atoi(pos);
} else if (os_strcmp(buf, "fils_hlp_wait_time") == 0) {
bss->fils_hlp_wait_time = atoi(pos);
} else if (os_strcmp(buf, "dhcp_server_port") == 0) {
bss->dhcp_server_port = atoi(pos);
} else if (os_strcmp(buf, "dhcp_relay_port") == 0) {
bss->dhcp_relay_port = atoi(pos);
} else if (os_strcmp(buf, "fils_discovery_min_interval") == 0) {
bss->fils_discovery_min_int = atoi(pos);
} else if (os_strcmp(buf, "fils_discovery_max_interval") == 0) {
bss->fils_discovery_max_int = atoi(pos);
#endif /* CONFIG_FILS */
} else if (os_strcmp(buf, "multicast_to_unicast") == 0) {
bss->multicast_to_unicast = atoi(pos);
} else if (os_strcmp(buf, "broadcast_deauth") == 0) {
bss->broadcast_deauth = atoi(pos);
} else if (os_strcmp(buf, "notify_mgmt_frames") == 0) {
bss->notify_mgmt_frames = atoi(pos);
#ifdef CONFIG_DPP
} else if (os_strcmp(buf, "dpp_name") == 0) {
os_free(bss->dpp_name);
bss->dpp_name = os_strdup(pos);
} else if (os_strcmp(buf, "dpp_mud_url") == 0) {
os_free(bss->dpp_mud_url);
bss->dpp_mud_url = os_strdup(pos);
} else if (os_strcmp(buf, "dpp_connector") == 0) {
os_free(bss->dpp_connector);
bss->dpp_connector = os_strdup(pos);
} else if (os_strcmp(buf, "dpp_netaccesskey") == 0) {
if (parse_wpabuf_hex(line, buf, &bss->dpp_netaccesskey, pos))
return 1;
} else if (os_strcmp(buf, "dpp_netaccesskey_expiry") == 0) {
bss->dpp_netaccesskey_expiry = strtol(pos, NULL, 0);
} else if (os_strcmp(buf, "dpp_csign") == 0) {
if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos))
return 1;
#ifdef CONFIG_DPP2
} else if (os_strcmp(buf, "dpp_controller") == 0) {
if (hostapd_dpp_controller_parse(bss, pos))
return 1;
} else if (os_strcmp(buf, "dpp_configurator_connectivity") == 0) {
bss->dpp_configurator_connectivity = atoi(pos);
} else if (os_strcmp(buf, "dpp_pfs") == 0) {
int val = atoi(pos);
if (val < 0 || val > 2) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid dpp_pfs value '%s'",
line, pos);
return -1;
}
bss->dpp_pfs = val;
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
#ifdef CONFIG_OWE
} else if (os_strcmp(buf, "owe_transition_bssid") == 0) {
if (hwaddr_aton(pos, bss->owe_transition_bssid)) {
wpa_printf(MSG_ERROR,
"Line %d: invalid owe_transition_bssid",
line);
return 1;
}
} else if (os_strcmp(buf, "owe_transition_ssid") == 0) {
size_t slen;
char *str = wpa_config_parse_string(pos, &slen);
if (!str || slen < 1 || slen > SSID_MAX_LEN) {
wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
line, pos);
os_free(str);
return 1;
}
os_memcpy(bss->owe_transition_ssid, str, slen);
bss->owe_transition_ssid_len = slen;
os_free(str);
} else if (os_strcmp(buf, "owe_transition_ifname") == 0) {
os_strlcpy(bss->owe_transition_ifname, pos,
sizeof(bss->owe_transition_ifname));
} else if (os_strcmp(buf, "owe_groups") == 0) {
if (hostapd_parse_intlist(&bss->owe_groups, pos)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid owe_groups value '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "owe_ptk_workaround") == 0) {
bss->owe_ptk_workaround = atoi(pos);
#endif /* CONFIG_OWE */
} else if (os_strcmp(buf, "coloc_intf_reporting") == 0) {
bss->coloc_intf_reporting = atoi(pos);
} else if (os_strcmp(buf, "multi_ap") == 0) {
int val = atoi(pos);
if (val < 0 || val > 3) {
wpa_printf(MSG_ERROR, "Line %d: Invalid multi_ap '%s'",
line, buf);
return -1;
}
bss->multi_ap = val;
} else if (os_strcmp(buf, "rssi_reject_assoc_rssi") == 0) {
conf->rssi_reject_assoc_rssi = atoi(pos);
} else if (os_strcmp(buf, "rssi_reject_assoc_timeout") == 0) {
conf->rssi_reject_assoc_timeout = atoi(pos);
} else if (os_strcmp(buf, "rssi_ignore_probe_request") == 0) {
conf->rssi_ignore_probe_request = atoi(pos);
} else if (os_strcmp(buf, "pbss") == 0) {
bss->pbss = atoi(pos);
} else if (os_strcmp(buf, "transition_disable") == 0) {
bss->transition_disable = strtol(pos, NULL, 16);
#ifdef CONFIG_AIRTIME_POLICY
} else if (os_strcmp(buf, "airtime_mode") == 0) {
int val = atoi(pos);
if (val < 0 || val > AIRTIME_MODE_MAX) {
wpa_printf(MSG_ERROR, "Line %d: Unknown airtime_mode",
line);
return 1;
}
conf->airtime_mode = val;
} else if (os_strcmp(buf, "airtime_update_interval") == 0) {
conf->airtime_update_interval = atoi(pos);
} else if (os_strcmp(buf, "airtime_bss_weight") == 0) {
bss->airtime_weight = atoi(pos);
} else if (os_strcmp(buf, "airtime_bss_limit") == 0) {
int val = atoi(pos);
if (val < 0 || val > 1) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid airtime_bss_limit (must be 0 or 1)",
line);
return 1;
}
bss->airtime_limit = val;
} else if (os_strcmp(buf, "airtime_sta_weight") == 0) {
if (add_airtime_weight(bss, pos) < 0) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid airtime weight '%s'",
line, pos);
return 1;
}
#endif /* CONFIG_AIRTIME_POLICY */
#ifdef CONFIG_MACSEC
} else if (os_strcmp(buf, "macsec_policy") == 0) {
int macsec_policy = atoi(pos);
if (macsec_policy < 0 || macsec_policy > 1) {
wpa_printf(MSG_ERROR,
"Line %d: invalid macsec_policy (%d): '%s'.",
line, macsec_policy, pos);
return 1;
}
bss->macsec_policy = macsec_policy;
} else if (os_strcmp(buf, "macsec_integ_only") == 0) {
int macsec_integ_only = atoi(pos);
if (macsec_integ_only < 0 || macsec_integ_only > 1) {
wpa_printf(MSG_ERROR,
"Line %d: invalid macsec_integ_only (%d): '%s'.",
line, macsec_integ_only, pos);
return 1;
}
bss->macsec_integ_only = macsec_integ_only;
} else if (os_strcmp(buf, "macsec_replay_protect") == 0) {
int macsec_replay_protect = atoi(pos);
if (macsec_replay_protect < 0 || macsec_replay_protect > 1) {
wpa_printf(MSG_ERROR,
"Line %d: invalid macsec_replay_protect (%d): '%s'.",
line, macsec_replay_protect, pos);
return 1;
}
bss->macsec_replay_protect = macsec_replay_protect;
} else if (os_strcmp(buf, "macsec_replay_window") == 0) {
bss->macsec_replay_window = atoi(pos);
} else if (os_strcmp(buf, "macsec_port") == 0) {
int macsec_port = atoi(pos);
if (macsec_port < 1 || macsec_port > 65534) {
wpa_printf(MSG_ERROR,
"Line %d: invalid macsec_port (%d): '%s'.",
line, macsec_port, pos);
return 1;
}
bss->macsec_port = macsec_port;
} else if (os_strcmp(buf, "mka_priority") == 0) {
int mka_priority = atoi(pos);
if (mka_priority < 0 || mka_priority > 255) {
wpa_printf(MSG_ERROR,
"Line %d: invalid mka_priority (%d): '%s'.",
line, mka_priority, pos);
return 1;
}
bss->mka_priority = mka_priority;
} else if (os_strcmp(buf, "mka_cak") == 0) {
size_t len = os_strlen(pos);
if (len > 2 * MACSEC_CAK_MAX_LEN ||
(len != 2 * 16 && len != 2 * 32) ||
hexstr2bin(pos, bss->mka_cak, len / 2)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.",
line, pos);
return 1;
}
bss->mka_cak_len = len / 2;
bss->mka_psk_set |= MKA_PSK_SET_CAK;
} else if (os_strcmp(buf, "mka_ckn") == 0) {
size_t len = os_strlen(pos);
if (len > 2 * MACSEC_CKN_MAX_LEN || /* too long */
len < 2 || /* too short */
len % 2 != 0 /* not an integral number of bytes */) {
wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
line, pos);
return 1;
}
bss->mka_ckn_len = len / 2;
if (hexstr2bin(pos, bss->mka_ckn, bss->mka_ckn_len)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
line, pos);
return -1;
}
bss->mka_psk_set |= MKA_PSK_SET_CKN;
#endif /* CONFIG_MACSEC */
} else if (os_strcmp(buf, "disable_11n") == 0) {
bss->disable_11n = !!atoi(pos);
} else if (os_strcmp(buf, "disable_11ac") == 0) {
bss->disable_11ac = !!atoi(pos);
} else if (os_strcmp(buf, "disable_11ax") == 0) {
bss->disable_11ax = !!atoi(pos);
#ifdef CONFIG_PASN
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcmp(buf, "force_kdk_derivation") == 0) {
bss->force_kdk_derivation = atoi(pos);
} else if (os_strcmp(buf, "pasn_corrupt_mic") == 0) {
bss->pasn_corrupt_mic = atoi(pos);
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strcmp(buf, "pasn_groups") == 0) {
if (hostapd_parse_intlist(&bss->pasn_groups, pos)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid pasn_groups value '%s'",
line, pos);
return 1;
}
} else if (os_strcmp(buf, "pasn_comeback_after") == 0) {
bss->pasn_comeback_after = atoi(pos);
#endif /* CONFIG_PASN */
} else if (os_strcmp(buf, "ext_capa_mask") == 0) {
if (get_hex_config(bss->ext_capa_mask, EXT_CAPA_MAX_LEN,
line, "ext_capa_mask", pos))
return 1;
} else if (os_strcmp(buf, "ext_capa") == 0) {
if (get_hex_config(bss->ext_capa, EXT_CAPA_MAX_LEN,
line, "ext_capa", pos))
return 1;
+ } else if (os_strcmp(buf, "rnr") == 0) {
+ bss->rnr = atoi(pos);
} else {
wpa_printf(MSG_ERROR,
"Line %d: unknown configuration item '%s'",
line, buf);
return 1;
}
return 0;
}
/**
* hostapd_config_read - Read and parse a configuration file
* @fname: Configuration file name (including path, if needed)
* Returns: Allocated configuration data structure
*/
struct hostapd_config * hostapd_config_read(const char *fname)
{
struct hostapd_config *conf;
FILE *f;
char buf[4096], *pos;
int line = 0;
int errors = 0;
size_t i;
f = fopen(fname, "r");
if (f == NULL) {
wpa_printf(MSG_ERROR, "Could not open configuration file '%s' "
"for reading.", fname);
return NULL;
}
conf = hostapd_config_defaults();
if (conf == NULL) {
fclose(f);
return NULL;
}
/* set default driver based on configuration */
conf->driver = wpa_drivers[0];
if (conf->driver == NULL) {
wpa_printf(MSG_ERROR, "No driver wrappers registered!");
hostapd_config_free(conf);
fclose(f);
return NULL;
}
conf->last_bss = conf->bss[0];
while (fgets(buf, sizeof(buf), f)) {
struct hostapd_bss_config *bss;
bss = conf->last_bss;
line++;
if (buf[0] == '#')
continue;
pos = buf;
while (*pos != '\0') {
if (*pos == '\n') {
*pos = '\0';
break;
}
pos++;
}
if (buf[0] == '\0')
continue;
pos = os_strchr(buf, '=');
if (pos == NULL) {
wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'",
line, buf);
errors++;
continue;
}
*pos = '\0';
pos++;
errors += hostapd_config_fill(conf, bss, buf, pos, line);
}
fclose(f);
for (i = 0; i < conf->num_bss; i++)
hostapd_set_security_params(conf->bss[i], 1);
if (hostapd_config_check(conf, 1))
errors++;
#ifndef WPA_IGNORE_CONFIG_ERRORS
if (errors) {
wpa_printf(MSG_ERROR, "%d errors found in configuration file "
"'%s'", errors, fname);
hostapd_config_free(conf);
conf = NULL;
}
#endif /* WPA_IGNORE_CONFIG_ERRORS */
return conf;
}
int hostapd_set_iface(struct hostapd_config *conf,
struct hostapd_bss_config *bss, const char *field,
char *value)
{
int errors;
size_t i;
errors = hostapd_config_fill(conf, bss, field, value, 0);
if (errors) {
wpa_printf(MSG_INFO, "Failed to set configuration field '%s' "
"to value '%s'", field, value);
return -1;
}
for (i = 0; i < conf->num_bss; i++)
hostapd_set_security_params(conf->bss[i], 0);
if (hostapd_config_check(conf, 0)) {
wpa_printf(MSG_ERROR, "Configuration check failed");
return -1;
}
return 0;
}
diff --git a/contrib/wpa/hostapd/ctrl_iface.c b/contrib/wpa/hostapd/ctrl_iface.c
index 4a2d60627070..6c99a3105f49 100644
--- a/contrib/wpa/hostapd/ctrl_iface.c
+++ b/contrib/wpa/hostapd/ctrl_iface.c
@@ -1,5265 +1,5280 @@
/*
* hostapd / UNIX domain socket -based control interface
* Copyright (c) 2004-2018, 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"
#ifndef CONFIG_NATIVE_WINDOWS
#ifdef CONFIG_TESTING_OPTIONS
#ifdef __NetBSD__
#include <net/if_ether.h>
#else
#include <net/ethernet.h>
#endif
#include <netinet/ip.h>
#endif /* CONFIG_TESTING_OPTIONS */
#include <sys/un.h>
#include <sys/stat.h>
#include <stddef.h>
#ifdef CONFIG_CTRL_IFACE_UDP
#include <netdb.h>
#endif /* CONFIG_CTRL_IFACE_UDP */
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/module_tests.h"
#include "common/version.h"
#include "common/ieee802_11_defs.h"
#include "common/ctrl_iface_common.h"
#ifdef CONFIG_DPP
#include "common/dpp.h"
#endif /* CONFIG_DPP */
#include "common/wpa_ctrl.h"
#include "common/ptksa_cache.h"
#include "crypto/tls.h"
#include "drivers/driver.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "radius/radius_client.h"
#include "radius/radius_server.h"
#include "l2_packet/l2_packet.h"
#include "ap/hostapd.h"
#include "ap/ap_config.h"
#include "ap/ieee802_1x.h"
#include "ap/wpa_auth.h"
#include "ap/pmksa_cache_auth.h"
#include "ap/ieee802_11.h"
#include "ap/sta_info.h"
#include "ap/wps_hostapd.h"
#include "ap/ctrl_iface_ap.h"
#include "ap/ap_drv_ops.h"
#include "ap/hs20.h"
#include "ap/wnm_ap.h"
#include "ap/wpa_auth.h"
#include "ap/beacon.h"
#include "ap/neighbor_db.h"
#include "ap/rrm.h"
#include "ap/dpp_hostapd.h"
#include "ap/dfs.h"
#include "wps/wps_defs.h"
#include "wps/wps.h"
#include "fst/fst_ctrl_iface.h"
#include "config_file.h"
#include "ctrl_iface.h"
#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
#ifdef CONFIG_CTRL_IFACE_UDP
#define HOSTAPD_CTRL_IFACE_PORT 8877
#define HOSTAPD_CTRL_IFACE_PORT_LIMIT 50
#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT 8878
#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT 50
#endif /* CONFIG_CTRL_IFACE_UDP */
static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
enum wpa_msg_type type,
const char *buf, size_t len);
static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
struct sockaddr_storage *from,
socklen_t fromlen, const char *input)
{
return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input);
}
static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
struct sockaddr_storage *from,
socklen_t fromlen)
{
return ctrl_iface_detach(&hapd->ctrl_dst, from, fromlen);
}
static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
struct sockaddr_storage *from,
socklen_t fromlen,
char *level)
{
return ctrl_iface_level(&hapd->ctrl_dst, from, fromlen, level);
}
static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
const char *txtaddr)
{
u8 addr[ETH_ALEN];
struct sta_info *sta;
wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr);
if (hwaddr_aton(txtaddr, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (sta)
return 0;
wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface "
"notification", MAC2STR(addr));
sta = ap_sta_add(hapd, addr);
if (sta == NULL)
return -1;
hostapd_new_assoc_sta(hapd, sta, 0);
return 0;
}
#ifdef NEED_AP_MLME
static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
const char *txtaddr)
{
u8 addr[ETH_ALEN];
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr);
if (hwaddr_aton(txtaddr, addr) ||
os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0)
return -1;
ieee802_11_send_sa_query_req(hapd, addr, trans_id);
return 0;
}
#endif /* NEED_AP_MLME */
#ifdef CONFIG_WPS
static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
{
char *pin = os_strchr(txt, ' ');
char *timeout_txt;
int timeout;
u8 addr_buf[ETH_ALEN], *addr = NULL;
char *pos;
if (pin == NULL)
return -1;
*pin++ = '\0';
timeout_txt = os_strchr(pin, ' ');
if (timeout_txt) {
*timeout_txt++ = '\0';
timeout = atoi(timeout_txt);
pos = os_strchr(timeout_txt, ' ');
if (pos) {
*pos++ = '\0';
if (hwaddr_aton(pos, addr_buf) == 0)
addr = addr_buf;
}
} else
timeout = 0;
return hostapd_wps_add_pin(hapd, addr, txt, pin, timeout);
}
static int hostapd_ctrl_iface_wps_check_pin(
struct hostapd_data *hapd, 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 hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd,
char *pos)
{
size_t len;
struct wpabuf *buf;
int ret;
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 = hostapd_wps_nfc_tag_read(hapd, buf);
wpabuf_free(buf);
return ret;
}
static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd,
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 = hostapd_wps_nfc_config_token(hapd, 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 hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd,
char *reply, size_t max_len,
int ndef)
{
struct wpabuf *buf;
int res;
buf = hostapd_wps_nfc_token_gen(hapd, 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 hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd,
char *cmd, char *reply,
size_t max_len)
{
if (os_strcmp(cmd, "WPS") == 0)
return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
max_len, 0);
if (os_strcmp(cmd, "NDEF") == 0)
return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply,
max_len, 1);
if (os_strcmp(cmd, "enable") == 0)
return hostapd_wps_nfc_token_enable(hapd);
if (os_strcmp(cmd, "disable") == 0) {
hostapd_wps_nfc_token_disable(hapd);
return 0;
}
return -1;
}
static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd,
char *cmd, char *reply,
size_t max_len)
{
struct wpabuf *buf;
int res;
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-CR") == 0)
buf = hostapd_wps_nfc_hs_cr(hapd, ndef);
else
buf = NULL;
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 hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
char *cmd)
{
size_t len;
struct wpabuf *req, *sel;
int ret;
char *pos, *role, *type, *pos2;
role = cmd;
pos = os_strchr(role, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
type = pos;
pos = os_strchr(type, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
pos2 = os_strchr(pos, ' ');
if (pos2 == NULL)
return -1;
*pos2++ = '\0';
len = os_strlen(pos);
if (len & 0x01)
return -1;
len /= 2;
req = wpabuf_alloc(len);
if (req == NULL)
return -1;
if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
wpabuf_free(req);
return -1;
}
len = os_strlen(pos2);
if (len & 0x01) {
wpabuf_free(req);
return -1;
}
len /= 2;
sel = wpabuf_alloc(len);
if (sel == NULL) {
wpabuf_free(req);
return -1;
}
if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
wpabuf_free(req);
wpabuf_free(sel);
return -1;
}
if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) {
ret = hostapd_wps_nfc_report_handover(hapd, req, sel);
} else {
wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
"reported: role=%s type=%s", role, type);
ret = -1;
}
wpabuf_free(req);
wpabuf_free(sel);
return ret;
}
#endif /* CONFIG_WPS_NFC */
static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
char *buf, size_t buflen)
{
int timeout = 300;
char *pos;
const char *pin_txt;
pos = os_strchr(txt, ' ');
if (pos)
*pos++ = '\0';
if (os_strcmp(txt, "disable") == 0) {
hostapd_wps_ap_pin_disable(hapd);
return os_snprintf(buf, buflen, "OK\n");
}
if (os_strcmp(txt, "random") == 0) {
if (pos)
timeout = atoi(pos);
pin_txt = hostapd_wps_ap_pin_random(hapd, timeout);
if (pin_txt == NULL)
return -1;
return os_snprintf(buf, buflen, "%s", pin_txt);
}
if (os_strcmp(txt, "get") == 0) {
pin_txt = hostapd_wps_ap_pin_get(hapd);
if (pin_txt == NULL)
return -1;
return os_snprintf(buf, buflen, "%s", pin_txt);
}
if (os_strcmp(txt, "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 (hostapd_wps_ap_pin_set(hapd, pin, timeout) < 0)
return -1;
return os_snprintf(buf, buflen, "%s", pin);
}
return -1;
}
static int hostapd_ctrl_iface_wps_config(struct hostapd_data *hapd, char *txt)
{
char *pos;
char *ssid, *auth, *encr = NULL, *key = NULL;
ssid = txt;
pos = os_strchr(txt, ' ');
if (!pos)
return -1;
*pos++ = '\0';
auth = pos;
pos = os_strchr(pos, ' ');
if (pos) {
*pos++ = '\0';
encr = pos;
pos = os_strchr(pos, ' ');
if (pos) {
*pos++ = '\0';
key = pos;
}
}
return hostapd_wps_config_ap(hapd, ssid, auth, encr, key);
}
static const char * pbc_status_str(enum pbc_status status)
{
switch (status) {
case WPS_PBC_STATUS_DISABLE:
return "Disabled";
case WPS_PBC_STATUS_ACTIVE:
return "Active";
case WPS_PBC_STATUS_TIMEOUT:
return "Timed-out";
case WPS_PBC_STATUS_OVERLAP:
return "Overlap";
default:
return "Unknown";
}
}
static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
int ret;
char *pos, *end;
pos = buf;
end = buf + buflen;
ret = os_snprintf(pos, end - pos, "PBC Status: %s\n",
pbc_status_str(hapd->wps_stats.pbc_status));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
ret = os_snprintf(pos, end - pos, "Last WPS result: %s\n",
(hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
"Success":
(hapd->wps_stats.status == WPS_STATUS_FAILURE ?
"Failed" : "None")));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
/* If status == Failure - Add possible Reasons */
if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
hapd->wps_stats.failure_reason > 0) {
ret = os_snprintf(pos, end - pos,
"Failure Reason: %s\n",
wps_ei_str(hapd->wps_stats.failure_reason));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (hapd->wps_stats.status) {
ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n",
MAC2STR(hapd->wps_stats.peer_addr));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
return pos - buf;
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_HS20
static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd,
const char *cmd)
{
u8 addr[ETH_ALEN];
const char *url;
if (hwaddr_aton(cmd, addr))
return -1;
url = cmd + 17;
if (*url == '\0') {
url = NULL;
} else {
if (*url != ' ')
return -1;
url++;
if (*url == '\0')
url = NULL;
}
return hs20_send_wnm_notification(hapd, addr, 1, url);
}
static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
const char *cmd)
{
u8 addr[ETH_ALEN];
int code, reauth_delay, ret;
const char *pos;
size_t url_len;
struct wpabuf *req;
/* <STA MAC Addr> <Code(0/1)> <Re-auth-Delay(sec)> [URL] */
if (hwaddr_aton(cmd, addr))
return -1;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
pos++;
code = atoi(pos);
pos = os_strchr(pos, ' ');
if (pos == NULL)
return -1;
pos++;
reauth_delay = atoi(pos);
url_len = 0;
pos = os_strchr(pos, ' ');
if (pos) {
pos++;
url_len = os_strlen(pos);
}
req = wpabuf_alloc(4 + url_len);
if (req == NULL)
return -1;
wpabuf_put_u8(req, code);
wpabuf_put_le16(req, reauth_delay);
wpabuf_put_u8(req, url_len);
if (pos)
wpabuf_put_data(req, pos, url_len);
wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " MACSTR
" to indicate imminent deauthentication (code=%d "
"reauth_delay=%d)", MAC2STR(addr), code, reauth_delay);
ret = hs20_send_wnm_notification_deauth_req(hapd, addr, req);
wpabuf_free(req);
return ret;
}
#endif /* CONFIG_HS20 */
#ifdef CONFIG_INTERWORKING
static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd,
const char *cmd)
{
u8 qos_map_set[16 + 2 * 21], count = 0;
const char *pos = cmd;
int val, ret;
for (;;) {
if (count == sizeof(qos_map_set)) {
wpa_printf(MSG_ERROR, "Too many qos_map_set parameters");
return -1;
}
val = atoi(pos);
if (val < 0 || val > 255) {
wpa_printf(MSG_INFO, "Invalid QoS Map Set");
return -1;
}
qos_map_set[count++] = val;
pos = os_strchr(pos, ',');
if (!pos)
break;
pos++;
}
if (count < 16 || count & 1) {
wpa_printf(MSG_INFO, "Invalid QoS Map Set");
return -1;
}
ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count);
if (ret) {
wpa_printf(MSG_INFO, "Failed to set QoS Map Set");
return -1;
}
os_memcpy(hapd->conf->qos_map_set, qos_map_set, count);
hapd->conf->qos_map_set_len = count;
return 0;
}
static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
const char *cmd)
{
u8 addr[ETH_ALEN];
struct sta_info *sta;
struct wpabuf *buf;
u8 *qos_map_set = hapd->conf->qos_map_set;
u8 qos_map_set_len = hapd->conf->qos_map_set_len;
int ret;
if (!qos_map_set_len) {
wpa_printf(MSG_INFO, "QoS Map Set is not set");
return -1;
}
if (hwaddr_aton(cmd, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "Station " MACSTR " not found "
"for QoS Map Configuration message",
MAC2STR(addr));
return -1;
}
if (!sta->qos_map_enabled) {
wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate "
"support for QoS Map", MAC2STR(addr));
return -1;
}
buf = wpabuf_alloc(2 + 2 + qos_map_set_len);
if (buf == NULL)
return -1;
wpabuf_put_u8(buf, WLAN_ACTION_QOS);
wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG);
/* QoS Map Set Element */
wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET);
wpabuf_put_u8(buf, qos_map_set_len);
wpabuf_put_data(buf, qos_map_set, qos_map_set_len);
ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
wpabuf_head(buf), wpabuf_len(buf));
wpabuf_free(buf);
return ret;
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_WNM_AP
static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
const char *cmd)
{
u8 addr[ETH_ALEN];
int disassoc_timer;
struct sta_info *sta;
if (hwaddr_aton(cmd, addr))
return -1;
if (cmd[17] != ' ')
return -1;
disassoc_timer = atoi(cmd + 17);
sta = ap_get_sta(hapd, addr);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "Station " MACSTR
" not found for disassociation imminent message",
MAC2STR(addr));
return -1;
}
return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
}
static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
const char *cmd)
{
u8 addr[ETH_ALEN];
const char *url, *timerstr;
int disassoc_timer;
struct sta_info *sta;
if (hwaddr_aton(cmd, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "Station " MACSTR
" not found for ESS disassociation imminent message",
MAC2STR(addr));
return -1;
}
timerstr = cmd + 17;
if (*timerstr != ' ')
return -1;
timerstr++;
disassoc_timer = atoi(timerstr);
if (disassoc_timer < 0 || disassoc_timer > 65535)
return -1;
url = os_strchr(timerstr, ' ');
if (url == NULL)
return -1;
url++;
return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
}
static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
const char *cmd)
{
u8 addr[ETH_ALEN];
const char *pos, *end;
int disassoc_timer = 0;
struct sta_info *sta;
- u8 req_mode = 0, valid_int = 0x01;
+ u8 req_mode = 0, valid_int = 0x01, dialog_token = 0x01;
u8 bss_term_dur[12];
char *url = NULL;
int ret;
u8 nei_rep[1000];
int nei_len;
u8 mbo[10];
size_t mbo_len = 0;
if (hwaddr_aton(cmd, addr)) {
wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
return -1;
}
sta = ap_get_sta(hapd, addr);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "Station " MACSTR
" not found for BSS TM Request message",
MAC2STR(addr));
return -1;
}
pos = os_strstr(cmd, " disassoc_timer=");
if (pos) {
pos += 16;
disassoc_timer = atoi(pos);
if (disassoc_timer < 0 || disassoc_timer > 65535) {
wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
return -1;
}
}
pos = os_strstr(cmd, " valid_int=");
if (pos) {
pos += 11;
valid_int = atoi(pos);
}
+ pos = os_strstr(cmd, " dialog_token=");
+ if (pos) {
+ pos += 14;
+ dialog_token = atoi(pos);
+ }
+
pos = os_strstr(cmd, " bss_term=");
if (pos) {
pos += 10;
req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
/* TODO: TSF configurable/learnable */
bss_term_dur[0] = 4; /* Subelement ID */
bss_term_dur[1] = 10; /* Length */
os_memset(&bss_term_dur[2], 0, 8);
end = os_strchr(pos, ',');
if (end == NULL) {
wpa_printf(MSG_DEBUG, "Invalid bss_term data");
return -1;
}
end++;
WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
}
nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep,
sizeof(nei_rep));
if (nei_len < 0)
return -1;
pos = os_strstr(cmd, " url=");
if (pos) {
size_t len;
pos += 5;
end = os_strchr(pos, ' ');
if (end)
len = end - pos;
else
len = os_strlen(pos);
url = os_malloc(len + 1);
if (url == NULL)
return -1;
os_memcpy(url, pos, len);
url[len] = '\0';
req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
}
if (os_strstr(cmd, " pref=1"))
req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
if (os_strstr(cmd, " abridged=1"))
req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
if (os_strstr(cmd, " disassoc_imminent=1"))
req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
#ifdef CONFIG_MBO
pos = os_strstr(cmd, "mbo=");
if (pos) {
unsigned int mbo_reason, cell_pref, reassoc_delay;
u8 *mbo_pos = mbo;
ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason,
&reassoc_delay, &cell_pref);
if (ret != 3) {
wpa_printf(MSG_DEBUG,
"MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>");
ret = -1;
goto fail;
}
if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
wpa_printf(MSG_DEBUG,
"Invalid MBO transition reason code %u",
mbo_reason);
ret = -1;
goto fail;
}
/* Valid values for Cellular preference are: 0, 1, 255 */
if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) {
wpa_printf(MSG_DEBUG,
"Invalid MBO cellular capability %u",
cell_pref);
ret = -1;
goto fail;
}
if (reassoc_delay > 65535 ||
(reassoc_delay &&
!(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
wpa_printf(MSG_DEBUG,
"MBO: Assoc retry delay is only valid in disassoc imminent mode");
ret = -1;
goto fail;
}
*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
*mbo_pos++ = 1;
*mbo_pos++ = mbo_reason;
*mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
*mbo_pos++ = 1;
*mbo_pos++ = cell_pref;
if (reassoc_delay) {
*mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
*mbo_pos++ = 2;
WPA_PUT_LE16(mbo_pos, reassoc_delay);
mbo_pos += 2;
}
mbo_len = mbo_pos - mbo;
}
#endif /* CONFIG_MBO */
ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
- valid_int, bss_term_dur, url,
+ valid_int, bss_term_dur, dialog_token, url,
nei_len ? nei_rep : NULL, nei_len,
mbo_len ? mbo : NULL, mbo_len);
#ifdef CONFIG_MBO
fail:
#endif /* CONFIG_MBO */
os_free(url);
return ret;
}
static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd,
const char *cmd)
{
u8 addr[ETH_ALEN];
struct sta_info *sta;
const char *pos;
unsigned int auto_report, timeout;
if (hwaddr_aton(cmd, addr)) {
wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
return -1;
}
sta = ap_get_sta(hapd, addr);
if (!sta) {
wpa_printf(MSG_DEBUG, "Station " MACSTR
" not found for Collocated Interference Request",
MAC2STR(addr));
return -1;
}
pos = cmd + 17;
if (*pos != ' ')
return -1;
pos++;
auto_report = atoi(pos);
pos = os_strchr(pos, ' ');
if (!pos)
return -1;
pos++;
timeout = atoi(pos);
return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout);
}
#endif /* CONFIG_WNM_AP */
static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
int ret = 0;
char *pos, *end;
pos = buf;
end = buf + buflen;
WPA_ASSERT(hapd->conf->wpa_key_mgmt);
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
ret = os_snprintf(pos, end - pos, "WPA-PSK ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
ret = os_snprintf(pos, end - pos, "WPA-EAP ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_IEEE80211R_AP
if (hapd->conf->wpa_key_mgmt & WPA_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 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
ret = os_snprintf(pos, end - pos, "FT-EAP ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_SHA384
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_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 */
#ifdef CONFIG_SAE
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
ret = os_snprintf(pos, end - pos, "FT-SAE ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
if (hapd->conf->wpa_key_mgmt & WPA_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 (hapd->conf->wpa_key_mgmt & WPA_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_FILS */
#endif /* CONFIG_IEEE80211R_AP */
if (hapd->conf->wpa_key_mgmt & WPA_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;
}
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#ifdef CONFIG_SAE
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
ret = os_snprintf(pos, end - pos, "SAE ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_SAE */
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (hapd->conf->wpa_key_mgmt &
WPA_KEY_MGMT_IEEE8021X_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;
}
#ifdef CONFIG_FILS
if (hapd->conf->wpa_key_mgmt & WPA_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 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
ret = os_snprintf(pos, end - pos, "FILS-SHA384 ");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
if (hapd->conf->wpa_key_mgmt & WPA_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 (hapd->conf->wpa_key_mgmt & WPA_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 */
if (pos > buf && *(pos - 1) == ' ') {
*(pos - 1) = '\0';
pos--;
}
return pos - buf;
}
static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
int ret;
char *pos, *end;
pos = buf;
end = buf + buflen;
ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n"
"ssid=%s\n",
MAC2STR(hapd->own_addr),
wpa_ssid_txt(hapd->conf->ssid.ssid,
hapd->conf->ssid.ssid_len));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
#ifdef CONFIG_WPS
ret = os_snprintf(pos, end - pos, "wps_state=%s\n",
hapd->conf->wps_state == 0 ? "disabled" :
(hapd->conf->wps_state == 1 ? "not configured" :
"configured"));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
if (hapd->conf->wps_state && hapd->conf->wpa &&
hapd->conf->ssid.wpa_passphrase) {
ret = os_snprintf(pos, end - pos, "passphrase=%s\n",
hapd->conf->ssid.wpa_passphrase);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (hapd->conf->wps_state && hapd->conf->wpa &&
hapd->conf->ssid.wpa_psk &&
hapd->conf->ssid.wpa_psk->group) {
char hex[PMK_LEN * 2 + 1];
wpa_snprintf_hex(hex, sizeof(hex),
hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
ret = os_snprintf(pos, end - pos, "psk=%s\n", hex);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (hapd->conf->multi_ap) {
struct hostapd_ssid *ssid = &hapd->conf->multi_ap_backhaul_ssid;
ret = os_snprintf(pos, end - pos, "multi_ap=%d\n",
hapd->conf->multi_ap);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
if (ssid->ssid_len) {
ret = os_snprintf(pos, end - pos,
"multi_ap_backhaul_ssid=%s\n",
wpa_ssid_txt(ssid->ssid,
ssid->ssid_len));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (hapd->conf->wps_state && hapd->conf->wpa &&
ssid->wpa_passphrase) {
ret = os_snprintf(pos, end - pos,
"multi_ap_backhaul_wpa_passphrase=%s\n",
ssid->wpa_passphrase);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (hapd->conf->wps_state && hapd->conf->wpa &&
ssid->wpa_psk &&
ssid->wpa_psk->group) {
char hex[PMK_LEN * 2 + 1];
wpa_snprintf_hex(hex, sizeof(hex), ssid->wpa_psk->psk,
PMK_LEN);
ret = os_snprintf(pos, end - pos,
"multi_ap_backhaul_wpa_psk=%s\n",
hex);
forced_memzero(hex, sizeof(hex));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
}
#endif /* CONFIG_WPS */
if (hapd->conf->wpa) {
ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
ret = os_snprintf(pos, end - pos, "key_mgmt=");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos);
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (hapd->conf->wpa) {
ret = os_snprintf(pos, end - pos, "group_cipher=%s\n",
wpa_cipher_txt(hapd->conf->wpa_group));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) {
ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher=");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise,
" ");
if (ret < 0)
return pos - buf;
pos += ret;
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) {
ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher=");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
ret = wpa_write_ciphers(pos, end, hapd->conf->wpa_pairwise,
" ");
if (ret < 0)
return pos - buf;
pos += ret;
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (hapd->conf->wpa && hapd->conf->wpa_deny_ptk0_rekey) {
ret = os_snprintf(pos, end - pos, "wpa_deny_ptk0_rekey=%d\n",
hapd->conf->wpa_deny_ptk0_rekey);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->extended_key_id) {
ret = os_snprintf(pos, end - pos, "extended_key_id=%d\n",
hapd->conf->extended_key_id);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
return pos - buf;
}
static void hostapd_disassoc_accept_mac(struct hostapd_data *hapd)
{
struct sta_info *sta;
struct vlan_description vlan_id;
if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED)
return;
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (!hostapd_maclist_found(hapd->conf->accept_mac,
hapd->conf->num_accept_mac,
sta->addr, &vlan_id) ||
(vlan_id.notempty &&
vlan_compare(&vlan_id, sta->vlan_desc)))
ap_sta_disconnect(hapd, sta, sta->addr,
WLAN_REASON_UNSPECIFIED);
}
}
static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
{
struct sta_info *sta;
struct vlan_description vlan_id;
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (hostapd_maclist_found(hapd->conf->deny_mac,
hapd->conf->num_deny_mac, sta->addr,
&vlan_id) &&
(!vlan_id.notempty ||
!vlan_compare(&vlan_id, sta->vlan_desc)))
ap_sta_disconnect(hapd, sta, sta->addr,
WLAN_REASON_UNSPECIFIED);
}
}
static int hostapd_ctrl_iface_set_band(struct hostapd_data *hapd,
const 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;
}
if (hostapd_drv_set_band(hapd, 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(hapd, EVENT_CHANNEL_LIST_CHANGED, &event);
}
return 0;
}
static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, 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 (0) {
#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);
hostapd_wps_update_ie(hapd);
}
- } else if (os_strcasecmp(cmd, "wps_testing_dummy_cred") == 0) {
- wps_testing_dummy_cred = atoi(value);
- wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
- wps_testing_dummy_cred);
+ } 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);
#endif /* CONFIG_WPS_TESTING */
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
hapd->ext_mgmt_frame_handling = atoi(value);
} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
hapd->ext_eapol_frame_io = atoi(value);
} else if (os_strcasecmp(cmd, "force_backlog_bytes") == 0) {
hapd->force_backlog_bytes = atoi(value);
#ifdef CONFIG_DPP
} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
os_free(hapd->dpp_config_obj_override);
hapd->dpp_config_obj_override = os_strdup(value);
} else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) {
os_free(hapd->dpp_discovery_override);
hapd->dpp_discovery_override = os_strdup(value);
} else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) {
os_free(hapd->dpp_groups_override);
hapd->dpp_groups_override = os_strdup(value);
} else if (os_strcasecmp(cmd,
"dpp_ignore_netaccesskey_mismatch") == 0) {
hapd->dpp_ignore_netaccesskey_mismatch = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
dpp_test = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_version_override") == 0) {
dpp_version_override = atoi(value);
#endif /* CONFIG_DPP */
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_MBO
} else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) {
int val;
if (!hapd->conf->mbo_enabled)
return -1;
val = atoi(value);
if (val < 0 || val > 1)
return -1;
hapd->mbo_assoc_disallow = val;
ieee802_11_update_beacons(hapd->iface);
/*
* TODO: Need to configure drivers that do AP MLME offload with
* disallowing station logic.
*/
#endif /* CONFIG_MBO */
#ifdef CONFIG_DPP
} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
os_free(hapd->dpp_configurator_params);
hapd->dpp_configurator_params = os_strdup(value);
} else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) {
hapd->dpp_init_max_tries = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) {
hapd->dpp_init_retry_time = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_resp_wait_time") == 0) {
hapd->dpp_resp_wait_time = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_resp_max_tries") == 0) {
hapd->dpp_resp_max_tries = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_resp_retry_time") == 0) {
hapd->dpp_resp_retry_time = atoi(value);
#endif /* CONFIG_DPP */
} else if (os_strcasecmp(cmd, "setband") == 0) {
ret = hostapd_ctrl_iface_set_band(hapd, value);
} else {
ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
if (ret)
return ret;
if (os_strcasecmp(cmd, "deny_mac_file") == 0) {
hostapd_disassoc_deny_mac(hapd);
} else if (os_strcasecmp(cmd, "accept_mac_file") == 0) {
hostapd_disassoc_accept_mac(hapd);
} else if (os_strncmp(cmd, "wme_ac_", 7) == 0 ||
os_strncmp(cmd, "wmm_ac_", 7) == 0) {
hapd->parameter_set_count++;
if (ieee802_11_update_beacons(hapd->iface))
wpa_printf(MSG_DEBUG,
"Failed to update beacons with WMM parameters");
} else if (os_strcmp(cmd, "wpa_passphrase") == 0 ||
os_strcmp(cmd, "sae_password") == 0 ||
os_strcmp(cmd, "sae_pwe") == 0) {
if (hapd->started)
hostapd_setup_sae_pt(hapd->conf);
} else if (os_strcasecmp(cmd, "transition_disable") == 0) {
wpa_auth_set_transition_disable(hapd->wpa_auth,
hapd->conf->transition_disable);
}
#ifdef CONFIG_TESTING_OPTIONS
if (os_strcmp(cmd, "ft_rsnxe_used") == 0)
wpa_auth_set_ft_rsnxe_used(hapd->wpa_auth,
hapd->conf->ft_rsnxe_used);
else if (os_strcmp(cmd, "oci_freq_override_eapol_m3") == 0)
wpa_auth_set_ocv_override_freq(
hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_EAPOL_M3,
atoi(value));
else if (os_strcmp(cmd, "oci_freq_override_eapol_g1") == 0)
wpa_auth_set_ocv_override_freq(
hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_EAPOL_G1,
atoi(value));
else if (os_strcmp(cmd, "oci_freq_override_ft_assoc") == 0)
wpa_auth_set_ocv_override_freq(
hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_FT_ASSOC,
atoi(value));
else if (os_strcmp(cmd, "oci_freq_override_fils_assoc") == 0)
wpa_auth_set_ocv_override_freq(
hapd->wpa_auth,
WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC, atoi(value));
#endif /* CONFIG_TESTING_OPTIONS */
}
return ret;
}
static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
char *buf, size_t buflen)
{
int res;
wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd);
if (os_strcmp(cmd, "version") == 0) {
res = os_snprintf(buf, buflen, "%s", VERSION_STR);
if (os_snprintf_error(buflen, res))
return -1;
return res;
} else if (os_strcmp(cmd, "tls_library") == 0) {
res = tls_get_library_version(buf, buflen);
if (os_snprintf_error(buflen, res))
return -1;
return res;
}
return -1;
}
static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface)
{
if (hostapd_enable_iface(iface) < 0) {
wpa_printf(MSG_ERROR, "Enabling of interface failed");
return -1;
}
return 0;
}
static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface)
{
if (hostapd_reload_iface(iface) < 0) {
wpa_printf(MSG_ERROR, "Reloading of interface failed");
return -1;
}
return 0;
}
static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
{
if (hostapd_disable_iface(iface) < 0) {
wpa_printf(MSG_ERROR, "Disabling of interface failed");
return -1;
}
return 0;
}
static int
hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data *hapd,
struct sta_info *sta, void *ctx)
{
struct hostapd_wpa_psk *psk;
const u8 *pmk;
int pmk_len;
int pmk_match;
int sta_match;
int bss_match;
int reason;
pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
for (psk = hapd->conf->ssid.wpa_psk; pmk && psk; psk = psk->next) {
pmk_match = PMK_LEN == pmk_len &&
os_memcmp(psk->psk, pmk, pmk_len) == 0;
sta_match = psk->group == 0 &&
os_memcmp(sta->addr, psk->addr, ETH_ALEN) == 0;
bss_match = psk->group == 1;
if (pmk_match && (sta_match || bss_match))
return 0;
}
wpa_printf(MSG_INFO, "STA " MACSTR
" PSK/passphrase no longer valid - disconnect",
MAC2STR(sta->addr));
reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
hostapd_drv_sta_deauth(hapd, sta->addr, reason);
ap_sta_deauthenticate(hapd, sta, reason);
return 0;
}
static int hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data *hapd)
{
struct hostapd_bss_config *conf = hapd->conf;
int err;
hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
err = hostapd_setup_wpa_psk(conf);
if (err < 0) {
wpa_printf(MSG_ERROR, "Reloading WPA-PSK passwords failed: %d",
err);
return -1;
}
ap_for_each_sta(hapd, hostapd_ctrl_iface_kick_mismatch_psk_sta_iter,
NULL);
return 0;
}
#ifdef CONFIG_TESTING_OPTIONS
static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd)
{
union wpa_event_data data;
char *pos, *param;
enum wpa_event_type event;
wpa_printf(MSG_DEBUG, "RADAR TEST: %s", cmd);
os_memset(&data, 0, sizeof(data));
param = os_strchr(cmd, ' ');
if (param == NULL)
return -1;
*param++ = '\0';
if (os_strcmp(cmd, "DETECTED") == 0)
event = EVENT_DFS_RADAR_DETECTED;
else if (os_strcmp(cmd, "CAC-FINISHED") == 0)
event = EVENT_DFS_CAC_FINISHED;
else if (os_strcmp(cmd, "CAC-ABORTED") == 0)
event = EVENT_DFS_CAC_ABORTED;
else if (os_strcmp(cmd, "NOP-FINISHED") == 0)
event = EVENT_DFS_NOP_FINISHED;
else {
wpa_printf(MSG_DEBUG, "Unsupported RADAR test command: %s",
cmd);
return -1;
}
pos = os_strstr(param, "freq=");
if (pos)
data.dfs_event.freq = atoi(pos + 5);
pos = os_strstr(param, "ht_enabled=1");
if (pos)
data.dfs_event.ht_enabled = 1;
pos = os_strstr(param, "chan_offset=");
if (pos)
data.dfs_event.chan_offset = atoi(pos + 12);
pos = os_strstr(param, "chan_width=");
if (pos)
data.dfs_event.chan_width = atoi(pos + 11);
pos = os_strstr(param, "cf1=");
if (pos)
data.dfs_event.cf1 = atoi(pos + 4);
pos = os_strstr(param, "cf2=");
if (pos)
data.dfs_event.cf2 = atoi(pos + 4);
wpa_supplicant_event(hapd, event, &data);
return 0;
}
static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd)
{
size_t len;
u8 *buf;
int res;
wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
len = os_strlen(cmd);
if (len & 1)
return -1;
len /= 2;
buf = os_malloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(cmd, buf, len) < 0) {
os_free(buf);
return -1;
}
res = hostapd_drv_send_mlme(hapd, buf, len, 0, NULL, 0, 0);
os_free(buf);
return res;
}
static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd,
char *cmd)
{
char *pos, *param;
size_t len;
u8 *buf;
int stype = 0, ok = 0;
union wpa_event_data event;
if (!hapd->ext_mgmt_frame_handling)
return -1;
/* stype=<val> ok=<0/1> buf=<frame hexdump> */
wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd);
pos = cmd;
param = os_strstr(pos, "stype=");
if (param) {
param += 6;
stype = atoi(param);
}
param = os_strstr(pos, " ok=");
if (param) {
param += 4;
ok = atoi(param);
}
param = os_strstr(pos, " buf=");
if (!param)
return -1;
param += 5;
len = os_strlen(param);
if (len & 1)
return -1;
len /= 2;
buf = os_malloc(len);
if (!buf || hexstr2bin(param, buf, len) < 0) {
os_free(buf);
return -1;
}
os_memset(&event, 0, sizeof(event));
event.tx_status.type = WLAN_FC_TYPE_MGMT;
event.tx_status.data = buf;
event.tx_status.data_len = len;
event.tx_status.stype = stype;
event.tx_status.ack = ok;
hapd->ext_mgmt_frame_handling = 0;
wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event);
hapd->ext_mgmt_frame_handling = 1;
os_free(buf);
return 0;
}
static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd,
char *cmd)
{
char *pos, *param;
size_t len;
u8 *buf;
int freq = 0, datarate = 0, ssi_signal = 0;
union wpa_event_data event;
if (!hapd->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;
hapd->ext_mgmt_frame_handling = 0;
wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event);
hapd->ext_mgmt_frame_handling = 1;
os_free(buf);
return 0;
}
static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, 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;
}
ieee802_1x_receive(hapd, src, buf, len);
os_free(buf);
return 0;
}
static int hostapd_ctrl_iface_eapol_tx(struct hostapd_data *hapd, char *cmd)
{
char *pos, *pos2;
u8 dst[ETH_ALEN], *buf;
int used, ret;
size_t len;
unsigned int prev;
int encrypt = 0;
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++;
pos2 = os_strchr(pos, ' ');
if (pos2) {
len = pos2 - pos;
encrypt = os_strstr(pos2, "encrypt=1") != NULL;
} else {
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 = hapd->ext_eapol_frame_io;
hapd->ext_eapol_frame_io = 0;
ret = hostapd_wpa_auth_send_eapol(hapd, dst, buf, len, encrypt);
hapd->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 hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
struct hostapd_data *hapd = 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(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s",
MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra);
}
static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd,
char *cmd)
{
int enabled = atoi(cmd);
char *pos;
const char *ifname;
if (!enabled) {
if (hapd->l2_test) {
l2_packet_deinit(hapd->l2_test);
hapd->l2_test = NULL;
wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
"test data: Disabled");
}
return 0;
}
if (hapd->l2_test)
return 0;
pos = os_strstr(cmd, " ifname=");
if (pos)
ifname = pos + 8;
else
ifname = hapd->conf->iface;
hapd->l2_test = l2_packet_init(ifname, hapd->own_addr,
ETHERTYPE_IP, hostapd_data_test_rx,
hapd, 1);
if (hapd->l2_test == NULL)
return -1;
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: Enabled");
return 0;
}
static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, 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 (hapd->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(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2],
sizeof(struct ether_header) + send_len) < 0)
return -1;
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR
" src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
return 0;
}
static int hostapd_ctrl_iface_data_test_frame(struct hostapd_data *hapd,
char *cmd)
{
u8 *buf;
struct ether_header *eth;
struct l2_packet_data *l2 = NULL;
size_t len;
u16 ethertype;
int res = -1;
const char *ifname = hapd->conf->iface;
if (os_strncmp(cmd, "ifname=", 7) == 0) {
cmd += 7;
ifname = cmd;
cmd = os_strchr(cmd, ' ');
if (cmd == NULL)
return -1;
*cmd++ = '\0';
}
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(ifname, hapd->own_addr, ethertype,
hostapd_data_test_rx, hapd, 1);
if (l2 == NULL)
goto done;
res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
wpa_dbg(hapd->msg_ctx, 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 int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
{
#ifdef WPA_TRACE_BFD
char *pos;
wpa_trace_fail_after = atoi(cmd);
pos = os_strchr(cmd, ':');
if (pos) {
pos++;
os_strlcpy(wpa_trace_fail_func, pos,
sizeof(wpa_trace_fail_func));
} else {
wpa_trace_fail_after = 0;
}
return 0;
#else /* WPA_TRACE_BFD */
return -1;
#endif /* WPA_TRACE_BFD */
}
static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
#ifdef WPA_TRACE_BFD
return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
wpa_trace_fail_func);
#else /* WPA_TRACE_BFD */
return -1;
#endif /* WPA_TRACE_BFD */
}
static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
{
#ifdef WPA_TRACE_BFD
char *pos;
wpa_trace_test_fail_after = atoi(cmd);
pos = os_strchr(cmd, ':');
if (pos) {
pos++;
os_strlcpy(wpa_trace_test_fail_func, pos,
sizeof(wpa_trace_test_fail_func));
} else {
wpa_trace_test_fail_after = 0;
}
return 0;
#else /* WPA_TRACE_BFD */
return -1;
#endif /* WPA_TRACE_BFD */
}
static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
#ifdef WPA_TRACE_BFD
return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
wpa_trace_test_fail_func);
#else /* WPA_TRACE_BFD */
return -1;
#endif /* WPA_TRACE_BFD */
}
static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
{
struct sta_info *sta;
u8 addr[ETH_ALEN];
u8 zero[WPA_TK_MAX_LEN];
os_memset(zero, 0, sizeof(zero));
if (hwaddr_aton(cmd, addr))
return -1;
if (is_broadcast_ether_addr(addr) && os_strstr(cmd, " BIGTK")) {
if (hapd->last_bigtk_alg == WPA_ALG_NONE)
return -1;
wpa_printf(MSG_INFO, "TESTING: Reset BIPN for BIGTK");
/* First, use a zero key to avoid any possible duplicate key
* avoidance in the driver. */
if (hostapd_drv_set_key(hapd->conf->iface, hapd,
hapd->last_bigtk_alg,
broadcast_ether_addr,
hapd->last_bigtk_key_idx, 0, 1, NULL, 0,
zero, hapd->last_bigtk_len,
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
return -1;
/* Set the previously configured key to reset its TSC */
return hostapd_drv_set_key(hapd->conf->iface, hapd,
hapd->last_bigtk_alg,
broadcast_ether_addr,
hapd->last_bigtk_key_idx, 0, 1, NULL,
0, hapd->last_bigtk,
hapd->last_bigtk_len,
KEY_FLAG_GROUP_TX_DEFAULT);
}
if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
if (hapd->last_igtk_alg == WPA_ALG_NONE)
return -1;
wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK");
/* First, use a zero key to avoid any possible duplicate key
* avoidance in the driver. */
if (hostapd_drv_set_key(hapd->conf->iface, hapd,
hapd->last_igtk_alg,
broadcast_ether_addr,
hapd->last_igtk_key_idx, 0, 1, NULL, 0,
zero, hapd->last_igtk_len,
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
return -1;
/* Set the previously configured key to reset its TSC */
return hostapd_drv_set_key(hapd->conf->iface, hapd,
hapd->last_igtk_alg,
broadcast_ether_addr,
hapd->last_igtk_key_idx, 0, 1, NULL,
0, hapd->last_igtk,
hapd->last_igtk_len,
KEY_FLAG_GROUP_TX_DEFAULT);
}
if (is_broadcast_ether_addr(addr)) {
if (hapd->last_gtk_alg == WPA_ALG_NONE)
return -1;
wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK");
/* First, use a zero key to avoid any possible duplicate key
* avoidance in the driver. */
if (hostapd_drv_set_key(hapd->conf->iface, hapd,
hapd->last_gtk_alg,
broadcast_ether_addr,
hapd->last_gtk_key_idx, 0, 1, NULL, 0,
zero, hapd->last_gtk_len,
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
return -1;
/* Set the previously configured key to reset its TSC */
return hostapd_drv_set_key(hapd->conf->iface, hapd,
hapd->last_gtk_alg,
broadcast_ether_addr,
hapd->last_gtk_key_idx, 0, 1, NULL,
0, hapd->last_gtk,
hapd->last_gtk_len,
KEY_FLAG_GROUP_TX_DEFAULT);
}
sta = ap_get_sta(hapd, addr);
if (!sta)
return -1;
if (sta->last_tk_alg == WPA_ALG_NONE)
return -1;
wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR,
MAC2STR(sta->addr));
/* First, use a zero key to avoid any possible duplicate key avoidance
* in the driver. */
if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
sta->addr, sta->last_tk_key_idx, 0, 1, NULL, 0,
zero, sta->last_tk_len,
KEY_FLAG_PAIRWISE_RX_TX) < 0)
return -1;
/* Set the previously configured key to reset its TSC/RSC */
return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
sta->addr, sta->last_tk_key_idx, 0, 1, NULL,
0, sta->last_tk, sta->last_tk_len,
KEY_FLAG_PAIRWISE_RX_TX);
}
static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd)
{
u8 addr[ETH_ALEN];
const char *pos = cmd;
enum wpa_alg alg;
enum key_flag key_flag;
int idx, set_tx;
u8 seq[6], key[WPA_TK_MAX_LEN];
size_t key_len;
/* parameters: alg addr idx set_tx seq key key_flag */
alg = atoi(pos);
pos = os_strchr(pos, ' ');
if (!pos)
return -1;
pos++;
if (hwaddr_aton(pos, addr))
return -1;
pos += 17;
if (*pos != ' ')
return -1;
pos++;
idx = atoi(pos);
pos = os_strchr(pos, ' ');
if (!pos)
return -1;
pos++;
set_tx = atoi(pos);
pos = os_strchr(pos, ' ');
if (!pos)
return -1;
pos++;
if (hexstr2bin(pos, seq, sizeof(seq)) < 0)
return -1;
pos += 2 * 6;
if (*pos != ' ')
return -1;
pos++;
if (!os_strchr(pos, ' '))
return -1;
key_len = (os_strchr(pos, ' ') - pos) / 2;
if (hexstr2bin(pos, key, key_len) < 0)
return -1;
pos += 2 * key_len;
if (*pos != ' ')
return -1;
pos++;
key_flag = atoi(pos);
pos = os_strchr(pos, ' ');
if (pos)
return -1;
wpa_printf(MSG_INFO, "TESTING: Set key");
return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx, 0,
set_tx, seq, 6, key, key_len, key_flag);
}
static void restore_tk(void *ctx1, void *ctx2)
{
struct hostapd_data *hapd = ctx1;
struct sta_info *sta = ctx2;
wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR,
MAC2STR(sta->addr));
/* This does not really restore the TSC properly, so this will result
* in replay protection issues for now since there is no clean way of
* preventing encryption of a single EAPOL frame. */
hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg,
sta->addr, sta->last_tk_key_idx, 0, 1, NULL, 0,
sta->last_tk, sta->last_tk_len,
KEY_FLAG_PAIRWISE_RX_TX);
}
static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd)
{
struct sta_info *sta;
u8 addr[ETH_ALEN];
int plain = os_strstr(cmd, "plaintext") != NULL;
if (hwaddr_aton(cmd, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (!sta || !sta->wpa_sm)
return -1;
if (plain && sta->last_tk_alg == WPA_ALG_NONE)
plain = 0; /* no need for special processing */
if (plain) {
wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
MAC2STR(sta->addr));
hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
sta->addr, sta->last_tk_key_idx, 0, 0, NULL,
0, NULL, 0, KEY_FLAG_PAIRWISE);
}
wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr));
return wpa_auth_resend_m1(sta->wpa_sm,
os_strstr(cmd, "change-anonce") != NULL,
plain ? restore_tk : NULL, hapd, sta);
}
static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd)
{
struct sta_info *sta;
u8 addr[ETH_ALEN];
int plain = os_strstr(cmd, "plaintext") != NULL;
if (hwaddr_aton(cmd, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (!sta || !sta->wpa_sm)
return -1;
if (plain && sta->last_tk_alg == WPA_ALG_NONE)
plain = 0; /* no need for special processing */
if (plain) {
wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
MAC2STR(sta->addr));
hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
sta->addr, sta->last_tk_key_idx, 0, 0, NULL,
0, NULL, 0, KEY_FLAG_PAIRWISE);
}
wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr));
return wpa_auth_resend_m3(sta->wpa_sm,
plain ? restore_tk : NULL, hapd, sta);
}
static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd,
const char *cmd)
{
struct sta_info *sta;
u8 addr[ETH_ALEN];
int plain = os_strstr(cmd, "plaintext") != NULL;
if (hwaddr_aton(cmd, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (!sta || !sta->wpa_sm)
return -1;
if (plain && sta->last_tk_alg == WPA_ALG_NONE)
plain = 0; /* no need for special processing */
if (plain) {
wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR,
MAC2STR(sta->addr));
hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE,
sta->addr, sta->last_tk_key_idx, 0, 0, NULL,
0, NULL, 0, KEY_FLAG_PAIRWISE);
}
wpa_printf(MSG_INFO,
"TESTING: Send group M1 for the same GTK and zero RSC to "
MACSTR, MAC2STR(sta->addr));
return wpa_auth_resend_group_m1(sta->wpa_sm,
plain ? restore_tk : NULL, hapd, sta);
}
static int hostapd_ctrl_rekey_ptk(struct hostapd_data *hapd, const char *cmd)
{
struct sta_info *sta;
u8 addr[ETH_ALEN];
if (hwaddr_aton(cmd, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (!sta || !sta->wpa_sm)
return -1;
return wpa_auth_rekey_ptk(hapd->wpa_auth, sta->wpa_sm);
}
static int hostapd_ctrl_get_pmksa_pmk(struct hostapd_data *hapd, const u8 *addr,
char *buf, size_t buflen)
{
struct rsn_pmksa_cache_entry *pmksa;
pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, addr, NULL);
if (!pmksa)
return -1;
return wpa_snprintf_hex(buf, buflen, pmksa->pmk, pmksa->pmk_len);
}
static int hostapd_ctrl_get_pmk(struct hostapd_data *hapd, const char *cmd,
char *buf, size_t buflen)
{
struct sta_info *sta;
u8 addr[ETH_ALEN];
const u8 *pmk;
int pmk_len;
if (hwaddr_aton(cmd, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (!sta || !sta->wpa_sm) {
wpa_printf(MSG_DEBUG, "No STA WPA state machine for " MACSTR,
MAC2STR(addr));
return hostapd_ctrl_get_pmksa_pmk(hapd, addr, buf, buflen);
}
pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
if (!pmk || !pmk_len) {
wpa_printf(MSG_DEBUG, "No PMK stored for " MACSTR,
MAC2STR(addr));
return hostapd_ctrl_get_pmksa_pmk(hapd, addr, buf, buflen);
}
return wpa_snprintf_hex(buf, buflen, pmk, pmk_len);
}
static int hostapd_ctrl_register_frame(struct hostapd_data *hapd,
const char *cmd)
{
u16 type;
char *pos, *end;
u8 match[10];
size_t match_len;
bool multicast = false;
type = strtol(cmd, &pos, 16);
if (*pos != ' ')
return -1;
pos++;
end = os_strchr(pos, ' ');
if (end) {
match_len = end - pos;
multicast = os_strstr(end, "multicast") != NULL;
} else {
match_len = os_strlen(pos) / 2;
}
if (hexstr2bin(pos, match, match_len))
return -1;
return hostapd_drv_register_frame(hapd, type, match, match_len,
multicast);
}
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef NEED_AP_MLME
static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params)
{
switch (params->bandwidth) {
case 0:
/* bandwidth not specified: use 20 MHz by default */
/* fall-through */
case 20:
if (params->center_freq1 &&
params->center_freq1 != params->freq)
return -1;
if (params->center_freq2 || params->sec_channel_offset)
return -1;
break;
case 40:
if (params->center_freq2 || !params->sec_channel_offset)
return -1;
if (!params->center_freq1)
break;
switch (params->sec_channel_offset) {
case 1:
if (params->freq + 10 != params->center_freq1)
return -1;
break;
case -1:
if (params->freq - 10 != params->center_freq1)
return -1;
break;
default:
return -1;
}
break;
case 80:
if (!params->center_freq1 || !params->sec_channel_offset)
return 1;
switch (params->sec_channel_offset) {
case 1:
if (params->freq - 10 != params->center_freq1 &&
params->freq + 30 != params->center_freq1)
return 1;
break;
case -1:
if (params->freq + 10 != params->center_freq1 &&
params->freq - 30 != params->center_freq1)
return -1;
break;
default:
return -1;
}
/* Adjacent and overlapped are not allowed for 80+80 */
if (params->center_freq2 &&
params->center_freq1 - params->center_freq2 <= 80 &&
params->center_freq2 - params->center_freq1 <= 80)
return 1;
break;
case 160:
if (!params->center_freq1 || params->center_freq2 ||
!params->sec_channel_offset)
return -1;
switch (params->sec_channel_offset) {
case 1:
if (params->freq + 70 != params->center_freq1 &&
params->freq + 30 != params->center_freq1 &&
params->freq - 10 != params->center_freq1 &&
params->freq - 50 != params->center_freq1)
return -1;
break;
case -1:
if (params->freq + 50 != params->center_freq1 &&
params->freq + 10 != params->center_freq1 &&
params->freq - 30 != params->center_freq1 &&
params->freq - 70 != params->center_freq1)
return -1;
break;
default:
return -1;
}
break;
default:
return -1;
}
return 0;
}
#endif /* NEED_AP_MLME */
static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
char *pos)
{
#ifdef NEED_AP_MLME
struct csa_settings settings;
int ret;
int dfs_range = 0;
unsigned int i;
int bandwidth;
u8 chan;
ret = hostapd_parse_csa_settings(pos, &settings);
if (ret)
return ret;
ret = hostapd_ctrl_check_freq_params(&settings.freq_params);
if (ret) {
wpa_printf(MSG_INFO,
"chanswitch: invalid frequency settings provided");
return ret;
}
switch (settings.freq_params.bandwidth) {
case 40:
bandwidth = CHAN_WIDTH_40;
break;
case 80:
if (settings.freq_params.center_freq2)
bandwidth = CHAN_WIDTH_80P80;
else
bandwidth = CHAN_WIDTH_80;
break;
case 160:
bandwidth = CHAN_WIDTH_160;
break;
default:
bandwidth = CHAN_WIDTH_20;
break;
}
if (settings.freq_params.center_freq1)
dfs_range += hostapd_is_dfs_overlap(
iface, bandwidth, settings.freq_params.center_freq1);
else
dfs_range += hostapd_is_dfs_overlap(
iface, bandwidth, settings.freq_params.freq);
if (settings.freq_params.center_freq2)
dfs_range += hostapd_is_dfs_overlap(
iface, bandwidth, settings.freq_params.center_freq2);
if (dfs_range) {
ret = ieee80211_freq_to_chan(settings.freq_params.freq, &chan);
if (ret == NUM_HOSTAPD_MODES) {
wpa_printf(MSG_ERROR,
"Failed to get channel for (freq=%d, sec_channel_offset=%d, bw=%d)",
settings.freq_params.freq,
settings.freq_params.sec_channel_offset,
settings.freq_params.bandwidth);
return -1;
}
settings.freq_params.channel = chan;
wpa_printf(MSG_DEBUG,
"DFS/CAC to (channel=%u, freq=%d, sec_channel_offset=%d, bw=%d, center_freq1=%d)",
settings.freq_params.channel,
settings.freq_params.freq,
settings.freq_params.sec_channel_offset,
settings.freq_params.bandwidth,
settings.freq_params.center_freq1);
/* Perform CAC and switch channel */
hostapd_switch_channel_fallback(iface, &settings.freq_params);
return 0;
}
for (i = 0; i < iface->num_bss; i++) {
/* Save CHAN_SWITCH VHT and HE config */
hostapd_chan_switch_config(iface->bss[i],
&settings.freq_params);
ret = hostapd_switch_channel(iface->bss[i], &settings);
if (ret) {
/* FIX: What do we do if CSA fails in the middle of
* submitting multi-BSS CSA requests? */
return ret;
}
}
return 0;
#else /* NEED_AP_MLME */
return -1;
#endif /* NEED_AP_MLME */
}
static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply,
int reply_size, const char *param)
{
#ifdef RADIUS_SERVER
if (os_strcmp(param, "radius_server") == 0) {
return radius_server_get_mib(hapd->radius_srv, reply,
reply_size);
}
#endif /* RADIUS_SERVER */
return -1;
}
static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, 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 -ENOBUFS;
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 -ENOBUFS;
}
ret = hostapd_drv_vendor_cmd(hapd, 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 int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd,
const char *cmd)
{
u8 addr[ETH_ALEN];
struct sta_info *sta;
if (hwaddr_aton(cmd, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (!sta || !sta->eapol_sm)
return -1;
eapol_auth_reauthenticate(sta->eapol_sm);
return 0;
}
static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd)
{
u8 addr[ETH_ALEN];
struct sta_info *sta;
char *pos = cmd, *param;
if (hwaddr_aton(pos, addr) || pos[17] != ' ')
return -1;
pos += 18;
param = pos;
pos = os_strchr(pos, ' ');
if (!pos)
return -1;
*pos++ = '\0';
sta = ap_get_sta(hapd, addr);
if (!sta || !sta->eapol_sm)
return -1;
return eapol_auth_set_conf(sta->eapol_sm, param, pos);
}
static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, 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;
}
#ifdef NEED_AP_MLME
static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
struct hostapd_iface *iface = hapd->iface;
char *pos, *end;
struct hostapd_sta_info *info;
struct os_reltime now;
if (!iface->num_sta_seen)
return 0;
sta_track_expire(iface, 0);
pos = buf;
end = buf + buflen;
os_get_reltime(&now);
dl_list_for_each_reverse(info, &iface->sta_seen,
struct hostapd_sta_info, list) {
struct os_reltime age;
int ret;
os_reltime_sub(&now, &info->last_seen, &age);
ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n",
MAC2STR(info->addr), (unsigned int) age.sec,
info->ssi_signal);
if (os_snprintf_error(end - pos, ret))
break;
pos += ret;
}
return pos - buf;
}
#endif /* NEED_AP_MLME */
static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd,
const char *cmd)
{
u8 addr[ETH_ALEN];
if (hwaddr_aton(cmd, addr)) {
wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address");
return -1;
}
return hostapd_send_lci_req(hapd, addr);
}
static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd)
{
u8 addr[ETH_ALEN];
char *token, *context = NULL;
int random_interval, min_ap;
u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS];
unsigned int n_responders;
token = str_token(cmd, " ", &context);
if (!token || hwaddr_aton(token, addr)) {
wpa_printf(MSG_INFO,
"CTRL: REQ_RANGE - Bad destination address");
return -1;
}
token = str_token(cmd, " ", &context);
if (!token)
return -1;
random_interval = atoi(token);
if (random_interval < 0 || random_interval > 0xffff)
return -1;
token = str_token(cmd, " ", &context);
if (!token)
return -1;
min_ap = atoi(token);
if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP)
return -1;
n_responders = 0;
while ((token = str_token(cmd, " ", &context))) {
if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) {
wpa_printf(MSG_INFO,
"CTRL: REQ_RANGE: Too many responders");
return -1;
}
if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) {
wpa_printf(MSG_INFO,
"CTRL: REQ_RANGE: Bad responder address");
return -1;
}
n_responders++;
}
if (!n_responders) {
wpa_printf(MSG_INFO,
"CTRL: REQ_RANGE - No FTM responder address");
return -1;
}
return hostapd_send_range_req(hapd, addr, random_interval, min_ap,
responders, n_responders);
}
static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd,
const char *cmd, char *reply,
size_t reply_size)
{
u8 addr[ETH_ALEN];
const char *pos;
struct wpabuf *req;
int ret;
u8 req_mode = 0;
if (hwaddr_aton(cmd, addr))
return -1;
pos = os_strchr(cmd, ' ');
if (!pos)
return -1;
pos++;
if (os_strncmp(pos, "req_mode=", 9) == 0) {
int val = hex2byte(pos + 9);
if (val < 0)
return -1;
req_mode = val;
pos += 11;
pos = os_strchr(pos, ' ');
if (!pos)
return -1;
pos++;
}
req = wpabuf_parse_bin(pos);
if (!req)
return -1;
ret = hostapd_send_beacon_req(hapd, addr, req_mode, req);
wpabuf_free(req);
if (ret >= 0)
ret = os_snprintf(reply, reply_size, "%d", ret);
return ret;
}
static int hostapd_ctrl_iface_show_neighbor(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
if (!(hapd->conf->radio_measurements[0] &
WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
wpa_printf(MSG_ERROR,
"CTRL: SHOW_NEIGHBOR: Neighbor report is not enabled");
return -1;
}
return hostapd_neighbor_show(hapd, buf, buflen);
}
static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
{
struct wpa_ssid_value ssid;
u8 bssid[ETH_ALEN];
struct wpabuf *nr, *lci = NULL, *civic = NULL;
int stationary = 0;
+ int bss_parameters = 0;
char *tmp;
- int ret;
+ int ret = -1;
if (!(hapd->conf->radio_measurements[0] &
WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
wpa_printf(MSG_ERROR,
"CTRL: SET_NEIGHBOR: Neighbor report is not enabled");
return -1;
}
if (hwaddr_aton(buf, bssid)) {
wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID");
return -1;
}
tmp = os_strstr(buf, "ssid=");
if (!tmp || ssid_parse(tmp + 5, &ssid)) {
wpa_printf(MSG_ERROR,
"CTRL: SET_NEIGHBOR: Bad or missing SSID");
return -1;
}
buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' ');
if (!buf)
return -1;
tmp = os_strstr(buf, "nr=");
if (!tmp) {
wpa_printf(MSG_ERROR,
"CTRL: SET_NEIGHBOR: Missing Neighbor Report element");
return -1;
}
buf = os_strchr(tmp, ' ');
if (buf)
*buf++ = '\0';
nr = wpabuf_parse_bin(tmp + 3);
if (!nr) {
wpa_printf(MSG_ERROR,
"CTRL: SET_NEIGHBOR: Bad Neighbor Report element");
return -1;
}
if (!buf)
goto set;
tmp = os_strstr(buf, "lci=");
if (tmp) {
buf = os_strchr(tmp, ' ');
if (buf)
*buf++ = '\0';
lci = wpabuf_parse_bin(tmp + 4);
if (!lci) {
wpa_printf(MSG_ERROR,
"CTRL: SET_NEIGHBOR: Bad LCI subelement");
- wpabuf_free(nr);
- return -1;
+ goto fail;
}
}
if (!buf)
goto set;
tmp = os_strstr(buf, "civic=");
if (tmp) {
buf = os_strchr(tmp, ' ');
if (buf)
*buf++ = '\0';
civic = wpabuf_parse_bin(tmp + 6);
if (!civic) {
wpa_printf(MSG_ERROR,
"CTRL: SET_NEIGHBOR: Bad civic subelement");
- wpabuf_free(nr);
- wpabuf_free(lci);
- return -1;
+ goto fail;
}
}
if (!buf)
goto set;
if (os_strstr(buf, "stat"))
stationary = 1;
+ tmp = os_strstr(buf, "bss_parameter=");
+ if (tmp) {
+ bss_parameters = atoi(tmp + 14);
+ if (bss_parameters < 0 || bss_parameters > 0xff) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: SET_NEIGHBOR: Bad bss_parameters subelement");
+ goto fail;
+ }
+ }
+
set:
ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic,
- stationary);
+ stationary, bss_parameters);
+fail:
wpabuf_free(nr);
wpabuf_free(lci);
wpabuf_free(civic);
return ret;
}
static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd,
char *buf)
{
struct wpa_ssid_value ssid;
struct wpa_ssid_value *ssidp = NULL;
u8 bssid[ETH_ALEN];
char *tmp;
if (hwaddr_aton(buf, bssid)) {
wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID");
return -1;
}
tmp = os_strstr(buf, "ssid=");
if (tmp) {
ssidp = &ssid;
if (ssid_parse(tmp + 5, &ssid)) {
wpa_printf(MSG_ERROR,
"CTRL: REMOVE_NEIGHBOR: Bad SSID");
return -1;
}
}
return hostapd_neighbor_remove(hapd, bssid, ssidp);
}
static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf,
size_t buflen)
{
int ret, i;
char *pos, *end;
ret = os_snprintf(buf, buflen, "%016llX:\n",
(long long unsigned) iface->drv_flags);
if (os_snprintf_error(buflen, ret))
return -1;
pos = buf + ret;
end = buf + buflen;
for (i = 0; i < 64; i++) {
if (iface->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 hostapd_ctrl_driver_flags2(struct hostapd_iface *iface, char *buf,
size_t buflen)
{
int ret, i;
char *pos, *end;
ret = os_snprintf(buf, buflen, "%016llX:\n",
(long long unsigned) iface->drv_flags2);
if (os_snprintf_error(buflen, ret))
return -1;
pos = buf + ret;
end = buf + buflen;
for (i = 0; i < 64; i++) {
if (iface->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 hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
const char *txtaddr)
{
u8 addr[ETH_ALEN];
struct vlan_description vlan_id;
if (!(*num))
return 0;
if (hwaddr_aton(txtaddr, addr))
return -1;
if (hostapd_maclist_found(*acl, *num, addr, &vlan_id))
hostapd_remove_acl_mac(acl, num, addr);
return 0;
}
static void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl,
int *num)
{
while (*num)
hostapd_remove_acl_mac(acl, num, (*acl)[0].addr);
}
static int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num,
char *buf, size_t buflen)
{
int i = 0, len = 0, ret = 0;
if (!acl)
return 0;
while (i < num) {
ret = os_snprintf(buf + len, buflen - len,
MACSTR " VLAN_ID=%d\n",
MAC2STR(acl[i].addr),
acl[i].vlan_id.untagged);
if (ret < 0 || (size_t) ret >= buflen - len)
return len;
i++;
len += ret;
}
return len;
}
static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num,
const char *cmd)
{
u8 addr[ETH_ALEN];
struct vlan_description vlan_id;
int ret = 0, vlanid = 0;
const char *pos;
if (hwaddr_aton(cmd, addr))
return -1;
pos = os_strstr(cmd, "VLAN_ID=");
if (pos)
vlanid = atoi(pos + 8);
if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) {
ret = hostapd_add_acl_maclist(acl, num, vlanid, addr);
if (ret != -1 && *acl)
qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
}
return ret < 0 ? -1 : 0;
}
static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd,
const char *field, char *buf,
size_t buflen)
{
wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'", field);
#ifdef CONFIG_DPP
if (os_strcmp(field, "dpp") == 0) {
int res;
#ifdef 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 */
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
field);
return -1;
}
#ifdef ANDROID
static int hostapd_ctrl_iface_driver_cmd(struct hostapd_data *hapd, char *cmd,
char *buf, size_t buflen)
{
int ret;
ret = hostapd_drv_driver_cmd(hapd, cmd, buf, buflen);
if (ret == 0) {
ret = os_snprintf(buf, buflen, "%s\n", "OK");
if (os_snprintf_error(buflen, ret))
ret = -1;
}
return ret;
}
#endif /* ANDROID */
static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
char *buf, char *reply,
int reply_size,
struct sockaddr_storage *from,
socklen_t fromlen)
{
int reply_len, res;
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, "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, "STATUS") == 0) {
reply_len = hostapd_ctrl_iface_status(hapd, reply,
reply_size);
} else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
reply_len = hostapd_drv_status(hapd, reply, reply_size);
} else if (os_strcmp(buf, "MIB") == 0) {
reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
if (reply_len >= 0) {
res = wpa_get_mib(hapd->wpa_auth, reply + reply_len,
reply_size - reply_len);
if (res < 0)
reply_len = -1;
else
reply_len += res;
}
if (reply_len >= 0) {
res = ieee802_1x_get_mib(hapd, reply + reply_len,
reply_size - reply_len);
if (res < 0)
reply_len = -1;
else
reply_len += res;
}
#ifndef CONFIG_NO_RADIUS
if (reply_len >= 0) {
res = radius_client_get_mib(hapd->radius,
reply + reply_len,
reply_size - reply_len);
if (res < 0)
reply_len = -1;
else
reply_len += res;
}
#endif /* CONFIG_NO_RADIUS */
} else if (os_strncmp(buf, "MIB ", 4) == 0) {
reply_len = hostapd_ctrl_iface_mib(hapd, reply, reply_size,
buf + 4);
} else if (os_strcmp(buf, "STA-FIRST") == 0) {
reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
reply_size);
} else if (os_strncmp(buf, "STA ", 4) == 0) {
reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply,
reply_size);
} else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
reply_size);
} else if (os_strcmp(buf, "ATTACH") == 0) {
if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL))
reply_len = -1;
} else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
if (hostapd_ctrl_iface_attach(hapd, from, fromlen, buf + 7))
reply_len = -1;
} else if (os_strcmp(buf, "DETACH") == 0) {
if (hostapd_ctrl_iface_detach(hapd, from, fromlen))
reply_len = -1;
} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
if (hostapd_ctrl_iface_level(hapd, from, fromlen,
buf + 6))
reply_len = -1;
} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
if (hostapd_ctrl_iface_new_sta(hapd, buf + 8))
reply_len = -1;
} else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) {
if (hostapd_ctrl_iface_deauthenticate(hapd, buf + 15))
reply_len = -1;
} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
reply_len = -1;
#ifdef CONFIG_TAXONOMY
} else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) {
reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10,
reply, reply_size);
#endif /* CONFIG_TAXONOMY */
} else if (os_strncmp(buf, "POLL_STA ", 9) == 0) {
if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9))
reply_len = -1;
} else if (os_strcmp(buf, "STOP_AP") == 0) {
if (hostapd_ctrl_iface_stop_ap(hapd))
reply_len = -1;
#ifdef NEED_AP_MLME
} else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
reply_len = -1;
#endif /* NEED_AP_MLME */
#ifdef CONFIG_WPS
} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
reply_len = hostapd_ctrl_iface_wps_check_pin(
hapd, buf + 14, reply, reply_size);
} else if (os_strcmp(buf, "WPS_PBC") == 0) {
if (hostapd_wps_button_pushed(hapd, NULL))
reply_len = -1;
} else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
if (hostapd_wps_cancel(hapd))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11,
reply, reply_size);
} else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) {
if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "WPS_GET_STATUS", 13) == 0) {
reply_len = hostapd_ctrl_iface_wps_get_status(hapd, reply,
reply_size);
#ifdef CONFIG_WPS_NFC
} else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17))
reply_len = -1;
} else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) {
reply_len = hostapd_ctrl_iface_wps_nfc_config_token(
hapd, buf + 21, reply, reply_size);
} else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) {
reply_len = hostapd_ctrl_iface_wps_nfc_token(
hapd, buf + 14, reply, reply_size);
} else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
reply_len = hostapd_ctrl_iface_nfc_get_handover_sel(
hapd, buf + 21, reply, reply_size);
} else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
if (hostapd_ctrl_iface_nfc_report_handover(hapd, buf + 20))
reply_len = -1;
#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS */
#ifdef CONFIG_INTERWORKING
} else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) {
if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16))
reply_len = -1;
} else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) {
if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18))
reply_len = -1;
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
} else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) {
if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15))
reply_len = -1;
} else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) {
if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
reply_len = -1;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_WNM_AP
} else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
reply_len = -1;
} else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
reply_len = -1;
} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
reply_len = -1;
} else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) {
if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15))
reply_len = -1;
#endif /* CONFIG_WNM_AP */
} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
reply_size);
} else if (os_strncmp(buf, "SET ", 4) == 0) {
if (hostapd_ctrl_iface_set(hapd, buf + 4))
reply_len = -1;
} else if (os_strncmp(buf, "GET ", 4) == 0) {
reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply,
reply_size);
} else if (os_strncmp(buf, "ENABLE", 6) == 0) {
if (hostapd_ctrl_iface_enable(hapd->iface))
reply_len = -1;
} else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) {
if (hostapd_ctrl_iface_reload_wpa_psk(hapd))
reply_len = -1;
} else if (os_strncmp(buf, "RELOAD", 6) == 0) {
if (hostapd_ctrl_iface_reload(hapd->iface))
reply_len = -1;
} else if (os_strncmp(buf, "DISABLE", 7) == 0) {
if (hostapd_ctrl_iface_disable(hapd->iface))
reply_len = -1;
} else if (os_strcmp(buf, "UPDATE_BEACON") == 0) {
if (ieee802_11_set_beacon(hapd))
reply_len = -1;
#ifdef CONFIG_TESTING_OPTIONS
} else if (os_strncmp(buf, "RADAR ", 6) == 0) {
if (hostapd_ctrl_iface_radar(hapd, buf + 6))
reply_len = -1;
} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
reply_len = -1;
} else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) {
if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd,
buf + 23) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "EAPOL_TX ", 9) == 0) {
if (hostapd_ctrl_iface_eapol_tx(hapd, buf + 9) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
if (hostapd_ctrl_iface_data_test_config(hapd, buf + 17) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
if (hostapd_ctrl_iface_data_test_tx(hapd, buf + 13) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
if (hostapd_ctrl_test_alloc_fail(hapd, buf + 16) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
reply_size);
} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "GET_FAIL") == 0) {
reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
} else if (os_strncmp(buf, "RESET_PN ", 9) == 0) {
if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "SET_KEY ", 8) == 0) {
if (hostapd_ctrl_set_key(hapd, buf + 8) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) {
if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) {
if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) {
if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "REKEY_PTK ", 10) == 0) {
if (hostapd_ctrl_rekey_ptk(hapd, buf + 10) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "REKEY_GTK") == 0) {
if (wpa_auth_rekey_gtk(hapd->wpa_auth) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "GET_PMK ", 8) == 0) {
reply_len = hostapd_ctrl_get_pmk(hapd, buf + 8, reply,
reply_size);
} else if (os_strncmp(buf, "REGISTER_FRAME ", 15) == 0) {
if (hostapd_ctrl_register_frame(hapd, buf + 16) < 0)
reply_len = -1;
#endif /* CONFIG_TESTING_OPTIONS */
} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
reply_len = -1;
} else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply,
reply_size);
} else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
ieee802_1x_erp_flush(hapd);
#ifdef RADIUS_SERVER
radius_server_erp_flush(hapd->radius_srv);
#endif /* RADIUS_SERVER */
} else if (os_strncmp(buf, "EAPOL_REAUTH ", 13) == 0) {
if (hostapd_ctrl_iface_eapol_reauth(hapd, buf + 13))
reply_len = -1;
} else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) {
if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10))
reply_len = -1;
} else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
reply_len = hostapd_ctrl_iface_log_level(
hapd, buf + 9, reply, reply_size);
#ifdef NEED_AP_MLME
} else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
reply_len = hostapd_ctrl_iface_track_sta_list(
hapd, reply, reply_size);
#endif /* NEED_AP_MLME */
} else if (os_strcmp(buf, "PMKSA") == 0) {
reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply,
reply_size);
} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
hostapd_ctrl_iface_pmksa_flush(hapd);
} else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
reply_len = -1;
} else if (os_strcmp(buf, "SHOW_NEIGHBOR") == 0) {
reply_len = hostapd_ctrl_iface_show_neighbor(hapd, reply,
reply_size);
} else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) {
if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16))
reply_len = -1;
} else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) {
if (hostapd_ctrl_iface_req_lci(hapd, buf + 8))
reply_len = -1;
} else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) {
if (hostapd_ctrl_iface_req_range(hapd, buf + 10))
reply_len = -1;
} else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) {
reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11,
reply, reply_size);
} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
reply_size);
} else if (os_strcmp(buf, "DRIVER_FLAGS2") == 0) {
reply_len = hostapd_ctrl_driver_flags2(hapd->iface, reply,
reply_size);
} else if (os_strcmp(buf, "TERMINATE") == 0) {
eloop_terminate();
} else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) {
if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) {
if (hostapd_ctrl_iface_acl_add_mac(
&hapd->conf->accept_mac,
&hapd->conf->num_accept_mac, buf + 19))
reply_len = -1;
} else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) {
if (!hostapd_ctrl_iface_acl_del_mac(
&hapd->conf->accept_mac,
&hapd->conf->num_accept_mac, buf + 19))
hostapd_disassoc_accept_mac(hapd);
else
reply_len = -1;
} else if (os_strcmp(buf + 11, "SHOW") == 0) {
reply_len = hostapd_ctrl_iface_acl_show_mac(
hapd->conf->accept_mac,
hapd->conf->num_accept_mac, reply, reply_size);
} else if (os_strcmp(buf + 11, "CLEAR") == 0) {
hostapd_ctrl_iface_acl_clear_list(
&hapd->conf->accept_mac,
&hapd->conf->num_accept_mac);
hostapd_disassoc_accept_mac(hapd);
}
} else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) {
if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) {
if (!hostapd_ctrl_iface_acl_add_mac(
&hapd->conf->deny_mac,
&hapd->conf->num_deny_mac, buf + 17))
hostapd_disassoc_deny_mac(hapd);
else
reply_len = -1;
} else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) {
if (hostapd_ctrl_iface_acl_del_mac(
&hapd->conf->deny_mac,
&hapd->conf->num_deny_mac, buf + 17))
reply_len = -1;
} else if (os_strcmp(buf + 9, "SHOW") == 0) {
reply_len = hostapd_ctrl_iface_acl_show_mac(
hapd->conf->deny_mac,
hapd->conf->num_deny_mac, reply, reply_size);
} else if (os_strcmp(buf + 9, "CLEAR") == 0) {
hostapd_ctrl_iface_acl_clear_list(
&hapd->conf->deny_mac,
&hapd->conf->num_deny_mac);
}
#ifdef CONFIG_DPP
} else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) {
res = hostapd_dpp_qr_code(hapd, 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) {
res = hostapd_dpp_nfc_uri(hapd, 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) {
res = hostapd_dpp_nfc_handover_req(hapd, 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) {
res = hostapd_dpp_nfc_handover_sel(hapd, 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) {
res = dpp_bootstrap_gen(hapd->iface->interfaces->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(hapd->iface->interfaces->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(hapd->iface->interfaces->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(hapd->iface->interfaces->dpp,
atoi(buf + 19),
reply, reply_size);
} else if (os_strncmp(buf, "DPP_BOOTSTRAP_SET ", 18) == 0) {
if (dpp_bootstrap_set(hapd->iface->interfaces->dpp,
atoi(buf + 18),
os_strchr(buf + 18, ' ')) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
if (hostapd_dpp_auth_init(hapd, buf + 13) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) {
if (hostapd_dpp_listen(hapd, buf + 11) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) {
hostapd_dpp_stop(hapd);
hostapd_dpp_listen_stop(hapd);
} else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) {
res = dpp_configurator_add(hapd->iface->interfaces->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_REMOVE ", 24) == 0) {
if (dpp_configurator_remove(hapd->iface->interfaces->dpp,
buf + 24) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) {
if (hostapd_dpp_configurator_sign(hapd, buf + 21) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) {
reply_len = dpp_configurator_get_key_id(
hapd->iface->interfaces->dpp,
atoi(buf + 25),
reply, reply_size);
} else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) {
res = hostapd_dpp_pkex_add(hapd, 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 (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
reply_len = -1;
#ifdef CONFIG_DPP2
} else if (os_strncmp(buf, "DPP_CONTROLLER_START ", 21) == 0) {
if (hostapd_dpp_controller_start(hapd, buf + 20) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "DPP_CONTROLLER_START") == 0) {
if (hostapd_dpp_controller_start(hapd, NULL) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "DPP_CONTROLLER_STOP") == 0) {
dpp_controller_stop(hapd->iface->interfaces->dpp);
} else if (os_strncmp(buf, "DPP_CHIRP ", 10) == 0) {
if (hostapd_dpp_chirp(hapd, buf + 9) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) {
hostapd_dpp_chirp_stop(hapd);
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
#ifdef RADIUS_SERVER
} else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) {
if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0)
reply_len = -1;
#endif /* RADIUS_SERVER */
} else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
reply_len = hostapd_ctrl_iface_get_capability(
hapd, buf + 15, reply, reply_size);
#ifdef CONFIG_PASN
} else if (os_strcmp(buf, "PTKSA_CACHE_LIST") == 0) {
reply_len = ptksa_cache_list(hapd->ptksa, reply, reply_size);
#endif /* CONFIG_PASN */
#ifdef ANDROID
} else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
reply_len = hostapd_ctrl_iface_driver_cmd(hapd, buf + 7, reply,
reply_size);
#endif /* ANDROID */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
}
if (reply_len < 0) {
os_memcpy(reply, "FAIL\n", 5);
reply_len = 5;
}
return reply_len;
}
static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
char buf[4096];
int res;
struct sockaddr_storage from;
socklen_t fromlen = sizeof(from);
char *reply, *pos = buf;
const int reply_size = 4096;
int reply_len;
int level = MSG_DEBUG;
#ifdef CONFIG_CTRL_IFACE_UDP
unsigned char lcookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
return;
}
buf[res] = '\0';
reply = os_malloc(reply_size);
if (reply == NULL) {
if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
fromlen) < 0) {
wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
strerror(errno));
}
return;
}
#ifdef CONFIG_CTRL_IFACE_UDP
if (os_strcmp(buf, "GET_COOKIE") == 0) {
os_memcpy(reply, "COOKIE=", 7);
wpa_snprintf_hex(reply + 7, 2 * CTRL_IFACE_COOKIE_LEN + 1,
hapd->ctrl_iface_cookie,
CTRL_IFACE_COOKIE_LEN);
reply_len = 7 + 2 * CTRL_IFACE_COOKIE_LEN;
goto done;
}
if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
hexstr2bin(buf + 7, lcookie, CTRL_IFACE_COOKIE_LEN) < 0) {
wpa_printf(MSG_DEBUG,
"CTRL: No cookie in the request - drop request");
os_free(reply);
return;
}
if (os_memcmp(hapd->ctrl_iface_cookie, lcookie,
CTRL_IFACE_COOKIE_LEN) != 0) {
wpa_printf(MSG_DEBUG,
"CTRL: Invalid cookie in the request - drop request");
os_free(reply);
return;
}
pos = buf + 7 + 2 * CTRL_IFACE_COOKIE_LEN;
while (*pos == ' ')
pos++;
#endif /* CONFIG_CTRL_IFACE_UDP */
if (os_strcmp(pos, "PING") == 0)
level = MSG_EXCESSIVE;
wpa_hexdump_ascii(level, "RX ctrl_iface", pos, res);
reply_len = hostapd_ctrl_iface_receive_process(hapd, pos,
reply, reply_size,
&from, fromlen);
#ifdef CONFIG_CTRL_IFACE_UDP
done:
#endif /* CONFIG_CTRL_IFACE_UDP */
if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
fromlen) < 0) {
wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
strerror(errno));
}
os_free(reply);
}
#ifndef CONFIG_CTRL_IFACE_UDP
static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
{
char *buf;
size_t len;
if (hapd->conf->ctrl_interface == NULL)
return NULL;
len = os_strlen(hapd->conf->ctrl_interface) +
os_strlen(hapd->conf->iface) + 2;
buf = os_malloc(len);
if (buf == NULL)
return NULL;
os_snprintf(buf, len, "%s/%s",
hapd->conf->ctrl_interface, hapd->conf->iface);
buf[len - 1] = '\0';
return buf;
}
#endif /* CONFIG_CTRL_IFACE_UDP */
static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
enum wpa_msg_type type,
const char *txt, size_t len)
{
struct hostapd_data *hapd = ctx;
if (hapd == NULL)
return;
hostapd_ctrl_iface_send(hapd, level, type, txt, len);
}
int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
{
#ifdef CONFIG_CTRL_IFACE_UDP
int port = HOSTAPD_CTRL_IFACE_PORT;
char p[32] = { 0 };
char port_str[40], *tmp;
char *pos;
struct addrinfo hints = { 0 }, *res, *saveres;
int n;
if (hapd->ctrl_sock > -1) {
wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
return 0;
}
if (hapd->conf->ctrl_interface == NULL)
return 0;
pos = os_strstr(hapd->conf->ctrl_interface, "udp:");
if (pos) {
pos += 4;
port = atoi(pos);
if (port <= 0) {
wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port");
goto fail;
}
}
dl_list_init(&hapd->ctrl_dst);
hapd->ctrl_sock = -1;
os_get_random(hapd->ctrl_iface_cookie, CTRL_IFACE_COOKIE_LEN);
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
hints.ai_flags = AI_PASSIVE;
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
hints.ai_family = AF_INET6;
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
hints.ai_family = AF_INET;
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
hints.ai_socktype = SOCK_DGRAM;
try_again:
os_snprintf(p, sizeof(p), "%d", port);
n = getaddrinfo(NULL, p, &hints, &res);
if (n) {
wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n));
goto fail;
}
saveres = res;
hapd->ctrl_sock = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (hapd->ctrl_sock < 0) {
wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
goto fail;
}
if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) {
port--;
if ((HOSTAPD_CTRL_IFACE_PORT - port) <
HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos)
goto try_again;
wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
goto fail;
}
freeaddrinfo(saveres);
os_snprintf(port_str, sizeof(port_str), "udp:%d", port);
tmp = os_strdup(port_str);
if (tmp) {
os_free(hapd->conf->ctrl_interface);
hapd->conf->ctrl_interface = tmp;
}
wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
if (eloop_register_read_sock(hapd->ctrl_sock,
hostapd_ctrl_iface_receive, hapd, NULL) <
0) {
hostapd_ctrl_iface_deinit(hapd);
return -1;
}
hapd->msg_ctx = hapd;
wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
return 0;
fail:
if (hapd->ctrl_sock >= 0)
close(hapd->ctrl_sock);
return -1;
#else /* CONFIG_CTRL_IFACE_UDP */
struct sockaddr_un addr;
int s = -1;
char *fname = NULL;
if (hapd->ctrl_sock > -1) {
wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
return 0;
}
dl_list_init(&hapd->ctrl_dst);
if (hapd->conf->ctrl_interface == NULL)
return 0;
if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
if (errno == EEXIST) {
wpa_printf(MSG_DEBUG, "Using existing control "
"interface directory.");
} else {
wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
strerror(errno));
goto fail;
}
}
if (hapd->conf->ctrl_interface_gid_set &&
lchown(hapd->conf->ctrl_interface, -1,
hapd->conf->ctrl_interface_gid) < 0) {
wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
strerror(errno));
return -1;
}
if (!hapd->conf->ctrl_interface_gid_set &&
hapd->iface->interfaces->ctrl_iface_group &&
lchown(hapd->conf->ctrl_interface, -1,
hapd->iface->interfaces->ctrl_iface_group) < 0) {
wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
strerror(errno));
return -1;
}
#ifdef ANDROID
/*
* Android is using umask 0077 which would leave the control interface
* directory without group access. This breaks things since Wi-Fi
* framework assumes that this directory can be accessed by other
* applications in the wifi group. Fix this by adding group access even
* if umask value would prevent this.
*/
if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) {
wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s",
strerror(errno));
/* Try to continue anyway */
}
#endif /* ANDROID */
if (os_strlen(hapd->conf->ctrl_interface) + 1 +
os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path))
goto fail;
s = socket(PF_UNIX, SOCK_DGRAM, 0);
if (s < 0) {
wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
goto fail;
}
os_memset(&addr, 0, sizeof(addr));
#ifdef __FreeBSD__
addr.sun_len = sizeof(addr);
#endif /* __FreeBSD__ */
addr.sun_family = AF_UNIX;
fname = hostapd_ctrl_iface_path(hapd);
if (fname == NULL)
goto fail;
os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
strerror(errno));
if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
" allow connections - assuming it was left"
"over from forced program termination");
if (unlink(fname) < 0) {
wpa_printf(MSG_ERROR,
"Could not unlink existing ctrl_iface socket '%s': %s",
fname, strerror(errno));
goto fail;
}
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
0) {
wpa_printf(MSG_ERROR,
"hostapd-ctrl-iface: bind(PF_UNIX): %s",
strerror(errno));
goto fail;
}
wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
"ctrl_iface socket '%s'", fname);
} else {
wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
"be in use - cannot override it");
wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
"not used anymore", fname);
os_free(fname);
fname = NULL;
goto fail;
}
}
if (hapd->conf->ctrl_interface_gid_set &&
lchown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s",
strerror(errno));
goto fail;
}
if (!hapd->conf->ctrl_interface_gid_set &&
hapd->iface->interfaces->ctrl_iface_group &&
lchown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) {
wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s",
strerror(errno));
goto fail;
}
if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
strerror(errno));
goto fail;
}
os_free(fname);
hapd->ctrl_sock = s;
if (eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
NULL) < 0) {
hostapd_ctrl_iface_deinit(hapd);
return -1;
}
hapd->msg_ctx = hapd;
wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
return 0;
fail:
if (s >= 0)
close(s);
if (fname) {
unlink(fname);
os_free(fname);
}
return -1;
#endif /* CONFIG_CTRL_IFACE_UDP */
}
void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
{
struct wpa_ctrl_dst *dst, *prev;
if (hapd->ctrl_sock > -1) {
#ifndef CONFIG_CTRL_IFACE_UDP
char *fname;
#endif /* !CONFIG_CTRL_IFACE_UDP */
eloop_unregister_read_sock(hapd->ctrl_sock);
close(hapd->ctrl_sock);
hapd->ctrl_sock = -1;
#ifndef CONFIG_CTRL_IFACE_UDP
fname = hostapd_ctrl_iface_path(hapd);
if (fname)
unlink(fname);
os_free(fname);
if (hapd->conf->ctrl_interface &&
rmdir(hapd->conf->ctrl_interface) < 0) {
if (errno == ENOTEMPTY) {
wpa_printf(MSG_DEBUG, "Control interface "
"directory not empty - leaving it "
"behind");
} else {
wpa_printf(MSG_ERROR,
"rmdir[ctrl_interface=%s]: %s",
hapd->conf->ctrl_interface,
strerror(errno));
}
}
#endif /* !CONFIG_CTRL_IFACE_UDP */
}
dl_list_for_each_safe(dst, prev, &hapd->ctrl_dst, struct wpa_ctrl_dst,
list)
os_free(dst);
#ifdef CONFIG_TESTING_OPTIONS
l2_packet_deinit(hapd->l2_test);
hapd->l2_test = NULL;
#endif /* CONFIG_TESTING_OPTIONS */
}
static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces,
char *buf)
{
if (hostapd_add_iface(interfaces, buf) < 0) {
wpa_printf(MSG_ERROR, "Adding interface %s failed", buf);
return -1;
}
return 0;
}
static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
char *buf)
{
if (hostapd_remove_iface(interfaces, buf) < 0) {
wpa_printf(MSG_ERROR, "Removing interface %s failed", buf);
return -1;
}
return 0;
}
static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces,
struct sockaddr_storage *from,
socklen_t fromlen, char *input)
{
return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen,
input);
}
static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces,
struct sockaddr_storage *from,
socklen_t fromlen)
{
return ctrl_iface_detach(&interfaces->global_ctrl_dst, from, fromlen);
}
static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
{
#ifdef CONFIG_WPS_TESTING
wps_version_number = 0x20;
- wps_testing_dummy_cred = 0;
+ wps_testing_stub_cred = 0;
wps_corrupt_pkhash = 0;
#endif /* CONFIG_WPS_TESTING */
#ifdef CONFIG_TESTING_OPTIONS
#ifdef CONFIG_DPP
dpp_test = DPP_TEST_DISABLED;
#ifdef CONFIG_DPP2
dpp_version_override = 2;
#else /* CONFIG_DPP2 */
dpp_version_override = 1;
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_DPP
dpp_global_clear(interfaces->dpp);
#endif /* CONFIG_DPP */
}
#ifdef CONFIG_FST
static int
hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces,
const char *cmd)
{
char ifname[IFNAMSIZ + 1];
struct fst_iface_cfg cfg;
struct hostapd_data *hapd;
struct fst_wpa_obj iface_obj;
if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
hapd = hostapd_get_iface(interfaces, ifname);
if (hapd) {
if (hapd->iface->fst) {
wpa_printf(MSG_INFO, "FST: Already attached");
return -1;
}
fst_hostapd_fill_iface_obj(hapd, &iface_obj);
hapd->iface->fst = fst_attach(ifname, hapd->own_addr,
&iface_obj, &cfg);
if (hapd->iface->fst)
return 0;
}
}
return -EINVAL;
}
static int
hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces,
const char *cmd)
{
char ifname[IFNAMSIZ + 1];
struct hostapd_data * hapd;
if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
hapd = hostapd_get_iface(interfaces, ifname);
if (hapd) {
if (!fst_iface_detach(ifname)) {
hapd->iface->fst = NULL;
hapd->iface->fst_ies = NULL;
return 0;
}
}
}
return -EINVAL;
}
#endif /* CONFIG_FST */
static struct hostapd_data *
hostapd_interfaces_get_hapd(struct hapd_interfaces *interfaces,
const char *ifname)
{
size_t i, j;
for (i = 0; i < interfaces->count; i++) {
struct hostapd_iface *iface = interfaces->iface[i];
for (j = 0; j < iface->num_bss; j++) {
struct hostapd_data *hapd;
hapd = iface->bss[j];
if (os_strcmp(ifname, hapd->conf->iface) == 0)
return hapd;
}
}
return NULL;
}
static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd,
struct hostapd_data *dst_hapd,
const char *param)
{
int res;
char *value;
value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
if (!value) {
wpa_printf(MSG_ERROR,
"DUP: cannot allocate buffer to stringify %s",
param);
goto error_return;
}
if (os_strcmp(param, "wpa") == 0) {
os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d",
src_hapd->conf->wpa);
} else if (os_strcmp(param, "wpa_key_mgmt") == 0 &&
src_hapd->conf->wpa_key_mgmt) {
res = hostapd_ctrl_iface_get_key_mgmt(
src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res))
goto error_stringify;
} else if (os_strcmp(param, "wpa_pairwise") == 0 &&
src_hapd->conf->wpa_pairwise) {
res = wpa_write_ciphers(value,
value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
src_hapd->conf->wpa_pairwise, " ");
if (res < 0)
goto error_stringify;
} else if (os_strcmp(param, "rsn_pairwise") == 0 &&
src_hapd->conf->rsn_pairwise) {
res = wpa_write_ciphers(value,
value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
src_hapd->conf->rsn_pairwise, " ");
if (res < 0)
goto error_stringify;
} else if (os_strcmp(param, "wpa_passphrase") == 0 &&
src_hapd->conf->ssid.wpa_passphrase) {
os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s",
src_hapd->conf->ssid.wpa_passphrase);
} else if (os_strcmp(param, "wpa_psk") == 0 &&
src_hapd->conf->ssid.wpa_psk_set) {
wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
} else {
wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param);
goto error_return;
}
res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value);
os_free(value);
return res;
error_stringify:
wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param);
error_return:
os_free(value);
return -1;
}
static int
hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces *interfaces,
const char *input,
char *reply, int reply_size)
{
size_t i, j;
int res;
char *pos, *end;
struct hostapd_iface *iface;
int show_ctrl = 0;
if (input)
show_ctrl = !!os_strstr(input, "ctrl");
pos = reply;
end = reply + reply_size;
for (i = 0; i < interfaces->count; i++) {
iface = interfaces->iface[i];
for (j = 0; j < iface->num_bss; j++) {
struct hostapd_bss_config *conf;
conf = iface->conf->bss[j];
if (show_ctrl)
res = os_snprintf(pos, end - pos,
"%s ctrl_iface=%s\n",
conf->iface,
conf->ctrl_interface ?
conf->ctrl_interface : "N/A");
else
res = os_snprintf(pos, end - pos, "%s\n",
conf->iface);
if (os_snprintf_error(end - pos, res)) {
*pos = '\0';
return pos - reply;
}
pos += res;
}
}
return pos - reply;
}
static int
hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces,
char *cmd)
{
char *p_start = cmd, *p_end;
struct hostapd_data *src_hapd, *dst_hapd;
/* cmd: "<src ifname> <dst ifname> <variable name> */
p_end = os_strchr(p_start, ' ');
if (!p_end) {
wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'",
cmd);
return -1;
}
*p_end = '\0';
src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
if (!src_hapd) {
wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'",
p_start);
return -1;
}
p_start = p_end + 1;
p_end = os_strchr(p_start, ' ');
if (!p_end) {
wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'",
cmd);
return -1;
}
*p_end = '\0';
dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
if (!dst_hapd) {
wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'",
p_start);
return -1;
}
p_start = p_end + 1;
return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start);
}
static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces,
const char *ifname,
char *buf, char *reply,
int reply_size,
struct sockaddr_storage *from,
socklen_t fromlen)
{
struct hostapd_data *hapd;
hapd = hostapd_interfaces_get_hapd(interfaces, ifname);
if (hapd == NULL) {
int res;
res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n");
if (os_snprintf_error(reply_size, res))
return -1;
return res;
}
return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size,
from, fromlen);
}
static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
struct hapd_interfaces *interfaces = eloop_ctx;
char buffer[256], *buf = buffer;
int res;
struct sockaddr_storage from;
socklen_t fromlen = sizeof(from);
char *reply;
int reply_len;
const int reply_size = 4096;
#ifdef CONFIG_CTRL_IFACE_UDP
unsigned char lcookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0,
(struct sockaddr *) &from, &fromlen);
if (res < 0) {
wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
strerror(errno));
return;
}
buf[res] = '\0';
wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
reply = os_malloc(reply_size);
if (reply == NULL) {
if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
fromlen) < 0) {
wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
strerror(errno));
}
return;
}
os_memcpy(reply, "OK\n", 3);
reply_len = 3;
#ifdef CONFIG_CTRL_IFACE_UDP
if (os_strcmp(buf, "GET_COOKIE") == 0) {
os_memcpy(reply, "COOKIE=", 7);
wpa_snprintf_hex(reply + 7, 2 * CTRL_IFACE_COOKIE_LEN + 1,
interfaces->ctrl_iface_cookie,
CTRL_IFACE_COOKIE_LEN);
reply_len = 7 + 2 * CTRL_IFACE_COOKIE_LEN;
goto send_reply;
}
if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
hexstr2bin(buf + 7, lcookie, CTRL_IFACE_COOKIE_LEN) < 0) {
wpa_printf(MSG_DEBUG,
"CTRL: No cookie in the request - drop request");
os_free(reply);
return;
}
if (os_memcmp(interfaces->ctrl_iface_cookie, lcookie,
CTRL_IFACE_COOKIE_LEN) != 0) {
wpa_printf(MSG_DEBUG,
"CTRL: Invalid cookie in the request - drop request");
os_free(reply);
return;
}
buf += 7 + 2 * CTRL_IFACE_COOKIE_LEN;
while (*buf == ' ')
buf++;
#endif /* CONFIG_CTRL_IFACE_UDP */
if (os_strncmp(buf, "IFNAME=", 7) == 0) {
char *pos = os_strchr(buf + 7, ' ');
if (pos) {
*pos++ = '\0';
reply_len = hostapd_global_ctrl_iface_ifname(
interfaces, buf + 7, pos, reply, reply_size,
&from, fromlen);
goto send_reply;
}
}
if (os_strcmp(buf, "PING") == 0) {
os_memcpy(reply, "PONG\n", 5);
reply_len = 5;
} else if (os_strncmp(buf, "RELOG", 5) == 0) {
if (wpa_debug_reopen_file() < 0)
reply_len = -1;
} else if (os_strcmp(buf, "FLUSH") == 0) {
hostapd_ctrl_iface_flush(interfaces);
} else if (os_strncmp(buf, "ADD ", 4) == 0) {
if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0)
reply_len = -1;
} else if (os_strncmp(buf, "REMOVE ", 7) == 0) {
if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "ATTACH") == 0) {
if (hostapd_global_ctrl_iface_attach(interfaces, &from,
fromlen, NULL))
reply_len = -1;
} else if (os_strncmp(buf, "ATTACH ", 7) == 0) {
if (hostapd_global_ctrl_iface_attach(interfaces, &from,
fromlen, buf + 7))
reply_len = -1;
} else if (os_strcmp(buf, "DETACH") == 0) {
if (hostapd_global_ctrl_iface_detach(interfaces, &from,
fromlen))
reply_len = -1;
#ifdef CONFIG_MODULE_TESTS
} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
if (hapd_module_tests() < 0)
reply_len = -1;
#endif /* CONFIG_MODULE_TESTS */
#ifdef CONFIG_FST
} else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
if (!hostapd_global_ctrl_iface_fst_attach(interfaces, buf + 11))
reply_len = os_snprintf(reply, reply_size, "OK\n");
else
reply_len = -1;
} else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
if (!hostapd_global_ctrl_iface_fst_detach(interfaces, buf + 11))
reply_len = os_snprintf(reply, reply_size, "OK\n");
else
reply_len = -1;
} 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_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
if (!hostapd_global_ctrl_iface_dup_network(interfaces,
buf + 12))
reply_len = os_snprintf(reply, reply_size, "OK\n");
else
reply_len = -1;
} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
reply_len = hostapd_global_ctrl_iface_interfaces(
interfaces, buf + 10, reply, sizeof(buffer));
} else if (os_strcmp(buf, "TERMINATE") == 0) {
eloop_terminate();
} else {
wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
"ignored");
reply_len = -1;
}
send_reply:
if (reply_len < 0) {
os_memcpy(reply, "FAIL\n", 5);
reply_len = 5;
}
if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
fromlen) < 0) {
wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
strerror(errno));
}
os_free(reply);
}
#ifndef CONFIG_CTRL_IFACE_UDP
static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface)
{
char *buf;
size_t len;
if (interface->global_iface_path == NULL)
return NULL;
len = os_strlen(interface->global_iface_path) +
os_strlen(interface->global_iface_name) + 2;
buf = os_malloc(len);
if (buf == NULL)
return NULL;
os_snprintf(buf, len, "%s/%s", interface->global_iface_path,
interface->global_iface_name);
buf[len - 1] = '\0';
return buf;
}
#endif /* CONFIG_CTRL_IFACE_UDP */
int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
{
#ifdef CONFIG_CTRL_IFACE_UDP
int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT;
char p[32] = { 0 };
char *pos;
struct addrinfo hints = { 0 }, *res, *saveres;
int n;
if (interface->global_ctrl_sock > -1) {
wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
return 0;
}
if (interface->global_iface_path == NULL)
return 0;
pos = os_strstr(interface->global_iface_path, "udp:");
if (pos) {
pos += 4;
port = atoi(pos);
if (port <= 0) {
wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port");
goto fail;
}
}
os_get_random(interface->ctrl_iface_cookie, CTRL_IFACE_COOKIE_LEN);
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
hints.ai_flags = AI_PASSIVE;
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
hints.ai_family = AF_INET6;
#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
hints.ai_family = AF_INET;
#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
hints.ai_socktype = SOCK_DGRAM;
try_again:
os_snprintf(p, sizeof(p), "%d", port);
n = getaddrinfo(NULL, p, &hints, &res);
if (n) {
wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n));
goto fail;
}
saveres = res;
interface->global_ctrl_sock = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (interface->global_ctrl_sock < 0) {
wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
goto fail;
}
if (bind(interface->global_ctrl_sock, res->ai_addr, res->ai_addrlen) <
0) {
port++;
if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) <
HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos)
goto try_again;
wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
goto fail;
}
freeaddrinfo(saveres);
wpa_printf(MSG_DEBUG, "global ctrl_iface_init UDP port: %d", port);
if (eloop_register_read_sock(interface->global_ctrl_sock,
hostapd_global_ctrl_iface_receive,
interface, NULL) < 0) {
hostapd_global_ctrl_iface_deinit(interface);
return -1;
}
wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
return 0;
fail:
if (interface->global_ctrl_sock >= 0)
close(interface->global_ctrl_sock);
return -1;
#else /* CONFIG_CTRL_IFACE_UDP */
struct sockaddr_un addr;
int s = -1;
char *fname = NULL;
if (interface->global_iface_path == NULL) {
wpa_printf(MSG_DEBUG, "ctrl_iface not configured!");
return 0;
}
if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) {
if (errno == EEXIST) {
wpa_printf(MSG_DEBUG, "Using existing control "
"interface directory.");
} else {
wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
strerror(errno));
goto fail;
}
} else if (interface->ctrl_iface_group &&
lchown(interface->global_iface_path, -1,
interface->ctrl_iface_group) < 0) {
wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
strerror(errno));
goto fail;
}
if (os_strlen(interface->global_iface_path) + 1 +
os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path))
goto fail;
s = socket(PF_UNIX, SOCK_DGRAM, 0);
if (s < 0) {
wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
goto fail;
}
os_memset(&addr, 0, sizeof(addr));
#ifdef __FreeBSD__
addr.sun_len = sizeof(addr);
#endif /* __FreeBSD__ */
addr.sun_family = AF_UNIX;
fname = hostapd_global_ctrl_iface_path(interface);
if (fname == NULL)
goto fail;
os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
strerror(errno));
if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
" allow connections - assuming it was left"
"over from forced program termination");
if (unlink(fname) < 0) {
wpa_printf(MSG_ERROR,
"Could not unlink existing ctrl_iface socket '%s': %s",
fname, strerror(errno));
goto fail;
}
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
0) {
wpa_printf(MSG_ERROR, "bind(PF_UNIX): %s",
strerror(errno));
goto fail;
}
wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
"ctrl_iface socket '%s'", fname);
} else {
wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
"be in use - cannot override it");
wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
"not used anymore", fname);
os_free(fname);
fname = NULL;
goto fail;
}
}
if (interface->ctrl_iface_group &&
lchown(fname, -1, interface->ctrl_iface_group) < 0) {
wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s",
strerror(errno));
goto fail;
}
if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
strerror(errno));
goto fail;
}
os_free(fname);
interface->global_ctrl_sock = s;
eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
interface, NULL);
wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
return 0;
fail:
if (s >= 0)
close(s);
if (fname) {
unlink(fname);
os_free(fname);
}
return -1;
#endif /* CONFIG_CTRL_IFACE_UDP */
}
void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
{
#ifndef CONFIG_CTRL_IFACE_UDP
char *fname = NULL;
#endif /* CONFIG_CTRL_IFACE_UDP */
struct wpa_ctrl_dst *dst, *prev;
if (interfaces->global_ctrl_sock > -1) {
eloop_unregister_read_sock(interfaces->global_ctrl_sock);
close(interfaces->global_ctrl_sock);
interfaces->global_ctrl_sock = -1;
#ifndef CONFIG_CTRL_IFACE_UDP
fname = hostapd_global_ctrl_iface_path(interfaces);
if (fname) {
unlink(fname);
os_free(fname);
}
if (interfaces->global_iface_path &&
rmdir(interfaces->global_iface_path) < 0) {
if (errno == ENOTEMPTY) {
wpa_printf(MSG_DEBUG, "Control interface "
"directory not empty - leaving it "
"behind");
} else {
wpa_printf(MSG_ERROR,
"rmdir[ctrl_interface=%s]: %s",
interfaces->global_iface_path,
strerror(errno));
}
}
#endif /* CONFIG_CTRL_IFACE_UDP */
}
os_free(interfaces->global_iface_path);
interfaces->global_iface_path = NULL;
dl_list_for_each_safe(dst, prev, &interfaces->global_ctrl_dst,
struct wpa_ctrl_dst, list)
os_free(dst);
}
static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst,
const char *buf)
{
/* Enable Probe Request events based on explicit request.
* Other events are enabled by default.
*/
if (str_starts(buf, RX_PROBE_REQUEST))
return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST);
return 1;
}
static void hostapd_ctrl_iface_send_internal(int sock, struct dl_list *ctrl_dst,
const char *ifname, int level,
const char *buf, size_t len)
{
struct wpa_ctrl_dst *dst, *next;
struct msghdr msg;
int idx, res;
struct iovec io[5];
char levelstr[10];
if (sock < 0 || dl_list_empty(ctrl_dst))
return;
res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
if (os_snprintf_error(sizeof(levelstr), res))
return;
idx = 0;
if (ifname) {
io[idx].iov_base = "IFNAME=";
io[idx].iov_len = 7;
idx++;
io[idx].iov_base = (char *) ifname;
io[idx].iov_len = os_strlen(ifname);
idx++;
io[idx].iov_base = " ";
io[idx].iov_len = 1;
idx++;
}
io[idx].iov_base = levelstr;
io[idx].iov_len = os_strlen(levelstr);
idx++;
io[idx].iov_base = (char *) buf;
io[idx].iov_len = len;
idx++;
os_memset(&msg, 0, sizeof(msg));
msg.msg_iov = io;
msg.msg_iovlen = idx;
idx = 0;
dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
if ((level >= dst->debug_level) &&
hostapd_ctrl_check_event_enabled(dst, buf)) {
sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send",
&dst->addr, dst->addrlen);
msg.msg_name = &dst->addr;
msg.msg_namelen = dst->addrlen;
if (sendmsg(sock, &msg, 0) < 0) {
int _errno = errno;
wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
"%d - %s",
idx, errno, strerror(errno));
dst->errors++;
if (dst->errors > 10 || _errno == ENOENT) {
ctrl_iface_detach(ctrl_dst,
&dst->addr,
dst->addrlen);
}
} else
dst->errors = 0;
}
idx++;
}
}
static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
enum wpa_msg_type type,
const char *buf, size_t len)
{
if (type != WPA_MSG_NO_GLOBAL) {
hostapd_ctrl_iface_send_internal(
hapd->iface->interfaces->global_ctrl_sock,
&hapd->iface->interfaces->global_ctrl_dst,
type != WPA_MSG_PER_INTERFACE ?
NULL : hapd->conf->iface,
level, buf, len);
}
if (type != WPA_MSG_ONLY_GLOBAL) {
hostapd_ctrl_iface_send_internal(
hapd->ctrl_sock, &hapd->ctrl_dst,
NULL, level, buf, len);
}
}
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/contrib/wpa/hostapd/hlr_auc_gw.milenage_db b/contrib/wpa/hostapd/hlr_auc_gw.milenage_db
index c156a29aeda0..a250653108b3 100644
--- a/contrib/wpa/hostapd/hlr_auc_gw.milenage_db
+++ b/contrib/wpa/hostapd/hlr_auc_gw.milenage_db
@@ -1,15 +1,15 @@
# Parameters for Milenage (Example algorithms for AKA).
# The example Ki, OPc, and AMF values here are from 3GPP TS 35.208 v6.0.0
# 4.3.20 Test Set 20. SQN is the last used SQN value.
# These values can be used for both UMTS (EAP-AKA) and GSM (EAP-SIM)
# authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but
-# dummy values will need to be included in this file.
+# stub values will need to be included in this file.
# IMSI Ki OPc AMF SQN [RES_len]
232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000
# Example using truncated 32-bit RES instead of 64-bit default
232010000000001 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 4
# These values are from Test Set 19 which has the AMF separation bit set to 1
# and as such, is suitable for EAP-AKA' test.
555444333222111 5122250214c33e723a5dd523fc145fc0 981d464c7c52eb6e5036234984ad0bcf c3ab 16f3b3f70fc1
diff --git a/contrib/wpa/hostapd/hostapd.conf b/contrib/wpa/hostapd/hostapd.conf
index b5d15061f850..67d4cefb920b 100644
--- a/contrib/wpa/hostapd/hostapd.conf
+++ b/contrib/wpa/hostapd/hostapd.conf
@@ -1,3035 +1,3043 @@
##### hostapd configuration file ##############################################
# Empty lines and lines starting with # are ignored
# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
# management frames with the Host AP driver); wlan0 with many nl80211 drivers
# Note: This attribute can be overridden by the values supplied with the '-i'
# command line parameter.
interface=wlan0
# In case of atheros and nl80211 driver interfaces, an additional
# configuration parameter, bridge, may be used to notify hostapd if the
# interface is included in a bridge. This parameter is not used with Host AP
# driver. If the bridge parameter is not set, the drivers will automatically
# figure out the bridge interface (assuming sysfs is enabled and mounted to
# /sys) and this parameter may not be needed.
#
# For nl80211, this parameter can be used to request the AP interface to be
# added to the bridge automatically (brctl may refuse to do this before hostapd
# has been started to change the interface mode). If needed, the bridge
# interface is also created.
#bridge=br0
# Driver interface type (hostap/wired/none/nl80211/bsd);
# default: hostap). nl80211 is used with all Linux mac80211 drivers.
# Use driver=none if building hostapd as a standalone RADIUS server that does
# not control any wireless/wired driver.
# driver=hostap
# Driver interface parameters (mainly for development testing use)
# driver_params=<params>
# hostapd event logger configuration
#
# Two output method: syslog and stdout (only usable if not forking to
# background).
#
# Module bitfield (ORed bitfield of modules that will be logged; -1 = all
# modules):
# bit 0 (1) = IEEE 802.11
# bit 1 (2) = IEEE 802.1X
# bit 2 (4) = RADIUS
# bit 3 (8) = WPA
# bit 4 (16) = driver interface
# bit 6 (64) = MLME
#
# Levels (minimum value for logged events):
# 0 = verbose debugging
# 1 = debugging
# 2 = informational messages
# 3 = notification
# 4 = warning
#
logger_syslog=-1
logger_syslog_level=2
logger_stdout=-1
logger_stdout_level=2
# Interface for separate control program. If this is specified, hostapd
# will create this directory and a UNIX domain socket for listening to requests
# from external programs (CLI/GUI, etc.) for status information and
# configuration. The socket file will be named based on the interface name, so
# multiple hostapd processes/interfaces can be run at the same time if more
# than one interface is used.
# /var/run/hostapd is the recommended directory for sockets and by default,
# hostapd_cli will use it when trying to connect with hostapd.
ctrl_interface=/var/run/hostapd
# Access control for the control interface can be configured by setting the
# directory to allow only members of a group to use sockets. This way, it is
# possible to run hostapd as root (since it needs to change network
# configuration and open raw sockets) and still allow GUI/CLI components to be
# run as non-root users. However, since the control interface can be used to
# change the network configuration, this access needs to be protected in many
# cases. By default, hostapd is configured to use gid 0 (root). If you
# want to allow non-root users to use the control interface, add a new group
# and change this value to match with that group. Add users that should have
# control interface access to this group.
#
# This variable can be a group name or gid.
#ctrl_interface_group=wheel
ctrl_interface_group=0
##### IEEE 802.11 related configuration #######################################
# SSID to be used in IEEE 802.11 management frames
ssid=test
# Alternative formats for configuring SSID
# (double quoted string, hexdump, printf-escaped string)
#ssid2="test"
#ssid2=74657374
#ssid2=P"hello\nthere"
# UTF-8 SSID: Whether the SSID is to be interpreted using UTF-8 encoding
#utf8_ssid=1
# Country code (ISO/IEC 3166-1). Used to set regulatory domain.
# Set as needed to indicate country in which device is operating.
# This can limit available channels and transmit power.
# These two octets are used as the first two octets of the Country String
# (dot11CountryString)
#country_code=US
# The third octet of the Country String (dot11CountryString)
# This parameter is used to set the third octet of the country string.
#
# All environments of the current frequency band and country (default)
#country3=0x20
# Outdoor environment only
#country3=0x4f
# Indoor environment only
#country3=0x49
# Noncountry entity (country_code=XX)
#country3=0x58
# IEEE 802.11 standard Annex E table indication: 0x01 .. 0x1f
# Annex E, Table E-4 (Global operating classes)
#country3=0x04
# Enable IEEE 802.11d. This advertises the country_code and the set of allowed
# channels and transmit power levels based on the regulatory limits. The
# country_code setting must be configured with the correct country for
# IEEE 802.11d functions.
# (default: 0 = disabled)
#ieee80211d=1
# Enable IEEE 802.11h. This enables radar detection and DFS support if
# available. DFS support is required on outdoor 5 GHz channels in most countries
# of the world. This can be used only with ieee80211d=1.
# (default: 0 = disabled)
#ieee80211h=1
# Add Power Constraint element to Beacon and Probe Response frames
# This config option adds Power Constraint element when applicable and Country
# element is added. Power Constraint element is required by Transmit Power
# Control. This can be used only with ieee80211d=1.
# Valid values are 0..255.
#local_pwr_constraint=3
# Set Spectrum Management subfield in the Capability Information field.
# This config option forces the Spectrum Management bit to be set. When this
# option is not set, the value of the Spectrum Management bit depends on whether
# DFS or TPC is required by regulatory authorities. This can be used only with
# ieee80211d=1 and local_pwr_constraint configured.
#spectrum_mgmt_required=1
# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
# needs to be set to hw_mode=a. For IEEE 802.11ax (HE) on 6 GHz this needs
# to be set to hw_mode=a. When using ACS (see channel parameter), a
# special value "any" can be used to indicate that any support band can be used.
# This special case is currently supported only with drivers with which
# offloaded ACS is used.
# Default: IEEE 802.11b
hw_mode=g
# Channel number (IEEE 802.11)
# (default: 0, i.e., not set)
# Please note that some drivers do not use this value from hostapd and the
# channel will need to be configured separately with iwconfig.
#
# If CONFIG_ACS build option is enabled, the channel can be selected
# automatically at run time by setting channel=acs_survey or channel=0, both of
# which will enable the ACS survey based algorithm.
channel=1
# Global operating class (IEEE 802.11, Annex E, Table E-4)
# This option allows hostapd to specify the operating class of the channel
# configured with the channel parameter. channel and op_class together can
# uniquely identify channels across different bands, including the 6 GHz band.
#op_class=131
# ACS tuning - Automatic Channel Selection
# See: https://wireless.wiki.kernel.org/en/users/documentation/acs
#
# You can customize the ACS survey algorithm with following variables:
#
# acs_num_scans requirement is 1..100 - number of scans to be performed that
# are used to trigger survey data gathering of an underlying device driver.
# Scans are passive and typically take a little over 100ms (depending on the
# driver) on each available channel for given hw_mode. Increasing this value
# means sacrificing startup time and gathering more data wrt channel
# interference that may help choosing a better channel. This can also help fine
# tune the ACS scan time in case a driver has different scan dwell times.
#
# acs_chan_bias is a space-separated list of <channel>:<bias> pairs. It can be
# used to increase (or decrease) the likelihood of a specific channel to be
# selected by the ACS algorithm. The total interference factor for each channel
# gets multiplied by the specified bias value before finding the channel with
# the lowest value. In other words, values between 0.0 and 1.0 can be used to
# make a channel more likely to be picked while values larger than 1.0 make the
# specified channel less likely to be picked. This can be used, e.g., to prefer
# the commonly used 2.4 GHz band channels 1, 6, and 11 (which is the default
# behavior on 2.4 GHz band if no acs_chan_bias parameter is specified).
#
# Defaults:
#acs_num_scans=5
#acs_chan_bias=1:0.8 6:0.8 11:0.8
# Channel list restriction. This option allows hostapd to select one of the
# provided channels when a channel should be automatically selected.
# Channel list can be provided as range using hyphen ('-') or individual
# channels can be specified by space (' ') separated values
# Default: all channels allowed in selected hw_mode
#chanlist=100 104 108 112 116
#chanlist=1 6 11-13
# Frequency list restriction. This option allows hostapd to select one of the
# provided frequencies when a frequency should be automatically selected.
# Frequency list can be provided as range using hyphen ('-') or individual
# frequencies can be specified by comma (',') separated values
# Default: all frequencies allowed in selected hw_mode
#freqlist=2437,5955,5975
#freqlist=2437,5985-6105
# Exclude DFS channels from ACS
# This option can be used to exclude all DFS channels from the ACS channel list
# in cases where the driver supports DFS channels.
#acs_exclude_dfs=1
# Include only preferred scan channels from 6 GHz band for ACS
# This option can be used to include only preferred scan channels in the 6 GHz
# band. This can be useful in particular for devices that operate only a 6 GHz
# BSS without a collocated 2.4/5 GHz BSS.
# Default behavior is to include all PSC and non-PSC channels.
#acs_exclude_6ghz_non_psc=1
# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
beacon_int=100
# DTIM (delivery traffic information message) period (range 1..255):
# number of beacons between DTIMs (1 = every beacon includes DTIM element)
# (default: 2)
dtim_period=2
# Maximum number of stations allowed in station table. New stations will be
# rejected after the station table is full. IEEE 802.11 has a limit of 2007
# different association IDs, so this number should not be larger than that.
# (default: 2007)
max_num_sta=255
# RTS/CTS threshold; -1 = disabled (default); range -1..65535
# If this field is not included in hostapd.conf, hostapd will not control
# RTS threshold and 'iwconfig wlan# rts <val>' can be used to set it.
rts_threshold=-1
# Fragmentation threshold; -1 = disabled (default); range -1, 256..2346
# If this field is not included in hostapd.conf, hostapd will not control
# fragmentation threshold and 'iwconfig wlan# frag <val>' can be used to set
# it.
fragm_threshold=-1
# Rate configuration
# Default is to enable all rates supported by the hardware. This configuration
# item allows this list be filtered so that only the listed rates will be left
# in the list. If the list is empty, all rates are used. This list can have
# entries that are not in the list of rates the hardware supports (such entries
# are ignored). The entries in this list are in 100 kbps, i.e., 11 Mbps = 110.
# If this item is present, at least one rate have to be matching with the rates
# hardware supports.
# default: use the most common supported rate setting for the selected
# hw_mode (i.e., this line can be removed from configuration file in most
# cases)
#supported_rates=10 20 55 110 60 90 120 180 240 360 480 540
# Basic rate set configuration
# List of rates (in 100 kbps) that are included in the basic rate set.
# If this item is not included, usually reasonable default set is used.
#basic_rates=10 20
#basic_rates=10 20 55 110
#basic_rates=60 120 240
# Beacon frame TX rate configuration
# This sets the TX rate that is used to transmit Beacon frames. If this item is
# not included, the driver default rate (likely lowest rate) is used.
# Legacy (CCK/OFDM rates):
# beacon_rate=<legacy rate in 100 kbps>
# HT:
# beacon_rate=ht:<HT MCS>
# VHT:
# beacon_rate=vht:<VHT MCS>
# HE:
# beacon_rate=he:<HE MCS>
#
# For example, beacon_rate=10 for 1 Mbps or beacon_rate=60 for 6 Mbps (OFDM).
#beacon_rate=10
# Short Preamble
# This parameter can be used to enable optional use of short preamble for
# frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance.
# This applies only to IEEE 802.11b-compatible networks and this should only be
# enabled if the local hardware supports use of short preamble. If any of the
# associated STAs do not support short preamble, use of short preamble will be
# disabled (and enabled when such STAs disassociate) dynamically.
# 0 = do not allow use of short preamble (default)
# 1 = allow use of short preamble
#preamble=1
# Station MAC address -based authentication
# Please note that this kind of access control requires a driver that uses
# hostapd to take care of management frame processing and as such, this can be
# used with driver=hostap or driver=nl80211, but not with driver=atheros.
# 0 = accept unless in deny list
# 1 = deny unless in accept list
# 2 = use external RADIUS server (accept/deny lists are searched first)
macaddr_acl=0
# Accept/deny lists are read from separate files (containing list of
# MAC addresses, one per line). Use absolute path name to make sure that the
# files can be read on SIGHUP configuration reloads.
#accept_mac_file=/etc/hostapd.accept
#deny_mac_file=/etc/hostapd.deny
# IEEE 802.11 specifies two authentication algorithms. hostapd can be
# configured to allow both of these or only one. Open system authentication
# should be used with IEEE 802.1X.
# Bit fields of allowed authentication algorithms:
# bit 0 = Open System Authentication
# bit 1 = Shared Key Authentication (requires WEP)
auth_algs=3
# Send empty SSID in beacons and ignore probe request frames that do not
# specify full SSID, i.e., require stations to know SSID.
# default: disabled (0)
# 1 = send empty (length=0) SSID in beacon and ignore probe request for
# broadcast SSID
# 2 = clear SSID (ASCII 0), but keep the original length (this may be required
# with some clients that do not support empty SSID) and ignore probe
# requests for broadcast SSID
ignore_broadcast_ssid=0
# Do not reply to broadcast Probe Request frames from unassociated STA if there
# is no room for additional stations (max_num_sta). This can be used to
# discourage a STA from trying to associate with this AP if the association
# would be rejected due to maximum STA limit.
# Default: 0 (disabled)
#no_probe_resp_if_max_sta=0
# Additional vendor specific elements for Beacon and Probe Response frames
# This parameter can be used to add additional vendor specific element(s) into
# the end of the Beacon and Probe Response frames. The format for these
# element(s) is a hexdump of the raw information elements (id+len+payload for
# one or more elements)
#vendor_elements=dd0411223301
# Additional vendor specific elements for (Re)Association Response frames
# This parameter can be used to add additional vendor specific element(s) into
# the end of the (Re)Association Response frames. The format for these
# element(s) is a hexdump of the raw information elements (id+len+payload for
# one or more elements)
#assocresp_elements=dd0411223301
# TX queue parameters (EDCF / bursting)
# tx_queue_<queue name>_<param>
# queues: data0, data1, data2, data3
# (data0 is the highest priority queue)
# parameters:
# aifs: AIFS (default 2)
# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
# 16383, 32767)
# cwmax: cwMax (same values as cwMin, cwMax >= cwMin)
# burst: maximum length (in milliseconds with precision of up to 0.1 ms) for
# bursting
#
# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
# These parameters are used by the access point when transmitting frames
# to the clients.
#
# Low priority / AC_BK = background
#tx_queue_data3_aifs=7
#tx_queue_data3_cwmin=15
#tx_queue_data3_cwmax=1023
#tx_queue_data3_burst=0
# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0
#
# Normal priority / AC_BE = best effort
#tx_queue_data2_aifs=3
#tx_queue_data2_cwmin=15
#tx_queue_data2_cwmax=63
#tx_queue_data2_burst=0
# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0
#
# High priority / AC_VI = video
#tx_queue_data1_aifs=1
#tx_queue_data1_cwmin=7
#tx_queue_data1_cwmax=15
#tx_queue_data1_burst=3.0
# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0
#
# Highest priority / AC_VO = voice
#tx_queue_data0_aifs=1
#tx_queue_data0_cwmin=3
#tx_queue_data0_cwmax=7
#tx_queue_data0_burst=1.5
# Note: for IEEE 802.11b mode: cWmin=7 cWmax=15 burst=3.3
# 802.1D Tag (= UP) to AC mappings
# WMM specifies following mapping of data frames to different ACs. This mapping
# can be configured using Linux QoS/tc and sch_pktpri.o module.
# 802.1D Tag 802.1D Designation Access Category WMM Designation
# 1 BK AC_BK Background
# 2 - AC_BK Background
# 0 BE AC_BE Best Effort
# 3 EE AC_BE Best Effort
# 4 CL AC_VI Video
# 5 VI AC_VI Video
# 6 VO AC_VO Voice
# 7 NC AC_VO Voice
# Data frames with no priority information: AC_BE
# Management frames: AC_VO
# PS-Poll frames: AC_BE
# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
# for 802.11a or 802.11g networks
# These parameters are sent to WMM clients when they associate.
# The parameters will be used by WMM clients for frames transmitted to the
# access point.
#
# note - txop_limit is in units of 32microseconds
# note - acm is admission control mandatory flag. 0 = admission control not
# required, 1 = mandatory
# note - Here cwMin and cmMax are in exponent form. The actual cw value used
# will be (2^n)-1 where n is the value given here. The allowed range for these
# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin.
#
wmm_enabled=1
#
# WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD]
# Enable this flag if U-APSD supported outside hostapd (eg., Firmware/driver)
#uapsd_advertisement_enabled=1
#
# Low priority / AC_BK = background
wmm_ac_bk_cwmin=4
wmm_ac_bk_cwmax=10
wmm_ac_bk_aifs=7
wmm_ac_bk_txop_limit=0
wmm_ac_bk_acm=0
# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10
#
# Normal priority / AC_BE = best effort
wmm_ac_be_aifs=3
wmm_ac_be_cwmin=4
wmm_ac_be_cwmax=10
wmm_ac_be_txop_limit=0
wmm_ac_be_acm=0
# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7
#
# High priority / AC_VI = video
wmm_ac_vi_aifs=2
wmm_ac_vi_cwmin=3
wmm_ac_vi_cwmax=4
wmm_ac_vi_txop_limit=94
wmm_ac_vi_acm=0
# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188
#
# Highest priority / AC_VO = voice
wmm_ac_vo_aifs=2
wmm_ac_vo_cwmin=2
wmm_ac_vo_cwmax=3
wmm_ac_vo_txop_limit=47
wmm_ac_vo_acm=0
# Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102
# Enable Multi-AP functionality
# 0 = disabled (default)
# 1 = AP support backhaul BSS
# 2 = AP support fronthaul BSS
# 3 = AP supports both backhaul BSS and fronthaul BSS
#multi_ap=0
# Static WEP key configuration
#
# The key number to use when transmitting.
# It must be between 0 and 3, and the corresponding key must be set.
# default: not set
#wep_default_key=0
# The WEP keys to use.
# A key may be a quoted string or unquoted hexadecimal digits.
# The key length should be 5, 13, or 16 characters, or 10, 26, or 32
# digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or
# 128-bit (152-bit) WEP is used.
# Only the default key must be supplied; the others are optional.
# default: not set
#wep_key0=123456789a
#wep_key1="vwxyz"
#wep_key2=0102030405060708090a0b0c0d
#wep_key3=".2.4.6.8.0.23"
# Station inactivity limit
#
# If a station does not send anything in ap_max_inactivity seconds, an
# empty data frame is sent to it in order to verify whether it is
# still in range. If this frame is not ACKed, the station will be
# disassociated and then deauthenticated. This feature is used to
# clear station table of old entries when the STAs move out of the
# range.
#
# The station can associate again with the AP if it is still in range;
# this inactivity poll is just used as a nicer way of verifying
# inactivity; i.e., client will not report broken connection because
# disassociation frame is not sent immediately without first polling
# the STA with a data frame.
# default: 300 (i.e., 5 minutes)
#ap_max_inactivity=300
#
# The inactivity polling can be disabled to disconnect stations based on
# inactivity timeout so that idle stations are more likely to be disconnected
# even if they are still in range of the AP. This can be done by setting
# skip_inactivity_poll to 1 (default 0).
#skip_inactivity_poll=0
# Disassociate stations based on excessive transmission failures or other
# indications of connection loss. This depends on the driver capabilities and
# may not be available with all drivers.
#disassoc_low_ack=1
# Maximum allowed Listen Interval (how many Beacon periods STAs are allowed to
# remain asleep). Default: 65535 (no limit apart from field size)
#max_listen_interval=100
# WDS (4-address frame) mode with per-station virtual interfaces
# (only supported with driver=nl80211)
# This mode allows associated stations to use 4-address frames to allow layer 2
# bridging to be used.
#wds_sta=1
# If bridge parameter is set, the WDS STA interface will be added to the same
# bridge by default. This can be overridden with the wds_bridge parameter to
# use a separate bridge.
#wds_bridge=wds-br0
# Start the AP with beaconing disabled by default.
#start_disabled=0
# Client isolation can be used to prevent low-level bridging of frames between
# associated stations in the BSS. By default, this bridging is allowed.
#ap_isolate=1
# BSS Load update period (in BUs)
# This field is used to enable and configure adding a BSS Load element into
# Beacon and Probe Response frames.
#bss_load_update_period=50
# Channel utilization averaging period (in BUs)
# This field is used to enable and configure channel utilization average
# calculation with bss_load_update_period. This should be in multiples of
# bss_load_update_period for more accurate calculation.
#chan_util_avg_period=600
# Fixed BSS Load value for testing purposes
# This field can be used to configure hostapd to add a fixed BSS Load element
# into Beacon and Probe Response frames for testing purposes. The format is
# <station count>:<channel utilization>:<available admission capacity>
#bss_load_test=12:80:20000
# Multicast to unicast conversion
# Request that the AP will do multicast-to-unicast conversion for ARP, IPv4, and
# IPv6 frames (possibly within 802.1Q). If enabled, such frames are to be sent
# to each station separately, with the DA replaced by their own MAC address
# rather than the group address.
#
# Note that this may break certain expectations of the receiver, such as the
# ability to drop unicast IP packets received within multicast L2 frames, or the
# ability to not send ICMP destination unreachable messages for packets received
# in L2 multicast (which is required, but the receiver can't tell the difference
# if this new option is enabled).
#
# This also doesn't implement the 802.11 DMS (directed multicast service).
#
#multicast_to_unicast=0
# Send broadcast Deauthentication frame on AP start/stop
# Default: 1 (enabled)
#broadcast_deauth=1
# Get notifications for received Management frames on control interface
# Default: 0 (disabled)
#notify_mgmt_frames=0
##### IEEE 802.11n related configuration ######################################
# ieee80211n: Whether IEEE 802.11n (HT) is enabled
# 0 = disabled (default)
# 1 = enabled
# Note: You will also need to enable WMM for full HT functionality.
# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band.
#ieee80211n=1
# disable_11n: Boolean (0/1) to disable HT for a specific BSS
#disable_11n=0
# ht_capab: HT capabilities (list of flags)
# LDPC coding capability: [LDPC] = supported
# Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary
# channel below the primary channel; [HT40+] = both 20 MHz and 40 MHz
# with secondary channel above the primary channel
# (20 MHz only if neither is set)
# Note: There are limits on which channels can be used with HT40- and
# HT40+. Following table shows the channels that may be available for
# HT40- and HT40+ use per IEEE 802.11n Annex J:
# freq HT40- HT40+
# 2.4 GHz 5-13 1-7 (1-9 in Europe/Japan)
# 5 GHz 40,48,56,64 36,44,52,60
# (depending on the location, not all of these channels may be available
# for use)
# Please note that 40 MHz channels may switch their primary and secondary
# channels if needed or creation of 40 MHz channel maybe rejected based
# on overlapping BSSes. These changes are done automatically when hostapd
# is setting up the 40 MHz channel.
# HT-greenfield: [GF] (disabled if not set)
# Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set)
# Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set)
# Tx STBC: [TX-STBC] (disabled if not set)
# Rx STBC: [RX-STBC1] (one spatial stream), [RX-STBC12] (one or two spatial
# streams), or [RX-STBC123] (one, two, or three spatial streams); Rx STBC
# disabled if none of these set
# HT-delayed Block Ack: [DELAYED-BA] (disabled if not set)
# Maximum A-MSDU length: [MAX-AMSDU-7935] for 7935 octets (3839 octets if not
# set)
# DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set)
# 40 MHz intolerant [40-INTOLERANT] (not advertised if not set)
# L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set)
#ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40]
# Require stations to support HT PHY (reject association if they do not)
#require_ht=1
# If set non-zero, require stations to perform scans of overlapping
# channels to test for stations which would be affected by 40 MHz traffic.
# This parameter sets the interval in seconds between these scans. Setting this
# to non-zero allows 2.4 GHz band AP to move dynamically to a 40 MHz channel if
# no co-existence issues with neighboring devices are found.
#obss_interval=0
##### IEEE 802.11ac related configuration #####################################
# ieee80211ac: Whether IEEE 802.11ac (VHT) is enabled
# 0 = disabled (default)
# 1 = enabled
# Note: You will also need to enable WMM for full VHT functionality.
# Note: hw_mode=a is used to specify that 5 GHz band is used with VHT.
#ieee80211ac=1
# disable_11ac: Boolean (0/1) to disable VHT for a specific BSS
#disable_11ac=0
# vht_capab: VHT capabilities (list of flags)
#
# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
# Indicates maximum MPDU length
# 0 = 3895 octets (default)
# 1 = 7991 octets
# 2 = 11454 octets
# 3 = reserved
#
# supported_chan_width: [VHT160] [VHT160-80PLUS80]
# Indicates supported Channel widths
# 0 = 160 MHz & 80+80 channel widths are not supported (default)
# 1 = 160 MHz channel width is supported
# 2 = 160 MHz & 80+80 channel widths are supported
# 3 = reserved
#
# Rx LDPC coding capability: [RXLDPC]
# Indicates support for receiving LDPC coded pkts
# 0 = Not supported (default)
# 1 = Supported
#
# Short GI for 80 MHz: [SHORT-GI-80]
# Indicates short GI support for reception of packets transmitted with TXVECTOR
# params format equal to VHT and CBW = 80Mhz
# 0 = Not supported (default)
# 1 = Supported
#
# Short GI for 160 MHz: [SHORT-GI-160]
# Indicates short GI support for reception of packets transmitted with TXVECTOR
# params format equal to VHT and CBW = 160Mhz
# 0 = Not supported (default)
# 1 = Supported
#
# Tx STBC: [TX-STBC-2BY1]
# Indicates support for the transmission of at least 2x1 STBC
# 0 = Not supported (default)
# 1 = Supported
#
# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234]
# Indicates support for the reception of PPDUs using STBC
# 0 = Not supported (default)
# 1 = support of one spatial stream
# 2 = support of one and two spatial streams
# 3 = support of one, two and three spatial streams
# 4 = support of one, two, three and four spatial streams
# 5,6,7 = reserved
#
# SU Beamformer Capable: [SU-BEAMFORMER]
# Indicates support for operation as a single user beamformer
# 0 = Not supported (default)
# 1 = Supported
#
# SU Beamformee Capable: [SU-BEAMFORMEE]
# Indicates support for operation as a single user beamformee
# 0 = Not supported (default)
# 1 = Supported
#
# Compressed Steering Number of Beamformer Antennas Supported:
# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4]
# Beamformee's capability indicating the maximum number of beamformer
# antennas the beamformee can support when sending compressed beamforming
# feedback
# If SU beamformer capable, set to maximum value minus 1
# else reserved (default)
#
# Number of Sounding Dimensions:
# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4]
# Beamformer's capability indicating the maximum value of the NUM_STS parameter
# in the TXVECTOR of a VHT NDP
# If SU beamformer capable, set to maximum value minus 1
# else reserved (default)
#
# MU Beamformer Capable: [MU-BEAMFORMER]
# Indicates support for operation as an MU beamformer
# 0 = Not supported or sent by Non-AP STA (default)
# 1 = Supported
#
# VHT TXOP PS: [VHT-TXOP-PS]
# Indicates whether or not the AP supports VHT TXOP Power Save Mode
# or whether or not the STA is in VHT TXOP Power Save mode
# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS
# mode
# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save
# mode
#
# +HTC-VHT Capable: [HTC-VHT]
# Indicates whether or not the STA supports receiving a VHT variant HT Control
# field.
# 0 = Not supported (default)
# 1 = supported
#
# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7]
# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv
# This field is an integer in the range of 0 to 7.
# The length defined by this field is equal to
# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
#
# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3]
# Indicates whether or not the STA supports link adaptation using VHT variant
# HT Control field
# If +HTC-VHTcapable is 1
# 0 = (no feedback) if the STA does not provide VHT MFB (default)
# 1 = reserved
# 2 = (Unsolicited) if the STA provides only unsolicited VHT MFB
# 3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the
# STA provides unsolicited VHT MFB
# Reserved if +HTC-VHTcapable is 0
#
# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN]
# Indicates the possibility of Rx antenna pattern change
# 0 = Rx antenna pattern might change during the lifetime of an association
# 1 = Rx antenna pattern does not change during the lifetime of an association
#
# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN]
# Indicates the possibility of Tx antenna pattern change
# 0 = Tx antenna pattern might change during the lifetime of an association
# 1 = Tx antenna pattern does not change during the lifetime of an association
#vht_capab=[SHORT-GI-80][HTC-VHT]
#
# Require stations to support VHT PHY (reject association if they do not)
#require_vht=1
# 0 = 20 or 40 MHz operating Channel width
# 1 = 80 MHz channel width
# 2 = 160 MHz channel width
# 3 = 80+80 MHz channel width
#vht_oper_chwidth=1
#
# center freq = 5 GHz + (5 * index)
# So index 42 gives center freq 5.210 GHz
# which is channel 42 in 5G band
#
#vht_oper_centr_freq_seg0_idx=42
#
# center freq = 5 GHz + (5 * index)
# So index 159 gives center freq 5.795 GHz
# which is channel 159 in 5G band
#
#vht_oper_centr_freq_seg1_idx=159
# Workaround to use station's nsts capability in (Re)Association Response frame
# This may be needed with some deployed devices as an interoperability
# workaround for beamforming if the AP's capability is greater than the
# station's capability. This is disabled by default and can be enabled by
# setting use_sta_nsts=1.
#use_sta_nsts=0
##### IEEE 802.11ax related configuration #####################################
#ieee80211ax: Whether IEEE 802.11ax (HE) is enabled
# 0 = disabled (default)
# 1 = enabled
#ieee80211ax=1
# disable_11ax: Boolean (0/1) to disable HE for a specific BSS
#disable_11ax=0
#he_su_beamformer: HE single user beamformer support
# 0 = not supported (default)
# 1 = supported
#he_su_beamformer=1
#he_su_beamformee: HE single user beamformee support
# 0 = not supported (default)
# 1 = supported
#he_su_beamformee=1
#he_mu_beamformer: HE multiple user beamformer support
# 0 = not supported (default)
# 1 = supported
#he_mu_beamformer=1
# he_bss_color: BSS color (1-63)
#he_bss_color=1
# he_bss_color_partial: BSS color AID equation
#he_bss_color_partial=0
#he_default_pe_duration: The duration of PE field in an HE PPDU in us
# Possible values are 0 us (default), 4 us, 8 us, 12 us, and 16 us
#he_default_pe_duration=0
#he_twt_required: Whether TWT is required
# 0 = not required (default)
# 1 = required
#he_twt_required=0
#he_twt_responder: Whether TWT (HE) responder is enabled
# 0 = disabled
# 1 = enabled if supported by the driver (default)
#he_twt_responder=1
#he_rts_threshold: Duration of STA transmission
# 0 = not set (default)
# unsigned integer = duration in units of 16 us
#he_rts_threshold=0
+#he_er_su_disable: Disable 242-tone HE ER SU PPDU reception by the AP
+# 0 = enable reception (default)
+# 1 = disable reception
+#he_er_su_disable=0
+
# HE operating channel information; see matching vht_* parameters for details.
# he_oper_centr_freq_seg0_idx field is used to indicate center frequency of 80
# and 160 MHz bandwidth operation. In 80+80 MHz operation, it is the center
# frequency of the lower frequency segment. he_oper_centr_freq_seg1_idx field
# is used only with 80+80 MHz bandwidth operation and it is used to transmit
# the center frequency of the second segment.
# On the 6 GHz band the center freq calculation starts from 5.950 GHz offset.
# For example idx=3 would result in 5965 MHz center frequency. In addition,
# he_oper_chwidth is ignored, and the channel width is derived from the
# configured operating class or center frequency indexes (see
# IEEE P802.11ax/D6.1 Annex E, Table E-4).
#he_oper_chwidth
#he_oper_centr_freq_seg0_idx
#he_oper_centr_freq_seg1_idx
#he_basic_mcs_nss_set: Basic NSS/MCS set
# 16-bit combination of 2-bit values of Max HE-MCS For 1..8 SS; each 2-bit
# value having following meaning:
# 0 = HE-MCS 0-7, 1 = HE-MCS 0-9, 2 = HE-MCS 0-11, 3 = not supported
#he_basic_mcs_nss_set
#he_mu_edca_qos_info_param_count
#he_mu_edca_qos_info_q_ack
#he_mu_edca_qos_info_queue_request=1
#he_mu_edca_qos_info_txop_request
#he_mu_edca_ac_be_aifsn=0
#he_mu_edca_ac_be_ecwmin=15
#he_mu_edca_ac_be_ecwmax=15
#he_mu_edca_ac_be_timer=255
#he_mu_edca_ac_bk_aifsn=0
#he_mu_edca_ac_bk_aci=1
#he_mu_edca_ac_bk_ecwmin=15
#he_mu_edca_ac_bk_ecwmax=15
#he_mu_edca_ac_bk_timer=255
#he_mu_edca_ac_vi_ecwmin=15
#he_mu_edca_ac_vi_ecwmax=15
#he_mu_edca_ac_vi_aifsn=0
#he_mu_edca_ac_vi_aci=2
#he_mu_edca_ac_vi_timer=255
#he_mu_edca_ac_vo_aifsn=0
#he_mu_edca_ac_vo_aci=3
#he_mu_edca_ac_vo_ecwmin=15
#he_mu_edca_ac_vo_ecwmax=15
#he_mu_edca_ac_vo_timer=255
# Spatial Reuse Parameter Set
#
# SR Control field value
# B0 = PSR Disallowed
# B1 = Non-SRG OBSS PD SR Disallowed
# B2 = Non-SRG Offset Present
# B3 = SRG Information Present
# B4 = HESIGA_Spatial_reuse_value15_allowed
#he_spr_sr_control
#
# Non-SRG OBSS PD Max Offset (included if he_spr_sr_control B2=1)
#he_spr_non_srg_obss_pd_max_offset
# SRG OBSS PD Min Offset (included if he_spr_sr_control B3=1)
#he_spr_srg_obss_pd_min_offset
#
# SRG OBSS PD Max Offset (included if he_spr_sr_control B3=1)
#he_spr_srg_obss_pd_max_offset
#
# SPR SRG BSS Color (included if he_spr_sr_control B3=1)
# This config represents SRG BSS Color Bitmap field of Spatial Reuse Parameter
# Set element that indicates the BSS color values used by members of the
# SRG of which the transmitting STA is a member. The value is in range of 0-63.
#he_spr_srg_bss_colors=1 2 10 63
#
# SPR SRG Partial BSSID (included if he_spr_sr_control B3=1)
# This config represents SRG Partial BSSID Bitmap field of Spatial Reuse
# Parameter Set element that indicates the Partial BSSID values used by members
# of the SRG of which the transmitting STA is a member. The value range
# corresponds to one of the 64 possible values of BSSID[39:44], where the lowest
# numbered bit corresponds to Partial BSSID value 0 and the highest numbered bit
# corresponds to Partial BSSID value 63.
#he_spr_srg_partial_bssid=0 1 3 63
#
#he_6ghz_max_mpdu: Maximum MPDU Length of HE 6 GHz band capabilities.
# Indicates maximum MPDU length
# 0 = 3895 octets
# 1 = 7991 octets
# 2 = 11454 octets (default)
#he_6ghz_max_mpdu=2
#
#he_6ghz_max_ampdu_len_exp: Maximum A-MPDU Length Exponent of HE 6 GHz band
# capabilities. Indicates the maximum length of A-MPDU pre-EOF padding that
# the STA can receive. This field is an integer in the range of 0 to 7.
# The length defined by this field is equal to
# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
# 0 = AMPDU length of 8k
# 1 = AMPDU length of 16k
# 2 = AMPDU length of 32k
# 3 = AMPDU length of 65k
# 4 = AMPDU length of 131k
# 5 = AMPDU length of 262k
# 6 = AMPDU length of 524k
# 7 = AMPDU length of 1048k (default)
#he_6ghz_max_ampdu_len_exp=7
#
#he_6ghz_rx_ant_pat: Rx Antenna Pattern Consistency of HE 6 GHz capability.
# Indicates the possibility of Rx antenna pattern change
# 0 = Rx antenna pattern might change during the lifetime of an association
# 1 = Rx antenna pattern does not change during the lifetime of an association
# (default)
#he_6ghz_rx_ant_pat=1
#
#he_6ghz_tx_ant_pat: Tx Antenna Pattern Consistency of HE 6 GHz capability.
# Indicates the possibility of Tx antenna pattern change
# 0 = Tx antenna pattern might change during the lifetime of an association
# 1 = Tx antenna pattern does not change during the lifetime of an association
# (default)
#he_6ghz_tx_ant_pat=1
# Unsolicited broadcast Probe Response transmission settings
# This is for the 6 GHz band only. If the interval is set to a non-zero value,
# the AP schedules unsolicited broadcast Probe Response frames to be
# transmitted for in-band discovery. Refer to
# IEEE P802.11ax/D8.0 26.17.2.3.2, AP behavior for fast passive scanning.
# Valid range: 0..20 TUs; default is 0 (disabled)
#unsol_bcast_probe_resp_interval=0
##### IEEE 802.1X-2004 related configuration ##################################
# Require IEEE 802.1X authorization
#ieee8021x=1
# IEEE 802.1X/EAPOL version
# hostapd is implemented based on IEEE Std 802.1X-2004 which defines EAPOL
# version 2. However, there are many client implementations that do not handle
# the new version number correctly (they seem to drop the frames completely).
# In order to make hostapd interoperate with these clients, the version number
# can be set to the older version (1) with this configuration value.
# Note: When using MACsec, eapol_version shall be set to 3, which is
# defined in IEEE Std 802.1X-2010.
#eapol_version=2
# Optional displayable message sent with EAP Request-Identity. The first \0
# in this string will be converted to ASCII-0 (nul). This can be used to
# separate network info (comma separated list of attribute=value pairs); see,
# e.g., RFC 4284.
#eap_message=hello
#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com
# WEP rekeying (disabled if key lengths are not set or are set to 0)
# Key lengths for default/broadcast and individual/unicast keys:
# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits)
# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits)
#wep_key_len_broadcast=5
#wep_key_len_unicast=5
# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once)
#wep_rekey_period=300
# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if
# only broadcast keys are used)
eapol_key_index_workaround=0
# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable
# reauthentication).
# Note: Reauthentications may enforce a disconnection, check the related
# parameter wpa_deny_ptk0_rekey for details.
#eap_reauth_period=3600
# Use PAE group address (01:80:c2:00:00:03) instead of individual target
# address when sending EAPOL frames with driver=wired. This is the most common
# mechanism used in wired authentication, but it also requires that the port
# is only used by one station.
#use_pae_group_addr=1
# EAP Re-authentication Protocol (ERP) authenticator (RFC 6696)
#
# Whether to initiate EAP authentication with EAP-Initiate/Re-auth-Start before
# EAP-Identity/Request
#erp_send_reauth_start=1
#
# Domain name for EAP-Initiate/Re-auth-Start. Omitted from the message if not
# set (no local ER server). This is also used by the integrated EAP server if
# ERP is enabled (eap_server_erp=1).
#erp_domain=example.com
##### MACsec ##################################################################
# macsec_policy: IEEE 802.1X/MACsec options
# This determines how sessions are secured with MACsec (only for MACsec
# drivers).
# 0: MACsec not in use (default)
# 1: MACsec enabled - Should secure, accept key server's advice to
# determine whether to use a secure session or not.
#
# macsec_integ_only: IEEE 802.1X/MACsec transmit mode
# This setting applies only when MACsec is in use, i.e.,
# - macsec_policy is enabled
# - the key server has decided to enable MACsec
# 0: Encrypt traffic (default)
# 1: Integrity only
#
# macsec_replay_protect: IEEE 802.1X/MACsec replay protection
# This setting applies only when MACsec is in use, i.e.,
# - macsec_policy is enabled
# - the key server has decided to enable MACsec
# 0: Replay protection disabled (default)
# 1: Replay protection enabled
#
# macsec_replay_window: IEEE 802.1X/MACsec replay protection window
# This determines a window in which replay is tolerated, to allow receipt
# of frames that have been misordered by the network.
# This setting applies only when MACsec replay protection active, i.e.,
# - macsec_replay_protect is enabled
# - the key server has decided to enable MACsec
# 0: No replay window, strict check (default)
# 1..2^32-1: number of packets that could be misordered
#
# macsec_port: IEEE 802.1X/MACsec port
# Port component of the SCI
# Range: 1-65534 (default: 1)
#
# mka_priority (Priority of MKA Actor)
# Range: 0..255 (default: 255)
#
# mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode
# This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair.
# In this mode, instances of hostapd can act as MACsec peers. The peer
# with lower priority will become the key server and start distributing SAKs.
# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-byte (128-bit)
# hex-string (32 hex-digits) or a 32-byte (256-bit) hex-string (64 hex-digits)
# mka_ckn (CKN = CAK Name) takes a 1..32-bytes (8..256 bit) hex-string
# (2..64 hex-digits)
##### Integrated EAP server ###################################################
# Optionally, hostapd can be configured to use an integrated EAP server
# to process EAP authentication locally without need for an external RADIUS
# server. This functionality can be used both as a local authentication server
# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices.
# Use integrated EAP server instead of external RADIUS authentication
# server. This is also needed if hostapd is configured to act as a RADIUS
# authentication server.
eap_server=0
# Path for EAP server user database
# If SQLite support is included, this can be set to "sqlite:/path/to/sqlite.db"
# to use SQLite database instead of a text file.
#eap_user_file=/etc/hostapd.eap_user
# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS
#ca_cert=/etc/hostapd.ca.pem
# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS
#server_cert=/etc/hostapd.server.pem
# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS
# This may point to the same file as server_cert if both certificate and key
# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be
# used by commenting out server_cert and specifying the PFX file as the
# private_key.
#private_key=/etc/hostapd.server.prv
# Passphrase for private key
#private_key_passwd=secret passphrase
# An alternative server certificate and private key can be configured with the
# following parameters (with values just like the parameters above without the
# '2' suffix). The ca_cert file (in PEM encoding) is used to add the trust roots
# for both server certificates and/or client certificates).
#
# The main use case for this alternative server certificate configuration is to
# enable both RSA and ECC public keys. The server will pick which one to use
# based on the client preferences for the cipher suite (in the TLS ClientHello
# message). It should be noted that number of deployed EAP peer implementations
# do not filter out the cipher suite list based on their local configuration and
# as such, configuration of alternative types of certificates on the server may
# result in interoperability issues.
#server_cert2=/etc/hostapd.server-ecc.pem
#private_key2=/etc/hostapd.server-ecc.prv
#private_key_passwd2=secret passphrase
# Server identity
# EAP methods that provide mechanism for authenticated server identity delivery
# use this value. If not set, "hostapd" is used as a default.
#server_id=server.example.com
# Enable CRL verification.
# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a
# valid CRL signed by the CA is required to be included in the ca_cert file.
# This can be done by using PEM format for CA certificate and CRL and
# concatenating these into one file. Whenever CRL changes, hostapd needs to be
# restarted to take the new CRL into use. Alternatively, crl_reload_interval can
# be used to configure periodic updating of the loaded CRL information.
# 0 = do not verify CRLs (default)
# 1 = check the CRL of the user certificate
# 2 = check all CRLs in the certificate path
#check_crl=1
# Specify whether to ignore certificate CRL validity time mismatches with
# errors X509_V_ERR_CRL_HAS_EXPIRED and X509_V_ERR_CRL_NOT_YET_VALID.
#
# 0 = ignore errors
# 1 = do not ignore errors (default)
#check_crl_strict=1
# CRL reload interval in seconds
# This can be used to reload ca_cert file and the included CRL on every new TLS
# session if difference between last reload and the current reload time in
# seconds is greater than crl_reload_interval.
# Note: If interval time is very short, CPU overhead may be negatively affected
# and it is advised to not go below 300 seconds.
# This is applicable only with check_crl values 1 and 2.
# 0 = do not reload CRLs (default)
# crl_reload_interval = 300
# If check_cert_subject is set, the value of every field will be checked
# against the DN of the subject in the client certificate. If the values do
# not match, the certificate verification will fail, rejecting the user.
# This option allows hostapd to match every individual field in the right order
# against the DN of the subject in the client certificate.
#
# For example, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234 will check
# every individual DN field of the subject in the client certificate. If OU=XYZ
# comes first in terms of the order in the client certificate (DN field of
# client certificate C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), hostapd will reject the
# client because the order of 'OU' is not matching the specified string in
# check_cert_subject.
#
# This option also allows '*' as a wildcard. This option has some limitation.
# It can only be used as per the following example.
#
# For example, check_cert_subject=C=US/O=XX/OU=Production* and we have two
# clients and DN of the subject in the first client certificate is
# (C=US/O=XX/OU=Production Unit) and DN of the subject in the second client is
# (C=US/O=XX/OU=Production Factory). In this case, hostapd will allow both
# clients because the value of 'OU' field in both client certificates matches
# 'OU' value in 'check_cert_subject' up to 'wildcard'.
#
# * (Allow all clients, e.g., check_cert_subject=*)
#check_cert_subject=string
# TLS Session Lifetime in seconds
# This can be used to allow TLS sessions to be cached and resumed with an
# abbreviated handshake when using EAP-TLS/TTLS/PEAP.
# (default: 0 = session caching and resumption disabled)
#tls_session_lifetime=3600
# TLS flags
# [ALLOW-SIGN-RSA-MD5] = allow MD5-based certificate signatures (depending on
# the TLS library, these may be disabled by default to enforce stronger
# security)
# [DISABLE-TIME-CHECKS] = ignore certificate validity time (this requests
# the TLS library to accept certificates even if they are not currently
# valid, i.e., have expired or have not yet become valid; this should be
# used only for testing purposes)
# [DISABLE-TLSv1.0] = disable use of TLSv1.0
# [ENABLE-TLSv1.0] = explicitly enable use of TLSv1.0 (this allows
# systemwide TLS policies to be overridden)
# [DISABLE-TLSv1.1] = disable use of TLSv1.1
# [ENABLE-TLSv1.1] = explicitly enable use of TLSv1.1 (this allows
# systemwide TLS policies to be overridden)
# [DISABLE-TLSv1.2] = disable use of TLSv1.2
# [ENABLE-TLSv1.2] = explicitly enable use of TLSv1.2 (this allows
# systemwide TLS policies to be overridden)
# [DISABLE-TLSv1.3] = disable use of TLSv1.3
# [ENABLE-TLSv1.3] = enable TLSv1.3 (experimental - disabled by default)
#tls_flags=[flag1][flag2]...
# Maximum number of EAP message rounds with data (default: 100)
#max_auth_rounds=100
# Maximum number of short EAP message rounds (default: 50)
#max_auth_rounds_short=50
# Cached OCSP stapling response (DER encoded)
# If set, this file is sent as a certificate status response by the EAP server
# if the EAP peer requests certificate status in the ClientHello message.
# This cache file can be updated, e.g., by running following command
# periodically to get an update from the OCSP responder:
# openssl ocsp \
# -no_nonce \
# -CAfile /etc/hostapd.ca.pem \
# -issuer /etc/hostapd.ca.pem \
# -cert /etc/hostapd.server.pem \
# -url http://ocsp.example.com:8888/ \
# -respout /tmp/ocsp-cache.der
#ocsp_stapling_response=/tmp/ocsp-cache.der
# Cached OCSP stapling response list (DER encoded OCSPResponseList)
# This is similar to ocsp_stapling_response, but the extended version defined in
# RFC 6961 to allow multiple OCSP responses to be provided.
#ocsp_stapling_response_multi=/tmp/ocsp-multi-cache.der
# dh_file: File path to DH/DSA parameters file (in PEM format)
# This is an optional configuration file for setting parameters for an
# ephemeral DH key exchange. In most cases, the default RSA authentication does
# not use this configuration. However, it is possible setup RSA to use
# ephemeral DH key exchange. In addition, ciphers with DSA keys always use
# ephemeral DH keys. This can be used to achieve forward secrecy. If the file
# is in DSA parameters format, it will be automatically converted into DH
# params. This parameter is required if anonymous EAP-FAST is used.
# You can generate DH parameters file with OpenSSL, e.g.,
# "openssl dhparam -out /etc/hostapd.dh.pem 2048"
#dh_file=/etc/hostapd.dh.pem
# OpenSSL cipher string
#
# This is an OpenSSL specific configuration option for configuring the default
# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW"
# by default) is used.
# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
# on cipher suite configuration. This is applicable only if hostapd is built to
# use OpenSSL.
#openssl_ciphers=DEFAULT:!EXP:!LOW
# OpenSSL ECDH curves
#
# This is an OpenSSL specific configuration option for configuring the ECDH
# curves for EAP-TLS/TTLS/PEAP/FAST server. If not set, automatic curve
# selection is enabled. If set to an empty string, ECDH curve configuration is
# not done (the exact library behavior depends on the library version).
# Otherwise, this is a colon separated list of the supported curves (e.g.,
# P-521:P-384:P-256). This is applicable only if hostapd is built to use
# OpenSSL. This must not be used for Suite B cases since the same OpenSSL
# parameter is set differently in those cases and this might conflict with that
# design.
#openssl_ecdh_curves=P-521:P-384:P-256
# Fragment size for EAP methods
#fragment_size=1400
# Finite cyclic group for EAP-pwd. Number maps to group of domain parameters
# using the IANA repository for IKE (RFC 2409).
#pwd_group=19
# Configuration data for EAP-SIM database/authentication gateway interface.
# This is a text string in implementation specific format. The example
# implementation in eap_sim_db.c uses this as the UNIX domain socket name for
# the HLR/AuC gateway (e.g., hlr_auc_gw). In this case, the path uses "unix:"
# prefix. If hostapd is built with SQLite support (CONFIG_SQLITE=y in .config),
# database file can be described with an optional db=<path> parameter.
#eap_sim_db=unix:/tmp/hlr_auc_gw.sock
#eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db
# EAP-SIM DB request timeout
# This parameter sets the maximum time to wait for a database request response.
# The parameter value is in seconds.
#eap_sim_db_timeout=1
# Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret,
# random value. It is configured as a 16-octet value in hex format. It can be
# generated, e.g., with the following command:
# od -tx1 -v -N16 /dev/random | colrm 1 8 | tr -d ' '
#pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f
# EAP-FAST authority identity (A-ID)
# A-ID indicates the identity of the authority that issues PACs. The A-ID
# should be unique across all issuing servers. In theory, this is a variable
# length field, but due to some existing implementations requiring A-ID to be
# 16 octets in length, it is strongly recommended to use that length for the
# field to provide interoperability with deployed peer implementations. This
# field is configured in hex format.
#eap_fast_a_id=101112131415161718191a1b1c1d1e1f
# EAP-FAST authority identifier information (A-ID-Info)
# This is a user-friendly name for the A-ID. For example, the enterprise name
# and server name in a human-readable format. This field is encoded as UTF-8.
#eap_fast_a_id_info=test server
# Enable/disable different EAP-FAST provisioning modes:
#0 = provisioning disabled
#1 = only anonymous provisioning allowed
#2 = only authenticated provisioning allowed
#3 = both provisioning modes allowed (default)
#eap_fast_prov=3
# EAP-FAST PAC-Key lifetime in seconds (hard limit)
#pac_key_lifetime=604800
# EAP-FAST PAC-Key refresh time in seconds (soft limit on remaining hard
# limit). The server will generate a new PAC-Key when this number of seconds
# (or fewer) of the lifetime remains.
#pac_key_refresh_time=86400
# EAP-TEAP authentication type
# 0 = inner EAP (default)
# 1 = Basic-Password-Auth
# 2 = Do not require Phase 2 authentication if client can be authenticated
# during Phase 1
#eap_teap_auth=0
# EAP-TEAP authentication behavior when using PAC
# 0 = perform inner authentication (default)
# 1 = skip inner authentication (inner EAP/Basic-Password-Auth)
#eap_teap_pac_no_inner=0
# EAP-TEAP behavior with Result TLV
# 0 = include with Intermediate-Result TLV (default)
# 1 = send in a separate message (for testing purposes)
#eap_teap_separate_result=0
# EAP-TEAP identities
# 0 = allow any identity type (default)
# 1 = require user identity
# 2 = require machine identity
# 3 = request user identity; accept either user or machine identity
# 4 = request machine identity; accept either user or machine identity
# 5 = require both user and machine identity
#eap_teap_id=0
# EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND
# (default: 0 = disabled).
#eap_sim_aka_result_ind=1
# EAP-SIM and EAP-AKA identity options
# 0 = do not use pseudonyms or fast reauthentication
# 1 = use pseudonyms, but not fast reauthentication
# 2 = do not use pseudonyms, but use fast reauthentication
# 3 = use pseudonyms and use fast reauthentication (default)
#eap_sim_id=3
# Trusted Network Connect (TNC)
# If enabled, TNC validation will be required before the peer is allowed to
# connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other
# EAP method is enabled, the peer will be allowed to connect without TNC.
#tnc=1
# EAP Re-authentication Protocol (ERP) - RFC 6696
#
# Whether to enable ERP on the EAP server.
#eap_server_erp=1
##### RADIUS client configuration #############################################
# for IEEE 802.1X with external Authentication Server, IEEE 802.11
# authentication with external ACL for MAC addresses, and accounting
# The own IP address of the access point (used as NAS-IP-Address)
own_ip_addr=127.0.0.1
# NAS-Identifier string for RADIUS messages. When used, this should be unique
# to the NAS within the scope of the RADIUS server. Please note that hostapd
# uses a separate RADIUS client for each BSS and as such, a unique
# nas_identifier value should be configured separately for each BSS. This is
# particularly important for cases where RADIUS accounting is used
# (Accounting-On/Off messages are interpreted as clearing all ongoing sessions
# and that may get interpreted as applying to all BSSes if the same
# NAS-Identifier value is used.) For example, a fully qualified domain name
# prefixed with a unique identifier of the BSS (e.g., BSSID) can be used here.
#
# When using IEEE 802.11r, nas_identifier must be set and must be between 1 and
# 48 octets long.
#
# It is mandatory to configure either own_ip_addr or nas_identifier to be
# compliant with the RADIUS protocol. When using RADIUS accounting, it is
# strongly recommended that nas_identifier is set to a unique value for each
# BSS.
#nas_identifier=ap.example.com
# RADIUS client forced local IP address for the access point
# Normally the local IP address is determined automatically based on configured
# IP addresses, but this field can be used to force a specific address to be
# used, e.g., when the device has multiple IP addresses.
#radius_client_addr=127.0.0.1
# RADIUS client forced local interface. Helps run properly with VRF
# Default is none set which allows the network stack to pick the appropriate
# interface automatically.
# Example below binds to eth0
#radius_client_dev=eth0
# RADIUS authentication server
#auth_server_addr=127.0.0.1
#auth_server_port=1812
#auth_server_shared_secret=secret
# RADIUS accounting server
#acct_server_addr=127.0.0.1
#acct_server_port=1813
#acct_server_shared_secret=secret
# Secondary RADIUS servers; to be used if primary one does not reply to
# RADIUS packets. These are optional and there can be more than one secondary
# server listed.
#auth_server_addr=127.0.0.2
#auth_server_port=1812
#auth_server_shared_secret=secret2
#
#acct_server_addr=127.0.0.2
#acct_server_port=1813
#acct_server_shared_secret=secret2
# Retry interval for trying to return to the primary RADIUS server (in
# seconds). RADIUS client code will automatically try to use the next server
# when the current server is not replying to requests. If this interval is set,
# primary server will be retried after configured amount of time even if the
# currently used secondary server is still working.
#radius_retry_primary_interval=600
# Interim accounting update interval
# If this is set (larger than 0) and acct_server is configured, hostapd will
# send interim accounting updates every N seconds. Note: if set, this overrides
# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this
# value should not be configured in hostapd.conf, if RADIUS server is used to
# control the interim interval.
# This value should not be less 600 (10 minutes) and must not be less than
# 60 (1 minute).
#radius_acct_interim_interval=600
# Request Chargeable-User-Identity (RFC 4372)
# This parameter can be used to configure hostapd to request CUI from the
# RADIUS server by including Chargeable-User-Identity attribute into
# Access-Request packets.
#radius_request_cui=1
# Dynamic VLAN mode; allow RADIUS authentication server to decide which VLAN
# is used for the stations. This information is parsed from following RADIUS
# attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN),
# Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value
# VLANID as a string). Optionally, the local MAC ACL list (accept_mac_file) can
# be used to set static client MAC address to VLAN ID mapping.
# Dynamic VLAN mode is also used with VLAN ID assignment based on WPA/WPA2
# passphrase from wpa_psk_file or vlan_id parameter from sae_password.
# 0 = disabled (default); only VLAN IDs from accept_mac_file will be used
# 1 = optional; use default interface if RADIUS server does not include VLAN ID
# 2 = required; reject authentication if RADIUS server does not include VLAN ID
#dynamic_vlan=0
# Per-Station AP_VLAN interface mode
# If enabled, each station is assigned its own AP_VLAN interface.
# This implies per-station group keying and ebtables filtering of inter-STA
# traffic (when passed through the AP).
# If the sta is not assigned to any VLAN, then its AP_VLAN interface will be
# added to the bridge given by the "bridge" configuration option (see above).
# Otherwise, it will be added to the per-VLAN bridge.
# 0 = disabled (default)
# 1 = enabled
#per_sta_vif=0
# VLAN interface list for dynamic VLAN mode is read from a separate text file.
# This list is used to map VLAN ID from the RADIUS server to a network
# interface. Each station is bound to one interface in the same way as with
# multiple BSSIDs or SSIDs. Each line in this text file is defining a new
# interface and the line must include VLAN ID and interface name separated by
# white space (space or tab).
# If no entries are provided by this file, the station is statically mapped
# to <bss-iface>.<vlan-id> interfaces.
# Each line can optionally also contain the name of a bridge to add the VLAN to
#vlan_file=/etc/hostapd.vlan
# Interface where 802.1q tagged packets should appear when a RADIUS server is
# used to determine which VLAN a station is on. hostapd creates a bridge for
# each VLAN. Then hostapd adds a VLAN interface (associated with the interface
# indicated by 'vlan_tagged_interface') and the appropriate wireless interface
# to the bridge.
#vlan_tagged_interface=eth0
# Bridge (prefix) to add the wifi and the tagged interface to. This gets the
# VLAN ID appended. It defaults to brvlan%d if no tagged interface is given
# and br%s.%d if a tagged interface is given, provided %s = tagged interface
# and %d = VLAN ID.
#vlan_bridge=brvlan
# When hostapd creates a VLAN interface on vlan_tagged_interfaces, it needs
# to know how to name it.
# 0 = vlan<XXX>, e.g., vlan1
# 1 = <vlan_tagged_interface>.<XXX>, e.g. eth0.1
#vlan_naming=0
# Arbitrary RADIUS attributes can be added into Access-Request and
# Accounting-Request packets by specifying the contents of the attributes with
# the following configuration parameters. There can be multiple of these to
# add multiple attributes. These parameters can also be used to override some
# of the attributes added automatically by hostapd.
# Format: <attr_id>[:<syntax:value>]
# attr_id: RADIUS attribute type (e.g., 26 = Vendor-Specific)
# syntax: s = string (UTF-8), d = integer, x = octet string
# value: attribute value in format indicated by the syntax
# If syntax and value parts are omitted, a null value (single 0x00 octet) is
# used.
#
# Additional Access-Request attributes
# radius_auth_req_attr=<attr_id>[:<syntax:value>]
# Examples:
# Operator-Name = "Operator"
#radius_auth_req_attr=126:s:Operator
# Service-Type = Framed (2)
#radius_auth_req_attr=6:d:2
# Connect-Info = "testing" (this overrides the automatically generated value)
#radius_auth_req_attr=77:s:testing
# Same Connect-Info value set as a hexdump
#radius_auth_req_attr=77:x:74657374696e67
#
# Additional Accounting-Request attributes
# radius_acct_req_attr=<attr_id>[:<syntax:value>]
# Examples:
# Operator-Name = "Operator"
#radius_acct_req_attr=126:s:Operator
# If SQLite support is included, path to a database from which additional
# RADIUS request attributes are extracted based on the station MAC address.
#
# The schema for the radius_attributes table is:
# id | sta | reqtype | attr : multi-key (sta, reqtype)
# id = autonumber
# sta = station MAC address in `11:22:33:44:55:66` format.
# type = `auth` | `acct` | NULL (match any)
# attr = existing config file format, e.g. `126:s:Test Operator`
#radius_req_attr_sqlite=radius_attr.sqlite
# Dynamic Authorization Extensions (RFC 5176)
# This mechanism can be used to allow dynamic changes to user session based on
# commands from a RADIUS server (or some other disconnect client that has the
# needed session information). For example, Disconnect message can be used to
# request an associated station to be disconnected.
#
# This is disabled by default. Set radius_das_port to non-zero UDP port
# number to enable.
#radius_das_port=3799
#
# DAS client (the host that can send Disconnect/CoA requests) and shared secret
# Format: <IP address> <shared secret>
# IP address 0.0.0.0 can be used to allow requests from any address.
#radius_das_client=192.168.1.123 shared secret here
#
# DAS Event-Timestamp time window in seconds
#radius_das_time_window=300
#
# DAS require Event-Timestamp
#radius_das_require_event_timestamp=1
#
# DAS require Message-Authenticator
#radius_das_require_message_authenticator=1
##### RADIUS authentication server configuration ##############################
# hostapd can be used as a RADIUS authentication server for other hosts. This
# requires that the integrated EAP server is also enabled and both
# authentication services are sharing the same configuration.
# File name of the RADIUS clients configuration for the RADIUS server. If this
# commented out, RADIUS server is disabled.
#radius_server_clients=/etc/hostapd.radius_clients
# The UDP port number for the RADIUS authentication server
#radius_server_auth_port=1812
# The UDP port number for the RADIUS accounting server
# Commenting this out or setting this to 0 can be used to disable RADIUS
# accounting while still enabling RADIUS authentication.
#radius_server_acct_port=1813
# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API)
#radius_server_ipv6=1
##### WPA/IEEE 802.11i configuration ##########################################
# Enable WPA. Setting this variable configures the AP to require WPA (either
# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice.
# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
# RADIUS authentication server must be configured, and WPA-EAP must be included
# in wpa_key_mgmt.
# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
# and/or WPA2 (full IEEE 802.11i/RSN):
# bit0 = WPA
# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
# Note that WPA3 is also configured with bit1 since it uses RSN just like WPA2.
# In other words, for WPA3, wpa=2 is used the configuration (and
# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK).
#wpa=2
# Extended Key ID support for Individually Addressed frames
#
# Extended Key ID allows to rekey PTK keys without the impacts the "normal"
# PTK rekeying with only a single Key ID 0 has. It can only be used when the
# driver supports it and RSN/WPA2 is used with a CCMP/GCMP pairwise cipher.
#
# 0 = force off, i.e., use only Key ID 0 (default)
# 1 = enable and use Extended Key ID support when possible
# 2 = identical to 1 but start with Key ID 1 when possible
#extended_key_id=0
# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
# (8..63 characters) that will be converted to PSK. This conversion uses SSID
# so the PSK changes when ASCII passphrase is used and the SSID is changed.
# wpa_psk (dot11RSNAConfigPSKValue)
# wpa_passphrase (dot11RSNAConfigPSKPassPhrase)
#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
#wpa_passphrase=secret passphrase
# Optionally, WPA PSKs can be read from a separate text file (containing list
# of (PSK,MAC address) pairs. This allows more than one PSK to be configured.
# Use absolute path name to make sure that the files can be read on SIGHUP
# configuration reloads.
#wpa_psk_file=/etc/hostapd.wpa_psk
# Optionally, WPA passphrase can be received from RADIUS authentication server
# This requires macaddr_acl to be set to 2 (RADIUS)
# 0 = disabled (default)
# 1 = optional; use default passphrase/psk if RADIUS server does not include
# Tunnel-Password
# 2 = required; reject authentication if RADIUS server does not include
# Tunnel-Password
#wpa_psk_radius=0
# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
# added to enable SHA256-based stronger algorithms.
# WPA-PSK = WPA-Personal / WPA2-Personal
# WPA-PSK-SHA256 = WPA2-Personal using SHA256
# WPA-EAP = WPA-Enterprise / WPA2-Enterprise
# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
# SAE = SAE (WPA3-Personal)
# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite
# FT-PSK = FT with passphrase/PSK
# FT-EAP = FT with EAP
# FT-EAP-SHA384 = FT with EAP using SHA384
# FT-SAE = FT with SAE
# FILS-SHA256 = Fast Initial Link Setup with SHA256
# FILS-SHA384 = Fast Initial Link Setup with SHA384
# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256
# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
# OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open)
# DPP = Device Provisioning Protocol
# OSEN = Hotspot 2.0 online signup with encryption
# (dot11RSNAConfigAuthenticationSuitesTable)
#wpa_key_mgmt=WPA-PSK WPA-EAP
# Set of accepted cipher suites (encryption algorithms) for pairwise keys
# (unicast packets). This is a space separated list of algorithms:
# CCMP = AES in Counter mode with CBC-MAC (CCMP-128)
# TKIP = Temporal Key Integrity Protocol
# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key
# GCMP = Galois/counter mode protocol (GCMP-128)
# GCMP-256 = Galois/counter mode protocol with 256-bit key
# Group cipher suite (encryption algorithm for broadcast and multicast frames)
# is automatically selected based on this configuration. If only CCMP is
# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
# TKIP will be used as the group cipher. The optional group_cipher parameter can
# be used to override this automatic selection.
#
# (dot11RSNAConfigPairwiseCiphersTable)
# Pairwise cipher for WPA (v1) (default: TKIP)
#wpa_pairwise=TKIP CCMP
# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value)
#rsn_pairwise=CCMP
# Optional override for automatic group cipher selection
# This can be used to select a specific group cipher regardless of which
# pairwise ciphers were enabled for WPA and RSN. It should be noted that
# overriding the group cipher with an unexpected value can result in
# interoperability issues and in general, this parameter is mainly used for
# testing purposes.
#group_cipher=CCMP
# Time interval for rekeying GTK (broadcast/multicast encryption keys) in
# seconds. (dot11RSNAConfigGroupRekeyTime)
# This defaults to 86400 seconds (once per day) when using CCMP/GCMP as the
# group cipher and 600 seconds (once per 10 minutes) when using TKIP as the
# group cipher.
#wpa_group_rekey=86400
# Rekey GTK when any STA that possesses the current GTK is leaving the BSS.
# (dot11RSNAConfigGroupRekeyStrict)
#wpa_strict_rekey=1
# The number of times EAPOL-Key Message 1/2 in the RSN Group Key Handshake is
#retried per GTK Handshake attempt. (dot11RSNAConfigGroupUpdateCount)
# This value should only be increased when stations are constantly
# deauthenticated during GTK rekeying with the log message
# "group key handshake failed...".
# You should consider to also increase wpa_pairwise_update_count then.
# Range 1..4294967295; default: 4
#wpa_group_update_count=4
# Time interval for rekeying GMK (master key used internally to generate GTKs
# (in seconds).
#wpa_gmk_rekey=86400
# Maximum lifetime for PTK in seconds. This can be used to enforce rekeying of
# PTK to mitigate some attacks against TKIP deficiencies.
# Warning: PTK rekeying is buggy with many drivers/devices and with such
# devices, the only secure method to rekey the PTK without Extended Key ID
# support requires a disconnection. Check the related parameter
# wpa_deny_ptk0_rekey for details.
#wpa_ptk_rekey=600
# Workaround for PTK rekey issues
#
# PTK0 rekeys (rekeying the PTK without "Extended Key ID for Individually
# Addressed Frames") can degrade the security and stability with some cards.
# To avoid such issues hostapd can replace those PTK rekeys (including EAP
# reauthentications) with disconnects.
#
# Available options:
# 0 = always rekey when configured/instructed (default)
# 1 = only rekey when the local driver is explicitly indicating it can perform
# this operation without issues
# 2 = never allow PTK0 rekeys
#wpa_deny_ptk0_rekey=0
# The number of times EAPOL-Key Message 1/4 and Message 3/4 in the RSN 4-Way
# Handshake are retried per 4-Way Handshake attempt.
# (dot11RSNAConfigPairwiseUpdateCount)
# Range 1..4294967295; default: 4
#wpa_pairwise_update_count=4
# Workaround for key reinstallation attacks
#
# This parameter can be used to disable retransmission of EAPOL-Key frames that
# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This
# is similar to setting wpa_group_update_count=1 and
# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with
# extended timeout on the response to avoid causing issues with stations that
# may use aggressive power saving have very long time in replying to the
# EAPOL-Key messages.
#
# This option can be used to work around key reinstallation attacks on the
# station (supplicant) side in cases those station devices cannot be updated
# for some reason. By removing the retransmissions the attacker cannot cause
# key reinstallation with a delayed frame transmission. This is related to the
# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079,
# CVE-2017-13080, and CVE-2017-13081.
#
# This workaround might cause interoperability issues and reduced robustness of
# key negotiation especially in environments with heavy traffic load due to the
# number of attempts to perform the key exchange is reduced significantly. As
# such, this workaround is disabled by default (unless overridden in build
# configuration). To enable this, set the parameter to 1.
#wpa_disable_eapol_key_retries=1
# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
# authentication and key handshake before actually associating with a new AP.
# (dot11RSNAPreauthenticationEnabled)
#rsn_preauth=1
#
# Space separated list of interfaces from which pre-authentication frames are
# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all
# interface that are used for connections to other APs. This could include
# wired interfaces and WDS links. The normal wireless data interface towards
# associated stations (e.g., wlan0) should not be added, since
# pre-authentication is only used with APs other than the currently associated
# one.
#rsn_preauth_interfaces=eth0
# ieee80211w: Whether management frame protection (MFP) is enabled
# 0 = disabled (default)
# 1 = optional
# 2 = required
#ieee80211w=0
# The most common configuration options for this based on the PMF (protected
# management frames) certification program are:
# PMF enabled: ieee80211w=1 and wpa_key_mgmt=WPA-EAP WPA-EAP-SHA256
# PMF required: ieee80211w=2 and wpa_key_mgmt=WPA-EAP-SHA256
# (and similarly for WPA-PSK and WPA-PSK-SHA256 if WPA2-Personal is used)
# WPA3-Personal-only mode: ieee80211w=2 and wpa_key_mgmt=SAE
# Group management cipher suite
# Default: AES-128-CMAC (BIP)
# Other options (depending on driver support):
# BIP-GMAC-128
# BIP-GMAC-256
# BIP-CMAC-256
# Note: All the stations connecting to the BSS will also need to support the
# selected cipher. The default AES-128-CMAC is the only option that is commonly
# available in deployed devices.
#group_mgmt_cipher=AES-128-CMAC
# Beacon Protection (management frame protection for Beacon frames)
# This depends on management frame protection being enabled (ieee80211w != 0)
# and beacon protection support indication from the driver.
# 0 = disabled (default)
# 1 = enabled
#beacon_prot=0
# Association SA Query maximum timeout (in TU = 1.024 ms; for MFP)
# (maximum time to wait for a SA Query response)
# dot11AssociationSAQueryMaximumTimeout, 1...4294967295
#assoc_sa_query_max_timeout=1000
# Association SA Query retry timeout (in TU = 1.024 ms; for MFP)
# (time between two subsequent SA Query requests)
# dot11AssociationSAQueryRetryTimeout, 1...4294967295
#assoc_sa_query_retry_timeout=201
# ocv: Operating Channel Validation
-# This is a countermeasure against multi-channel man-in-the-middle attacks.
+# This is a countermeasure against multi-channel on-path attacks.
# Enabling this depends on the driver's support for OCV when the driver SME is
# used. If hostapd SME is used, this will be enabled just based on this
# configuration.
# Enabling this automatically also enables ieee80211w, if not yet enabled.
# 0 = disabled (default)
# 1 = enabled
# 2 = enabled in workaround mode - Allow STA that claims OCV capability to
# connect even if the STA doesn't send OCI or negotiate PMF. This
# workaround is to improve interoperability with legacy STAs which are
# wrongly copying reserved bits of RSN capabilities from the AP's
# RSNE into (Re)Association Request frames. When this configuration is
# enabled, the AP considers STA is OCV capable only when the STA indicates
# MFP capability in (Re)Association Request frames and sends OCI in
# EAPOL-Key msg 2/4/FT Reassociation Request frame/FILS (Re)Association
# Request frame; otherwise, the AP disables OCV for the current connection
# with the STA. Enabling this workaround mode reduced OCV protection to
# some extend since it allows misbehavior to go through. As such, this
# should be enabled only if interoperability with misbehaving STAs is
# needed.
#ocv=1
# disable_pmksa_caching: Disable PMKSA caching
# This parameter can be used to disable caching of PMKSA created through EAP
# authentication. RSN preauthentication may still end up using PMKSA caching if
# it is enabled (rsn_preauth=1).
# 0 = PMKSA caching enabled (default)
# 1 = PMKSA caching disabled
#disable_pmksa_caching=0
# okc: Opportunistic Key Caching (aka Proactive Key Caching)
# Allow PMK cache to be shared opportunistically among configured interfaces
# and BSSes (i.e., all configurations within a single hostapd process).
# 0 = disabled (default)
# 1 = enabled
#okc=1
# SAE password
# This parameter can be used to set passwords for SAE. By default, the
# wpa_passphrase value is used if this separate parameter is not used, but
# wpa_passphrase follows the WPA-PSK constraints (8..63 characters) even though
# SAE passwords do not have such constraints. If the BSS enabled both SAE and
# WPA-PSK and both values are set, SAE uses the sae_password values and WPA-PSK
# uses the wpa_passphrase value.
#
# Each sae_password entry is added to a list of available passwords. This
# corresponds to the dot11RSNAConfigPasswordValueEntry. sae_password value
# starts with the password (dot11RSNAConfigPasswordCredential). That value can
# be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and
# by optional password identifier (dot11RSNAConfigPasswordIdentifier). In
# addition, an optional VLAN ID specification can be used to bind the station
# to the specified VLAN whenever the specific SAE password entry is used.
#
# If the peer MAC address is not included or is set to the wildcard address
# (ff:ff:ff:ff:ff:ff), the entry is available for any station to use. If a
# specific peer MAC address is included, only a station with that MAC address
# is allowed to use the entry.
#
# If the password identifier (with non-zero length) is included, the entry is
# limited to be used only with that specified identifier.
# The last matching (based on peer MAC address and identifier) entry is used to
# select which password to use. Setting sae_password to an empty string has a
# special meaning of removing all previously added entries.
#
# sae_password uses the following encoding:
#<password/credential>[|mac=<peer mac>][|vlanid=<VLAN ID>]
#[|pk=<m:ECPrivateKey-base64>][|id=<identifier>]
# Examples:
#sae_password=secret
#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff
#sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier
#sae_password=example secret|vlanid=3|id=pw identifier
# SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold)
# This parameter defines how many open SAE instances can be in progress at the
# same time before the anti-clogging mechanism is taken into use.
#sae_anti_clogging_threshold=5 (deprecated)
#anti_clogging_threshold=5
# Maximum number of SAE synchronization errors (dot11RSNASAESync)
# The offending SAE peer will be disconnected if more than this many
# synchronization errors happen.
#sae_sync=5
# Enabled SAE finite cyclic groups
# SAE implementation are required to support group 19 (ECC group defined over a
# 256-bit prime order field). This configuration parameter can be used to
# specify a set of allowed groups. If not included, only the mandatory group 19
# is enabled.
# The group values are listed in the IANA registry:
# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
# Note that groups 1, 2, 5, 22, 23, and 24 should not be used in production
# purposes due limited security (see RFC 8247). Groups that are not as strong as
# group 19 (ECC, NIST P-256) are unlikely to be useful for production use cases
# since all implementations are required to support group 19.
#sae_groups=19 20 21
# Require MFP for all associations using SAE
# This parameter can be used to enforce negotiation of MFP for all associations
# that negotiate use of SAE. This is used in cases where SAE-capable devices are
# known to be MFP-capable and the BSS is configured with optional MFP
# (ieee80211w=1) for legacy support. The non-SAE stations can connect without
# MFP while SAE stations are required to negotiate MFP if sae_require_mfp=1.
#sae_require_mfp=0
# SAE Confirm behavior
# By default, AP will send out only SAE Commit message in response to a received
# SAE Commit message. This parameter can be set to 1 to override that behavior
# to send both SAE Commit and SAE Confirm messages without waiting for the STA
# to send its SAE Confirm message first.
#sae_confirm_immediate=0
# SAE mechanism for PWE derivation
# 0 = hunting-and-pecking loop only (default without password identifier)
# 1 = hash-to-element only (default with password identifier)
# 2 = both hunting-and-pecking loop and hash-to-element enabled
# Note: The default value is likely to change from 0 to 2 once the new
# hash-to-element mechanism has received more interoperability testing.
# When using SAE password identifier, the hash-to-element mechanism is used
# regardless of the sae_pwe parameter value.
#sae_pwe=0
# FILS Cache Identifier (16-bit value in hexdump format)
#fils_cache_id=0011
# FILS Realm Information
# One or more FILS realms need to be configured when FILS is enabled. This list
# of realms is used to define which realms (used in keyName-NAI by the client)
# can be used with FILS shared key authentication for ERP.
#fils_realm=example.com
#fils_realm=example.org
# FILS DH Group for PFS
# 0 = PFS disabled with FILS shared key authentication (default)
# 1-65535 DH Group to use for FILS PFS
#fils_dh_group=0
# OWE DH groups
# OWE implementations are required to support group 19 (NIST P-256). All groups
# that are supported by the implementation (e.g., groups 19, 20, and 21 when
# using OpenSSL) are enabled by default. This configuration parameter can be
# used to specify a limited set of allowed groups. The group values are listed
# in the IANA registry:
# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10
#owe_groups=19 20 21
# OWE PTK derivation workaround
# Initial OWE implementation used SHA256 when deriving the PTK for all OWE
# groups. This was supposed to change to SHA384 for group 20 and SHA512 for
# group 21. This parameter can be used to enable workaround for interoperability
# with stations that use SHA256 with groups 20 and 21. By default (0) only the
# appropriate hash function is accepted. When workaround is enabled (1), the
# appropriate hash function is tried first and if that fails, SHA256-based PTK
# derivation is attempted. This workaround can result in reduced security for
# groups 20 and 21, but is required for interoperability with older
# implementations. There is no impact to group 19 behavior. The workaround is
# disabled by default and can be enabled by uncommenting the following line.
#owe_ptk_workaround=1
# OWE transition mode configuration
# Pointer to the matching open/OWE BSS
#owe_transition_bssid=<bssid>
# SSID in same format as ssid2 described above.
#owe_transition_ssid=<SSID>
# Alternatively, OWE transition mode BSSID/SSID can be configured with a
# reference to a BSS operated by this hostapd process.
#owe_transition_ifname=<ifname>
# DHCP server for FILS HLP
# If configured, hostapd will act as a DHCP relay for all FILS HLP requests
# that include a DHCPDISCOVER message and send them to the specific DHCP
# server for processing. hostapd will then wait for a response from that server
# before replying with (Re)Association Response frame that encapsulates this
# DHCP response. own_ip_addr is used as the local address for the communication
# with the DHCP server.
#dhcp_server=127.0.0.1
# DHCP server UDP port
# Default: 67
#dhcp_server_port=67
# DHCP relay UDP port on the local device
# Default: 67; 0 means not to bind any specific port
#dhcp_relay_port=67
# DHCP rapid commit proxy
# If set to 1, this enables hostapd to act as a DHCP rapid commit proxy to
# allow the rapid commit options (two message DHCP exchange) to be used with a
# server that supports only the four message DHCP exchange. This is disabled by
# default (= 0) and can be enabled by setting this to 1.
#dhcp_rapid_commit_proxy=0
# Wait time for FILS HLP (dot11HLPWaitTime) in TUs
# default: 30 TUs (= 30.72 milliseconds)
#fils_hlp_wait_time=30
# FILS Discovery frame transmission minimum and maximum interval settings.
# If fils_discovery_max_interval is non-zero, the AP enables FILS Discovery
# frame transmission. These values use TUs as the unit and have allowed range
# of 0-10000. fils_discovery_min_interval defaults to 20.
#fils_discovery_min_interval=20
#fils_discovery_max_interval=0
# Transition Disable indication
# The AP can notify authenticated stations to disable transition mode in their
# network profiles when the network has completed transition steps, i.e., once
# sufficiently large number of APs in the ESS have been updated to support the
# more secure alternative. When this indication is used, the stations are
# expected to automatically disable transition mode and less secure security
# options. This includes use of WEP, TKIP (including use of TKIP as the group
# cipher), and connections without PMF.
# Bitmap bits:
# bit 0 (0x01): WPA3-Personal (i.e., disable WPA2-Personal = WPA-PSK and only
# allow SAE to be used)
# bit 1 (0x02): SAE-PK (disable SAE without use of SAE-PK)
# bit 2 (0x04): WPA3-Enterprise (move to requiring PMF)
# bit 3 (0x08): Enhanced Open (disable use of open network; require OWE)
# (default: 0 = do not include Transition Disable KDE)
#transition_disable=0x01
# PASN ECDH groups
# PASN implementations are required to support group 19 (NIST P-256). If this
# parameter is not set, only group 19 is supported by default. This
# configuration parameter can be used to specify a limited set of allowed
# groups. The group values are listed in the IANA registry:
# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10
#pasn_groups=19 20 21
# PASN comeback after time in TUs
# In case the AP is temporarily unable to handle a PASN authentication exchange
# due to a too large number of parallel operations, this value indicates to the
# peer after how many TUs it can try the PASN exchange again.
# (default: 10 TUs)
#pasn_comeback_after=10
##### IEEE 802.11r configuration ##############################################
# Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
# MDID is used to indicate a group of APs (within an ESS, i.e., sharing the
# same SSID) between which a STA can use Fast BSS Transition.
# 2-octet identifier as a hex string.
#mobility_domain=a1b2
# PMK-R0 Key Holder identifier (dot11FTR0KeyHolderID)
# 1 to 48 octet identifier.
# This is configured with nas_identifier (see RADIUS client section above).
# Default lifetime of the PMK-R0 in seconds; range 60..4294967295
# (default: 14 days / 1209600 seconds; 0 = disable timeout)
# (dot11FTR0KeyLifetime)
#ft_r0_key_lifetime=1209600
# Maximum lifetime for PMK-R1; applied only if not zero
# PMK-R1 is removed at latest after this limit.
# Removing any PMK-R1 for expiry can be disabled by setting this to -1.
# (default: 0)
#r1_max_key_lifetime=0
# PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID)
# 6-octet identifier as a hex string.
# Defaults to BSSID.
#r1_key_holder=000102030405
# Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535)
# (dot11FTReassociationDeadline)
#reassociation_deadline=1000
# List of R0KHs in the same Mobility Domain
# format: <MAC address> <NAS Identifier> <256-bit key as hex string>
# This list is used to map R0KH-ID (NAS Identifier) to a destination MAC
# address when requesting PMK-R1 key from the R0KH that the STA used during the
# Initial Mobility Domain Association.
#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
# And so on.. One line per R0KH.
# Wildcard entry:
# Upon receiving a response from R0KH, it will be added to this list, so
# subsequent requests won't be broadcast. If R0KH does not reply, it will be
# temporarily blocked (see rkh_neg_timeout).
#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff
# List of R1KHs in the same Mobility Domain
# format: <MAC address> <R1KH-ID> <256-bit key as hex string>
# This list is used to map R1KH-ID to a destination MAC address when sending
# PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD
# that can request PMK-R1 keys.
#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f
#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff
# And so on.. One line per R1KH.
# Wildcard entry:
# Upon receiving a request from an R1KH not yet known, it will be added to this
# list and thus will receive push notifications.
#r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff
# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above)
# Special values: 0 -> do not expire
# Warning: do not cache implies no sequence number validation with wildcards
#rkh_pos_timeout=86400 (default = 1 day)
# Timeout (milliseconds) for requesting PMK-R1 from R0KH using PULL request
# and number of retries.
#rkh_pull_timeout=1000 (default = 1 second)
#rkh_pull_retries=4 (default)
# Timeout (seconds) for non replying R0KH (see wildcard entries above)
# Special values: 0 -> do not cache
# default: 60 seconds
#rkh_neg_timeout=60
# Note: The R0KH/R1KH keys used to be 128-bit in length before the message
# format was changed. That shorter key length is still supported for backwards
# compatibility of the configuration files. If such a shorter key is used, a
# 256-bit key is derived from it. For new deployments, configuring the 256-bit
# key is recommended.
# Whether PMK-R1 push is enabled at R0KH
# 0 = do not push PMK-R1 to all configured R1KHs (default)
# 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived
#pmk_r1_push=1
# Whether to enable FT-over-DS
# 0 = FT-over-DS disabled
# 1 = FT-over-DS enabled (default)
#ft_over_ds=1
# Whether to generate FT response locally for PSK networks
# This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks as
# the required information (PSK and other session data) is already locally
# available.
# 0 = disabled (default)
# 1 = enabled
#ft_psk_generate_local=0
##### Neighbor table ##########################################################
# Maximum number of entries kept in AP table (either for neighbor table or for
# detecting Overlapping Legacy BSS Condition). The oldest entry will be
# removed when adding a new entry that would make the list grow over this
# limit. Note! WFA certification for IEEE 802.11g requires that OLBC is
# enabled, so this field should not be set to 0 when using IEEE 802.11g.
# default: 255
#ap_table_max_size=255
# Number of seconds of no frames received after which entries may be deleted
# from the AP table. Since passive scanning is not usually performed frequently
# this should not be set to very small value. In addition, there is no
# guarantee that every scan cycle will receive beacon frames from the
# neighboring APs.
# default: 60
#ap_table_expiration_time=3600
# Maximum number of stations to track on the operating channel
# This can be used to detect dualband capable stations before they have
# associated, e.g., to provide guidance on which colocated BSS to use.
# Default: 0 (disabled)
#track_sta_max_num=100
# Maximum age of a station tracking entry in seconds
# Default: 180
#track_sta_max_age=180
# Do not reply to group-addressed Probe Request from a station that was seen on
# another radio.
# Default: Disabled
#
# This can be used with enabled track_sta_max_num configuration on another
# interface controlled by the same hostapd process to restrict Probe Request
# frame handling from replying to group-addressed Probe Request frames from a
# station that has been detected to be capable of operating on another band,
# e.g., to try to reduce likelihood of the station selecting a 2.4 GHz BSS when
# the AP operates both a 2.4 GHz and 5 GHz BSS concurrently.
#
# Note: Enabling this can cause connectivity issues and increase latency for
# discovering the AP.
#no_probe_resp_if_seen_on=wlan1
# Reject authentication from a station that was seen on another radio.
# Default: Disabled
#
# This can be used with enabled track_sta_max_num configuration on another
# interface controlled by the same hostapd process to reject authentication
# attempts from a station that has been detected to be capable of operating on
# another band, e.g., to try to reduce likelihood of the station selecting a
# 2.4 GHz BSS when the AP operates both a 2.4 GHz and 5 GHz BSS concurrently.
#
# Note: Enabling this can cause connectivity issues and increase latency for
# connecting with the AP.
#no_auth_if_seen_on=wlan1
##### Wi-Fi Protected Setup (WPS) #############################################
# WPS state
# 0 = WPS disabled (default)
# 1 = WPS enabled, not configured
# 2 = WPS enabled, configured
#wps_state=2
# Whether to manage this interface independently from other WPS interfaces
# By default, a single hostapd process applies WPS operations to all configured
# interfaces. This parameter can be used to disable that behavior for a subset
# of interfaces. If this is set to non-zero for an interface, WPS commands
# issued on that interface do not apply to other interfaces and WPS operations
# performed on other interfaces do not affect this interface.
#wps_independent=0
# AP can be configured into a locked state where new WPS Registrar are not
# accepted, but previously authorized Registrars (including the internal one)
# can continue to add new Enrollees.
#ap_setup_locked=1
# Universally Unique IDentifier (UUID; see RFC 4122) of the device
# This value is used as the UUID for the internal WPS Registrar. If the AP
# is also using UPnP, this value should be set to the device's UPnP UUID.
# If not configured, UUID will be generated based on the local MAC address.
#uuid=12345678-9abc-def0-1234-56789abcdef0
# Note: If wpa_psk_file is set, WPS is used to generate random, per-device PSKs
# that will be appended to the wpa_psk_file. If wpa_psk_file is not set, the
# default PSK (wpa_psk/wpa_passphrase) will be delivered to Enrollees. Use of
# per-device PSKs is recommended as the more secure option (i.e., make sure to
# set wpa_psk_file when using WPS with WPA-PSK).
# When an Enrollee requests access to the network with PIN method, the Enrollee
# PIN will need to be entered for the Registrar. PIN request notifications are
# sent to hostapd ctrl_iface monitor. In addition, they can be written to a
# text file that could be used, e.g., to populate the AP administration UI with
# pending PIN requests. If the following variable is set, the PIN requests will
# be written to the configured file.
#wps_pin_requests=/var/run/hostapd_wps_pin_requests
# Device Name
# User-friendly description of device; up to 32 octets encoded in UTF-8
#device_name=Wireless AP
# Manufacturer
# The manufacturer of the device (up to 64 ASCII characters)
#manufacturer=Company
# Model Name
# Model of the device (up to 32 ASCII characters)
#model_name=WAP
# Model Number
# Additional device description (up to 32 ASCII characters)
#model_number=123
# Serial Number
# Serial number of the device (up to 32 characters)
#serial_number=12345
# Primary Device Type
# Used format: <categ>-<OUI>-<subcateg>
# categ = Category as an integer value
# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for
# default WPS OUI
# subcateg = OUI-specific Sub Category as an integer value
# Examples:
# 1-0050F204-1 (Computer / PC)
# 1-0050F204-2 (Computer / Server)
# 5-0050F204-1 (Storage / NAS)
# 6-0050F204-1 (Network Infrastructure / AP)
#device_type=6-0050F204-1
# OS Version
# 4-octet operating system version number (hex string)
#os_version=01020300
# Config Methods
# List of the supported configuration methods
# Available methods: usba ethernet label display ext_nfc_token int_nfc_token
# nfc_interface push_button keypad virtual_display physical_display
# virtual_push_button physical_push_button
#config_methods=label virtual_display virtual_push_button keypad
# WPS capability discovery workaround for PBC with Windows 7
# Windows 7 uses incorrect way of figuring out AP's WPS capabilities by acting
# as a Registrar and using M1 from the AP. The config methods attribute in that
# message is supposed to indicate only the configuration method supported by
# the AP in Enrollee role, i.e., to add an external Registrar. For that case,
# PBC shall not be used and as such, the PushButton config method is removed
# from M1 by default. If pbc_in_m1=1 is included in the configuration file,
# the PushButton config method is left in M1 (if included in config_methods
# parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from a label
# in the AP).
#pbc_in_m1=1
# Static access point PIN for initial configuration and adding Registrars
# If not set, hostapd will not allow external WPS Registrars to control the
# access point. The AP PIN can also be set at runtime with hostapd_cli
# wps_ap_pin command. Use of temporary (enabled by user action) and random
# AP PIN is much more secure than configuring a static AP PIN here. As such,
# use of the ap_pin parameter is not recommended if the AP device has means for
# displaying a random PIN.
#ap_pin=12345670
# Skip building of automatic WPS credential
# This can be used to allow the automatically generated Credential attribute to
# be replaced with pre-configured Credential(s).
#skip_cred_build=1
# Additional Credential attribute(s)
# This option can be used to add pre-configured Credential attributes into M8
# message when acting as a Registrar. If skip_cred_build=1, this data will also
# be able to override the Credential attribute that would have otherwise been
# automatically generated based on network configuration. This configuration
# option points to an external file that much contain the WPS Credential
# attribute(s) as binary data.
#extra_cred=hostapd.cred
# Credential processing
# 0 = process received credentials internally (default)
# 1 = do not process received credentials; just pass them over ctrl_iface to
# external program(s)
# 2 = process received credentials internally and pass them over ctrl_iface
# to external program(s)
# Note: With wps_cred_processing=1, skip_cred_build should be set to 1 and
# extra_cred be used to provide the Credential data for Enrollees.
#
# wps_cred_processing=1 will disabled automatic updates of hostapd.conf file
# both for Credential processing and for marking AP Setup Locked based on
# validation failures of AP PIN. An external program is responsible on updating
# the configuration appropriately in this case.
#wps_cred_processing=0
# Whether to enable SAE (WPA3-Personal transition mode) automatically for
# WPA2-PSK credentials received using WPS.
# 0 = only add the explicitly listed WPA2-PSK configuration (default)
# 1 = add both the WPA2-PSK and SAE configuration and enable PMF so that the
# AP gets configured in WPA3-Personal transition mode (supports both
# WPA2-Personal (PSK) and WPA3-Personal (SAE) clients).
#wps_cred_add_sae=0
# AP Settings Attributes for M7
# By default, hostapd generates the AP Settings Attributes for M7 based on the
# current configuration. It is possible to override this by providing a file
# with pre-configured attributes. This is similar to extra_cred file format,
# but the AP Settings attributes are not encapsulated in a Credential
# attribute.
#ap_settings=hostapd.ap_settings
# Multi-AP backhaul BSS config
# Used in WPS when multi_ap=2 or 3. Defines "backhaul BSS" credentials.
# These are passed in WPS M8 instead of the normal (fronthaul) credentials
# if the Enrollee has the Multi-AP subelement set. Backhaul SSID is formatted
# like ssid2. The key is set like wpa_psk or wpa_passphrase.
#multi_ap_backhaul_ssid="backhaul"
#multi_ap_backhaul_wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
#multi_ap_backhaul_wpa_passphrase=secret passphrase
# WPS UPnP interface
# If set, support for external Registrars is enabled.
#upnp_iface=br0
# Friendly Name (required for UPnP)
# Short description for end use. Should be less than 64 characters.
#friendly_name=WPS Access Point
# Manufacturer URL (optional for UPnP)
#manufacturer_url=http://www.example.com/
# Model Description (recommended for UPnP)
# Long description for end user. Should be less than 128 characters.
#model_description=Wireless Access Point
# Model URL (optional for UPnP)
#model_url=http://www.example.com/model/
# Universal Product Code (optional for UPnP)
# 12-digit, all-numeric code that identifies the consumer package.
#upc=123456789012
# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band, ad = 60 GHz)
# This value should be set according to RF band(s) supported by the AP if
# hw_mode is not set. For dual band dual concurrent devices, this needs to be
# set to ag to allow both RF bands to be advertized.
#wps_rf_bands=ag
# NFC password token for WPS
# These parameters can be used to configure a fixed NFC password token for the
# AP. This can be generated, e.g., with nfc_pw_token from wpa_supplicant. When
# these parameters are used, the AP is assumed to be deployed with a NFC tag
# that includes the matching NFC password token (e.g., written based on the
# NDEF record from nfc_pw_token).
#
#wps_nfc_dev_pw_id: Device Password ID (16..65535)
#wps_nfc_dh_pubkey: Hexdump of DH Public Key
#wps_nfc_dh_privkey: Hexdump of DH Private Key
#wps_nfc_dev_pw: Hexdump of Device Password
# Application Extension attribute for Beacon and Probe Response frames
# This parameter can be used to add application extension into WPS IE. The
# contents of this parameter starts with 16-octet (32 hexdump characters) of
# UUID to identify the specific application and that is followed by the actual
# application specific data.
#wps_application_ext=<hexdump>
##### Wi-Fi Direct (P2P) ######################################################
# Enable P2P Device management
#manage_p2p=1
# Allow cross connection
#allow_cross_connection=1
##### Device Provisioning Protocol (DPP) ######################################
# Name for Enrollee's DPP Configuration Request
#dpp_name=Test
# MUD URL for Enrollee's DPP Configuration Request (optional)
#dpp_mud_url=https://example.com/mud
#dpp_connector
#dpp_netaccesskey
#dpp_netaccesskey_expiry
#dpp_csign
#dpp_controller
# Configurator Connectivity indication
# 0: no Configurator is currently connected (default)
# 1: advertise that a Configurator is available
#dpp_configurator_connectivity=0
# DPP PFS
# 0: allow PFS to be used or not used (default)
# 1: require PFS to be used (note: not compatible with DPP R1)
# 2: do not allow PFS to be used
#dpp_pfs=0
#### TDLS (IEEE 802.11z-2010) #################################################
# Prohibit use of TDLS in this BSS
#tdls_prohibit=1
# Prohibit use of TDLS Channel Switching in this BSS
#tdls_prohibit_chan_switch=1
##### IEEE 802.11v-2011 #######################################################
# Time advertisement
# 0 = disabled (default)
# 2 = UTC time at which the TSF timer is 0
#time_advertisement=2
# Local time zone as specified in 8.3 of IEEE Std 1003.1-2004:
# stdoffset[dst[offset][,start[/time],end[/time]]]
#time_zone=EST5
# WNM-Sleep Mode (extended sleep mode for stations)
# 0 = disabled (default)
# 1 = enabled (allow stations to use WNM-Sleep Mode)
#wnm_sleep_mode=1
# WNM-Sleep Mode GTK/IGTK workaround
# Normally, WNM-Sleep Mode exit with management frame protection negotiated
# would result in the current GTK/IGTK getting added into the WNM-Sleep Mode
# Response frame. Some station implementations may have a vulnerability that
# results in GTK/IGTK reinstallation based on this frame being replayed. This
# configuration parameter can be used to disable that behavior and use EAPOL-Key
# frames for GTK/IGTK update instead. This would likely be only used with
# wpa_disable_eapol_key_retries=1 that enables a workaround for similar issues
# with EAPOL-Key. This is related to station side vulnerabilities CVE-2017-13087
# and CVE-2017-13088. To enable this AP-side workaround, set the parameter to 1.
#wnm_sleep_mode_no_keys=0
# BSS Transition Management
# 0 = disabled (default)
# 1 = enabled
#bss_transition=1
# Proxy ARP
# 0 = disabled (default)
# 1 = enabled
#proxy_arp=1
# IPv6 Neighbor Advertisement multicast-to-unicast conversion
# This can be used with Proxy ARP to allow multicast NAs to be forwarded to
# associated STAs using link layer unicast delivery.
# 0 = disabled (default)
# 1 = enabled
#na_mcast_to_ucast=0
##### IEEE 802.11u-2011 #######################################################
# Enable Interworking service
#interworking=1
# Access Network Type
# 0 = Private network
# 1 = Private network with guest access
# 2 = Chargeable public network
# 3 = Free public network
# 4 = Personal device network
# 5 = Emergency services only network
# 14 = Test or experimental
# 15 = Wildcard
#access_network_type=0
# Whether the network provides connectivity to the Internet
# 0 = Unspecified
# 1 = Network provides connectivity to the Internet
#internet=1
# Additional Step Required for Access
# Note: This is only used with open network, i.e., ASRA shall ne set to 0 if
# RSN is used.
#asra=0
# Emergency services reachable
#esr=0
# Unauthenticated emergency service accessible
#uesa=0
# Venue Info (optional)
# The available values are defined in IEEE Std 802.11u-2011, 7.3.1.34.
# Example values (group,type):
# 0,0 = Unspecified
# 1,7 = Convention Center
# 1,13 = Coffee Shop
# 2,0 = Unspecified Business
# 7,1 Private Residence
#venue_group=7
#venue_type=1
# Homogeneous ESS identifier (optional; dot11HESSID)
# If set, this shall be identifical to one of the BSSIDs in the homogeneous
# ESS and this shall be set to the same value across all BSSs in homogeneous
# ESS.
#hessid=02:03:04:05:06:07
# Roaming Consortium List
# Arbitrary number of Roaming Consortium OIs can be configured with each line
# adding a new OI to the list. The first three entries are available through
# Beacon and Probe Response frames. Any additional entry will be available only
# through ANQP queries. Each OI is between 3 and 15 octets and is configured as
# a hexstring.
#roaming_consortium=021122
#roaming_consortium=2233445566
# Venue Name information
# This parameter can be used to configure one or more Venue Name Duples for
# Venue Name ANQP information. Each entry has a two or three character language
# code (ISO-639) separated by colon from the venue name string.
# Note that venue_group and venue_type have to be set for Venue Name
# information to be complete.
#venue_name=eng:Example venue
#venue_name=fin:Esimerkkipaikka
# Alternative format for language:value strings:
# (double quoted string, printf-escaped string)
#venue_name=P"eng:Example\nvenue"
# Venue URL information
# This parameter can be used to configure one or more Venue URL Duples to
# provide additional information corresponding to Venue Name information.
# Each entry has a Venue Number value separated by colon from the Venue URL
# string. Venue Number indicates the corresponding venue_name entry (1 = 1st
# venue_name, 2 = 2nd venue_name, and so on; 0 = no matching venue_name)
#venue_url=1:http://www.example.com/info-eng
#venue_url=2:http://www.example.com/info-fin
# Network Authentication Type
# This parameter indicates what type of network authentication is used in the
# network.
# format: <network auth type indicator (1-octet hex str)> [redirect URL]
# Network Authentication Type Indicator values:
# 00 = Acceptance of terms and conditions
# 01 = On-line enrollment supported
# 02 = http/https redirection
# 03 = DNS redirection
#network_auth_type=00
#network_auth_type=02http://www.example.com/redirect/me/here/
# IP Address Type Availability
# format: <1-octet encoded value as hex str>
# (ipv4_type & 0x3f) << 2 | (ipv6_type & 0x3)
# ipv4_type:
# 0 = Address type not available
# 1 = Public IPv4 address available
# 2 = Port-restricted IPv4 address available
# 3 = Single NATed private IPv4 address available
# 4 = Double NATed private IPv4 address available
# 5 = Port-restricted IPv4 address and single NATed IPv4 address available
# 6 = Port-restricted IPv4 address and double NATed IPv4 address available
# 7 = Availability of the address type is not known
# ipv6_type:
# 0 = Address type not available
# 1 = Address type available
# 2 = Availability of the address type not known
#ipaddr_type_availability=14
# Domain Name
# format: <variable-octet str>[,<variable-octet str>]
#domain_name=example.com,another.example.com,yet-another.example.com
# 3GPP Cellular Network information
# format: <MCC1,MNC1>[;<MCC2,MNC2>][;...]
#anqp_3gpp_cell_net=244,91;310,026;234,56
# NAI Realm information
# One or more realm can be advertised. Each nai_realm line adds a new realm to
# the set. These parameters provide information for stations using Interworking
# network selection to allow automatic connection to a network based on
# credentials.
# format: <encoding>,<NAI Realm(s)>[,<EAP Method 1>][,<EAP Method 2>][,...]
# encoding:
# 0 = Realm formatted in accordance with IETF RFC 4282
# 1 = UTF-8 formatted character string that is not formatted in
# accordance with IETF RFC 4282
# NAI Realm(s): Semi-colon delimited NAI Realm(s)
# EAP Method: <EAP Method>[:<[AuthParam1:Val1]>][<[AuthParam2:Val2]>][...]
# EAP Method types, see:
# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4
# AuthParam (Table 8-188 in IEEE Std 802.11-2012):
# ID 2 = Non-EAP Inner Authentication Type
# 1 = PAP, 2 = CHAP, 3 = MSCHAP, 4 = MSCHAPV2
# ID 3 = Inner authentication EAP Method Type
# ID 5 = Credential Type
# 1 = SIM, 2 = USIM, 3 = NFC Secure Element, 4 = Hardware Token,
# 5 = Softoken, 6 = Certificate, 7 = username/password, 9 = Anonymous,
# 10 = Vendor Specific
#nai_realm=0,example.com;example.net
# EAP methods EAP-TLS with certificate and EAP-TTLS/MSCHAPv2 with
# username/password
#nai_realm=0,example.org,13[5:6],21[2:4][5:7]
# Arbitrary ANQP-element configuration
# Additional ANQP-elements with arbitrary values can be defined by specifying
# their contents in raw format as a hexdump of the payload. Note that these
# values will override ANQP-element contents that may have been specified in the
# more higher layer configuration parameters listed above.
# format: anqp_elem=<InfoID>:<hexdump of payload>
# For example, AP Geospatial Location ANQP-element with unknown location:
#anqp_elem=265:0000
# For example, AP Civic Location ANQP-element with unknown location:
#anqp_elem=266:000000
# GAS Address 3 behavior
# 0 = P2P specification (Address3 = AP BSSID) workaround enabled by default
# based on GAS request Address3
# 1 = IEEE 802.11 standard compliant regardless of GAS request Address3
# 2 = Force non-compliant behavior (Address3 = AP BSSID for all cases)
#gas_address3=0
# QoS Map Set configuration
#
# Comma delimited QoS Map Set in decimal values
# (see IEEE Std 802.11-2012, 8.4.2.97)
#
# format:
# [<DSCP Exceptions[DSCP,UP]>,]<UP 0 range[low,high]>,...<UP 7 range[low,high]>
#
# There can be up to 21 optional DSCP Exceptions which are pairs of DSCP Value
# (0..63 or 255) and User Priority (0..7). This is followed by eight DSCP Range
# descriptions with DSCP Low Value and DSCP High Value pairs (0..63 or 255) for
# each UP starting from 0. If both low and high value are set to 255, the
# corresponding UP is not used.
#
# default: not set
#qos_map_set=53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,255
##### Hotspot 2.0 #############################################################
# Enable Hotspot 2.0 support
#hs20=1
# Disable Downstream Group-Addressed Forwarding (DGAF)
# This can be used to configure a network where no group-addressed frames are
# allowed. The AP will not forward any group-address frames to the stations and
# random GTKs are issued for each station to prevent associated stations from
# forging such frames to other stations in the BSS.
#disable_dgaf=1
# OSU Server-Only Authenticated L2 Encryption Network
#osen=1
# ANQP Domain ID (0..65535)
# An identifier for a set of APs in an ESS that share the same common ANQP
# information. 0 = Some of the ANQP information is unique to this AP (default).
#anqp_domain_id=1234
# Deauthentication request timeout
# If the RADIUS server indicates that the station is not allowed to connect to
# the BSS/ESS, the AP can allow the station some time to download a
# notification page (URL included in the message). This parameter sets that
# timeout in seconds.
#hs20_deauth_req_timeout=60
# Operator Friendly Name
# This parameter can be used to configure one or more Operator Friendly Name
# Duples. Each entry has a two or three character language code (ISO-639)
# separated by colon from the operator friendly name string.
#hs20_oper_friendly_name=eng:Example operator
#hs20_oper_friendly_name=fin:Esimerkkioperaattori
# Connection Capability
# This can be used to advertise what type of IP traffic can be sent through the
# hotspot (e.g., due to firewall allowing/blocking protocols/ports).
# format: <IP Protocol>:<Port Number>:<Status>
# IP Protocol: 1 = ICMP, 6 = TCP, 17 = UDP
# Port Number: 0..65535
# Status: 0 = Closed, 1 = Open, 2 = Unknown
# Each hs20_conn_capab line is added to the list of advertised tuples.
#hs20_conn_capab=1:0:2
#hs20_conn_capab=6:22:1
#hs20_conn_capab=17:5060:0
# WAN Metrics
# format: <WAN Info>:<DL Speed>:<UL Speed>:<DL Load>:<UL Load>:<LMD>
# WAN Info: B0-B1: Link Status, B2: Symmetric Link, B3: At Capabity
# (encoded as two hex digits)
# Link Status: 1 = Link up, 2 = Link down, 3 = Link in test state
# Downlink Speed: Estimate of WAN backhaul link current downlink speed in kbps;
# 1..4294967295; 0 = unknown
# Uplink Speed: Estimate of WAN backhaul link current uplink speed in kbps
# 1..4294967295; 0 = unknown
# Downlink Load: Current load of downlink WAN connection (scaled to 255 = 100%)
# Uplink Load: Current load of uplink WAN connection (scaled to 255 = 100%)
# Load Measurement Duration: Duration for measuring downlink/uplink load in
# tenths of a second (1..65535); 0 if load cannot be determined
#hs20_wan_metrics=01:8000:1000:80:240:3000
# Operating Class Indication
# List of operating classes the BSSes in this ESS use. The Global operating
# classes in Table E-4 of IEEE Std 802.11-2012 Annex E define the values that
# can be used in this.
# format: hexdump of operating class octets
# for example, operating classes 81 (2.4 GHz channels 1-13) and 115 (5 GHz
# channels 36-48):
#hs20_operating_class=5173
# Terms and Conditions information
#
# hs20_t_c_filename contains the Terms and Conditions filename that the AP
# indicates in RADIUS Access-Request messages.
#hs20_t_c_filename=terms-and-conditions
#
# hs20_t_c_timestamp contains the Terms and Conditions timestamp that the AP
# indicates in RADIUS Access-Request messages. Usually, this contains the number
# of seconds since January 1, 1970 00:00 UTC showing the time when the file was
# last modified.
#hs20_t_c_timestamp=1234567
#
# hs20_t_c_server_url contains a template for the Terms and Conditions server
# URL. This template is used to generate the URL for a STA that needs to
# acknowledge Terms and Conditions. Unlike the other hs20_t_c_* parameters, this
# parameter is used on the authentication server, not the AP.
# Macros:
# @1@ = MAC address of the STA (colon separated hex octets)
#hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123
# OSU and Operator icons
# <Icon Width>:<Icon Height>:<Language code>:<Icon Type>:<Name>:<file path>
#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
# OSU SSID (see ssid2 for format description)
# This is the SSID used for all OSU connections to all the listed OSU Providers.
#osu_ssid="example"
# OSU Providers
# One or more sets of following parameter. Each OSU provider is started by the
# mandatory osu_server_uri item. The other parameters add information for the
# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN
# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI
# value for OSEN authentication when using a shared BSS (Single SSID) for OSU.
#
#osu_server_uri=https://example.com/osu/
#osu_friendly_name=eng:Example operator
#osu_friendly_name=fin:Esimerkkipalveluntarjoaja
#osu_nai=anonymous@example.com
#osu_nai2=anonymous@example.com
#osu_method_list=1 0
#osu_icon=icon32
#osu_icon=icon64
#osu_service_desc=eng:Example services
#osu_service_desc=fin:Esimerkkipalveluja
#
#osu_server_uri=...
# Operator Icons
# Operator icons are specified using references to the hs20_icon entries
# (Name subfield). This information, if present, is advertsised in the
# Operator Icon Metadata ANQO-element.
#operator_icon=icon32
#operator_icon=icon64
##### Multiband Operation (MBO) ###############################################
#
# MBO enabled
# 0 = disabled (default)
# 1 = enabled
#mbo=1
#
# Cellular data connection preference
# 0 = Excluded - AP does not want STA to use the cellular data connection
# 1 = AP prefers the STA not to use cellular data connection
# 255 = AP prefers the STA to use cellular data connection
#mbo_cell_data_conn_pref=1
##### Optimized Connectivity Experience (OCE) #################################
#
# Enable OCE specific features (bitmap)
# BIT(0) - Reserved
# Set BIT(1) (= 2) to enable OCE in STA-CFON mode
# Set BIT(2) (= 4) to enable OCE in AP mode
# Default is 0 = OCE disabled
#oce=0
# RSSI-based association rejection
#
# Reject STA association if RSSI is below given threshold (in dBm)
# Allowed range: -60 to -90 dBm; default = 0 (rejection disabled)
# Note: This rejection happens based on a signal strength detected while
# receiving a single frame and as such, there is significant risk of the value
# not being accurate and this resulting in valid stations being rejected. As
# such, this functionality is not recommended to be used for purposes other than
# testing.
#rssi_reject_assoc_rssi=-75
#
# Association retry delay in seconds allowed by the STA if RSSI has not met the
# threshold (range: 0..255, default=30).
#rssi_reject_assoc_timeout=30
# Ignore Probe Request frames if RSSI is below given threshold (in dBm)
# Allowed range: -60 to -90 dBm; default = 0 (rejection disabled)
#rssi_ignore_probe_request=-75
##### Fast Session Transfer (FST) support #####################################
#
# The options in this section are only available when the build configuration
# option CONFIG_FST is set while compiling hostapd. They allow this interface
# to be a part of FST setup.
#
# FST is the transfer of a session from a channel to another channel, in the
# same or different frequency bands.
#
# For detals, see IEEE Std 802.11ad-2012.
# Identifier of an FST Group the interface belongs to.
#fst_group_id=bond0
# Interface priority within the FST Group.
# Announcing a higher priority for an interface means declaring it more
# preferable for FST switch.
# fst_priority is in 1..255 range with 1 being the lowest priority.
#fst_priority=100
# Default LLT value for this interface in milliseconds. The value used in case
# no value provided during session setup. Default is 50 ms.
# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2
# Transitioning between states).
#fst_llt=100
##### Radio measurements / location ###########################################
# The content of a LCI measurement subelement
#lci=<Hexdump of binary data of the LCI report>
# The content of a location civic measurement subelement
#civic=<Hexdump of binary data of the location civic report>
# Enable neighbor report via radio measurements
#rrm_neighbor_report=1
# Enable beacon report via radio measurements
#rrm_beacon_report=1
# Publish fine timing measurement (FTM) responder functionality
# This parameter only controls publishing via Extended Capabilities element.
# Actual functionality is managed outside hostapd.
#ftm_responder=0
# Publish fine timing measurement (FTM) initiator functionality
# This parameter only controls publishing via Extended Capabilities element.
# Actual functionality is managed outside hostapd.
#ftm_initiator=0
#
# Stationary AP config indicates that the AP doesn't move hence location data
# can be considered as always up to date. If configured, LCI data will be sent
# as a radio measurement even if the request doesn't contain a max age element
# that allows sending of such data. Default: 0.
#stationary_ap=0
+# Enable reduced neighbor reporting (RNR)
+#rnr=0
+
##### Airtime policy configuration ###########################################
# Set the airtime policy operating mode:
# 0 = disabled (default)
# 1 = static config
# 2 = per-BSS dynamic config
# 3 = per-BSS limit mode
#airtime_mode=0
# Interval (in milliseconds) to poll the kernel for updated station activity in
# dynamic and limit modes
#airtime_update_interval=200
# Static configuration of station weights (when airtime_mode=1). Kernel default
# weight is 256; set higher for larger airtime share, lower for smaller share.
# Each entry is a MAC address followed by a weight.
#airtime_sta_weight=02:01:02:03:04:05 256
#airtime_sta_weight=02:01:02:03:04:06 512
# Per-BSS airtime weight. In multi-BSS mode, set for each BSS and hostapd will
# configure station weights to enforce the correct ratio between BSS weights
# depending on the number of active stations. The *ratios* between different
# BSSes is what's important, not the absolute numbers.
# Must be set for all BSSes if airtime_mode=2 or 3, has no effect otherwise.
#airtime_bss_weight=1
# Whether the current BSS should be limited (when airtime_mode=3).
#
# If set, the BSS weight ratio will be applied in the case where the current BSS
# would exceed the share defined by the BSS weight ratio. E.g., if two BSSes are
# set to the same weights, and one is set to limited, the limited BSS will get
# no more than half the available airtime, but if the non-limited BSS has more
# stations active, that *will* be allowed to exceed its half of the available
# airtime.
#airtime_bss_limit=1
##### EDMG support ############################################################
#
# Enable EDMG capability for AP mode in the 60 GHz band. Default value is false.
# To configure channel bonding for an EDMG AP use edmg_channel below.
# If enable_edmg is set and edmg_channel is not set, EDMG CB1 will be
# configured.
#enable_edmg=1
#
# Configure channel bonding for AP mode in the 60 GHz band.
# This parameter is relevant only if enable_edmg is set.
# Default value is 0 (no channel bonding).
#edmg_channel=9
##### TESTING OPTIONS #########################################################
#
# The options in this section are only available when the build configuration
# option CONFIG_TESTING_OPTIONS is set while compiling hostapd. They allow
# testing some scenarios that are otherwise difficult to reproduce.
#
# Ignore probe requests sent to hostapd with the given probability, must be a
# floating point number in the range [0, 1).
#ignore_probe_probability=0.0
#
# Ignore authentication frames with the given probability
#ignore_auth_probability=0.0
#
# Ignore association requests with the given probability
#ignore_assoc_probability=0.0
#
# Ignore reassociation requests with the given probability
#ignore_reassoc_probability=0.0
#
# Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability
#corrupt_gtk_rekey_mic_probability=0.0
#
# Include only ECSA IE without CSA IE where possible
# (channel switch operating class is needed)
#ecsa_ie_only=0
##### Multiple BSSID support ##################################################
#
# Above configuration is using the default interface (wlan#, or multi-SSID VLAN
# interfaces). Other BSSIDs can be added by using separator 'bss' with
# default interface name to be allocated for the data packets of the new BSS.
#
# hostapd will generate BSSID mask based on the BSSIDs that are
# configured. hostapd will verify that dev_addr & MASK == dev_addr. If this is
# not the case, the MAC address of the radio must be changed before starting
# hostapd (ifconfig wlan0 hw ether <MAC addr>). If a BSSID is configured for
# every secondary BSS, this limitation is not applied at hostapd and other
# masks may be used if the driver supports them (e.g., swap the locally
# administered bit)
#
# BSSIDs are assigned in order to each BSS, unless an explicit BSSID is
# specified using the 'bssid' parameter.
# If an explicit BSSID is specified, it must be chosen such that it:
# - results in a valid MASK that covers it and the dev_addr
# - is not the same as the MAC address of the radio
# - is not the same as any other explicitly specified BSSID
#
# Alternatively, the 'use_driver_iface_addr' parameter can be used to request
# hostapd to use the driver auto-generated interface address (e.g., to use the
# exact MAC addresses allocated to the device).
#
# Not all drivers support multiple BSSes. The exact mechanism for determining
# the driver capabilities is driver specific. With the current (i.e., a recent
# kernel) drivers using nl80211, this information can be checked with "iw list"
# (search for "valid interface combinations").
#
# Please note that hostapd uses some of the values configured for the first BSS
# as the defaults for the following BSSes. However, it is recommended that all
# BSSes include explicit configuration of all relevant configuration items.
#
#bss=wlan0_0
#ssid=test2
# most of the above items can be used here (apart from radio interface specific
# items, like channel)
#bss=wlan0_1
#bssid=00:13:10:95:fe:0b
# ...
diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c
index eaa628ad0676..0e7fdd6bccfb 100644
--- a/contrib/wpa/hostapd/hostapd_cli.c
+++ b/contrib/wpa/hostapd/hostapd_cli.c
@@ -1,2197 +1,2197 @@
/*
* hostapd - command line interface for hostapd daemon
* Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <dirent.h>
#include "common/wpa_ctrl.h"
#include "common/ieee802_11_defs.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/edit.h"
#include "common/version.h"
#include "common/cli.h"
#ifndef CONFIG_NO_CTRL_IFACE
static const char *const hostapd_cli_version =
"hostapd_cli v" VERSION_STR "\n"
"Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> and contributors";
static struct wpa_ctrl *ctrl_conn;
static int hostapd_cli_quit = 0;
static int hostapd_cli_attached = 0;
#ifndef CONFIG_CTRL_IFACE_DIR
#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
#endif /* CONFIG_CTRL_IFACE_DIR */
static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
static const char *client_socket_dir = NULL;
static char *ctrl_ifname = NULL;
static const char *pid_file = NULL;
static const char *action_file = NULL;
static int ping_interval = 5;
static int interactive = 0;
static int event_handler_registered = 0;
static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */
static void print_help(FILE *stream, const char *cmd);
static char ** list_cmd_list(void);
static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx);
static void update_stations(struct wpa_ctrl *ctrl);
static void cli_event(const char *str);
static void usage(void)
{
fprintf(stderr, "%s\n", hostapd_cli_version);
fprintf(stderr,
"\n"
"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvBr] "
"[-a<path>] \\\n"
" [-P<pid file>] [-G<ping interval>] [command..]\n"
"\n"
"Options:\n"
" -h help (show this usage text)\n"
" -v shown version information\n"
" -p<path> path to find control sockets (default: "
"/var/run/hostapd)\n"
" -s<dir_path> dir path to open client sockets (default: "
CONFIG_CTRL_IFACE_DIR ")\n"
" -a<file> run in daemon mode executing the action file "
"based on events\n"
" from hostapd\n"
" -r try to reconnect when client socket is "
"disconnected.\n"
" This is useful only when used with -a.\n"
" -B run a daemon in the background\n"
" -i<ifname> Interface to listen on (default: first "
"interface found in the\n"
" socket path)\n\n");
print_help(stderr, NULL);
}
static void register_event_handler(struct wpa_ctrl *ctrl)
{
if (!ctrl_conn)
return;
if (interactive) {
event_handler_registered =
!eloop_register_read_sock(wpa_ctrl_get_fd(ctrl),
hostapd_cli_receive,
NULL, NULL);
}
}
static void unregister_event_handler(struct wpa_ctrl *ctrl)
{
if (!ctrl_conn)
return;
if (interactive && event_handler_registered) {
eloop_unregister_read_sock(wpa_ctrl_get_fd(ctrl));
event_handler_registered = 0;
}
}
static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
{
#ifndef CONFIG_CTRL_IFACE_UDP
char *cfile;
int flen;
#endif /* !CONFIG_CTRL_IFACE_UDP */
if (ifname == NULL)
return NULL;
#ifdef CONFIG_CTRL_IFACE_UDP
ctrl_conn = wpa_ctrl_open(ifname);
return ctrl_conn;
#else /* CONFIG_CTRL_IFACE_UDP */
flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2;
cfile = malloc(flen);
if (cfile == NULL)
return NULL;
snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
if (client_socket_dir && client_socket_dir[0] &&
access(client_socket_dir, F_OK) < 0) {
perror(client_socket_dir);
free(cfile);
return NULL;
}
ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
free(cfile);
return ctrl_conn;
#endif /* CONFIG_CTRL_IFACE_UDP */
}
static void hostapd_cli_close_connection(void)
{
if (ctrl_conn == NULL)
return;
unregister_event_handler(ctrl_conn);
if (hostapd_cli_attached) {
wpa_ctrl_detach(ctrl_conn);
hostapd_cli_attached = 0;
}
wpa_ctrl_close(ctrl_conn);
ctrl_conn = NULL;
}
static int hostapd_cli_reconnect(const char *ifname)
{
char *next_ctrl_ifname;
hostapd_cli_close_connection();
if (!ifname)
return -1;
next_ctrl_ifname = os_strdup(ifname);
os_free(ctrl_ifname);
ctrl_ifname = next_ctrl_ifname;
if (!ctrl_ifname)
return -1;
ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
if (!ctrl_conn)
return -1;
if (!interactive && !action_file)
return 0;
if (wpa_ctrl_attach(ctrl_conn) == 0) {
hostapd_cli_attached = 1;
register_event_handler(ctrl_conn);
update_stations(ctrl_conn);
} else {
printf("Warning: Failed to attach to hostapd.\n");
}
return 0;
}
static void hostapd_cli_msg_cb(char *msg, size_t len)
{
cli_event(msg);
printf("%s\n", msg);
}
static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
{
char buf[4096];
size_t len;
int ret;
if (ctrl_conn == NULL) {
printf("Not connected to hostapd - command dropped.\n");
return -1;
}
len = sizeof(buf) - 1;
ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
hostapd_cli_msg_cb);
if (ret == -2) {
printf("'%s' command timed out.\n", cmd);
return -2;
} else if (ret < 0) {
printf("'%s' command failed.\n", cmd);
return -1;
}
if (print) {
buf[len] = '\0';
printf("%s", buf);
}
return 0;
}
static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
{
return _wpa_ctrl_command(ctrl, cmd, 1);
}
static int hostapd_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd,
int min_args, int argc, char *argv[])
{
char buf[4096];
if (argc < min_args) {
printf("Invalid %s command - at least %d argument%s required.\n",
cmd, min_args, min_args > 1 ? "s are" : " is");
return -1;
}
if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0)
return -1;
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "PING");
}
static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "RELOG");
}
static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
return wpa_ctrl_command(ctrl, "STATUS");
}
static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc > 0) {
char buf[100];
os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]);
return wpa_ctrl_command(ctrl, buf);
}
return wpa_ctrl_command(ctrl, "MIB");
}
static int hostapd_cli_exec(const char *program, const char *arg1,
const char *arg2)
{
char *arg;
size_t len;
int res;
len = os_strlen(arg1) + os_strlen(arg2) + 2;
arg = os_malloc(len);
if (arg == NULL)
return -1;
os_snprintf(arg, len, "%s %s", arg1, arg2);
res = os_exec(program, arg, 1);
os_free(arg);
return res;
}
static void hostapd_cli_action_process(char *msg, size_t len)
{
const char *pos;
pos = msg;
if (*pos == '<') {
pos = os_strchr(pos, '>');
if (pos)
pos++;
else
pos = msg;
}
hostapd_cli_exec(action_file, ctrl_ifname, pos);
}
static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char buf[64];
if (argc < 1) {
printf("Invalid 'sta' command - at least one argument, STA "
"address, is required.\n");
return -1;
}
if (argc > 1)
snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]);
else
snprintf(buf, sizeof(buf), "STA %s", argv[0]);
return wpa_ctrl_command(ctrl, buf);
}
static char ** hostapd_complete_stations(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
switch (arg) {
case 1:
res = cli_txt_list_array(&stations);
break;
}
return res;
}
static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[64];
if (argc != 1) {
printf("Invalid 'new_sta' command - exactly one argument, STA "
"address, is required.\n");
return -1;
}
snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]);
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[64];
if (argc < 1) {
printf("Invalid 'deauthenticate' command - exactly one "
"argument, STA address, is required.\n");
return -1;
}
if (argc > 1)
os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s",
argv[0], argv[1]);
else
os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]);
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[64];
if (argc < 1) {
printf("Invalid 'disassociate' command - exactly one "
"argument, STA address, is required.\n");
return -1;
}
if (argc > 1)
os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s",
argv[0], argv[1]);
else
os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]);
return wpa_ctrl_command(ctrl, buf);
}
#ifdef CONFIG_TAXONOMY
static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[64];
if (argc != 1) {
printf("Invalid 'signature' command - exactly one argument, STA address, is required.\n");
return -1;
}
os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
return wpa_ctrl_command(ctrl, buf);
}
#endif /* CONFIG_TAXONOMY */
static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[64];
if (argc != 1) {
printf("Invalid 'sa_query' command - exactly one argument, "
"STA address, is required.\n");
return -1;
}
snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
return wpa_ctrl_command(ctrl, buf);
}
#ifdef CONFIG_WPS
static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[256];
if (argc < 2) {
printf("Invalid 'wps_pin' command - at least two arguments, "
"UUID and PIN, are required.\n");
return -1;
}
if (argc > 3)
snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s",
argv[0], argv[1], argv[2], argv[3]);
else if (argc > 2)
snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s",
argv[0], argv[1], argv[2]);
else
snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]);
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256];
int res;
if (argc != 1 && argc != 2) {
printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
"- PIN to be verified\n");
return -1;
}
if (argc == 2)
res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
argv[0], argv[1]);
else
res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
argv[0]);
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long WPS_CHECK_PIN command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "WPS_PBC");
}
static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "WPS_CANCEL");
}
#ifdef CONFIG_WPS_NFC
static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
int ret;
char *buf;
size_t buflen;
if (argc != 1) {
printf("Invalid 'wps_nfc_tag_read' command - one argument "
"is required.\n");
return -1;
}
buflen = 18 + os_strlen(argv[0]);
buf = os_malloc(buflen);
if (buf == NULL)
return -1;
os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
ret = wpa_ctrl_command(ctrl, buf);
os_free(buf);
return ret;
}
static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
char cmd[64];
int res;
if (argc != 1) {
printf("Invalid 'wps_nfc_config_token' command - one argument "
"is required.\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
argv[0]);
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
char cmd[64];
int res;
if (argc != 1) {
printf("Invalid 'wps_nfc_token' command - one argument is "
"required.\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long WPS_NFC_TOKEN command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
char cmd[64];
int res;
if (argc != 2) {
printf("Invalid 'nfc_get_handover_sel' command - two arguments "
"are required.\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
argv[0], argv[1]);
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long NFC_GET_HANDOVER_SEL command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
#endif /* CONFIG_WPS_NFC */
static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[64];
if (argc < 1) {
printf("Invalid 'wps_ap_pin' command - at least one argument "
"is required.\n");
return -1;
}
if (argc > 2)
snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
argv[0], argv[1], argv[2]);
else if (argc > 1)
snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
argv[0], argv[1]);
else
snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "WPS_GET_STATUS");
}
static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[256];
char ssid_hex[2 * SSID_MAX_LEN + 1];
char key_hex[2 * 64 + 1];
int i;
if (argc < 1) {
printf("Invalid 'wps_config' command - at least two arguments "
"are required.\n");
return -1;
}
ssid_hex[0] = '\0';
for (i = 0; i < SSID_MAX_LEN; i++) {
if (argv[0][i] == '\0')
break;
os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
}
key_hex[0] = '\0';
if (argc > 3) {
for (i = 0; i < 64; i++) {
if (argv[3][i] == '\0')
break;
os_snprintf(&key_hex[i * 2], 3, "%02x",
argv[3][i]);
}
}
if (argc > 3)
snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s",
ssid_hex, argv[1], argv[2], key_hex);
else if (argc > 2)
snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s",
ssid_hex, argv[1], argv[2]);
else
snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s",
ssid_hex, argv[1]);
return wpa_ctrl_command(ctrl, buf);
}
#endif /* CONFIG_WPS */
static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[300];
int res;
if (argc < 2) {
printf("Invalid 'disassoc_imminent' command - two arguments "
"(STA addr and Disassociation Timer) are needed\n");
return -1;
}
res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
argv[0], argv[1]);
if (os_snprintf_error(sizeof(buf), res))
return -1;
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[300];
int res;
if (argc < 3) {
printf("Invalid 'ess_disassoc' command - three arguments (STA "
"addr, disassoc timer, and URL) are needed\n");
return -1;
}
res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
argv[0], argv[1], argv[2]);
if (os_snprintf_error(sizeof(buf), res))
return -1;
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[2000], *tmp;
int res, i, total;
if (argc < 1) {
printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n");
return -1;
}
res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]);
if (os_snprintf_error(sizeof(buf), res))
return -1;
total = res;
for (i = 1; i < argc; i++) {
tmp = &buf[total];
res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]);
if (os_snprintf_error(sizeof(buf) - total, res))
return -1;
total += res;
}
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "GET_CONFIG");
}
static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
char *addr, size_t addr_len, int print)
{
char buf[4096], *pos;
size_t len;
int ret;
if (ctrl_conn == NULL) {
printf("Not connected to hostapd - command dropped.\n");
return -1;
}
len = sizeof(buf) - 1;
ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
hostapd_cli_msg_cb);
if (ret == -2) {
printf("'%s' command timed out.\n", cmd);
return -2;
} else if (ret < 0) {
printf("'%s' command failed.\n", cmd);
return -1;
}
buf[len] = '\0';
if (memcmp(buf, "FAIL", 4) == 0)
return -1;
if (print)
printf("%s", buf);
pos = buf;
while (*pos != '\0' && *pos != '\n')
pos++;
*pos = '\0';
os_strlcpy(addr, buf, addr_len);
return 0;
}
static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char addr[32], cmd[64];
if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1))
return 0;
do {
snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0);
return -1;
}
static int hostapd_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char addr[32], cmd[64];
if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
return 0;
do {
if (os_strcmp(addr, "") != 0)
printf("%s\n", addr);
os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
return 0;
}
static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
print_help(stdout, argc > 0 ? argv[0] : NULL);
return 0;
}
static char ** hostapd_cli_complete_help(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
switch (arg) {
case 1:
res = list_cmd_list();
break;
}
return res;
}
static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
printf("%s\n\n%s\n", hostapd_cli_version, cli_full_license);
return 0;
}
static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
char buf[200];
int res;
if (argc != 1) {
printf("Invalid 'set_qos_map_set' command - "
"one argument (comma delimited QoS map set) "
"is needed\n");
return -1;
}
res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
if (os_snprintf_error(sizeof(buf), res))
return -1;
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
char buf[50];
int res;
if (argc != 1) {
printf("Invalid 'send_qos_map_conf' command - "
"one argument (STA addr) is needed\n");
return -1;
}
res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
if (os_snprintf_error(sizeof(buf), res))
return -1;
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[300];
int res;
if (argc < 2) {
printf("Invalid 'hs20_wnm_notif' command - two arguments (STA "
"addr and URL) are needed\n");
return -1;
}
res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
argv[0], argv[1]);
if (os_snprintf_error(sizeof(buf), res))
return -1;
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char buf[300];
int res;
if (argc < 3) {
printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n");
return -1;
}
if (argc > 3)
res = os_snprintf(buf, sizeof(buf),
"HS20_DEAUTH_REQ %s %s %s %s",
argv[0], argv[1], argv[2], argv[3]);
else
res = os_snprintf(buf, sizeof(buf),
"HS20_DEAUTH_REQ %s %s %s",
argv[0], argv[1], argv[2]);
if (os_snprintf_error(sizeof(buf), res))
return -1;
return wpa_ctrl_command(ctrl, buf);
}
static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
hostapd_cli_quit = 1;
if (interactive)
eloop_terminate();
return 0;
}
static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
if (argc != 1) {
printf("Invalid LEVEL command: needs one argument (debug "
"level)\n");
return 0;
}
snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
return wpa_ctrl_command(ctrl, cmd);
}
static void update_stations(struct wpa_ctrl *ctrl)
{
char addr[32], cmd[64];
if (!ctrl || !interactive)
return;
cli_txt_list_flush(&stations);
if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
return;
do {
if (os_strcmp(addr, "") != 0)
cli_txt_list_add(&stations, addr);
os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
}
static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl,
struct dl_list *interfaces)
{
struct dirent *dent;
DIR *dir;
if (!ctrl || !interfaces)
return;
dir = opendir(ctrl_iface_dir);
if (dir == NULL)
return;
while ((dent = readdir(dir))) {
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0)
continue;
cli_txt_list_add(interfaces, dent->d_name);
}
closedir(dir);
}
static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
{
struct dirent *dent;
DIR *dir;
dir = opendir(ctrl_iface_dir);
if (dir == NULL) {
printf("Control interface directory '%s' could not be "
"opened.\n", ctrl_iface_dir);
return;
}
printf("Available interfaces:\n");
while ((dent = readdir(dir))) {
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0)
continue;
printf("%s\n", dent->d_name);
}
closedir(dir);
}
static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc < 1) {
hostapd_cli_list_interfaces(ctrl);
return 0;
}
if (hostapd_cli_reconnect(argv[0]) != 0) {
printf("Could not connect to interface '%s' - re-trying\n",
ctrl_ifname);
}
return 0;
}
static char ** hostapd_complete_interface(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
DEFINE_DL_LIST(interfaces);
switch (arg) {
case 1:
hostapd_cli_get_interfaces(ctrl_conn, &interfaces);
res = cli_txt_list_array(&interfaces);
cli_txt_list_flush(&interfaces);
break;
}
return res;
}
static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[2048];
int res;
if (argc != 2) {
printf("Invalid SET command: needs two arguments (variable "
"name and value)\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long SET command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static char ** hostapd_complete_set(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
const char *fields[] = {
#ifdef CONFIG_WPS_TESTING
- "wps_version_number", "wps_testing_dummy_cred",
+ "wps_version_number", "wps_testing_stub_cred",
"wps_corrupt_pkhash",
#endif /* CONFIG_WPS_TESTING */
#ifdef CONFIG_INTERWORKING
"gas_frag_limit",
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_TESTING_OPTIONS
"ext_mgmt_frame_handling", "ext_eapol_frame_io",
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_MBO
"mbo_assoc_disallow",
#endif /* CONFIG_MBO */
"deny_mac_file", "accept_mac_file",
};
int i, num_fields = ARRAY_SIZE(fields);
if (arg == 1) {
char **res;
res = os_calloc(num_fields + 1, sizeof(char *));
if (!res)
return NULL;
for (i = 0; i < num_fields; i++) {
res[i] = os_strdup(fields[i]);
if (!res[i])
return res;
}
return res;
}
return NULL;
}
static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
int res;
if (argc != 1) {
printf("Invalid GET command: needs one argument (variable "
"name)\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long GET command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static char ** hostapd_complete_get(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
const char *fields[] = {
"version", "tls_library",
};
int i, num_fields = ARRAY_SIZE(fields);
if (arg == 1) {
char **res;
res = os_calloc(num_fields + 1, sizeof(char *));
if (!res)
return NULL;
for (i = 0; i < num_fields; i++) {
res[i] = os_strdup(fields[i]);
if (!res[i])
return res;
}
return res;
}
return NULL;
}
#ifdef CONFIG_FST
static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
int res;
int i;
int total;
if (argc <= 0) {
printf("FST command: parameters are required.\n");
return -1;
}
total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER");
for (i = 0; i < argc; i++) {
res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s",
argv[i]);
if (os_snprintf_error(sizeof(cmd) - total, res)) {
printf("Too long fst command.\n");
return -1;
}
total += res;
}
return wpa_ctrl_command(ctrl, cmd);
}
#endif /* CONFIG_FST */
static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
char cmd[256];
int res;
int i;
char *tmp;
int total;
if (argc < 2) {
printf("Invalid chan_switch command: needs at least two "
"arguments (count and freq)\n"
"usage: <cs_count> <freq> [sec_channel_offset=] "
"[center_freq1=] [center_freq2=] [bandwidth=] "
"[blocktx] [ht|vht]\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
argv[0], argv[1]);
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long CHAN_SWITCH command.\n");
return -1;
}
total = res;
for (i = 2; i < argc; i++) {
tmp = cmd + total;
res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
if (os_snprintf_error(sizeof(cmd) - total, res)) {
printf("Too long CHAN_SWITCH command.\n");
return -1;
}
total += res;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "ENABLE");
}
static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "RELOAD");
}
static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DISABLE");
}
static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "UPDATE_BEACON");
}
static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
int res;
if (argc < 2 || argc > 4) {
printf("Invalid vendor command\n"
"usage: <vendor id> <command id> [<hex formatted command argument>] [nested=<0|1>]\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s%s%s", argv[0],
argv[1], argc >= 3 ? argv[2] : "",
argc == 4 ? " " : "", argc == 4 ? argv[3] : "");
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long VENDOR command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "ERP_FLUSH");
}
static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256];
int res;
res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s",
argc >= 1 ? " " : "",
argc >= 1 ? argv[0] : "",
argc == 2 ? " " : "",
argc == 2 ? argv[1] : "");
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long option\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int hostapd_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc == 0)
return -1;
return hostapd_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]);
}
static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "PMKSA");
}
static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "PMKSA_FLUSH");
}
static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[2048];
int res;
if (argc < 3 || argc > 6) {
printf("Invalid set_neighbor command: needs 3-6 arguments\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s",
argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "",
argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : "");
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long SET_NEIGHBOR command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int hostapd_cli_cmd_show_neighbor(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "SHOW_NEIGHBOR");
}
static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "REMOVE_NEIGHBOR", 1, argc, argv);
}
static int hostapd_cli_cmd_req_lci(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256];
int res;
if (argc != 1) {
printf("Invalid req_lci command - requires destination address\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "REQ_LCI %s", argv[0]);
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long REQ_LCI command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int hostapd_cli_cmd_req_range(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc < 4) {
printf("Invalid req_range command: needs at least 4 arguments - dest address, randomization interval, min AP count, and 1 to 16 AP addresses\n");
return -1;
}
return hostapd_cli_cmd(ctrl, "REQ_RANGE", 4, argc, argv);
}
static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DRIVER_FLAGS");
}
#ifdef CONFIG_DPP
static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_bootstrap_set(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_SET", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN");
}
static int hostapd_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv);
}
static int hostapd_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_configurator_sign(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_SIGN", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
}
#ifdef CONFIG_DPP2
static int hostapd_cli_cmd_dpp_controller_start(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_CONTROLLER_START", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_controller_stop(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DPP_CONTROLLER_STOP");
}
static int hostapd_cli_cmd_dpp_chirp(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DPP_CHIRP", 1, argc, argv);
}
static int hostapd_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DPP_STOP_CHIRP");
}
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
static int hostapd_cli_cmd_accept_macacl(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "ACCEPT_ACL", 1, argc, argv);
}
static int hostapd_cli_cmd_deny_macacl(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "DENY_ACL", 1, argc, argv);
}
static int hostapd_cli_cmd_poll_sta(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "POLL_STA", 1, argc, argv);
}
static int hostapd_cli_cmd_req_beacon(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return hostapd_cli_cmd(ctrl, "REQ_BEACON", 2, argc, argv);
}
static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "RELOAD_WPA_PSK");
}
#ifdef ANDROID
static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return hostapd_cli_cmd(ctrl, "DRIVER", 1, argc, argv);
}
#endif /* ANDROID */
struct hostapd_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
char ** (*completion)(const char *str, int pos);
const char *usage;
};
static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
{ "ping", hostapd_cli_cmd_ping, NULL,
"= pings hostapd" },
{ "mib", hostapd_cli_cmd_mib, NULL,
"= get MIB variables (dot1x, dot11, radius)" },
{ "relog", hostapd_cli_cmd_relog, NULL,
"= reload/truncate debug log output file" },
{ "status", hostapd_cli_cmd_status, NULL,
"= show interface status info" },
{ "sta", hostapd_cli_cmd_sta, hostapd_complete_stations,
"<addr> = get MIB variables for one station" },
{ "all_sta", hostapd_cli_cmd_all_sta, NULL,
"= get MIB variables for all stations" },
{ "list_sta", hostapd_cli_cmd_list_sta, NULL,
"= list all stations" },
{ "new_sta", hostapd_cli_cmd_new_sta, NULL,
"<addr> = add a new station" },
{ "deauthenticate", hostapd_cli_cmd_deauthenticate,
hostapd_complete_stations,
"<addr> = deauthenticate a station" },
{ "disassociate", hostapd_cli_cmd_disassociate,
hostapd_complete_stations,
"<addr> = disassociate a station" },
#ifdef CONFIG_TAXONOMY
{ "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
"<addr> = get taxonomy signature for a station" },
#endif /* CONFIG_TAXONOMY */
{ "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
"<addr> = send SA Query to a station" },
#ifdef CONFIG_WPS
{ "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
"<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
"<PIN> = verify PIN checksum" },
{ "wps_pbc", hostapd_cli_cmd_wps_pbc, NULL,
"= indicate button pushed to initiate PBC" },
{ "wps_cancel", hostapd_cli_cmd_wps_cancel, NULL,
"= cancel the pending WPS operation" },
#ifdef CONFIG_WPS_NFC
{ "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read, NULL,
"<hexdump> = report read NFC tag with WPS data" },
{ "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token, NULL,
"<WPS/NDEF> = build NFC configuration token" },
{ "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token, NULL,
"<WPS/NDEF/enable/disable> = manager NFC password token" },
{ "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel, NULL,
NULL },
#endif /* CONFIG_WPS_NFC */
{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin, NULL,
"<cmd> [params..] = enable/disable AP PIN" },
{ "wps_config", hostapd_cli_cmd_wps_config, NULL,
"<SSID> <auth> <encr> <key> = configure AP" },
{ "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
"= show current WPS status" },
#endif /* CONFIG_WPS */
{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
"= send Disassociation Imminent notification" },
{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
"= send ESS Dissassociation Imminent notification" },
{ "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL,
"= send BSS Transition Management Request" },
{ "get_config", hostapd_cli_cmd_get_config, NULL,
"= show current configuration" },
{ "help", hostapd_cli_cmd_help, hostapd_cli_complete_help,
"= show this usage help" },
{ "interface", hostapd_cli_cmd_interface, hostapd_complete_interface,
"[ifname] = show interfaces/select interface" },
#ifdef CONFIG_FST
{ "fst", hostapd_cli_cmd_fst, NULL,
"<params...> = send FST-MANAGER control interface command" },
#endif /* CONFIG_FST */
{ "raw", hostapd_cli_cmd_raw, NULL,
"<params..> = send unprocessed command" },
{ "level", hostapd_cli_cmd_level, NULL,
"<debug level> = change debug level" },
{ "license", hostapd_cli_cmd_license, NULL,
"= show full hostapd_cli license" },
{ "quit", hostapd_cli_cmd_quit, NULL,
"= exit hostapd_cli" },
{ "set", hostapd_cli_cmd_set, hostapd_complete_set,
"<name> <value> = set runtime variables" },
{ "get", hostapd_cli_cmd_get, hostapd_complete_get,
"<name> = get runtime info" },
{ "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL,
"<arg,arg,...> = set QoS Map set element" },
{ "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf,
hostapd_complete_stations,
"<addr> = send QoS Map Configure frame" },
{ "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
"<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
" [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
" = initiate channel switch announcement" },
{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
"<addr> <url>\n"
" = send WNM-Notification Subscription Remediation Request" },
{ "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL,
"<addr> <code (0/1)> <Re-auth-Delay(sec)> [url]\n"
" = send WNM-Notification imminent deauthentication indication" },
{ "vendor", hostapd_cli_cmd_vendor, NULL,
"<vendor id> <sub command id> [<hex formatted data>]\n"
" = send vendor driver command" },
{ "enable", hostapd_cli_cmd_enable, NULL,
"= enable hostapd on current interface" },
{ "reload", hostapd_cli_cmd_reload, NULL,
"= reload configuration for current interface" },
{ "disable", hostapd_cli_cmd_disable, NULL,
"= disable hostapd on current interface" },
{ "update_beacon", hostapd_cli_cmd_update_beacon, NULL,
"= update Beacon frame contents\n"},
{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
"= drop all ERP keys"},
{ "log_level", hostapd_cli_cmd_log_level, NULL,
"[level] = show/change log verbosity level" },
{ "pmksa", hostapd_cli_cmd_pmksa, NULL,
" = show PMKSA cache entries" },
{ "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL,
" = flush PMKSA cache" },
{ "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL,
"<addr> <ssid=> <nr=> [lci=] [civic=] [stat]\n"
" = add AP to neighbor database" },
{ "show_neighbor", hostapd_cli_cmd_show_neighbor, NULL,
" = show neighbor database entries" },
{ "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL,
"<addr> [ssid=<hex>] = remove AP from neighbor database" },
{ "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations,
"<addr> = send LCI request to a station"},
{ "req_range", hostapd_cli_cmd_req_range, NULL,
" = send FTM range request"},
{ "driver_flags", hostapd_cli_cmd_driver_flags, NULL,
" = show supported driver flags"},
#ifdef CONFIG_DPP
{ "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL,
"report a scanned DPP URI from a QR Code" },
{ "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL,
"type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
{ "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL,
"*|<id> = remove DPP bootstrap information" },
{ "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL,
"<id> = get DPP bootstrap URI" },
{ "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL,
"<id> = show DPP bootstrap information" },
{ "dpp_bootstrap_set", hostapd_cli_cmd_dpp_bootstrap_set, NULL,
"<id> [conf=..] [ssid=<SSID>] [ssid_charset=#] [psk=<PSK>] [pass=<passphrase>] [configurator=<id>] [conn_status=#] [akm_use_selector=<0|1>] [group_id=..] [expiry=#] [csrattrs=..] = set DPP configurator parameters" },
{ "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL,
"peer=<id> [own=<id>] = initiate DPP bootstrapping" },
{ "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL,
"<freq in MHz> = start DPP listen" },
{ "dpp_stop_listen", hostapd_cli_cmd_dpp_stop_listen, NULL,
"= stop DPP listen" },
{ "dpp_configurator_add", hostapd_cli_cmd_dpp_configurator_add, NULL,
"[curve=..] [key=..] = add DPP configurator" },
{ "dpp_configurator_remove", hostapd_cli_cmd_dpp_configurator_remove,
NULL,
"*|<id> = remove DPP configurator" },
{ "dpp_configurator_get_key", hostapd_cli_cmd_dpp_configurator_get_key,
NULL,
"<id> = Get DPP configurator's private key" },
{ "dpp_configurator_sign", hostapd_cli_cmd_dpp_configurator_sign, NULL,
"conf=<role> configurator=<id> = generate self DPP configuration" },
{ "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL,
"add PKEX code" },
{ "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL,
"*|<id> = remove DPP pkex information" },
#ifdef CONFIG_DPP2
{ "dpp_controller_start", hostapd_cli_cmd_dpp_controller_start, NULL,
"[tcp_port=<port>] [role=..] = start DPP controller" },
{ "dpp_controller_stop", hostapd_cli_cmd_dpp_controller_stop, NULL,
"= stop DPP controller" },
{ "dpp_chirp", hostapd_cli_cmd_dpp_chirp, NULL,
"own=<BI ID> iter=<count> = start DPP chirp" },
{ "dpp_stop_chirp", hostapd_cli_cmd_dpp_stop_chirp, NULL,
"= stop DPP chirp" },
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
{ "accept_acl", hostapd_cli_cmd_accept_macacl, NULL,
"=Add/Delete/Show/Clear accept MAC ACL" },
{ "deny_acl", hostapd_cli_cmd_deny_macacl, NULL,
"=Add/Delete/Show/Clear deny MAC ACL" },
{ "poll_sta", hostapd_cli_cmd_poll_sta, hostapd_complete_stations,
"<addr> = poll a STA to check connectivity with a QoS null frame" },
{ "req_beacon", hostapd_cli_cmd_req_beacon, NULL,
"<addr> [req_mode=] <measurement request hexdump> = send a Beacon report request to a station" },
{ "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
"= reload wpa_psk_file only" },
#ifdef ANDROID
{ "driver", hostapd_cli_cmd_driver, NULL,
"<driver sub command> [<hex formatted data>] = send driver command data" },
#endif /* ANDROID */
{ NULL, NULL, NULL, NULL }
};
/*
* Prints command usage, lines are padded with the specified string.
*/
static void print_cmd_help(FILE *stream, const struct hostapd_cli_cmd *cmd,
const char *pad)
{
char c;
size_t n;
if (cmd->usage == NULL)
return;
fprintf(stream, "%s%s ", pad, cmd->cmd);
for (n = 0; (c = cmd->usage[n]); n++) {
fprintf(stream, "%c", c);
if (c == '\n')
fprintf(stream, "%s", pad);
}
fprintf(stream, "\n");
}
static void print_help(FILE *stream, const char *cmd)
{
int n;
fprintf(stream, "commands:\n");
for (n = 0; hostapd_cli_commands[n].cmd; n++) {
if (cmd == NULL || str_starts(hostapd_cli_commands[n].cmd, cmd))
print_cmd_help(stream, &hostapd_cli_commands[n], " ");
}
}
static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
const struct hostapd_cli_cmd *cmd, *match = NULL;
int count;
count = 0;
cmd = hostapd_cli_commands;
while (cmd->cmd) {
if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) {
match = cmd;
if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
/* we have an exact match */
count = 1;
break;
}
count++;
}
cmd++;
}
if (count > 1) {
printf("Ambiguous command '%s'; possible commands:", argv[0]);
cmd = hostapd_cli_commands;
while (cmd->cmd) {
if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) ==
0) {
printf(" %s", cmd->cmd);
}
cmd++;
}
printf("\n");
} else if (count == 0) {
printf("Unknown command '%s'\n", argv[0]);
} else {
match->handler(ctrl, argc - 1, &argv[1]);
}
}
static void cli_event(const char *str)
{
const char *start, *s;
start = os_strchr(str, '>');
if (start == NULL)
return;
start++;
if (str_starts(start, AP_STA_CONNECTED)) {
s = os_strchr(start, ' ');
if (s == NULL)
return;
cli_txt_list_add(&stations, s + 1);
return;
}
if (str_starts(start, AP_STA_DISCONNECTED)) {
s = os_strchr(start, ' ');
if (s == NULL)
return;
cli_txt_list_del_addr(&stations, s + 1);
return;
}
}
static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
int action_monitor)
{
int first = 1;
if (ctrl_conn == NULL)
return;
while (wpa_ctrl_pending(ctrl)) {
char buf[4096];
size_t len = sizeof(buf) - 1;
if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
buf[len] = '\0';
if (action_monitor)
hostapd_cli_action_process(buf, len);
else {
cli_event(buf);
if (in_read && first)
printf("\n");
first = 0;
printf("%s\n", buf);
}
} else {
printf("Could not read pending message.\n");
break;
}
}
}
static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
hostapd_cli_recv_pending(ctrl_conn, 0, 0);
}
static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx)
{
if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
printf("Connection to hostapd lost - trying to reconnect\n");
hostapd_cli_close_connection();
}
if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0)
printf("Connection to hostapd re-established\n");
if (ctrl_conn)
hostapd_cli_recv_pending(ctrl_conn, 1, 0);
eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
}
static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx)
{
eloop_terminate();
}
static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd)
{
char *argv[max_args];
int argc;
argc = tokenize_cmd(cmd, argv);
if (argc)
wpa_request(ctrl_conn, argc, argv);
}
static void hostapd_cli_edit_eof_cb(void *ctx)
{
eloop_terminate();
}
static char ** list_cmd_list(void)
{
char **res;
int i, count;
count = ARRAY_SIZE(hostapd_cli_commands);
res = os_calloc(count + 1, sizeof(char *));
if (res == NULL)
return NULL;
for (i = 0; hostapd_cli_commands[i].cmd; i++) {
res[i] = os_strdup(hostapd_cli_commands[i].cmd);
if (res[i] == NULL)
break;
}
return res;
}
static char ** hostapd_cli_cmd_completion(const char *cmd, const char *str,
int pos)
{
int i;
for (i = 0; hostapd_cli_commands[i].cmd; i++) {
if (os_strcasecmp(hostapd_cli_commands[i].cmd, cmd) != 0)
continue;
if (hostapd_cli_commands[i].completion)
return hostapd_cli_commands[i].completion(str, pos);
if (!hostapd_cli_commands[i].usage)
return NULL;
edit_clear_line();
printf("\r%s\n", hostapd_cli_commands[i].usage);
edit_redraw();
break;
}
return NULL;
}
static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str,
int pos)
{
char **res;
const char *end;
char *cmd;
end = os_strchr(str, ' ');
if (end == NULL || str + pos < end)
return list_cmd_list();
cmd = os_malloc(pos + 1);
if (cmd == NULL)
return NULL;
os_memcpy(cmd, str, pos);
cmd[end - str] = '\0';
res = hostapd_cli_cmd_completion(cmd, str, pos);
os_free(cmd);
return res;
}
static void hostapd_cli_interactive(void)
{
char *hfile = NULL;
char *home;
printf("\nInteractive mode\n\n");
#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR
home = CONFIG_HOSTAPD_CLI_HISTORY_DIR;
#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
home = getenv("HOME");
#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
if (home) {
const char *fname = ".hostapd_cli_history";
int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
hfile = os_malloc(hfile_len);
if (hfile)
os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
}
eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
hostapd_cli_edit_completion_cb, NULL, hfile, NULL);
eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
eloop_run();
cli_txt_list_flush(&stations);
edit_deinit(hfile, NULL);
os_free(hfile);
eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
}
static void hostapd_cli_cleanup(void)
{
hostapd_cli_close_connection();
if (pid_file)
os_daemonize_terminate(pid_file);
os_program_deinit();
}
static void hostapd_cli_action(struct wpa_ctrl *ctrl)
{
fd_set rfds;
int fd, res;
struct timeval tv;
char buf[256];
size_t len;
fd = wpa_ctrl_get_fd(ctrl);
while (!hostapd_cli_quit) {
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
tv.tv_sec = ping_interval;
tv.tv_usec = 0;
res = select(fd + 1, &rfds, NULL, NULL, &tv);
if (res < 0 && errno != EINTR) {
perror("select");
break;
}
if (FD_ISSET(fd, &rfds))
hostapd_cli_recv_pending(ctrl, 0, 1);
else {
len = sizeof(buf) - 1;
if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
hostapd_cli_action_process) < 0 ||
len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
printf("hostapd did not reply to PING "
"command - exiting\n");
break;
}
}
}
}
int main(int argc, char *argv[])
{
int warning_displayed = 0;
int c;
int daemonize = 0;
int reconnect = 0;
if (os_program_init())
return -1;
for (;;) {
c = getopt(argc, argv, "a:BhG:i:p:P:rs:v");
if (c < 0)
break;
switch (c) {
case 'a':
action_file = optarg;
break;
case 'B':
daemonize = 1;
break;
case 'G':
ping_interval = atoi(optarg);
break;
case 'h':
usage();
return 0;
case 'v':
printf("%s\n", hostapd_cli_version);
return 0;
case 'i':
os_free(ctrl_ifname);
ctrl_ifname = os_strdup(optarg);
break;
case 'p':
ctrl_iface_dir = optarg;
break;
case 'P':
pid_file = optarg;
break;
case 'r':
reconnect = 1;
break;
case 's':
client_socket_dir = optarg;
break;
default:
usage();
return -1;
}
}
interactive = (argc == optind) && (action_file == NULL);
if (interactive) {
printf("%s\n\n%s\n\n", hostapd_cli_version, cli_license);
}
if (eloop_init())
return -1;
for (;;) {
if (ctrl_ifname == NULL) {
struct dirent *dent;
DIR *dir = opendir(ctrl_iface_dir);
if (dir) {
while ((dent = readdir(dir))) {
if (os_strcmp(dent->d_name, ".") == 0
||
os_strcmp(dent->d_name, "..") == 0)
continue;
printf("Selected interface '%s'\n",
dent->d_name);
ctrl_ifname = os_strdup(dent->d_name);
break;
}
closedir(dir);
}
}
hostapd_cli_reconnect(ctrl_ifname);
if (ctrl_conn) {
if (warning_displayed)
printf("Connection established.\n");
break;
}
if (!interactive && !reconnect) {
perror("Failed to connect to hostapd - "
"wpa_ctrl_open");
return -1;
}
if (!warning_displayed) {
printf("Could not connect to hostapd - re-trying\n");
warning_displayed = 1;
}
os_sleep(1, 0);
continue;
}
if (action_file && !hostapd_cli_attached)
return -1;
if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
return -1;
if (reconnect && action_file && ctrl_ifname) {
while (!hostapd_cli_quit) {
if (ctrl_conn)
hostapd_cli_action(ctrl_conn);
os_sleep(1, 0);
hostapd_cli_reconnect(ctrl_ifname);
}
} else if (interactive)
hostapd_cli_interactive();
else if (action_file)
hostapd_cli_action(ctrl_conn);
else
wpa_request(ctrl_conn, argc - optind, &argv[optind]);
unregister_event_handler(ctrl_conn);
os_free(ctrl_ifname);
eloop_destroy();
hostapd_cli_cleanup();
return 0;
}
#else /* CONFIG_NO_CTRL_IFACE */
int main(int argc, char *argv[])
{
return -1;
}
#endif /* CONFIG_NO_CTRL_IFACE */
diff --git a/contrib/wpa/src/ap/acs.c b/contrib/wpa/src/ap/acs.c
index a112045364e3..46429f265433 100644
--- a/contrib/wpa/src/ap/acs.c
+++ b/contrib/wpa/src/ap/acs.c
@@ -1,1137 +1,1135 @@
/*
* ACS - Automatic Channel Selection module
* Copyright (c) 2011, Atheros Communications
* Copyright (c) 2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include <math.h>
#include "utils/common.h"
#include "utils/list.h"
#include "common/ieee802_11_defs.h"
#include "common/hw_features_common.h"
#include "common/wpa_ctrl.h"
#include "drivers/driver.h"
#include "hostapd.h"
#include "ap_drv_ops.h"
#include "ap_config.h"
#include "hw_features.h"
#include "acs.h"
/*
* Automatic Channel Selection
* ===========================
*
* More info at
* ------------
* http://wireless.kernel.org/en/users/Documentation/acs
*
* How to use
* ----------
* - make sure you have CONFIG_ACS=y in hostapd's .config
* - use channel=0 or channel=acs to enable ACS
*
* How does it work
* ----------------
* 1. passive scans are used to collect survey data
* (it is assumed that scan trigger collection of survey data in driver)
* 2. interference factor is calculated for each channel
* 3. ideal channel is picked depending on channel width by using adjacent
* channel interference factors
*
* Known limitations
* -----------------
* - Current implementation depends heavily on the amount of time willing to
* spend gathering survey data during hostapd startup. Short traffic bursts
* may be missed and a suboptimal channel may be picked.
* - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
*
* Todo / Ideas
* ------------
* - implement other interference computation methods
* - BSS/RSSI based
* - spectral scan based
* (should be possibly to hook this up with current ACS scans)
* - add wpa_supplicant support (for P2P)
* - collect a histogram of interference over time allowing more educated
* guess about an ideal channel (perhaps CSA could be used to migrate AP to a
* new "better" channel while running)
* - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
* when choosing the ideal channel
*
* Survey interference factor implementation details
* -------------------------------------------------
* Generic interference_factor in struct hostapd_channel_data is used.
*
* The survey interference factor is defined as the ratio of the
* observed busy time over the time we spent on the channel,
* this value is then amplified by the observed noise floor on
* the channel in comparison to the lowest noise floor observed
* on the entire band.
*
* This corresponds to:
* ---
* (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
* ---
*
* The coefficient of 2 reflects the way power in "far-field"
* radiation decreases as the square of distance from the antenna [1].
* What this does is it decreases the observed busy time ratio if the
* noise observed was low but increases it if the noise was high,
* proportionally to the way "far field" radiation changes over
* distance.
*
* If channel busy time is not available the fallback is to use channel RX time.
*
* Since noise floor is in dBm it is necessary to convert it into Watts so that
* combined channel interference (e.g., HT40, which uses two channels) can be
* calculated easily.
* ---
* (busy time - tx time) / (active time - tx time) *
* 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
* ---
*
* However to account for cases where busy/rx time is 0 (channel load is then
* 0%) channel noise floor signal power is combined into the equation so a
* channel with lower noise floor is preferred. The equation becomes:
* ---
* 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
* 2^(10^(chan_nf/10) + 10^(band_min_nf/10))
* ---
*
* All this "interference factor" is purely subjective and only time
* will tell how usable this is. By using the minimum noise floor we
* remove any possible issues due to card calibration. The computation
* of the interference factor then is dependent on what the card itself
* picks up as the minimum noise, not an actual real possible card
* noise value.
*
* Total interference computation details
* --------------------------------------
* The above channel interference factor is calculated with no respect to
* target operational bandwidth.
*
* To find an ideal channel the above data is combined by taking into account
* the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
* overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
* on 5 GHz.
*
* Each valid and possible channel spec (i.e., channel + width) is taken and its
* interference factor is computed by summing up interferences of each channel
* it overlaps. The one with least total interference is picked up.
*
* Note: This implies base channel interference factor must be non-negative
* allowing easy summing up.
*
* Example ACS analysis printout
* -----------------------------
*
* ACS: Trying survey-based ACS
* ACS: Survey analysis for channel 1 (2412 MHz)
* ACS: 1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
* ACS: 2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
* ACS: 3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
* ACS: 4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
* ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
* ACS: * interference factor average: 0.0557166
* ACS: Survey analysis for channel 2 (2417 MHz)
* ACS: 1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
* ACS: 2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
* ACS: 3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
* ACS: 4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
* ACS: 5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
* ACS: * interference factor average: 0.050832
* ACS: Survey analysis for channel 3 (2422 MHz)
* ACS: 1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
* ACS: 2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
* ACS: 3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
* ACS: 4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
* ACS: 5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
* ACS: * interference factor average: 0.0148838
* ACS: Survey analysis for channel 4 (2427 MHz)
* ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
* ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
* ACS: 4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
* ACS: 5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
* ACS: * interference factor average: 0.0160801
* ACS: Survey analysis for channel 5 (2432 MHz)
* ACS: 1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
* ACS: 2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
* ACS: 3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
* ACS: 4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
* ACS: 5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
* ACS: * interference factor average: 0.232244
* ACS: Survey analysis for channel 6 (2437 MHz)
* ACS: 1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
* ACS: 2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
* ACS: 3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
* ACS: 4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
* ACS: 5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
* ACS: * interference factor average: 0.232298
* ACS: Survey analysis for channel 7 (2442 MHz)
* ACS: 1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
* ACS: 2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
* ACS: 3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
* ACS: 4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
* ACS: 5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
* ACS: * interference factor average: 0.195031
* ACS: Survey analysis for channel 8 (2447 MHz)
* ACS: 1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
* ACS: 2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
* ACS: 3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
* ACS: 4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
* ACS: 5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
* ACS: * interference factor average: 0.0865885
* ACS: Survey analysis for channel 9 (2452 MHz)
* ACS: 1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
* ACS: 2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
* ACS: 4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
* ACS: * interference factor average: 0.00993022
* ACS: Survey analysis for channel 10 (2457 MHz)
* ACS: 1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
* ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
* ACS: 3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
* ACS: 4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
* ACS: * interference factor average: 0.0136033
* ACS: Survey analysis for channel 11 (2462 MHz)
* ACS: 1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
* ACS: 2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
* ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
* ACS: 4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
* ACS: 5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
* ACS: * interference factor average: 0.0271605
* ACS: Survey analysis for channel 12 (2467 MHz)
* ACS: 1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
* ACS: 2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
* ACS: 3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
* ACS: 4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
* ACS: 5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
* ACS: * interference factor average: 0.0148992
* ACS: Survey analysis for channel 13 (2472 MHz)
* ACS: 1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
* ACS: 2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
* ACS: 3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
* ACS: 4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
* ACS: 5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
* ACS: * interference factor average: 0.0260179
* ACS: Survey analysis for selected bandwidth 20MHz
* ACS: * channel 1: total interference = 0.121432
* ACS: * channel 2: total interference = 0.137512
* ACS: * channel 3: total interference = 0.369757
* ACS: * channel 4: total interference = 0.546338
* ACS: * channel 5: total interference = 0.690538
* ACS: * channel 6: total interference = 0.762242
* ACS: * channel 7: total interference = 0.756092
* ACS: * channel 8: total interference = 0.537451
* ACS: * channel 9: total interference = 0.332313
* ACS: * channel 10: total interference = 0.152182
* ACS: * channel 11: total interference = 0.0916111
* ACS: * channel 12: total interference = 0.0816809
* ACS: * channel 13: total interference = 0.0680776
* ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
*
* [1] http://en.wikipedia.org/wiki/Near_and_far_field
*/
static int acs_request_scan(struct hostapd_iface *iface);
static int acs_survey_is_sufficient(struct freq_survey *survey);
static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
{
struct freq_survey *survey, *tmp;
if (dl_list_empty(&chan->survey_list))
return;
dl_list_for_each_safe(survey, tmp, &chan->survey_list,
struct freq_survey, list) {
dl_list_del(&survey->list);
os_free(survey);
}
}
static void acs_cleanup_mode(struct hostapd_hw_modes *mode)
{
int i;
struct hostapd_channel_data *chan;
for (i = 0; i < mode->num_channels; i++) {
chan = &mode->channels[i];
if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
acs_clean_chan_surveys(chan);
dl_list_init(&chan->survey_list);
chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
chan->min_nf = 0;
}
}
void acs_cleanup(struct hostapd_iface *iface)
{
int i;
for (i = 0; i < iface->num_hw_features; i++)
acs_cleanup_mode(&iface->hw_features[i]);
iface->chans_surveyed = 0;
iface->acs_num_completed_scans = 0;
}
static void acs_fail(struct hostapd_iface *iface)
{
wpa_printf(MSG_ERROR, "ACS: Failed to start");
acs_cleanup(iface);
hostapd_disable_iface(iface);
}
static long double
acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
{
long double factor, busy, total;
if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
busy = survey->channel_time_busy;
else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
busy = survey->channel_time_rx;
else {
- /* This shouldn't really happen as survey data is checked in
- * acs_sanity_check() */
wpa_printf(MSG_ERROR, "ACS: Survey data missing");
return 0;
}
total = survey->channel_time;
if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
busy -= survey->channel_time_tx;
total -= survey->channel_time_tx;
}
/* TODO: figure out the best multiplier for noise floor base */
factor = pow(10, survey->nf / 5.0L) +
(total ? (busy / total) : 0) *
pow(2, pow(10, (long double) survey->nf / 10.0L) -
pow(10, (long double) min_nf / 10.0L));
return factor;
}
static void
acs_survey_chan_interference_factor(struct hostapd_iface *iface,
struct hostapd_channel_data *chan)
{
struct freq_survey *survey;
unsigned int i = 0;
long double int_factor = 0;
unsigned count = 0;
if (dl_list_empty(&chan->survey_list) ||
(chan->flag & HOSTAPD_CHAN_DISABLED))
return;
chan->interference_factor = 0;
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
{
i++;
if (!acs_survey_is_sufficient(survey)) {
wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
continue;
}
count++;
int_factor = acs_survey_interference_factor(survey,
iface->lowest_nf);
chan->interference_factor += int_factor;
wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
i, chan->min_nf, int_factor,
survey->nf, (unsigned long) survey->channel_time,
(unsigned long) survey->channel_time_busy,
(unsigned long) survey->channel_time_rx);
}
if (count)
chan->interference_factor /= count;
}
static int acs_usable_bw40_chan(const struct hostapd_channel_data *chan)
{
const int allowed[] = { 5180, 5220, 5260, 5300, 5500, 5540, 5580, 5620,
5660, 5745, 5785, 4920, 4960, 5955, 5995, 6035,
6075, 6115, 6155, 6195, 6235, 6275, 6315, 6355,
6395, 6435, 6475, 6515, 6555, 6595, 6635, 6675,
6715, 6755, 6795, 6835, 6875, 6915, 6955, 6995,
7035, 7075 };
unsigned int i;
for (i = 0; i < ARRAY_SIZE(allowed); i++)
if (chan->freq == allowed[i])
return 1;
return 0;
}
static int acs_usable_bw80_chan(const struct hostapd_channel_data *chan)
{
- const int allowed[] = { 5180, 5260, 5550, 5580, 5660, 5745, 5955, 6035,
+ const int allowed[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955, 6035,
6115, 6195, 6275, 6355, 6435, 6515, 6595, 6675,
6755, 6835, 6915, 6995 };
unsigned int i;
for (i = 0; i < ARRAY_SIZE(allowed); i++)
if (chan->freq == allowed[i])
return 1;
return 0;
}
static int acs_usable_bw160_chan(const struct hostapd_channel_data *chan)
{
const int allowed[] = { 5180, 5500, 5955, 6115, 6275, 6435, 6595, 6755,
6915 };
unsigned int i;
for (i = 0; i < ARRAY_SIZE(allowed); i++)
if (chan->freq == allowed[i])
return 1;
return 0;
}
static int acs_survey_is_sufficient(struct freq_survey *survey)
{
if (!(survey->filled & SURVEY_HAS_NF)) {
wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
return 0;
}
if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
return 0;
}
if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
!(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
wpa_printf(MSG_INFO,
"ACS: Survey is missing RX and busy time (at least one is required)");
return 0;
}
return 1;
}
static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
{
struct freq_survey *survey;
int ret = -1;
dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
{
if (acs_survey_is_sufficient(survey)) {
ret = 1;
break;
}
ret = 0;
}
if (ret == -1)
ret = 1; /* no survey list entries */
if (!ret) {
wpa_printf(MSG_INFO,
"ACS: Channel %d has insufficient survey data",
chan->chan);
}
return ret;
}
static int acs_surveys_are_sufficient_mode(struct hostapd_hw_modes *mode)
{
int i;
struct hostapd_channel_data *chan;
for (i = 0; i < mode->num_channels; i++) {
chan = &mode->channels[i];
if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
acs_survey_list_is_sufficient(chan))
return 1;
}
return 0;
}
static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
{
int i;
struct hostapd_hw_modes *mode;
for (i = 0; i < iface->num_hw_features; i++) {
mode = &iface->hw_features[i];
if (!hostapd_hw_skip_mode(iface, mode) &&
acs_surveys_are_sufficient_mode(mode))
return 1;
}
return 0;
}
static int acs_usable_chan(struct hostapd_channel_data *chan)
{
return !dl_list_empty(&chan->survey_list) &&
!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
acs_survey_list_is_sufficient(chan);
}
static int is_in_chanlist(struct hostapd_iface *iface,
struct hostapd_channel_data *chan)
{
if (!iface->conf->acs_ch_list.num)
return 1;
return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
}
static int is_in_freqlist(struct hostapd_iface *iface,
struct hostapd_channel_data *chan)
{
if (!iface->conf->acs_freq_list.num)
return 1;
return freq_range_list_includes(&iface->conf->acs_freq_list,
chan->freq);
}
static void acs_survey_mode_interference_factor(
struct hostapd_iface *iface, struct hostapd_hw_modes *mode)
{
int i;
struct hostapd_channel_data *chan;
for (i = 0; i < mode->num_channels; i++) {
chan = &mode->channels[i];
if (!acs_usable_chan(chan))
continue;
if (!is_in_chanlist(iface, chan))
continue;
if (!is_in_freqlist(iface, chan))
continue;
wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
chan->chan, chan->freq);
acs_survey_chan_interference_factor(iface, chan);
wpa_printf(MSG_DEBUG, "ACS: * interference factor average: %Lg",
chan->interference_factor);
}
}
static void acs_survey_all_chans_interference_factor(
struct hostapd_iface *iface)
{
int i;
struct hostapd_hw_modes *mode;
for (i = 0; i < iface->num_hw_features; i++) {
mode = &iface->hw_features[i];
if (!hostapd_hw_skip_mode(iface, mode))
acs_survey_mode_interference_factor(iface, mode);
}
}
static struct hostapd_channel_data *
acs_find_chan_mode(struct hostapd_hw_modes *mode, int freq)
{
struct hostapd_channel_data *chan;
int i;
for (i = 0; i < mode->num_channels; i++) {
chan = &mode->channels[i];
if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
if (chan->freq == freq)
return chan;
}
return NULL;
}
static struct hostapd_channel_data *
acs_find_chan(struct hostapd_iface *iface, int freq)
{
int i;
struct hostapd_hw_modes *mode;
struct hostapd_channel_data *chan;
for (i = 0; i < iface->num_hw_features; i++) {
mode = &iface->hw_features[i];
if (!hostapd_hw_skip_mode(iface, mode)) {
chan = acs_find_chan_mode(mode, freq);
if (chan)
return chan;
}
}
return NULL;
}
static int is_24ghz_mode(enum hostapd_hw_mode mode)
{
return mode == HOSTAPD_MODE_IEEE80211B ||
mode == HOSTAPD_MODE_IEEE80211G;
}
static int is_common_24ghz_chan(int chan)
{
return chan == 1 || chan == 6 || chan == 11;
}
#ifndef ACS_ADJ_WEIGHT
#define ACS_ADJ_WEIGHT 0.85
#endif /* ACS_ADJ_WEIGHT */
#ifndef ACS_NEXT_ADJ_WEIGHT
#define ACS_NEXT_ADJ_WEIGHT 0.55
#endif /* ACS_NEXT_ADJ_WEIGHT */
#ifndef ACS_24GHZ_PREFER_1_6_11
/*
* Select commonly used channels 1, 6, 11 by default even if a neighboring
* channel has a smaller interference factor as long as it is not better by more
* than this multiplier.
*/
#define ACS_24GHZ_PREFER_1_6_11 0.8
#endif /* ACS_24GHZ_PREFER_1_6_11 */
static void
acs_find_ideal_chan_mode(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode,
int n_chans, u32 bw,
struct hostapd_channel_data **rand_chan,
struct hostapd_channel_data **ideal_chan,
long double *ideal_factor)
{
struct hostapd_channel_data *chan, *adj_chan = NULL;
long double factor;
int i, j;
unsigned int k;
for (i = 0; i < mode->num_channels; i++) {
double total_weight;
struct acs_bias *bias, tmp_bias;
chan = &mode->channels[i];
/* Since in the current ACS implementation the first channel is
* always a primary channel, skip channels not available as
* primary until more sophisticated channel selection is
* implemented. */
if (!chan_pri_allowed(chan))
continue;
if (!is_in_chanlist(iface, chan))
continue;
if (!is_in_freqlist(iface, chan))
continue;
if (!chan_bw_allowed(chan, bw, 1, 1)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: BW %u is not supported",
chan->chan, bw);
continue;
}
/* HT40 on 5 GHz has a limited set of primary channels as per
* 11n Annex J */
if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
((iface->conf->ieee80211n &&
iface->conf->secondary_channel) ||
is_6ghz_freq(chan->freq)) &&
!acs_usable_bw40_chan(chan)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 40 MHz bandwidth",
chan->chan);
continue;
}
if (mode->mode == HOSTAPD_MODE_IEEE80211A &&
(iface->conf->ieee80211ac || iface->conf->ieee80211ax)) {
if (hostapd_get_oper_chwidth(iface->conf) ==
CHANWIDTH_80MHZ &&
!acs_usable_bw80_chan(chan)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 80 MHz bandwidth",
chan->chan);
continue;
}
if (hostapd_get_oper_chwidth(iface->conf) ==
CHANWIDTH_160MHZ &&
!acs_usable_bw160_chan(chan)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: not allowed as primary channel for 160 MHz bandwidth",
chan->chan);
continue;
}
}
factor = 0;
if (acs_usable_chan(chan))
factor = chan->interference_factor;
total_weight = 1;
for (j = 1; j < n_chans; j++) {
adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
if (!adj_chan)
break;
if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
wpa_printf(MSG_DEBUG,
"ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
chan->chan, adj_chan->chan, bw);
break;
}
if (acs_usable_chan(adj_chan)) {
factor += adj_chan->interference_factor;
total_weight += 1;
}
}
if (j != n_chans) {
wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
chan->chan);
continue;
}
/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
* channel interference factor. */
if (is_24ghz_mode(mode->mode)) {
for (j = 0; j < n_chans; j++) {
adj_chan = acs_find_chan(iface, chan->freq +
(j * 20) - 5);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_ADJ_WEIGHT *
adj_chan->interference_factor;
total_weight += ACS_ADJ_WEIGHT;
}
adj_chan = acs_find_chan(iface, chan->freq +
(j * 20) - 10);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_NEXT_ADJ_WEIGHT *
adj_chan->interference_factor;
total_weight += ACS_NEXT_ADJ_WEIGHT;
}
adj_chan = acs_find_chan(iface, chan->freq +
(j * 20) + 5);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_ADJ_WEIGHT *
adj_chan->interference_factor;
total_weight += ACS_ADJ_WEIGHT;
}
adj_chan = acs_find_chan(iface, chan->freq +
(j * 20) + 10);
if (adj_chan && acs_usable_chan(adj_chan)) {
factor += ACS_NEXT_ADJ_WEIGHT *
adj_chan->interference_factor;
total_weight += ACS_NEXT_ADJ_WEIGHT;
}
}
}
factor /= total_weight;
bias = NULL;
if (iface->conf->acs_chan_bias) {
for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
bias = &iface->conf->acs_chan_bias[k];
if (bias->channel == chan->chan)
break;
bias = NULL;
}
} else if (is_24ghz_mode(mode->mode) &&
is_common_24ghz_chan(chan->chan)) {
tmp_bias.channel = chan->chan;
tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
bias = &tmp_bias;
}
if (bias) {
factor *= bias->bias;
wpa_printf(MSG_DEBUG,
"ACS: * channel %d: total interference = %Lg (%f bias)",
chan->chan, factor, bias->bias);
} else {
wpa_printf(MSG_DEBUG,
"ACS: * channel %d: total interference = %Lg",
chan->chan, factor);
}
if (acs_usable_chan(chan) &&
(!*ideal_chan || factor < *ideal_factor)) {
*ideal_factor = factor;
*ideal_chan = chan;
}
/* This channel would at least be usable */
if (!(*rand_chan))
*rand_chan = chan;
}
}
/*
* At this point it's assumed chan->interference_factor has been computed.
* This function should be reusable regardless of interference computation
* option (survey, BSS, spectral, ...). chan->interference factor must be
* summable (i.e., must be always greater than zero).
*/
static struct hostapd_channel_data *
acs_find_ideal_chan(struct hostapd_iface *iface)
{
struct hostapd_channel_data *ideal_chan = NULL,
*rand_chan = NULL;
long double ideal_factor = 0;
int i;
int n_chans = 1;
u32 bw;
struct hostapd_hw_modes *mode;
if (is_6ghz_op_class(iface->conf->op_class)) {
bw = op_class_to_bandwidth(iface->conf->op_class);
n_chans = bw / 20;
goto bw_selected;
}
/* TODO: HT40- support */
if (iface->conf->ieee80211n &&
iface->conf->secondary_channel == -1) {
wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
return NULL;
}
if (iface->conf->ieee80211n &&
iface->conf->secondary_channel)
n_chans = 2;
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CHANWIDTH_80MHZ:
n_chans = 4;
break;
case CHANWIDTH_160MHZ:
n_chans = 8;
break;
}
}
bw = num_chan_to_bw(n_chans);
bw_selected:
/* TODO: VHT/HE80+80. Update acs_adjust_center_freq() too. */
wpa_printf(MSG_DEBUG,
"ACS: Survey analysis for selected bandwidth %d MHz", bw);
for (i = 0; i < iface->num_hw_features; i++) {
mode = &iface->hw_features[i];
if (!hostapd_hw_skip_mode(iface, mode))
acs_find_ideal_chan_mode(iface, mode, n_chans, bw,
&rand_chan, &ideal_chan,
&ideal_factor);
}
if (ideal_chan) {
wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
ideal_chan->chan, ideal_chan->freq, ideal_factor);
return ideal_chan;
}
return rand_chan;
}
static void acs_adjust_center_freq(struct hostapd_iface *iface)
{
int offset;
wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
switch (hostapd_get_oper_chwidth(iface->conf)) {
case CHANWIDTH_USE_HT:
offset = 2 * iface->conf->secondary_channel;
break;
case CHANWIDTH_80MHZ:
offset = 6;
break;
case CHANWIDTH_160MHZ:
offset = 14;
break;
default:
/* TODO: How can this be calculated? Adjust
* acs_find_ideal_chan() */
wpa_printf(MSG_INFO,
"ACS: Only VHT20/40/80/160 is supported now");
return;
}
hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
iface->conf->channel + offset);
}
static int acs_study_survey_based(struct hostapd_iface *iface)
{
wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
if (!iface->chans_surveyed) {
wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
return -1;
}
if (!acs_surveys_are_sufficient(iface)) {
wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
return -1;
}
acs_survey_all_chans_interference_factor(iface);
return 0;
}
static int acs_study_options(struct hostapd_iface *iface)
{
if (acs_study_survey_based(iface) == 0)
return 0;
/* TODO: If no surveys are available/sufficient this is a good
* place to fallback to BSS-based ACS */
return -1;
}
static void acs_study(struct hostapd_iface *iface)
{
struct hostapd_channel_data *ideal_chan;
int err;
err = acs_study_options(iface);
if (err < 0) {
wpa_printf(MSG_ERROR, "ACS: All study options have failed");
goto fail;
}
ideal_chan = acs_find_ideal_chan(iface);
if (!ideal_chan) {
wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
err = -1;
goto fail;
}
iface->conf->channel = ideal_chan->chan;
iface->freq = ideal_chan->freq;
if (iface->conf->ieee80211ac || iface->conf->ieee80211ax)
acs_adjust_center_freq(iface);
err = 0;
fail:
/*
* hostapd_setup_interface_complete() will return -1 on failure,
* 0 on success and 0 is HOSTAPD_CHAN_VALID :)
*/
if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
acs_cleanup(iface);
return;
}
/* This can possibly happen if channel parameters (secondary
* channel, center frequencies) are misconfigured */
wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
acs_fail(iface);
}
static void acs_scan_complete(struct hostapd_iface *iface)
{
int err;
iface->scan_cb = NULL;
wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
iface->conf->acs_num_scans);
err = hostapd_drv_get_survey(iface->bss[0], 0);
if (err) {
wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
goto fail;
}
if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
err = acs_request_scan(iface);
if (err) {
wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
goto fail;
}
return;
}
acs_study(iface);
return;
fail:
hostapd_acs_completed(iface, 1);
acs_fail(iface);
}
static int * acs_request_scan_add_freqs(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode,
int *freq)
{
struct hostapd_channel_data *chan;
int i;
for (i = 0; i < mode->num_channels; i++) {
chan = &mode->channels[i];
if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
if (!is_in_chanlist(iface, chan))
continue;
if (!is_in_freqlist(iface, chan))
continue;
*freq++ = chan->freq;
}
return freq;
}
static int acs_request_scan(struct hostapd_iface *iface)
{
struct wpa_driver_scan_params params;
int i, *freq;
int num_channels;
struct hostapd_hw_modes *mode;
os_memset(&params, 0, sizeof(params));
num_channels = 0;
for (i = 0; i < iface->num_hw_features; i++) {
mode = &iface->hw_features[i];
if (!hostapd_hw_skip_mode(iface, mode))
num_channels += mode->num_channels;
}
params.freqs = os_calloc(num_channels + 1, sizeof(params.freqs[0]));
if (params.freqs == NULL)
return -1;
freq = params.freqs;
for (i = 0; i < iface->num_hw_features; i++) {
mode = &iface->hw_features[i];
if (!hostapd_hw_skip_mode(iface, mode))
freq = acs_request_scan_add_freqs(iface, mode, freq);
}
*freq = 0;
if (params.freqs == freq) {
wpa_printf(MSG_ERROR, "ACS: No available channels found");
os_free(params.freqs);
return -1;
}
iface->scan_cb = acs_scan_complete;
wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
iface->acs_num_completed_scans + 1,
iface->conf->acs_num_scans);
if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
acs_cleanup(iface);
os_free(params.freqs);
return -1;
}
os_free(params.freqs);
return 0;
}
enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
{
wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
wpa_printf(MSG_INFO, "ACS: Offloading to driver");
if (hostapd_drv_do_acs(iface->bss[0]))
return HOSTAPD_CHAN_INVALID;
return HOSTAPD_CHAN_ACS;
}
if (!iface->current_mode &&
iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)
return HOSTAPD_CHAN_INVALID;
acs_cleanup(iface);
if (acs_request_scan(iface) < 0)
return HOSTAPD_CHAN_INVALID;
hostapd_set_state(iface, HAPD_IFACE_ACS);
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
return HOSTAPD_CHAN_ACS;
}
diff --git a/contrib/wpa/src/ap/ap_config.c b/contrib/wpa/src/ap/ap_config.c
index 7b6d54c35fc2..86b6e097cf89 100644
--- a/contrib/wpa/src/ap/ap_config.c
+++ b/contrib/wpa/src/ap/ap_config.c
@@ -1,1639 +1,1648 @@
/*
* hostapd / Configuration helper functions
* Copyright (c) 2003-2014, 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"
#include "utils/common.h"
#include "crypto/sha1.h"
#include "crypto/tls.h"
#include "radius/radius_client.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_1x_defs.h"
#include "common/eapol_common.h"
#include "common/dhcp.h"
#include "common/sae.h"
#include "eap_common/eap_wsc_common.h"
#include "eap_server/eap.h"
#include "wpa_auth.h"
#include "sta_info.h"
#include "airtime_policy.h"
#include "ap_config.h"
static void hostapd_config_free_vlan(struct hostapd_bss_config *bss)
{
struct hostapd_vlan *vlan, *prev;
vlan = bss->vlan;
prev = NULL;
while (vlan) {
prev = vlan;
vlan = vlan->next;
os_free(prev);
}
bss->vlan = NULL;
}
#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES
#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0
#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */
void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
{
dl_list_init(&bss->anqp_elem);
bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
bss->logger_syslog = (unsigned int) -1;
bss->logger_stdout = (unsigned int) -1;
#ifdef CONFIG_WEP
bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED;
bss->wep_rekeying_period = 300;
/* use key0 in individual key and key1 in broadcast key */
bss->broadcast_key_idx_min = 1;
bss->broadcast_key_idx_max = 2;
#else /* CONFIG_WEP */
bss->auth_algs = WPA_AUTH_ALG_OPEN;
#endif /* CONFIG_WEP */
bss->eap_reauth_period = 3600;
bss->wpa_group_rekey = 600;
bss->wpa_gmk_rekey = 86400;
bss->wpa_deny_ptk0_rekey = PTK0_REKEY_ALLOW_ALWAYS;
bss->wpa_group_update_count = 4;
bss->wpa_pairwise_update_count = 4;
bss->wpa_disable_eapol_key_retries =
DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES;
bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
#ifdef CONFIG_NO_TKIP
bss->wpa_pairwise = WPA_CIPHER_CCMP;
bss->wpa_group = WPA_CIPHER_CCMP;
#else /* CONFIG_NO_TKIP */
bss->wpa_pairwise = WPA_CIPHER_TKIP;
bss->wpa_group = WPA_CIPHER_TKIP;
#endif /* CONFIG_NO_TKIP */
bss->rsn_pairwise = 0;
bss->max_num_sta = MAX_STA_COUNT;
bss->dtim_period = 2;
bss->radius_server_auth_port = 1812;
bss->eap_sim_db_timeout = 1;
bss->eap_sim_id = 3;
bss->ap_max_inactivity = AP_MAX_INACTIVITY;
bss->eapol_version = EAPOL_VERSION;
bss->max_listen_interval = 65535;
bss->pwd_group = 19; /* ECC: GF(p=256) */
bss->assoc_sa_query_max_timeout = 1000;
bss->assoc_sa_query_retry_timeout = 201;
bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
#ifdef EAP_SERVER_FAST
/* both anonymous and authenticated provisioning */
bss->eap_fast_prov = 3;
bss->pac_key_lifetime = 7 * 24 * 60 * 60;
bss->pac_key_refresh_time = 1 * 24 * 60 * 60;
#endif /* EAP_SERVER_FAST */
/* Set to -1 as defaults depends on HT in setup */
bss->wmm_enabled = -1;
#ifdef CONFIG_IEEE80211R_AP
bss->ft_over_ds = 1;
bss->rkh_pos_timeout = 86400;
bss->rkh_neg_timeout = 60;
bss->rkh_pull_timeout = 1000;
bss->rkh_pull_retries = 4;
bss->r0_key_lifetime = 1209600;
#endif /* CONFIG_IEEE80211R_AP */
bss->radius_das_time_window = 300;
bss->anti_clogging_threshold = 5;
bss->sae_sync = 5;
bss->gas_frag_limit = 1400;
#ifdef CONFIG_FILS
dl_list_init(&bss->fils_realms);
bss->fils_hlp_wait_time = 30;
bss->dhcp_server_port = DHCP_SERVER_PORT;
bss->dhcp_relay_port = DHCP_SERVER_PORT;
bss->fils_discovery_min_int = 20;
#endif /* CONFIG_FILS */
bss->broadcast_deauth = 1;
#ifdef CONFIG_MBO
bss->mbo_cell_data_conn_pref = -1;
#endif /* CONFIG_MBO */
/* Disable TLS v1.3 by default for now to avoid interoperability issue.
* This can be enabled by default once the implementation has been fully
* completed and tested with other implementations. */
bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3;
bss->max_auth_rounds = 100;
bss->max_auth_rounds_short = 50;
bss->send_probe_response = 1;
#ifdef CONFIG_HS20
bss->hs20_release = (HS20_VERSION >> 4) + 1;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MACSEC
bss->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
bss->macsec_port = 1;
#endif /* CONFIG_MACSEC */
/* Default to strict CRL checking. */
bss->check_crl_strict = 1;
#ifdef CONFIG_TESTING_OPTIONS
bss->sae_commit_status = -1;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_PASN
/* comeback after 10 TUs */
bss->pasn_comeback_after = 10;
#endif /* CONFIG_PASN */
}
struct hostapd_config * hostapd_config_defaults(void)
{
#define ecw2cw(ecw) ((1 << (ecw)) - 1)
struct hostapd_config *conf;
struct hostapd_bss_config *bss;
const int aCWmin = 4, aCWmax = 10;
const struct hostapd_wmm_ac_params ac_bk =
{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
const struct hostapd_wmm_ac_params ac_be =
{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
{ aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
{ aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
const struct hostapd_tx_queue_params txq_bk =
{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
const struct hostapd_tx_queue_params txq_be =
{ 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0};
const struct hostapd_tx_queue_params txq_vi =
{ 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30};
const struct hostapd_tx_queue_params txq_vo =
{ 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
(ecw2cw(aCWmin) + 1) / 2 - 1, 15};
#undef ecw2cw
conf = os_zalloc(sizeof(*conf));
bss = os_zalloc(sizeof(*bss));
if (conf == NULL || bss == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate memory for "
"configuration data.");
os_free(conf);
os_free(bss);
return NULL;
}
conf->bss = os_calloc(1, sizeof(struct hostapd_bss_config *));
if (conf->bss == NULL) {
os_free(conf);
os_free(bss);
return NULL;
}
conf->bss[0] = bss;
bss->radius = os_zalloc(sizeof(*bss->radius));
if (bss->radius == NULL) {
os_free(conf->bss);
os_free(conf);
os_free(bss);
return NULL;
}
hostapd_config_defaults_bss(bss);
conf->num_bss = 1;
conf->beacon_int = 100;
conf->rts_threshold = -2; /* use driver default: 2347 */
conf->fragm_threshold = -2; /* user driver default: 2346 */
/* Set to invalid value means do not add Power Constraint IE */
conf->local_pwr_constraint = -1;
conf->wmm_ac_params[0] = ac_be;
conf->wmm_ac_params[1] = ac_bk;
conf->wmm_ac_params[2] = ac_vi;
conf->wmm_ac_params[3] = ac_vo;
conf->tx_queue[0] = txq_vo;
conf->tx_queue[1] = txq_vi;
conf->tx_queue[2] = txq_be;
conf->tx_queue[3] = txq_bk;
conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
conf->ap_table_max_size = 255;
conf->ap_table_expiration_time = 60;
conf->track_sta_max_age = 180;
#ifdef CONFIG_TESTING_OPTIONS
conf->ignore_probe_probability = 0.0;
conf->ignore_auth_probability = 0.0;
conf->ignore_assoc_probability = 0.0;
conf->ignore_reassoc_probability = 0.0;
conf->corrupt_gtk_rekey_mic_probability = 0.0;
conf->ecsa_ie_only = 0;
#endif /* CONFIG_TESTING_OPTIONS */
conf->acs = 0;
conf->acs_ch_list.num = 0;
#ifdef CONFIG_ACS
conf->acs_num_scans = 5;
#endif /* CONFIG_ACS */
#ifdef CONFIG_IEEE80211AX
conf->he_op.he_rts_threshold = HE_OPERATION_RTS_THRESHOLD_MASK >>
HE_OPERATION_RTS_THRESHOLD_OFFSET;
/* Set default basic MCS/NSS set to single stream MCS 0-7 */
conf->he_op.he_basic_mcs_nss_set = 0xfffc;
conf->he_op.he_bss_color_disabled = 1;
conf->he_op.he_bss_color_partial = 0;
- conf->he_op.he_bss_color = 1;
+ conf->he_op.he_bss_color = os_random() % 63 + 1;
conf->he_op.he_twt_responder = 1;
conf->he_6ghz_max_mpdu = 2;
conf->he_6ghz_max_ampdu_len_exp = 7;
conf->he_6ghz_rx_ant_pat = 1;
conf->he_6ghz_tx_ant_pat = 1;
#endif /* CONFIG_IEEE80211AX */
/* The third octet of the country string uses an ASCII space character
* by default to indicate that the regulations encompass all
* environments for the current frequency band in the country. */
conf->country[2] = ' ';
conf->rssi_reject_assoc_rssi = 0;
conf->rssi_reject_assoc_timeout = 30;
#ifdef CONFIG_AIRTIME_POLICY
conf->airtime_update_interval = AIRTIME_DEFAULT_UPDATE_INTERVAL;
#endif /* CONFIG_AIRTIME_POLICY */
return conf;
}
int hostapd_mac_comp(const void *a, const void *b)
{
return os_memcmp(a, b, sizeof(macaddr));
}
static int hostapd_config_read_wpa_psk(const char *fname,
struct hostapd_ssid *ssid)
{
FILE *f;
char buf[128], *pos;
const char *keyid;
char *context;
char *context2;
char *token;
char *name;
char *value;
int line = 0, ret = 0, len, ok;
u8 addr[ETH_ALEN];
struct hostapd_wpa_psk *psk;
if (!fname)
return 0;
f = fopen(fname, "r");
if (!f) {
wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", fname);
return -1;
}
while (fgets(buf, sizeof(buf), f)) {
int vlan_id = 0;
int wps = 0;
line++;
if (buf[0] == '#')
continue;
pos = buf;
while (*pos != '\0') {
if (*pos == '\n') {
*pos = '\0';
break;
}
pos++;
}
if (buf[0] == '\0')
continue;
context = NULL;
keyid = NULL;
while ((token = str_token(buf, " ", &context))) {
if (!os_strchr(token, '='))
break;
context2 = NULL;
name = str_token(token, "=", &context2);
if (!name)
break;
value = str_token(token, "", &context2);
if (!value)
value = "";
if (!os_strcmp(name, "keyid")) {
keyid = value;
} else if (!os_strcmp(name, "wps")) {
wps = atoi(value);
} else if (!os_strcmp(name, "vlanid")) {
vlan_id = atoi(value);
} else {
wpa_printf(MSG_ERROR,
"Unrecognized '%s=%s' on line %d in '%s'",
name, value, line, fname);
ret = -1;
break;
}
}
if (ret == -1)
break;
if (!token)
token = "";
if (hwaddr_aton(token, addr)) {
wpa_printf(MSG_ERROR,
"Invalid MAC address '%s' on line %d in '%s'",
token, line, fname);
ret = -1;
break;
}
psk = os_zalloc(sizeof(*psk));
if (psk == NULL) {
wpa_printf(MSG_ERROR, "WPA PSK allocation failed");
ret = -1;
break;
}
psk->vlan_id = vlan_id;
if (is_zero_ether_addr(addr))
psk->group = 1;
else
os_memcpy(psk->addr, addr, ETH_ALEN);
pos = str_token(buf, "", &context);
if (!pos) {
wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'",
line, fname);
os_free(psk);
ret = -1;
break;
}
ok = 0;
len = os_strlen(pos);
if (len == 2 * PMK_LEN &&
hexstr2bin(pos, psk->psk, PMK_LEN) == 0)
ok = 1;
else if (len >= 8 && len < 64 &&
pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len,
4096, psk->psk, PMK_LEN) == 0)
ok = 1;
if (!ok) {
wpa_printf(MSG_ERROR,
"Invalid PSK '%s' on line %d in '%s'",
pos, line, fname);
os_free(psk);
ret = -1;
break;
}
if (keyid) {
len = os_strlcpy(psk->keyid, keyid, sizeof(psk->keyid));
if ((size_t) len >= sizeof(psk->keyid)) {
wpa_printf(MSG_ERROR,
"PSK keyid too long on line %d in '%s'",
line, fname);
os_free(psk);
ret = -1;
break;
}
}
psk->wps = wps;
psk->next = ssid->wpa_psk;
ssid->wpa_psk = psk;
}
fclose(f);
return ret;
}
static int hostapd_derive_psk(struct hostapd_ssid *ssid)
{
ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
if (ssid->wpa_psk == NULL) {
wpa_printf(MSG_ERROR, "Unable to alloc space for PSK");
return -1;
}
wpa_hexdump_ascii(MSG_DEBUG, "SSID",
(u8 *) ssid->ssid, ssid->ssid_len);
wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)",
(u8 *) ssid->wpa_passphrase,
os_strlen(ssid->wpa_passphrase));
pbkdf2_sha1(ssid->wpa_passphrase,
ssid->ssid, ssid->ssid_len,
4096, ssid->wpa_psk->psk, PMK_LEN);
wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)",
ssid->wpa_psk->psk, PMK_LEN);
return 0;
}
int hostapd_setup_sae_pt(struct hostapd_bss_config *conf)
{
#ifdef CONFIG_SAE
struct hostapd_ssid *ssid = &conf->ssid;
struct sae_password_entry *pw;
if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf) &&
!hostapd_sae_pk_in_use(conf)) ||
conf->sae_pwe == 3 ||
!wpa_key_mgmt_sae(conf->wpa_key_mgmt))
return 0; /* PT not needed */
sae_deinit_pt(ssid->pt);
ssid->pt = NULL;
if (ssid->wpa_passphrase) {
ssid->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
ssid->ssid_len,
(const u8 *) ssid->wpa_passphrase,
os_strlen(ssid->wpa_passphrase),
NULL);
if (!ssid->pt)
return -1;
}
for (pw = conf->sae_passwords; pw; pw = pw->next) {
sae_deinit_pt(pw->pt);
pw->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
ssid->ssid_len,
(const u8 *) pw->password,
os_strlen(pw->password),
pw->identifier);
if (!pw->pt)
return -1;
}
#endif /* CONFIG_SAE */
return 0;
}
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
{
struct hostapd_ssid *ssid = &conf->ssid;
if (hostapd_setup_sae_pt(conf) < 0)
return -1;
if (ssid->wpa_passphrase != NULL) {
if (ssid->wpa_psk != NULL) {
wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK "
"instead of passphrase");
} else {
wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on "
"passphrase");
if (hostapd_derive_psk(ssid) < 0)
return -1;
}
ssid->wpa_psk->group = 1;
}
return hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid);
}
static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
int num_servers)
{
int i;
for (i = 0; i < num_servers; i++) {
os_free(servers[i].shared_secret);
}
os_free(servers);
}
struct hostapd_radius_attr *
hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type)
{
for (; attr; attr = attr->next) {
if (attr->type == type)
return attr;
}
return NULL;
}
struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value)
{
const char *pos;
char syntax;
struct hostapd_radius_attr *attr;
size_t len;
attr = os_zalloc(sizeof(*attr));
if (!attr)
return NULL;
attr->type = atoi(value);
pos = os_strchr(value, ':');
if (!pos) {
attr->val = wpabuf_alloc(1);
if (!attr->val) {
os_free(attr);
return NULL;
}
wpabuf_put_u8(attr->val, 0);
return attr;
}
pos++;
if (pos[0] == '\0' || pos[1] != ':') {
os_free(attr);
return NULL;
}
syntax = *pos++;
pos++;
switch (syntax) {
case 's':
attr->val = wpabuf_alloc_copy(pos, os_strlen(pos));
break;
case 'x':
len = os_strlen(pos);
if (len & 1)
break;
len /= 2;
attr->val = wpabuf_alloc(len);
if (!attr->val)
break;
if (hexstr2bin(pos, wpabuf_put(attr->val, len), len) < 0) {
wpabuf_free(attr->val);
os_free(attr);
return NULL;
}
break;
case 'd':
attr->val = wpabuf_alloc(4);
if (attr->val)
wpabuf_put_be32(attr->val, atoi(pos));
break;
default:
os_free(attr);
return NULL;
}
if (!attr->val) {
os_free(attr);
return NULL;
}
return attr;
}
void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
{
struct hostapd_radius_attr *prev;
while (attr) {
prev = attr;
attr = attr->next;
wpabuf_free(prev->val);
os_free(prev);
}
}
void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
{
hostapd_config_free_radius_attr(user->accept_attr);
os_free(user->identity);
bin_clear_free(user->password, user->password_len);
bin_clear_free(user->salt, user->salt_len);
os_free(user);
}
void hostapd_config_free_eap_users(struct hostapd_eap_user *user)
{
struct hostapd_eap_user *prev_user;
while (user) {
prev_user = user;
user = user->next;
hostapd_config_free_eap_user(prev_user);
}
}
#ifdef CONFIG_WEP
static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
{
int i;
for (i = 0; i < NUM_WEP_KEYS; i++) {
bin_clear_free(keys->key[i], keys->len[i]);
keys->key[i] = NULL;
}
}
#endif /* CONFIG_WEP */
void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l)
{
struct hostapd_wpa_psk *psk, *tmp;
for (psk = *l; psk;) {
tmp = psk;
psk = psk->next;
bin_clear_free(tmp, sizeof(*tmp));
}
*l = NULL;
}
static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
{
struct anqp_element *elem;
while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element,
list))) {
dl_list_del(&elem->list);
wpabuf_free(elem->payload);
os_free(elem);
}
}
static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf)
{
#ifdef CONFIG_FILS
struct fils_realm *realm;
while ((realm = dl_list_first(&conf->fils_realms, struct fils_realm,
list))) {
dl_list_del(&realm->list);
os_free(realm);
}
#endif /* CONFIG_FILS */
}
static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf)
{
struct sae_password_entry *pw, *tmp;
pw = conf->sae_passwords;
conf->sae_passwords = NULL;
while (pw) {
tmp = pw;
pw = pw->next;
str_clear_free(tmp->password);
os_free(tmp->identifier);
#ifdef CONFIG_SAE
sae_deinit_pt(tmp->pt);
#endif /* CONFIG_SAE */
#ifdef CONFIG_SAE_PK
sae_deinit_pk(tmp->pk);
#endif /* CONFIG_SAE_PK */
os_free(tmp);
}
}
#ifdef CONFIG_DPP2
static void hostapd_dpp_controller_conf_free(struct dpp_controller_conf *conf)
{
struct dpp_controller_conf *prev;
while (conf) {
prev = conf;
conf = conf->next;
os_free(prev);
}
}
#endif /* CONFIG_DPP2 */
void hostapd_config_free_bss(struct hostapd_bss_config *conf)
{
#if defined(CONFIG_WPS) || defined(CONFIG_HS20)
size_t i;
#endif
if (conf == NULL)
return;
hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
str_clear_free(conf->ssid.wpa_passphrase);
os_free(conf->ssid.wpa_psk_file);
#ifdef CONFIG_WEP
hostapd_config_free_wep(&conf->ssid.wep);
#endif /* CONFIG_WEP */
#ifdef CONFIG_FULL_DYNAMIC_VLAN
os_free(conf->ssid.vlan_tagged_interface);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
#ifdef CONFIG_SAE
sae_deinit_pt(conf->ssid.pt);
#endif /* CONFIG_SAE */
hostapd_config_free_eap_users(conf->eap_user);
os_free(conf->eap_user_sqlite);
os_free(conf->eap_req_id_text);
os_free(conf->erp_domain);
os_free(conf->accept_mac);
os_free(conf->deny_mac);
os_free(conf->nas_identifier);
if (conf->radius) {
hostapd_config_free_radius(conf->radius->auth_servers,
conf->radius->num_auth_servers);
hostapd_config_free_radius(conf->radius->acct_servers,
conf->radius->num_acct_servers);
os_free(conf->radius->force_client_dev);
}
hostapd_config_free_radius_attr(conf->radius_auth_req_attr);
hostapd_config_free_radius_attr(conf->radius_acct_req_attr);
os_free(conf->radius_req_attr_sqlite);
os_free(conf->rsn_preauth_interfaces);
os_free(conf->ctrl_interface);
os_free(conf->ca_cert);
os_free(conf->server_cert);
os_free(conf->server_cert2);
os_free(conf->private_key);
os_free(conf->private_key2);
os_free(conf->private_key_passwd);
os_free(conf->private_key_passwd2);
os_free(conf->check_cert_subject);
os_free(conf->ocsp_stapling_response);
os_free(conf->ocsp_stapling_response_multi);
os_free(conf->dh_file);
os_free(conf->openssl_ciphers);
os_free(conf->openssl_ecdh_curves);
os_free(conf->pac_opaque_encr_key);
os_free(conf->eap_fast_a_id);
os_free(conf->eap_fast_a_id_info);
os_free(conf->eap_sim_db);
os_free(conf->radius_server_clients);
os_free(conf->radius);
os_free(conf->radius_das_shared_secret);
hostapd_config_free_vlan(conf);
os_free(conf->time_zone);
#ifdef CONFIG_IEEE80211R_AP
{
struct ft_remote_r0kh *r0kh, *r0kh_prev;
struct ft_remote_r1kh *r1kh, *r1kh_prev;
r0kh = conf->r0kh_list;
conf->r0kh_list = NULL;
while (r0kh) {
r0kh_prev = r0kh;
r0kh = r0kh->next;
os_free(r0kh_prev);
}
r1kh = conf->r1kh_list;
conf->r1kh_list = NULL;
while (r1kh) {
r1kh_prev = r1kh;
r1kh = r1kh->next;
os_free(r1kh_prev);
}
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_WPS
os_free(conf->wps_pin_requests);
os_free(conf->device_name);
os_free(conf->manufacturer);
os_free(conf->model_name);
os_free(conf->model_number);
os_free(conf->serial_number);
os_free(conf->config_methods);
os_free(conf->ap_pin);
os_free(conf->extra_cred);
os_free(conf->ap_settings);
hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk);
str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase);
os_free(conf->upnp_iface);
os_free(conf->friendly_name);
os_free(conf->manufacturer_url);
os_free(conf->model_description);
os_free(conf->model_url);
os_free(conf->upc);
for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
wpabuf_free(conf->wps_vendor_ext[i]);
wpabuf_free(conf->wps_application_ext);
wpabuf_free(conf->wps_nfc_dh_pubkey);
wpabuf_free(conf->wps_nfc_dh_privkey);
wpabuf_free(conf->wps_nfc_dev_pw);
#endif /* CONFIG_WPS */
os_free(conf->roaming_consortium);
os_free(conf->venue_name);
os_free(conf->venue_url);
os_free(conf->nai_realm_data);
os_free(conf->network_auth_type);
os_free(conf->anqp_3gpp_cell_net);
os_free(conf->domain_name);
hostapd_config_free_anqp_elem(conf);
#ifdef CONFIG_RADIUS_TEST
os_free(conf->dump_msk_file);
#endif /* CONFIG_RADIUS_TEST */
#ifdef CONFIG_HS20
os_free(conf->hs20_oper_friendly_name);
os_free(conf->hs20_wan_metrics);
os_free(conf->hs20_connection_capability);
os_free(conf->hs20_operating_class);
os_free(conf->hs20_icons);
if (conf->hs20_osu_providers) {
for (i = 0; i < conf->hs20_osu_providers_count; i++) {
struct hs20_osu_provider *p;
size_t j;
p = &conf->hs20_osu_providers[i];
os_free(p->friendly_name);
os_free(p->server_uri);
os_free(p->method_list);
for (j = 0; j < p->icons_count; j++)
os_free(p->icons[j]);
os_free(p->icons);
os_free(p->osu_nai);
os_free(p->osu_nai2);
os_free(p->service_desc);
}
os_free(conf->hs20_osu_providers);
}
if (conf->hs20_operator_icon) {
for (i = 0; i < conf->hs20_operator_icon_count; i++)
os_free(conf->hs20_operator_icon[i]);
os_free(conf->hs20_operator_icon);
}
os_free(conf->subscr_remediation_url);
os_free(conf->hs20_sim_provisioning_url);
os_free(conf->t_c_filename);
os_free(conf->t_c_server_url);
#endif /* CONFIG_HS20 */
wpabuf_free(conf->vendor_elements);
wpabuf_free(conf->assocresp_elements);
os_free(conf->sae_groups);
#ifdef CONFIG_OWE
os_free(conf->owe_groups);
#endif /* CONFIG_OWE */
os_free(conf->wowlan_triggers);
os_free(conf->server_id);
#ifdef CONFIG_TESTING_OPTIONS
wpabuf_free(conf->own_ie_override);
wpabuf_free(conf->sae_commit_override);
wpabuf_free(conf->rsne_override_eapol);
wpabuf_free(conf->rsnxe_override_eapol);
wpabuf_free(conf->rsne_override_ft);
wpabuf_free(conf->rsnxe_override_ft);
wpabuf_free(conf->gtk_rsc_override);
wpabuf_free(conf->igtk_rsc_override);
#endif /* CONFIG_TESTING_OPTIONS */
os_free(conf->no_probe_resp_if_seen_on);
os_free(conf->no_auth_if_seen_on);
hostapd_config_free_fils_realms(conf);
#ifdef CONFIG_DPP
os_free(conf->dpp_name);
os_free(conf->dpp_mud_url);
os_free(conf->dpp_connector);
wpabuf_free(conf->dpp_netaccesskey);
wpabuf_free(conf->dpp_csign);
#ifdef CONFIG_DPP2
hostapd_dpp_controller_conf_free(conf->dpp_controller);
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
hostapd_config_free_sae_passwords(conf);
#ifdef CONFIG_AIRTIME_POLICY
{
struct airtime_sta_weight *wt, *wt_prev;
wt = conf->airtime_weight_list;
conf->airtime_weight_list = NULL;
while (wt) {
wt_prev = wt;
wt = wt->next;
os_free(wt_prev);
}
}
#endif /* CONFIG_AIRTIME_POLICY */
#ifdef CONFIG_PASN
os_free(conf->pasn_groups);
#endif /* CONFIG_PASN */
os_free(conf);
}
/**
* hostapd_config_free - Free hostapd configuration
* @conf: Configuration data from hostapd_config_read().
*/
void hostapd_config_free(struct hostapd_config *conf)
{
size_t i;
if (conf == NULL)
return;
for (i = 0; i < conf->num_bss; i++)
hostapd_config_free_bss(conf->bss[i]);
os_free(conf->bss);
os_free(conf->supported_rates);
os_free(conf->basic_rates);
os_free(conf->acs_ch_list.range);
os_free(conf->acs_freq_list.range);
os_free(conf->driver_params);
#ifdef CONFIG_ACS
os_free(conf->acs_chan_bias);
#endif /* CONFIG_ACS */
wpabuf_free(conf->lci);
wpabuf_free(conf->civic);
os_free(conf);
}
/**
* hostapd_maclist_found - Find a MAC address from a list
* @list: MAC address list
* @num_entries: Number of addresses in the list
* @addr: Address to search for
* @vlan_id: Buffer for returning VLAN ID or %NULL if not needed
* Returns: 1 if address is in the list or 0 if not.
*
* Perform a binary search for given MAC address from a pre-sorted list.
*/
int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
const u8 *addr, struct vlan_description *vlan_id)
{
int start, end, middle, res;
start = 0;
end = num_entries - 1;
while (start <= end) {
middle = (start + end) / 2;
res = os_memcmp(list[middle].addr, addr, ETH_ALEN);
if (res == 0) {
if (vlan_id)
*vlan_id = list[middle].vlan_id;
return 1;
}
if (res < 0)
start = middle + 1;
else
end = middle - 1;
}
return 0;
}
int hostapd_rate_found(int *list, int rate)
{
int i;
if (list == NULL)
return 0;
for (i = 0; list[i] >= 0; i++)
if (list[i] == rate)
return 1;
return 0;
}
int hostapd_vlan_valid(struct hostapd_vlan *vlan,
struct vlan_description *vlan_desc)
{
struct hostapd_vlan *v = vlan;
int i;
if (!vlan_desc->notempty || vlan_desc->untagged < 0 ||
vlan_desc->untagged > MAX_VLAN_ID)
return 0;
for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
if (vlan_desc->tagged[i] < 0 ||
vlan_desc->tagged[i] > MAX_VLAN_ID)
return 0;
}
if (!vlan_desc->untagged && !vlan_desc->tagged[0])
return 0;
while (v) {
if (!vlan_compare(&v->vlan_desc, vlan_desc) ||
v->vlan_id == VLAN_ID_WILDCARD)
return 1;
v = v->next;
}
return 0;
}
const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
{
struct hostapd_vlan *v = vlan;
while (v) {
if (v->vlan_id == vlan_id)
return v->ifname;
v = v->next;
}
return NULL;
}
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *p2p_dev_addr,
const u8 *prev_psk, int *vlan_id)
{
struct hostapd_wpa_psk *psk;
int next_ok = prev_psk == NULL;
if (vlan_id)
*vlan_id = 0;
if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) {
wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
" p2p_dev_addr=" MACSTR " prev_psk=%p",
MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk);
addr = NULL; /* Use P2P Device Address for matching */
} else {
wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
" prev_psk=%p",
MAC2STR(addr), prev_psk);
}
for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
if (next_ok &&
(psk->group ||
(addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
(!addr && p2p_dev_addr &&
os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
0))) {
if (vlan_id)
*vlan_id = psk->vlan_id;
return psk->psk;
}
if (psk->psk == prev_psk)
next_ok = 1;
}
return NULL;
}
#ifdef CONFIG_SAE_PK
static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss)
{
struct sae_password_entry *pw;
bool res = false;
if (bss->ssid.wpa_passphrase &&
#ifdef CONFIG_TESTING_OPTIONS
!bss->sae_pk_password_check_skip &&
#endif /* CONFIG_TESTING_OPTIONS */
sae_pk_valid_password(bss->ssid.wpa_passphrase))
res = true;
for (pw = bss->sae_passwords; pw; pw = pw->next) {
if (!pw->pk &&
#ifdef CONFIG_TESTING_OPTIONS
!bss->sae_pk_password_check_skip &&
#endif /* CONFIG_TESTING_OPTIONS */
sae_pk_valid_password(pw->password))
return true;
if (bss->ssid.wpa_passphrase && res && pw->pk &&
os_strcmp(bss->ssid.wpa_passphrase, pw->password) == 0)
res = false;
}
return res;
}
#endif /* CONFIG_SAE_PK */
static bool hostapd_config_check_bss_6g(struct hostapd_bss_config *bss)
{
if (bss->wpa != WPA_PROTO_RSN) {
wpa_printf(MSG_ERROR,
"Pre-RSNA security methods are not allowed in 6 GHz");
return false;
}
if (bss->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED) {
wpa_printf(MSG_ERROR,
"Management frame protection is required in 6 GHz");
return false;
}
if (bss->wpa_key_mgmt & (WPA_KEY_MGMT_PSK |
WPA_KEY_MGMT_FT_PSK |
WPA_KEY_MGMT_PSK_SHA256)) {
wpa_printf(MSG_ERROR, "Invalid AKM suite for 6 GHz");
return false;
}
if (bss->rsn_pairwise & (WPA_CIPHER_WEP40 |
WPA_CIPHER_WEP104 |
WPA_CIPHER_TKIP)) {
wpa_printf(MSG_ERROR,
"Invalid pairwise cipher suite for 6 GHz");
return false;
}
if (bss->wpa_group & (WPA_CIPHER_WEP40 |
WPA_CIPHER_WEP104 |
WPA_CIPHER_TKIP)) {
wpa_printf(MSG_ERROR, "Invalid group cipher suite for 6 GHz");
return false;
}
return true;
}
static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
struct hostapd_config *conf,
int full_config)
{
if (full_config && is_6ghz_op_class(conf->op_class) &&
!hostapd_config_check_bss_6g(bss))
return -1;
if (full_config && bss->ieee802_1x && !bss->eap_server &&
!bss->radius->auth_servers) {
wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no "
"EAP authenticator configured).");
return -1;
}
#ifdef CONFIG_WEP
if (bss->wpa) {
int wep, i;
wep = bss->default_wep_key_len > 0 ||
bss->individual_wep_key_len > 0;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (bss->ssid.wep.keys_set) {
wep = 1;
break;
}
}
if (wep) {
wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported");
return -1;
}
}
#endif /* CONFIG_WEP */
if (full_config && bss->wpa &&
bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
"RADIUS checking (macaddr_acl=2) enabled.");
return -1;
}
if (full_config && bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
bss->ssid.wpa_psk_file == NULL &&
(bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
"is not configured.");
return -1;
}
if (full_config && !is_zero_ether_addr(bss->bssid)) {
size_t i;
for (i = 0; i < conf->num_bss; i++) {
if (conf->bss[i] != bss &&
(hostapd_mac_comp(conf->bss[i]->bssid,
bss->bssid) == 0)) {
wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR
" on interface '%s' and '%s'.",
MAC2STR(bss->bssid),
conf->bss[i]->iface, bss->iface);
return -1;
}
}
}
#ifdef CONFIG_IEEE80211R_AP
if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
(bss->nas_identifier == NULL ||
os_strlen(bss->nas_identifier) < 1 ||
os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) {
wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires "
"nas_identifier to be configured as a 1..48 octet "
"string");
return -1;
}
#endif /* CONFIG_IEEE80211R_AP */
if (full_config && conf->ieee80211n &&
conf->hw_mode == HOSTAPD_MODE_IEEE80211B) {
bss->disable_11n = true;
wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not "
"allowed, disabling HT capabilities");
}
#ifdef CONFIG_WEP
if (full_config && conf->ieee80211n &&
bss->ssid.security_policy == SECURITY_STATIC_WEP) {
bss->disable_11n = true;
wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not "
"allowed, disabling HT capabilities");
}
#endif /* CONFIG_WEP */
if (full_config && conf->ieee80211n && bss->wpa &&
!(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
{
bss->disable_11n = true;
wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 "
"requires CCMP/GCMP to be enabled, disabling HT "
"capabilities");
}
#ifdef CONFIG_IEEE80211AC
#ifdef CONFIG_WEP
if (full_config && conf->ieee80211ac &&
bss->ssid.security_policy == SECURITY_STATIC_WEP) {
bss->disable_11ac = true;
wpa_printf(MSG_ERROR,
"VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities");
}
#endif /* CONFIG_WEP */
if (full_config && conf->ieee80211ac && bss->wpa &&
!(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
{
bss->disable_11ac = true;
wpa_printf(MSG_ERROR,
"VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities");
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
#ifdef CONFIG_WEP
if (full_config && conf->ieee80211ax &&
bss->ssid.security_policy == SECURITY_STATIC_WEP) {
bss->disable_11ax = true;
wpa_printf(MSG_ERROR,
"HE (IEEE 802.11ax) with WEP is not allowed, disabling HE capabilities");
}
#endif /* CONFIG_WEP */
if (full_config && conf->ieee80211ax && bss->wpa &&
!(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
{
bss->disable_11ax = true;
wpa_printf(MSG_ERROR,
"HE (IEEE 802.11ax) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling HE capabilities");
}
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_WPS
if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) {
wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
"configuration forced WPS to be disabled");
bss->wps_state = 0;
}
#ifdef CONFIG_WEP
if (full_config && bss->wps_state &&
bss->ssid.wep.keys_set && bss->wpa == 0) {
wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be "
"disabled");
bss->wps_state = 0;
}
#endif /* CONFIG_WEP */
if (full_config && bss->wps_state && bss->wpa &&
(!(bss->wpa & 2) ||
!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
WPA_CIPHER_CCMP_256 |
WPA_CIPHER_GCMP_256)))) {
wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
"WPA2/CCMP/GCMP forced WPS to be disabled");
bss->wps_state = 0;
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_HS20
if (full_config && bss->hs20 &&
(!(bss->wpa & 2) ||
!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
WPA_CIPHER_CCMP_256 |
WPA_CIPHER_GCMP_256)))) {
wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP "
"configuration is required for Hotspot 2.0 "
"functionality");
return -1;
}
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
if (full_config && bss->mbo_enabled && (bss->wpa & 2) &&
bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
wpa_printf(MSG_ERROR,
"MBO: PMF needs to be enabled whenever using WPA2 with MBO");
return -1;
}
#endif /* CONFIG_MBO */
#ifdef CONFIG_OCV
if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION &&
bss->ocv) {
wpa_printf(MSG_ERROR,
"OCV: PMF needs to be enabled whenever using OCV");
return -1;
}
#endif /* CONFIG_OCV */
#ifdef CONFIG_SAE_PK
if (full_config && hostapd_sae_pk_in_use(bss) &&
hostapd_sae_pk_password_without_pk(bss)) {
wpa_printf(MSG_ERROR,
"SAE-PK: SAE password uses SAE-PK style, but does not have PK configured");
return -1;
}
#endif /* CONFIG_SAE_PK */
+#ifdef CONFIG_FILS
+ if (full_config && bss->fils_discovery_min_int &&
+ bss->unsol_bcast_probe_resp_interval) {
+ wpa_printf(MSG_ERROR,
+ "Cannot enable both FILS discovery and unsolicited broadcast Probe Response at the same time");
+ return -1;
+ }
+#endif /* CONFIG_FILS */
+
return 0;
}
static int hostapd_config_check_cw(struct hostapd_config *conf, int queue)
{
int tx_cwmin = conf->tx_queue[queue].cwmin;
int tx_cwmax = conf->tx_queue[queue].cwmax;
int ac_cwmin = conf->wmm_ac_params[queue].cwmin;
int ac_cwmax = conf->wmm_ac_params[queue].cwmax;
if (tx_cwmin > tx_cwmax) {
wpa_printf(MSG_ERROR,
"Invalid TX queue cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
tx_cwmin, tx_cwmax);
return -1;
}
if (ac_cwmin > ac_cwmax) {
wpa_printf(MSG_ERROR,
"Invalid WMM AC cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
ac_cwmin, ac_cwmax);
return -1;
}
return 0;
}
int hostapd_config_check(struct hostapd_config *conf, int full_config)
{
size_t i;
if (full_config && conf->ieee80211d &&
(!conf->country[0] || !conf->country[1])) {
wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without "
"setting the country_code");
return -1;
}
if (full_config && conf->ieee80211h && !conf->ieee80211d) {
wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without "
"IEEE 802.11d enabled");
return -1;
}
if (full_config && conf->local_pwr_constraint != -1 &&
!conf->ieee80211d) {
wpa_printf(MSG_ERROR, "Cannot add Power Constraint element without Country element");
return -1;
}
if (full_config && conf->spectrum_mgmt_required &&
conf->local_pwr_constraint == -1) {
wpa_printf(MSG_ERROR, "Cannot set Spectrum Management bit without Country and Power Constraint elements");
return -1;
}
#ifdef CONFIG_AIRTIME_POLICY
if (full_config && conf->airtime_mode > AIRTIME_MODE_STATIC &&
!conf->airtime_update_interval) {
wpa_printf(MSG_ERROR, "Airtime update interval cannot be zero");
return -1;
}
#endif /* CONFIG_AIRTIME_POLICY */
for (i = 0; i < NUM_TX_QUEUES; i++) {
if (hostapd_config_check_cw(conf, i))
return -1;
}
for (i = 0; i < conf->num_bss; i++) {
if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
return -1;
}
return 0;
}
void hostapd_set_security_params(struct hostapd_bss_config *bss,
int full_config)
{
#ifdef CONFIG_WEP
if (bss->individual_wep_key_len == 0) {
/* individual keys are not use; can use key idx0 for
* broadcast keys */
bss->broadcast_key_idx_min = 0;
}
#endif /* CONFIG_WEP */
if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
bss->rsn_pairwise = bss->wpa_pairwise;
if (bss->group_cipher)
bss->wpa_group = bss->group_cipher;
else
bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
bss->wpa_pairwise,
bss->rsn_pairwise);
if (!bss->wpa_group_rekey_set)
bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ?
600 : 86400;
if (full_config) {
bss->radius->auth_server = bss->radius->auth_servers;
bss->radius->acct_server = bss->radius->acct_servers;
}
if (bss->wpa && bss->ieee802_1x) {
bss->ssid.security_policy = SECURITY_WPA;
} else if (bss->wpa) {
bss->ssid.security_policy = SECURITY_WPA_PSK;
} else if (bss->ieee802_1x) {
int cipher = WPA_CIPHER_NONE;
bss->ssid.security_policy = SECURITY_IEEE_802_1X;
#ifdef CONFIG_WEP
bss->ssid.wep.default_len = bss->default_wep_key_len;
if (full_config && bss->default_wep_key_len) {
cipher = bss->default_wep_key_len >= 13 ?
WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
} else if (full_config && bss->ssid.wep.keys_set) {
if (bss->ssid.wep.len[0] >= 13)
cipher = WPA_CIPHER_WEP104;
else
cipher = WPA_CIPHER_WEP40;
}
#endif /* CONFIG_WEP */
bss->wpa_group = cipher;
bss->wpa_pairwise = cipher;
bss->rsn_pairwise = cipher;
if (full_config)
bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
#ifdef CONFIG_WEP
} else if (bss->ssid.wep.keys_set) {
int cipher = WPA_CIPHER_WEP40;
if (bss->ssid.wep.len[0] >= 13)
cipher = WPA_CIPHER_WEP104;
bss->ssid.security_policy = SECURITY_STATIC_WEP;
bss->wpa_group = cipher;
bss->wpa_pairwise = cipher;
bss->rsn_pairwise = cipher;
if (full_config)
bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
#endif /* CONFIG_WEP */
} else if (bss->osen) {
bss->ssid.security_policy = SECURITY_OSEN;
bss->wpa_group = WPA_CIPHER_CCMP;
bss->wpa_pairwise = 0;
bss->rsn_pairwise = WPA_CIPHER_CCMP;
} else {
bss->ssid.security_policy = SECURITY_PLAINTEXT;
if (full_config) {
bss->wpa_group = WPA_CIPHER_NONE;
bss->wpa_pairwise = WPA_CIPHER_NONE;
bss->rsn_pairwise = WPA_CIPHER_NONE;
bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
}
}
}
int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf)
{
int with_id = 0, without_id = 0;
struct sae_password_entry *pw;
if (conf->ssid.wpa_passphrase)
without_id = 1;
for (pw = conf->sae_passwords; pw; pw = pw->next) {
if (pw->identifier)
with_id = 1;
else
without_id = 1;
if (with_id && without_id)
break;
}
if (with_id && !without_id)
return 2;
return with_id;
}
bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf)
{
#ifdef CONFIG_SAE_PK
struct sae_password_entry *pw;
for (pw = conf->sae_passwords; pw; pw = pw->next) {
if (pw->pk)
return true;
}
#endif /* CONFIG_SAE_PK */
return false;
}
#ifdef CONFIG_SAE_PK
bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf)
{
bool with_pk = false;
struct sae_password_entry *pw;
if (conf->ssid.wpa_passphrase)
return false;
for (pw = conf->sae_passwords; pw; pw = pw->next) {
if (!pw->pk)
return false;
with_pk = true;
}
return with_pk;
}
#endif /* CONFIG_SAE_PK */
diff --git a/contrib/wpa/src/ap/ap_config.h b/contrib/wpa/src/ap/ap_config.h
index ced36f9cc828..b8f791e56307 100644
--- a/contrib/wpa/src/ap/ap_config.h
+++ b/contrib/wpa/src/ap/ap_config.h
@@ -1,1193 +1,1196 @@
/*
* hostapd / Configuration definitions and helpers functions
* Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef HOSTAPD_CONFIG_H
#define HOSTAPD_CONFIG_H
#include "common/defs.h"
#include "utils/list.h"
#include "ip_addr.h"
#include "common/wpa_common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "crypto/sha256.h"
#include "wps/wps.h"
#include "fst/fst.h"
#include "vlan.h"
/**
* mesh_conf - local MBSS state and settings
*/
struct mesh_conf {
u8 meshid[32];
u8 meshid_len;
/* Active Path Selection Protocol Identifier */
u8 mesh_pp_id;
/* Active Path Selection Metric Identifier */
u8 mesh_pm_id;
/* Congestion Control Mode Identifier */
u8 mesh_cc_id;
/* Synchronization Protocol Identifier */
u8 mesh_sp_id;
/* Authentication Protocol Identifier */
u8 mesh_auth_id;
u8 *rsn_ie;
int rsn_ie_len;
#define MESH_CONF_SEC_NONE BIT(0)
#define MESH_CONF_SEC_AUTH BIT(1)
#define MESH_CONF_SEC_AMPE BIT(2)
unsigned int security;
enum mfp_options ieee80211w;
int ocv;
unsigned int pairwise_cipher;
unsigned int group_cipher;
unsigned int mgmt_group_cipher;
int dot11MeshMaxRetries;
int dot11MeshRetryTimeout; /* msec */
int dot11MeshConfirmTimeout; /* msec */
int dot11MeshHoldingTimeout; /* msec */
};
#define MAX_STA_COUNT 2007
#define MAX_VLAN_ID 4094
typedef u8 macaddr[ETH_ALEN];
struct mac_acl_entry {
macaddr addr;
struct vlan_description vlan_id;
};
struct hostapd_radius_servers;
struct ft_remote_r0kh;
struct ft_remote_r1kh;
#ifdef CONFIG_WEP
#define NUM_WEP_KEYS 4
struct hostapd_wep_keys {
u8 idx;
u8 *key[NUM_WEP_KEYS];
size_t len[NUM_WEP_KEYS];
int keys_set;
size_t default_len; /* key length used for dynamic key generation */
};
#endif /* CONFIG_WEP */
typedef enum hostap_security_policy {
SECURITY_PLAINTEXT = 0,
#ifdef CONFIG_WEP
SECURITY_STATIC_WEP = 1,
#endif /* CONFIG_WEP */
SECURITY_IEEE_802_1X = 2,
SECURITY_WPA_PSK = 3,
SECURITY_WPA = 4,
SECURITY_OSEN = 5
} secpolicy;
struct hostapd_ssid {
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
u32 short_ssid;
unsigned int ssid_set:1;
unsigned int utf8_ssid:1;
unsigned int wpa_passphrase_set:1;
unsigned int wpa_psk_set:1;
char vlan[IFNAMSIZ + 1];
secpolicy security_policy;
struct hostapd_wpa_psk *wpa_psk;
char *wpa_passphrase;
char *wpa_psk_file;
struct sae_pt *pt;
#ifdef CONFIG_WEP
struct hostapd_wep_keys wep;
#endif /* CONFIG_WEP */
#define DYNAMIC_VLAN_DISABLED 0
#define DYNAMIC_VLAN_OPTIONAL 1
#define DYNAMIC_VLAN_REQUIRED 2
int dynamic_vlan;
#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
#define DYNAMIC_VLAN_NAMING_END 2
int vlan_naming;
int per_sta_vif;
#ifdef CONFIG_FULL_DYNAMIC_VLAN
char *vlan_tagged_interface;
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
};
#define VLAN_ID_WILDCARD -1
struct hostapd_vlan {
struct hostapd_vlan *next;
int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
struct vlan_description vlan_desc;
char ifname[IFNAMSIZ + 1];
char bridge[IFNAMSIZ + 1];
int configured;
int dynamic_vlan;
#ifdef CONFIG_FULL_DYNAMIC_VLAN
#define DVLAN_CLEAN_WLAN_PORT 0x8
int clean;
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
};
#define PMK_LEN 32
#define KEYID_LEN 32
#define MIN_PASSPHRASE_LEN 8
#define MAX_PASSPHRASE_LEN 63
struct hostapd_sta_wpa_psk_short {
struct hostapd_sta_wpa_psk_short *next;
unsigned int is_passphrase:1;
u8 psk[PMK_LEN];
char passphrase[MAX_PASSPHRASE_LEN + 1];
int ref; /* (number of references held) - 1 */
};
struct hostapd_wpa_psk {
struct hostapd_wpa_psk *next;
int group;
char keyid[KEYID_LEN];
int wps;
u8 psk[PMK_LEN];
u8 addr[ETH_ALEN];
u8 p2p_dev_addr[ETH_ALEN];
int vlan_id;
};
struct hostapd_eap_user {
struct hostapd_eap_user *next;
u8 *identity;
size_t identity_len;
struct {
int vendor;
u32 method;
} methods[EAP_MAX_METHODS];
u8 *password;
size_t password_len;
u8 *salt;
size_t salt_len; /* non-zero when password is salted */
int phase2;
int force_version;
unsigned int wildcard_prefix:1;
unsigned int password_hash:1; /* whether password is hashed with
* nt_password_hash() */
unsigned int remediation:1;
unsigned int macacl:1;
int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
struct hostapd_radius_attr *accept_attr;
u32 t_c_timestamp;
};
struct hostapd_radius_attr {
u8 type;
struct wpabuf *val;
struct hostapd_radius_attr *next;
};
#define NUM_TX_QUEUES 4
#define MAX_ROAMING_CONSORTIUM_LEN 15
struct hostapd_roaming_consortium {
u8 len;
u8 oi[MAX_ROAMING_CONSORTIUM_LEN];
};
struct hostapd_lang_string {
u8 lang[3];
u8 name_len;
u8 name[252];
};
struct hostapd_venue_url {
u8 venue_number;
u8 url_len;
u8 url[254];
};
#define MAX_NAI_REALMS 10
#define MAX_NAI_REALMLEN 255
#define MAX_NAI_EAP_METHODS 5
#define MAX_NAI_AUTH_TYPES 4
struct hostapd_nai_realm_data {
u8 encoding;
char realm_buf[MAX_NAI_REALMLEN + 1];
char *realm[MAX_NAI_REALMS];
u8 eap_method_count;
struct hostapd_nai_realm_eap {
u8 eap_method;
u8 num_auths;
u8 auth_id[MAX_NAI_AUTH_TYPES];
u8 auth_val[MAX_NAI_AUTH_TYPES];
} eap_method[MAX_NAI_EAP_METHODS];
};
struct anqp_element {
struct dl_list list;
u16 infoid;
struct wpabuf *payload;
};
struct fils_realm {
struct dl_list list;
u8 hash[2];
char realm[];
};
struct sae_password_entry {
struct sae_password_entry *next;
char *password;
char *identifier;
u8 peer_addr[ETH_ALEN];
int vlan_id;
struct sae_pt *pt;
struct sae_pk *pk;
};
struct dpp_controller_conf {
struct dpp_controller_conf *next;
u8 pkhash[SHA256_MAC_LEN];
struct hostapd_ip_addr ipaddr;
};
struct airtime_sta_weight {
struct airtime_sta_weight *next;
unsigned int weight;
u8 addr[ETH_ALEN];
};
#define EXT_CAPA_MAX_LEN 15
/**
* struct hostapd_bss_config - Per-BSS configuration
*/
struct hostapd_bss_config {
char iface[IFNAMSIZ + 1];
char bridge[IFNAMSIZ + 1];
char vlan_bridge[IFNAMSIZ + 1];
char wds_bridge[IFNAMSIZ + 1];
enum hostapd_logger_level logger_syslog_level, logger_stdout_level;
unsigned int logger_syslog; /* module bitfield */
unsigned int logger_stdout; /* module bitfield */
int max_num_sta; /* maximum number of STAs in station table */
int dtim_period;
unsigned int bss_load_update_period;
unsigned int chan_util_avg_period;
int ieee802_1x; /* use IEEE 802.1X */
int eapol_version;
int eap_server; /* Use internal EAP server instead of external
* RADIUS server */
struct hostapd_eap_user *eap_user;
char *eap_user_sqlite;
char *eap_sim_db;
unsigned int eap_sim_db_timeout;
int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
struct hostapd_ip_addr own_ip_addr;
char *nas_identifier;
struct hostapd_radius_servers *radius;
int acct_interim_interval;
int radius_request_cui;
struct hostapd_radius_attr *radius_auth_req_attr;
struct hostapd_radius_attr *radius_acct_req_attr;
char *radius_req_attr_sqlite;
int radius_das_port;
unsigned int radius_das_time_window;
int radius_das_require_event_timestamp;
int radius_das_require_message_authenticator;
struct hostapd_ip_addr radius_das_client_addr;
u8 *radius_das_shared_secret;
size_t radius_das_shared_secret_len;
struct hostapd_ssid ssid;
char *eap_req_id_text; /* optional displayable message sent with
* EAP Request-Identity */
size_t eap_req_id_text_len;
int eapol_key_index_workaround;
#ifdef CONFIG_WEP
size_t default_wep_key_len;
int individual_wep_key_len;
int wep_rekeying_period;
int broadcast_key_idx_min, broadcast_key_idx_max;
#endif /* CONFIG_WEP */
int eap_reauth_period;
int erp_send_reauth_start;
char *erp_domain;
enum macaddr_acl {
ACCEPT_UNLESS_DENIED = 0,
DENY_UNLESS_ACCEPTED = 1,
USE_EXTERNAL_RADIUS_AUTH = 2
} macaddr_acl;
struct mac_acl_entry *accept_mac;
int num_accept_mac;
struct mac_acl_entry *deny_mac;
int num_deny_mac;
int wds_sta;
int isolate;
int start_disabled;
int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
int extended_key_id;
int wpa_key_mgmt;
enum mfp_options ieee80211w;
int group_mgmt_cipher;
int beacon_prot;
/* dot11AssociationSAQueryMaximumTimeout (in TUs) */
unsigned int assoc_sa_query_max_timeout;
/* dot11AssociationSAQueryRetryTimeout (in TUs) */
int assoc_sa_query_retry_timeout;
#ifdef CONFIG_OCV
int ocv; /* Operating Channel Validation */
#endif /* CONFIG_OCV */
enum {
PSK_RADIUS_IGNORED = 0,
PSK_RADIUS_ACCEPTED = 1,
PSK_RADIUS_REQUIRED = 2
} wpa_psk_radius;
int wpa_pairwise;
int group_cipher; /* wpa_group value override from configuation */
int wpa_group;
int wpa_group_rekey;
int wpa_group_rekey_set;
int wpa_strict_rekey;
int wpa_gmk_rekey;
int wpa_ptk_rekey;
enum ptk0_rekey_handling wpa_deny_ptk0_rekey;
u32 wpa_group_update_count;
u32 wpa_pairwise_update_count;
int wpa_disable_eapol_key_retries;
int rsn_pairwise;
int rsn_preauth;
char *rsn_preauth_interfaces;
#ifdef CONFIG_IEEE80211R_AP
/* IEEE 802.11r - Fast BSS Transition */
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r1_key_holder[FT_R1KH_ID_LEN];
u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */
int rkh_pos_timeout;
int rkh_neg_timeout;
int rkh_pull_timeout; /* ms */
int rkh_pull_retries;
u32 reassociation_deadline;
struct ft_remote_r0kh *r0kh_list;
struct ft_remote_r1kh *r1kh_list;
int pmk_r1_push;
int ft_over_ds;
int ft_psk_generate_local;
int r1_max_key_lifetime;
#endif /* CONFIG_IEEE80211R_AP */
char *ctrl_interface; /* directory for UNIX domain sockets */
#ifndef CONFIG_NATIVE_WINDOWS
gid_t ctrl_interface_gid;
#endif /* CONFIG_NATIVE_WINDOWS */
int ctrl_interface_gid_set;
char *ca_cert;
char *server_cert;
char *server_cert2;
char *private_key;
char *private_key2;
char *private_key_passwd;
char *private_key_passwd2;
char *check_cert_subject;
int check_crl;
int check_crl_strict;
unsigned int crl_reload_interval;
unsigned int tls_session_lifetime;
unsigned int tls_flags;
unsigned int max_auth_rounds;
unsigned int max_auth_rounds_short;
char *ocsp_stapling_response;
char *ocsp_stapling_response_multi;
char *dh_file;
char *openssl_ciphers;
char *openssl_ecdh_curves;
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
char *eap_fast_a_id_info;
int eap_fast_prov;
int pac_key_lifetime;
int pac_key_refresh_time;
int eap_teap_auth;
int eap_teap_pac_no_inner;
int eap_teap_separate_result;
int eap_teap_id;
int eap_sim_aka_result_ind;
int eap_sim_id;
int tnc;
int fragment_size;
u16 pwd_group;
char *radius_server_clients;
int radius_server_auth_port;
int radius_server_acct_port;
int radius_server_ipv6;
int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
* address instead of individual address
* (for driver_wired.c).
*/
int ap_max_inactivity;
int ignore_broadcast_ssid;
int no_probe_resp_if_max_sta;
int wmm_enabled;
int wmm_uapsd;
struct hostapd_vlan *vlan;
macaddr bssid;
/*
* Maximum listen interval that STAs can use when associating with this
* BSS. If a STA tries to use larger value, the association will be
* denied with status code 51.
*/
u16 max_listen_interval;
int disable_pmksa_caching;
int okc; /* Opportunistic Key Caching */
int wps_state;
#ifdef CONFIG_WPS
int wps_independent;
int ap_setup_locked;
u8 uuid[16];
char *wps_pin_requests;
char *device_name;
char *manufacturer;
char *model_name;
char *model_number;
char *serial_number;
u8 device_type[WPS_DEV_TYPE_LEN];
char *config_methods;
u8 os_version[4];
char *ap_pin;
int skip_cred_build;
u8 *extra_cred;
size_t extra_cred_len;
int wps_cred_processing;
int wps_cred_add_sae;
int force_per_enrollee_psk;
u8 *ap_settings;
size_t ap_settings_len;
struct hostapd_ssid multi_ap_backhaul_ssid;
char *upnp_iface;
char *friendly_name;
char *manufacturer_url;
char *model_description;
char *model_url;
char *upc;
struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
struct wpabuf *wps_application_ext;
int wps_nfc_pw_from_config;
int wps_nfc_dev_pw_id;
struct wpabuf *wps_nfc_dh_pubkey;
struct wpabuf *wps_nfc_dh_privkey;
struct wpabuf *wps_nfc_dev_pw;
#endif /* CONFIG_WPS */
int pbc_in_m1;
char *server_id;
#define P2P_ENABLED BIT(0)
#define P2P_GROUP_OWNER BIT(1)
#define P2P_GROUP_FORMATION BIT(2)
#define P2P_MANAGE BIT(3)
#define P2P_ALLOW_CROSS_CONNECTION BIT(4)
int p2p;
#ifdef CONFIG_P2P
u8 ip_addr_go[4];
u8 ip_addr_mask[4];
u8 ip_addr_start[4];
u8 ip_addr_end[4];
#endif /* CONFIG_P2P */
int disassoc_low_ack;
int skip_inactivity_poll;
#define TDLS_PROHIBIT BIT(0)
#define TDLS_PROHIBIT_CHAN_SWITCH BIT(1)
int tdls;
bool disable_11n;
bool disable_11ac;
bool disable_11ax;
/* IEEE 802.11v */
int time_advertisement;
char *time_zone;
int wnm_sleep_mode;
int wnm_sleep_mode_no_keys;
int bss_transition;
/* IEEE 802.11u - Interworking */
int interworking;
int access_network_type;
int internet;
int asra;
int esr;
int uesa;
int venue_info_set;
u8 venue_group;
u8 venue_type;
u8 hessid[ETH_ALEN];
/* IEEE 802.11u - Roaming Consortium list */
unsigned int roaming_consortium_count;
struct hostapd_roaming_consortium *roaming_consortium;
/* IEEE 802.11u - Venue Name duples */
unsigned int venue_name_count;
struct hostapd_lang_string *venue_name;
/* Venue URL duples */
unsigned int venue_url_count;
struct hostapd_venue_url *venue_url;
/* IEEE 802.11u - Network Authentication Type */
u8 *network_auth_type;
size_t network_auth_type_len;
/* IEEE 802.11u - IP Address Type Availability */
u8 ipaddr_type_availability;
u8 ipaddr_type_configured;
/* IEEE 802.11u - 3GPP Cellular Network */
u8 *anqp_3gpp_cell_net;
size_t anqp_3gpp_cell_net_len;
/* IEEE 802.11u - Domain Name */
u8 *domain_name;
size_t domain_name_len;
unsigned int nai_realm_count;
struct hostapd_nai_realm_data *nai_realm_data;
struct dl_list anqp_elem; /* list of struct anqp_element */
u16 gas_comeback_delay;
size_t gas_frag_limit;
int gas_address3;
u8 qos_map_set[16 + 2 * 21];
unsigned int qos_map_set_len;
int osen;
int proxy_arp;
int na_mcast_to_ucast;
#ifdef CONFIG_HS20
int hs20;
int hs20_release;
int disable_dgaf;
u16 anqp_domain_id;
unsigned int hs20_oper_friendly_name_count;
struct hostapd_lang_string *hs20_oper_friendly_name;
u8 *hs20_wan_metrics;
u8 *hs20_connection_capability;
size_t hs20_connection_capability_len;
u8 *hs20_operating_class;
u8 hs20_operating_class_len;
struct hs20_icon {
u16 width;
u16 height;
char language[3];
char type[256];
char name[256];
char file[256];
} *hs20_icons;
size_t hs20_icons_count;
u8 osu_ssid[SSID_MAX_LEN];
size_t osu_ssid_len;
struct hs20_osu_provider {
unsigned int friendly_name_count;
struct hostapd_lang_string *friendly_name;
char *server_uri;
int *method_list;
char **icons;
size_t icons_count;
char *osu_nai;
char *osu_nai2;
unsigned int service_desc_count;
struct hostapd_lang_string *service_desc;
} *hs20_osu_providers, *last_osu;
size_t hs20_osu_providers_count;
size_t hs20_osu_providers_nai_count;
char **hs20_operator_icon;
size_t hs20_operator_icon_count;
unsigned int hs20_deauth_req_timeout;
char *subscr_remediation_url;
u8 subscr_remediation_method;
char *hs20_sim_provisioning_url;
char *t_c_filename;
u32 t_c_timestamp;
char *t_c_server_url;
#endif /* CONFIG_HS20 */
u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
#ifdef CONFIG_RADIUS_TEST
char *dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */
struct wpabuf *vendor_elements;
struct wpabuf *assocresp_elements;
unsigned int anti_clogging_threshold;
unsigned int sae_sync;
int sae_require_mfp;
int sae_confirm_immediate;
int sae_pwe;
int *sae_groups;
struct sae_password_entry *sae_passwords;
char *wowlan_triggers; /* Wake-on-WLAN triggers */
#ifdef CONFIG_TESTING_OPTIONS
u8 bss_load_test[5];
u8 bss_load_test_set;
struct wpabuf *own_ie_override;
int sae_reflection_attack;
int sae_commit_status;
int sae_pk_omit;
int sae_pk_password_check_skip;
struct wpabuf *sae_commit_override;
struct wpabuf *rsne_override_eapol;
struct wpabuf *rsnxe_override_eapol;
struct wpabuf *rsne_override_ft;
struct wpabuf *rsnxe_override_ft;
struct wpabuf *gtk_rsc_override;
struct wpabuf *igtk_rsc_override;
int no_beacon_rsnxe;
int skip_prune_assoc;
int ft_rsnxe_used;
unsigned int oci_freq_override_eapol_m3;
unsigned int oci_freq_override_eapol_g1;
unsigned int oci_freq_override_saquery_req;
unsigned int oci_freq_override_saquery_resp;
unsigned int oci_freq_override_ft_assoc;
unsigned int oci_freq_override_fils_assoc;
unsigned int oci_freq_override_wnm_sleep;
#endif /* CONFIG_TESTING_OPTIONS */
#define MESH_ENABLED BIT(0)
int mesh;
u8 radio_measurements[RRM_CAPABILITIES_IE_LEN];
int vendor_vht;
int use_sta_nsts;
char *no_probe_resp_if_seen_on;
char *no_auth_if_seen_on;
int pbss;
#ifdef CONFIG_MBO
int mbo_enabled;
/**
* oce - Enable OCE in AP and/or STA-CFON mode
* - BIT(0) is Reserved
* - Set BIT(1) to enable OCE in STA-CFON mode
* - Set BIT(2) to enable OCE in AP mode
*/
unsigned int oce;
int mbo_cell_data_conn_pref;
#endif /* CONFIG_MBO */
int ftm_responder;
int ftm_initiator;
#ifdef CONFIG_FILS
u8 fils_cache_id[FILS_CACHE_ID_LEN];
int fils_cache_id_set;
struct dl_list fils_realms; /* list of struct fils_realm */
int fils_dh_group;
struct hostapd_ip_addr dhcp_server;
int dhcp_rapid_commit_proxy;
unsigned int fils_hlp_wait_time;
u16 dhcp_server_port;
u16 dhcp_relay_port;
u32 fils_discovery_min_int;
u32 fils_discovery_max_int;
#endif /* CONFIG_FILS */
int multicast_to_unicast;
int broadcast_deauth;
int notify_mgmt_frames;
#ifdef CONFIG_DPP
char *dpp_name;
char *dpp_mud_url;
char *dpp_connector;
struct wpabuf *dpp_netaccesskey;
unsigned int dpp_netaccesskey_expiry;
struct wpabuf *dpp_csign;
#ifdef CONFIG_DPP2
struct dpp_controller_conf *dpp_controller;
int dpp_configurator_connectivity;
int dpp_pfs;
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
#ifdef CONFIG_OWE
macaddr owe_transition_bssid;
u8 owe_transition_ssid[SSID_MAX_LEN];
size_t owe_transition_ssid_len;
char owe_transition_ifname[IFNAMSIZ + 1];
int *owe_groups;
int owe_ptk_workaround;
#endif /* CONFIG_OWE */
int coloc_intf_reporting;
u8 send_probe_response;
u8 transition_disable;
#define BACKHAUL_BSS 1
#define FRONTHAUL_BSS 2
int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
#ifdef CONFIG_AIRTIME_POLICY
unsigned int airtime_weight;
int airtime_limit;
struct airtime_sta_weight *airtime_weight_list;
#endif /* CONFIG_AIRTIME_POLICY */
#ifdef CONFIG_MACSEC
/**
* macsec_policy - Determines the policy for MACsec secure session
*
* 0: MACsec not in use (default)
* 1: MACsec enabled - Should secure, accept key server's advice to
* determine whether to use a secure session or not.
*/
int macsec_policy;
/**
* macsec_integ_only - Determines how MACsec are transmitted
*
* This setting applies only when MACsec is in use, i.e.,
* - macsec_policy is enabled
* - the key server has decided to enable MACsec
*
* 0: Encrypt traffic (default)
* 1: Integrity only
*/
int macsec_integ_only;
/**
* macsec_replay_protect - Enable MACsec replay protection
*
* This setting applies only when MACsec is in use, i.e.,
* - macsec_policy is enabled
* - the key server has decided to enable MACsec
*
* 0: Replay protection disabled (default)
* 1: Replay protection enabled
*/
int macsec_replay_protect;
/**
* macsec_replay_window - MACsec replay protection window
*
* A window in which replay is tolerated, to allow receipt of frames
* that have been misordered by the network.
*
* This setting applies only when MACsec replay protection active, i.e.,
* - macsec_replay_protect is enabled
* - the key server has decided to enable MACsec
*
* 0: No replay window, strict check (default)
* 1..2^32-1: number of packets that could be misordered
*/
u32 macsec_replay_window;
/**
* macsec_port - MACsec port (in SCI)
*
* Port component of the SCI.
*
* Range: 1-65534 (default: 1)
*/
int macsec_port;
/**
* mka_priority - Priority of MKA Actor
*
* Range: 0-255 (default: 255)
*/
int mka_priority;
/**
* mka_ckn - MKA pre-shared CKN
*/
#define MACSEC_CKN_MAX_LEN 32
size_t mka_ckn_len;
u8 mka_ckn[MACSEC_CKN_MAX_LEN];
/**
* mka_cak - MKA pre-shared CAK
*/
#define MACSEC_CAK_MAX_LEN 32
size_t mka_cak_len;
u8 mka_cak[MACSEC_CAK_MAX_LEN];
#define MKA_PSK_SET_CKN BIT(0)
#define MKA_PSK_SET_CAK BIT(1)
#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK)
/**
* mka_psk_set - Whether mka_ckn and mka_cak are set
*/
u8 mka_psk_set;
#endif /* CONFIG_MACSEC */
#ifdef CONFIG_PASN
#ifdef CONFIG_TESTING_OPTIONS
/*
* Normally, KDK should be derived if and only if both sides support
* secure LTF. Allow forcing KDK derivation for testing purposes.
*/
int force_kdk_derivation;
/* If set, corrupt the MIC in the 2nd Authentication frame of PASN */
int pasn_corrupt_mic;
#endif /* CONFIG_TESTING_OPTIONS */
int *pasn_groups;
/*
* The time in TUs after which the non-AP STA is requested to retry the
* PASN authentication in case there are too many parallel operations.
*/
u16 pasn_comeback_after;
#endif /* CONFIG_PASN */
unsigned int unsol_bcast_probe_resp_interval;
u8 ext_capa_mask[EXT_CAPA_MAX_LEN];
u8 ext_capa[EXT_CAPA_MAX_LEN];
+
+ u8 rnr;
};
/**
* struct he_phy_capabilities_info - HE PHY capabilities
*/
struct he_phy_capabilities_info {
bool he_su_beamformer;
bool he_su_beamformee;
bool he_mu_beamformer;
};
/**
* struct he_operation - HE operation
*/
struct he_operation {
u8 he_bss_color;
u8 he_bss_color_disabled;
u8 he_bss_color_partial;
u8 he_default_pe_duration;
u8 he_twt_required;
u8 he_twt_responder;
u16 he_rts_threshold;
+ u8 he_er_su_disable;
u16 he_basic_mcs_nss_set;
};
/**
* struct spatial_reuse - Spatial reuse
*/
struct spatial_reuse {
u8 sr_control;
u8 non_srg_obss_pd_max_offset;
u8 srg_obss_pd_min_offset;
u8 srg_obss_pd_max_offset;
u8 srg_bss_color_bitmap[8];
u8 srg_partial_bssid_bitmap[8];
};
/**
* struct hostapd_config - Per-radio interface configuration
*/
struct hostapd_config {
struct hostapd_bss_config **bss, *last_bss;
size_t num_bss;
u16 beacon_int;
int rts_threshold;
int fragm_threshold;
u8 op_class;
u8 channel;
int enable_edmg;
u8 edmg_channel;
u8 acs;
struct wpa_freq_range_list acs_ch_list;
struct wpa_freq_range_list acs_freq_list;
u8 acs_freq_list_present;
int acs_exclude_dfs;
enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
int acs_exclude_6ghz_non_psc;
enum {
LONG_PREAMBLE = 0,
SHORT_PREAMBLE = 1
} preamble;
int *supported_rates;
int *basic_rates;
unsigned int beacon_rate;
enum beacon_rate_type rate_type;
const struct wpa_driver_ops *driver;
char *driver_params;
int ap_table_max_size;
int ap_table_expiration_time;
unsigned int track_sta_max_num;
unsigned int track_sta_max_age;
char country[3]; /* first two octets: country code as described in
* ISO/IEC 3166-1. Third octet:
* ' ' (ascii 32): all environments
* 'O': Outdoor environemnt only
* 'I': Indoor environment only
* 'X': Used with noncountry entity ("XXX")
* 0x00..0x31: identifying IEEE 802.11 standard
* Annex E table (0x04 = global table)
*/
int ieee80211d;
int ieee80211h; /* DFS */
/*
* Local power constraint is an octet encoded as an unsigned integer in
* units of decibels. Invalid value -1 indicates that Power Constraint
* element will not be added.
*/
int local_pwr_constraint;
/* Control Spectrum Management bit */
int spectrum_mgmt_required;
struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
/*
* WMM AC parameters, in same order as 802.1D, i.e.
* 0 = BE (best effort)
* 1 = BK (background)
* 2 = VI (video)
* 3 = VO (voice)
*/
struct hostapd_wmm_ac_params wmm_ac_params[4];
int ht_op_mode_fixed;
u16 ht_capab;
int ieee80211n;
int secondary_channel;
int no_pri_sec_switch;
int require_ht;
int obss_interval;
u32 vht_capab;
int ieee80211ac;
int require_vht;
u8 vht_oper_chwidth;
u8 vht_oper_centr_freq_seg0_idx;
u8 vht_oper_centr_freq_seg1_idx;
u8 ht40_plus_minus_allowed;
/* Use driver-generated interface addresses when adding multiple BSSs */
u8 use_driver_iface_addr;
#ifdef CONFIG_FST
struct fst_iface_cfg fst_cfg;
#endif /* CONFIG_FST */
#ifdef CONFIG_P2P
u8 p2p_go_ctwindow;
#endif /* CONFIG_P2P */
#ifdef CONFIG_TESTING_OPTIONS
double ignore_probe_probability;
double ignore_auth_probability;
double ignore_assoc_probability;
double ignore_reassoc_probability;
double corrupt_gtk_rekey_mic_probability;
int ecsa_ie_only;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_ACS
unsigned int acs_num_scans;
struct acs_bias {
int channel;
double bias;
} *acs_chan_bias;
unsigned int num_acs_chan_bias;
#endif /* CONFIG_ACS */
struct wpabuf *lci;
struct wpabuf *civic;
int stationary_ap;
int ieee80211ax;
#ifdef CONFIG_IEEE80211AX
struct he_phy_capabilities_info he_phy_capab;
struct he_operation he_op;
struct ieee80211_he_mu_edca_parameter_set he_mu_edca;
struct spatial_reuse spr;
u8 he_oper_chwidth;
u8 he_oper_centr_freq_seg0_idx;
u8 he_oper_centr_freq_seg1_idx;
u8 he_6ghz_max_mpdu;
u8 he_6ghz_max_ampdu_len_exp;
u8 he_6ghz_rx_ant_pat;
u8 he_6ghz_tx_ant_pat;
#endif /* CONFIG_IEEE80211AX */
/* VHT enable/disable config from CHAN_SWITCH */
#define CH_SWITCH_VHT_ENABLED BIT(0)
#define CH_SWITCH_VHT_DISABLED BIT(1)
unsigned int ch_switch_vht_config;
/* HE enable/disable config from CHAN_SWITCH */
#define CH_SWITCH_HE_ENABLED BIT(0)
#define CH_SWITCH_HE_DISABLED BIT(1)
unsigned int ch_switch_he_config;
int rssi_reject_assoc_rssi;
int rssi_reject_assoc_timeout;
int rssi_ignore_probe_request;
#ifdef CONFIG_AIRTIME_POLICY
enum {
AIRTIME_MODE_OFF = 0,
AIRTIME_MODE_STATIC = 1,
AIRTIME_MODE_DYNAMIC = 2,
AIRTIME_MODE_LIMIT = 3,
__AIRTIME_MODE_MAX,
} airtime_mode;
unsigned int airtime_update_interval;
#define AIRTIME_MODE_MAX (__AIRTIME_MODE_MAX - 1)
#endif /* CONFIG_AIRTIME_POLICY */
};
static inline u8 hostapd_get_oper_chwidth(struct hostapd_config *conf)
{
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax)
return conf->he_oper_chwidth;
#endif /* CONFIG_IEEE80211AX */
return conf->vht_oper_chwidth;
}
static inline void
hostapd_set_oper_chwidth(struct hostapd_config *conf, u8 oper_chwidth)
{
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax)
conf->he_oper_chwidth = oper_chwidth;
#endif /* CONFIG_IEEE80211AX */
conf->vht_oper_chwidth = oper_chwidth;
}
static inline u8
hostapd_get_oper_centr_freq_seg0_idx(struct hostapd_config *conf)
{
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax)
return conf->he_oper_centr_freq_seg0_idx;
#endif /* CONFIG_IEEE80211AX */
return conf->vht_oper_centr_freq_seg0_idx;
}
static inline void
hostapd_set_oper_centr_freq_seg0_idx(struct hostapd_config *conf,
u8 oper_centr_freq_seg0_idx)
{
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax)
conf->he_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
#endif /* CONFIG_IEEE80211AX */
conf->vht_oper_centr_freq_seg0_idx = oper_centr_freq_seg0_idx;
}
static inline u8
hostapd_get_oper_centr_freq_seg1_idx(struct hostapd_config *conf)
{
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax)
return conf->he_oper_centr_freq_seg1_idx;
#endif /* CONFIG_IEEE80211AX */
return conf->vht_oper_centr_freq_seg1_idx;
}
static inline void
hostapd_set_oper_centr_freq_seg1_idx(struct hostapd_config *conf,
u8 oper_centr_freq_seg1_idx)
{
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax)
conf->he_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
#endif /* CONFIG_IEEE80211AX */
conf->vht_oper_centr_freq_seg1_idx = oper_centr_freq_seg1_idx;
}
int hostapd_mac_comp(const void *a, const void *b);
struct hostapd_config * hostapd_config_defaults(void);
void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr);
void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
void hostapd_config_free_eap_users(struct hostapd_eap_user *user);
void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
void hostapd_config_free_bss(struct hostapd_bss_config *conf);
void hostapd_config_free(struct hostapd_config *conf);
int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
const u8 *addr, struct vlan_description *vlan_id);
int hostapd_rate_found(int *list, int rate);
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *p2p_dev_addr,
const u8 *prev_psk, int *vlan_id);
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
int hostapd_vlan_valid(struct hostapd_vlan *vlan,
struct vlan_description *vlan_desc);
const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
int vlan_id);
struct hostapd_radius_attr *
hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
struct hostapd_radius_attr * hostapd_parse_radius_attr(const char *value);
int hostapd_config_check(struct hostapd_config *conf, int full_config);
void hostapd_set_security_params(struct hostapd_bss_config *bss,
int full_config);
int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf);
bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf);
bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf);
int hostapd_setup_sae_pt(struct hostapd_bss_config *conf);
#endif /* HOSTAPD_CONFIG_H */
diff --git a/contrib/wpa/src/ap/beacon.c b/contrib/wpa/src/ap/beacon.c
index 15fc2b3db064..22782f54e480 100644
--- a/contrib/wpa/src/ap/beacon.c
+++ b/contrib/wpa/src/ap/beacon.c
@@ -1,1865 +1,1938 @@
/*
* hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response
* Copyright (c) 2002-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2008-2012, 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"
#ifndef CONFIG_NATIVE_WINDOWS
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/hw_features_common.h"
#include "common/wpa_ctrl.h"
#include "wps/wps_defs.h"
#include "p2p/p2p.h"
#include "hostapd.h"
#include "ieee802_11.h"
#include "wpa_auth.h"
#include "wmm.h"
#include "ap_config.h"
#include "sta_info.h"
#include "p2p_hostapd.h"
#include "ap_drv_ops.h"
#include "beacon.h"
#include "hs20.h"
#include "dfs.h"
#include "taxonomy.h"
#include "ieee802_11_auth.h"
#ifdef NEED_AP_MLME
static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
{
if (len < 2 + 5)
return eid;
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->bss_load_test_set) {
*eid++ = WLAN_EID_BSS_LOAD;
*eid++ = 5;
os_memcpy(eid, hapd->conf->bss_load_test, 5);
eid += 5;
return eid;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (hapd->conf->bss_load_update_period) {
*eid++ = WLAN_EID_BSS_LOAD;
*eid++ = 5;
WPA_PUT_LE16(eid, hapd->num_sta);
eid += 2;
*eid++ = hapd->iface->channel_utilization;
WPA_PUT_LE16(eid, 0); /* no available admission capabity */
eid += 2;
}
return eid;
}
static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
{
u8 erp = 0;
if (hapd->iface->current_mode == NULL ||
hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
return 0;
if (hapd->iface->olbc)
erp |= ERP_INFO_USE_PROTECTION;
if (hapd->iface->num_sta_non_erp > 0) {
erp |= ERP_INFO_NON_ERP_PRESENT |
ERP_INFO_USE_PROTECTION;
}
if (hapd->iface->num_sta_no_short_preamble > 0 ||
hapd->iconf->preamble == LONG_PREAMBLE)
erp |= ERP_INFO_BARKER_PREAMBLE_MODE;
return erp;
}
static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid)
{
*eid++ = WLAN_EID_DS_PARAMS;
*eid++ = 1;
*eid++ = hapd->iconf->channel;
return eid;
}
static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
{
if (hapd->iface->current_mode == NULL ||
hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
return eid;
/* Set NonERP_present and use_protection bits if there
* are any associated NonERP stations. */
/* TODO: use_protection bit can be set to zero even if
* there are NonERP stations present. This optimization
* might be useful if NonERP stations are "quiet".
* See 802.11g/D6 E-1 for recommended practice.
* In addition, Non ERP present might be set, if AP detects Non ERP
* operation on other APs. */
/* Add ERP Information element */
*eid++ = WLAN_EID_ERP_INFO;
*eid++ = 1;
*eid++ = ieee802_11_erp_info(hapd);
return eid;
}
static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
u8 local_pwr_constraint = 0;
int dfs;
if (hapd->iface->current_mode == NULL ||
hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
return eid;
/* Let host drivers add this IE if DFS support is offloaded */
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
return eid;
/*
* There is no DFS support and power constraint was not directly
* requested by config option.
*/
if (!hapd->iconf->ieee80211h &&
hapd->iconf->local_pwr_constraint == -1)
return eid;
/* Check if DFS is required by regulatory. */
dfs = hostapd_is_dfs_required(hapd->iface);
if (dfs < 0) {
wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
dfs);
dfs = 0;
}
if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1)
return eid;
/*
* ieee80211h (DFS) is enabled so Power Constraint element shall
* be added when running on DFS channel whenever local_pwr_constraint
* is configured or not. In order to meet regulations when TPC is not
* implemented using a transmit power that is below the legal maximum
* (including any mitigation factor) should help. In this case,
* indicate 3 dB below maximum allowed transmit power.
*/
if (hapd->iconf->local_pwr_constraint == -1)
local_pwr_constraint = 3;
/*
* A STA that is not an AP shall use a transmit power less than or
* equal to the local maximum transmit power level for the channel.
* The local maximum transmit power can be calculated from the formula:
* local max TX pwr = max TX pwr - local pwr constraint
* Where max TX pwr is maximum transmit power level specified for
* channel in Country element and local pwr constraint is specified
* for channel in this Power Constraint element.
*/
/* Element ID */
*pos++ = WLAN_EID_PWR_CONSTRAINT;
/* Length */
*pos++ = 1;
/* Local Power Constraint */
if (local_pwr_constraint)
*pos++ = local_pwr_constraint;
else
*pos++ = hapd->iconf->local_pwr_constraint;
return pos;
}
static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing,
struct hostapd_channel_data *start,
struct hostapd_channel_data *prev)
{
if (end - pos < 3)
return pos;
/* first channel number */
*pos++ = start->chan;
/* number of channels */
*pos++ = (prev->chan - start->chan) / chan_spacing + 1;
/* maximum transmit power level */
*pos++ = start->max_tx_power;
return pos;
}
static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
int max_len)
{
u8 *pos = eid;
u8 *end = eid + max_len;
int i;
struct hostapd_hw_modes *mode;
struct hostapd_channel_data *start, *prev;
int chan_spacing = 1;
if (!hapd->iconf->ieee80211d || max_len < 6 ||
hapd->iface->current_mode == NULL)
return eid;
*pos++ = WLAN_EID_COUNTRY;
pos++; /* length will be set later */
os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */
pos += 3;
mode = hapd->iface->current_mode;
if (mode->mode == HOSTAPD_MODE_IEEE80211A)
chan_spacing = 4;
start = prev = NULL;
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *chan = &mode->channels[i];
if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
if (start && prev &&
prev->chan + chan_spacing == chan->chan &&
start->max_tx_power == chan->max_tx_power) {
prev = chan;
continue; /* can use same entry */
}
if (start && prev) {
pos = hostapd_eid_country_add(pos, end, chan_spacing,
start, prev);
start = NULL;
}
/* Start new group */
start = prev = chan;
}
if (start) {
pos = hostapd_eid_country_add(pos, end, chan_spacing,
start, prev);
}
if ((pos - eid) & 1) {
if (end - pos < 1)
return eid;
*pos++ = 0; /* pad for 16-bit alignment */
}
eid[1] = (pos - eid) - 2;
return pos;
}
const u8 * hostapd_wpa_ie(struct hostapd_data *hapd, u8 eid)
{
const u8 *ies;
size_t ies_len;
ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len);
if (!ies)
return NULL;
return get_ie(ies, ies_len, eid);
}
static const u8 * hostapd_vendor_wpa_ie(struct hostapd_data *hapd,
u32 vendor_type)
{
const u8 *ies;
size_t ies_len;
ies = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ies_len);
if (!ies)
return NULL;
return get_vendor_ie(ies, ies_len, vendor_type);
}
static u8 * hostapd_get_rsne(struct hostapd_data *hapd, u8 *pos, size_t len)
{
const u8 *ie;
ie = hostapd_wpa_ie(hapd, WLAN_EID_RSN);
if (!ie || 2U + ie[1] > len)
return pos;
os_memcpy(pos, ie, 2 + ie[1]);
return pos + 2 + ie[1];
}
static u8 * hostapd_get_mde(struct hostapd_data *hapd, u8 *pos, size_t len)
{
const u8 *ie;
ie = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN);
if (!ie || 2U + ie[1] > len)
return pos;
os_memcpy(pos, ie, 2 + ie[1]);
return pos + 2 + ie[1];
}
static u8 * hostapd_get_rsnxe(struct hostapd_data *hapd, u8 *pos, size_t len)
{
const u8 *ie;
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->no_beacon_rsnxe) {
wpa_printf(MSG_INFO, "TESTING: Do not add RSNXE into Beacon");
return pos;
}
#endif /* CONFIG_TESTING_OPTIONS */
ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
if (!ie || 2U + ie[1] > len)
return pos;
os_memcpy(pos, ie, 2 + ie[1]);
return pos + 2 + ie[1];
}
static u8 * hostapd_get_wpa_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
{
const u8 *ie;
ie = hostapd_vendor_wpa_ie(hapd, WPA_IE_VENDOR_TYPE);
if (!ie || 2U + ie[1] > len)
return pos;
os_memcpy(pos, ie, 2 + ie[1]);
return pos + 2 + ie[1];
}
static u8 * hostapd_get_osen_ie(struct hostapd_data *hapd, u8 *pos, size_t len)
{
const u8 *ie;
ie = hostapd_vendor_wpa_ie(hapd, OSEN_IE_VENDOR_TYPE);
if (!ie || 2U + ie[1] > len)
return pos;
os_memcpy(pos, ie, 2 + ie[1]);
return pos + 2 + ie[1];
}
static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
{
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only)
return eid;
#endif /* CONFIG_TESTING_OPTIONS */
if (!hapd->cs_freq_params.channel)
return eid;
*eid++ = WLAN_EID_CHANNEL_SWITCH;
*eid++ = 3;
*eid++ = hapd->cs_block_tx;
*eid++ = hapd->cs_freq_params.channel;
*eid++ = hapd->cs_count;
return eid;
}
static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
{
if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
return eid;
*eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
*eid++ = 4;
*eid++ = hapd->cs_block_tx;
*eid++ = hapd->iface->cs_oper_class;
*eid++ = hapd->cs_freq_params.channel;
*eid++ = hapd->cs_count;
return eid;
}
static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
{
u8 op_class, channel;
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) ||
!hapd->iface->freq)
return eid;
if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
hapd->iconf->secondary_channel,
hostapd_get_oper_chwidth(hapd->iconf),
&op_class, &channel) ==
NUM_HOSTAPD_MODES)
return eid;
*eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES;
*eid++ = 2;
/* Current Operating Class */
*eid++ = op_class;
/* TODO: Advertise all the supported operating classes */
*eid++ = 0;
return eid;
}
static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
const struct ieee80211_mgmt *req,
int is_p2p, size_t *resp_len)
{
struct ieee80211_mgmt *resp;
u8 *pos, *epos, *csa_pos;
size_t buflen;
#define MAX_PROBERESP_LEN 768
buflen = MAX_PROBERESP_LEN;
#ifdef CONFIG_WPS
if (hapd->wps_probe_resp_ie)
buflen += wpabuf_len(hapd->wps_probe_resp_ie);
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
if (hapd->p2p_probe_resp_ie)
buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
#endif /* CONFIG_P2P */
#ifdef CONFIG_FST
if (hapd->iface->fst_ies)
buflen += wpabuf_len(hapd->iface->fst_ies);
#endif /* CONFIG_FST */
if (hapd->conf->vendor_elements)
buflen += wpabuf_len(hapd->conf->vendor_elements);
if (hapd->conf->vendor_vht) {
buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
2 + sizeof(struct ieee80211_vht_operation);
}
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
3 + sizeof(struct ieee80211_he_operation) +
3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
3 + sizeof(struct ieee80211_spatial_reuse);
if (is_6ghz_op_class(hapd->iconf->op_class))
buflen += sizeof(struct ieee80211_he_6ghz_oper_info) +
3 + sizeof(struct ieee80211_he_6ghz_band_cap);
}
#endif /* CONFIG_IEEE80211AX */
+ buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
buflen += hostapd_mbo_ie_len(hapd);
buflen += hostapd_eid_owe_trans_len(hapd);
buflen += hostapd_eid_dpp_cc_len(hapd);
resp = os_zalloc(buflen);
if (resp == NULL)
return NULL;
epos = ((u8 *) resp) + MAX_PROBERESP_LEN;
resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_PROBE_RESP);
if (req)
os_memcpy(resp->da, req->sa, ETH_ALEN);
os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
resp->u.probe_resp.beacon_int =
host_to_le16(hapd->iconf->beacon_int);
/* hardware or low-level driver will setup seq_ctrl and timestamp */
resp->u.probe_resp.capab_info =
host_to_le16(hostapd_own_capab_info(hapd));
pos = resp->u.probe_resp.variable;
*pos++ = WLAN_EID_SSID;
*pos++ = hapd->conf->ssid.ssid_len;
os_memcpy(pos, hapd->conf->ssid.ssid, hapd->conf->ssid.ssid_len);
pos += hapd->conf->ssid.ssid_len;
/* Supported rates */
pos = hostapd_eid_supp_rates(hapd, pos);
/* DS Params */
pos = hostapd_eid_ds_params(hapd, pos);
pos = hostapd_eid_country(hapd, pos, epos - pos);
/* Power Constraint element */
pos = hostapd_eid_pwr_constraint(hapd, pos);
/* CSA IE */
csa_pos = hostapd_eid_csa(hapd, pos);
if (csa_pos != pos)
hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1;
pos = csa_pos;
/* ERP Information element */
pos = hostapd_eid_erp_info(hapd, pos);
/* Extended supported rates */
pos = hostapd_eid_ext_supp_rates(hapd, pos);
pos = hostapd_get_rsne(hapd, pos, epos - pos);
pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
pos = hostapd_get_mde(hapd, pos, epos - pos);
/* eCSA IE */
csa_pos = hostapd_eid_ecsa(hapd, pos);
if (csa_pos != pos)
hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1;
pos = csa_pos;
pos = hostapd_eid_supported_op_classes(hapd, pos);
pos = hostapd_eid_ht_capabilities(hapd, pos);
pos = hostapd_eid_ht_operation(hapd, pos);
pos = hostapd_eid_ext_capab(hapd, pos);
pos = hostapd_eid_time_adv(hapd, pos);
pos = hostapd_eid_time_zone(hapd, pos);
pos = hostapd_eid_interworking(hapd, pos);
pos = hostapd_eid_adv_proto(hapd, pos);
pos = hostapd_eid_roaming_consortium(hapd, pos);
#ifdef CONFIG_FST
if (hapd->iface->fst_ies) {
os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies),
wpabuf_len(hapd->iface->fst_ies));
pos += wpabuf_len(hapd->iface->fst_ies);
}
#endif /* CONFIG_FST */
#ifdef CONFIG_IEEE80211AC
if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
!is_6ghz_op_class(hapd->iconf->op_class)) {
pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
pos = hostapd_eid_vht_operation(hapd, pos);
pos = hostapd_eid_txpower_envelope(hapd, pos);
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
is_6ghz_op_class(hapd->iconf->op_class))
pos = hostapd_eid_txpower_envelope(hapd, pos);
#endif /* CONFIG_IEEE80211AX */
if ((hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) ||
(hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax))
pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
pos = hostapd_eid_fils_indic(hapd, pos, 0);
pos = hostapd_get_rsnxe(hapd, pos, epos - pos);
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP);
pos = hostapd_eid_he_operation(hapd, pos);
pos = hostapd_eid_spatial_reuse(hapd, pos);
pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
pos = hostapd_eid_he_6ghz_band_cap(hapd, pos);
}
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_IEEE80211AC
if (hapd->conf->vendor_vht)
pos = hostapd_eid_vendor_vht(hapd, pos);
#endif /* CONFIG_IEEE80211AC */
/* WPA / OSEN */
pos = hostapd_get_wpa_ie(hapd, pos, epos - pos);
pos = hostapd_get_osen_ie(hapd, pos, epos - pos);
/* Wi-Fi Alliance WMM */
pos = hostapd_eid_wmm(hapd, pos);
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
wpabuf_len(hapd->wps_probe_resp_ie));
pos += wpabuf_len(hapd->wps_probe_resp_ie);
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
if ((hapd->conf->p2p & P2P_ENABLED) && is_p2p &&
hapd->p2p_probe_resp_ie) {
os_memcpy(pos, wpabuf_head(hapd->p2p_probe_resp_ie),
wpabuf_len(hapd->p2p_probe_resp_ie));
pos += wpabuf_len(hapd->p2p_probe_resp_ie);
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_P2P_MANAGER
if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
P2P_MANAGE)
pos = hostapd_eid_p2p_manage(hapd, pos);
#endif /* CONFIG_P2P_MANAGER */
#ifdef CONFIG_HS20
pos = hostapd_eid_hs20_indication(hapd, pos);
#endif /* CONFIG_HS20 */
pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
pos = hostapd_eid_dpp_cc(hapd, pos, (u8 *) resp + buflen - pos);
if (hapd->conf->vendor_elements) {
os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
wpabuf_len(hapd->conf->vendor_elements));
pos += wpabuf_len(hapd->conf->vendor_elements);
}
*resp_len = pos - (u8 *) resp;
return (u8 *) resp;
}
enum ssid_match_result {
NO_SSID_MATCH,
EXACT_SSID_MATCH,
- WILDCARD_SSID_MATCH
+ WILDCARD_SSID_MATCH,
+ CO_LOCATED_SSID_MATCH,
};
static enum ssid_match_result ssid_match(struct hostapd_data *hapd,
const u8 *ssid, size_t ssid_len,
const u8 *ssid_list,
size_t ssid_list_len,
const u8 *short_ssid_list,
size_t short_ssid_list_len)
{
const u8 *pos, *end;
+ struct hostapd_iface *iface = hapd->iface;
int wildcard = 0;
+ size_t i, j;
if (ssid_len == 0)
wildcard = 1;
if (ssid_len == hapd->conf->ssid.ssid_len &&
os_memcmp(ssid, hapd->conf->ssid.ssid, ssid_len) == 0)
return EXACT_SSID_MATCH;
if (ssid_list) {
pos = ssid_list;
end = ssid_list + ssid_list_len;
while (end - pos >= 2) {
if (2 + pos[1] > end - pos)
break;
if (pos[1] == 0)
wildcard = 1;
if (pos[1] == hapd->conf->ssid.ssid_len &&
os_memcmp(pos + 2, hapd->conf->ssid.ssid,
pos[1]) == 0)
return EXACT_SSID_MATCH;
pos += 2 + pos[1];
}
}
if (short_ssid_list) {
pos = short_ssid_list;
end = short_ssid_list + short_ssid_list_len;
while (end - pos >= 4) {
if (hapd->conf->ssid.short_ssid == WPA_GET_LE32(pos))
return EXACT_SSID_MATCH;
pos += 4;
}
}
- return wildcard ? WILDCARD_SSID_MATCH : NO_SSID_MATCH;
+ if (wildcard)
+ return WILDCARD_SSID_MATCH;
+
+ if (!iface->interfaces || iface->interfaces->count <= 1 ||
+ is_6ghz_op_class(hapd->iconf->op_class))
+ return NO_SSID_MATCH;
+
+ for (i = 0; i < iface->interfaces->count; i++) {
+ struct hostapd_iface *colocated;
+
+ colocated = iface->interfaces->iface[i];
+
+ if (colocated == iface ||
+ !is_6ghz_op_class(colocated->conf->op_class))
+ continue;
+
+ for (j = 0; j < colocated->num_bss; j++) {
+ struct hostapd_bss_config *conf;
+
+ conf = colocated->bss[j]->conf;
+ if (ssid_len == conf->ssid.ssid_len &&
+ os_memcmp(ssid, conf->ssid.ssid, ssid_len) == 0)
+ return CO_LOCATED_SSID_MATCH;
+ }
+ }
+
+ return NO_SSID_MATCH;
}
void sta_track_expire(struct hostapd_iface *iface, int force)
{
struct os_reltime now;
struct hostapd_sta_info *info;
if (!iface->num_sta_seen)
return;
os_get_reltime(&now);
while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
list))) {
if (!force &&
!os_reltime_expired(&now, &info->last_seen,
iface->conf->track_sta_max_age))
break;
force = 0;
wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for "
MACSTR, iface->bss[0]->conf->iface,
MAC2STR(info->addr));
dl_list_del(&info->list);
iface->num_sta_seen--;
sta_track_del(info);
}
}
static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
const u8 *addr)
{
struct hostapd_sta_info *info;
dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
return info;
return NULL;
}
void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
{
struct hostapd_sta_info *info;
info = sta_track_get(iface, addr);
if (info) {
/* Move the most recent entry to the end of the list */
dl_list_del(&info->list);
dl_list_add_tail(&iface->sta_seen, &info->list);
os_get_reltime(&info->last_seen);
info->ssi_signal = ssi_signal;
return;
}
/* Add a new entry */
info = os_zalloc(sizeof(*info));
if (info == NULL)
return;
os_memcpy(info->addr, addr, ETH_ALEN);
os_get_reltime(&info->last_seen);
info->ssi_signal = ssi_signal;
if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
/* Expire oldest entry to make room for a new one */
sta_track_expire(iface, 1);
}
wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for "
MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr));
dl_list_add_tail(&iface->sta_seen, &info->list);
iface->num_sta_seen++;
}
struct hostapd_data *
sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
const char *ifname)
{
struct hapd_interfaces *interfaces = iface->interfaces;
size_t i, j;
for (i = 0; i < interfaces->count; i++) {
struct hostapd_data *hapd = NULL;
iface = interfaces->iface[i];
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
if (os_strcmp(ifname, hapd->conf->iface) == 0)
break;
hapd = NULL;
}
if (hapd && sta_track_get(iface, addr))
return hapd;
}
return NULL;
}
#ifdef CONFIG_TAXONOMY
void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
struct wpabuf **probe_ie_taxonomy)
{
struct hostapd_sta_info *info;
info = sta_track_get(iface, addr);
if (!info)
return;
wpabuf_free(*probe_ie_taxonomy);
*probe_ie_taxonomy = info->probe_ie_taxonomy;
info->probe_ie_taxonomy = NULL;
}
#endif /* CONFIG_TAXONOMY */
void handle_probe_req(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int ssi_signal)
{
u8 *resp;
struct ieee802_11_elems elems;
const u8 *ie;
size_t ie_len;
size_t i, resp_len;
int noack;
enum ssid_match_result res;
int ret;
u16 csa_offs[2];
size_t csa_offs_len;
struct radius_sta rad_info;
if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
ssi_signal < hapd->iconf->rssi_ignore_probe_request)
return;
if (len < IEEE80211_HDRLEN)
return;
ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN;
if (hapd->iconf->track_sta_max_num)
sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
ie_len = len - IEEE80211_HDRLEN;
ret = hostapd_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
&rad_info, 1);
if (ret == HOSTAPD_ACL_REJECT) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Ignore Probe Request frame from " MACSTR
" due to ACL reject ", MAC2STR(mgmt->sa));
return;
}
for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
mgmt->sa, mgmt->da, mgmt->bssid,
ie, ie_len, ssi_signal) > 0)
return;
if (!hapd->conf->send_probe_response)
return;
if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR,
MAC2STR(mgmt->sa));
return;
}
if ((!elems.ssid || !elems.supp_rates)) {
wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request "
"without SSID or supported rates element",
MAC2STR(mgmt->sa));
return;
}
/*
* No need to reply if the Probe Request frame was sent on an adjacent
* channel. IEEE Std 802.11-2012 describes this as a requirement for an
* AP with dot11RadioMeasurementActivated set to true, but strictly
* speaking does not allow such ignoring of Probe Request frames if
* dot11RadioMeasurementActivated is false. Anyway, this can help reduce
* number of unnecessary Probe Response frames for cases where the STA
* is less likely to see them (Probe Request frame sent on a
* neighboring, but partially overlapping, channel).
*/
if (elems.ds_params &&
hapd->iface->current_mode &&
(hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
hapd->iconf->channel != elems.ds_params[0]) {
wpa_printf(MSG_DEBUG,
"Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u",
hapd->iconf->channel, elems.ds_params[0]);
return;
}
#ifdef CONFIG_P2P
if (hapd->p2p && hapd->p2p_group && elems.wps_ie) {
struct wpabuf *wps;
wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
if (wps && !p2p_group_match_dev_type(hapd->p2p_group, wps)) {
wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
"due to mismatch with Requested Device "
"Type");
wpabuf_free(wps);
return;
}
wpabuf_free(wps);
}
if (hapd->p2p && hapd->p2p_group && elems.p2p) {
struct wpabuf *p2p;
p2p = ieee802_11_vendor_ie_concat(ie, ie_len, P2P_IE_VENDOR_TYPE);
if (p2p && !p2p_group_match_dev_id(hapd->p2p_group, p2p)) {
wpa_printf(MSG_MSGDUMP, "P2P: Ignore Probe Request "
"due to mismatch with Device ID");
wpabuf_free(p2p);
return;
}
wpabuf_free(p2p);
}
#endif /* CONFIG_P2P */
if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0 &&
elems.ssid_list_len == 0 && elems.short_ssid_list_len == 0) {
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
"broadcast SSID ignored", MAC2STR(mgmt->sa));
return;
}
#ifdef CONFIG_P2P
if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
P2P_WILDCARD_SSID_LEN) == 0) {
/* Process P2P Wildcard SSID like Wildcard SSID */
elems.ssid_len = 0;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_TAXONOMY
{
struct sta_info *sta;
struct hostapd_sta_info *info;
if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) {
taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len);
} else if ((info = sta_track_get(hapd->iface,
mgmt->sa)) != NULL) {
taxonomy_hostapd_sta_info_probe_req(hapd, info,
ie, ie_len);
}
}
#endif /* CONFIG_TAXONOMY */
res = ssid_match(hapd, elems.ssid, elems.ssid_len,
elems.ssid_list, elems.ssid_list_len,
elems.short_ssid_list, elems.short_ssid_list_len);
if (res == NO_SSID_MATCH) {
if (!(mgmt->da[0] & 0x01)) {
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
" for foreign SSID '%s' (DA " MACSTR ")%s",
MAC2STR(mgmt->sa),
wpa_ssid_txt(elems.ssid, elems.ssid_len),
MAC2STR(mgmt->da),
elems.ssid_list ? " (SSID list)" : "");
}
return;
}
if (hapd->conf->ignore_broadcast_ssid && res == WILDCARD_SSID_MATCH) {
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for "
"broadcast SSID ignored", MAC2STR(mgmt->sa));
return;
}
#ifdef CONFIG_INTERWORKING
if (hapd->conf->interworking &&
elems.interworking && elems.interworking_len >= 1) {
u8 ant = elems.interworking[0] & 0x0f;
if (ant != INTERWORKING_ANT_WILDCARD &&
ant != hapd->conf->access_network_type) {
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
" for mismatching ANT %u ignored",
MAC2STR(mgmt->sa), ant);
return;
}
}
if (hapd->conf->interworking && elems.interworking &&
(elems.interworking_len == 7 || elems.interworking_len == 9)) {
const u8 *hessid;
if (elems.interworking_len == 7)
hessid = elems.interworking + 1;
else
hessid = elems.interworking + 1 + 2;
if (!is_broadcast_ether_addr(hessid) &&
os_memcmp(hessid, hapd->conf->hessid, ETH_ALEN) != 0) {
wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
" for mismatching HESSID " MACSTR
" ignored",
MAC2STR(mgmt->sa), MAC2STR(hessid));
return;
}
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_P2P
if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
supp_rates_11b_only(&elems)) {
/* Indicates support for 11b rates only */
wpa_printf(MSG_EXCESSIVE, "P2P: Ignore Probe Request from "
MACSTR " with only 802.11b rates",
MAC2STR(mgmt->sa));
return;
}
#endif /* CONFIG_P2P */
/* TODO: verify that supp_rates contains at least one matching rate
* with AP configuration */
if (hapd->conf->no_probe_resp_if_seen_on &&
is_multicast_ether_addr(mgmt->da) &&
is_multicast_ether_addr(mgmt->bssid) &&
sta_track_seen_on(hapd->iface, mgmt->sa,
hapd->conf->no_probe_resp_if_seen_on)) {
wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
" since STA has been seen on %s",
hapd->conf->iface, MAC2STR(mgmt->sa),
hapd->conf->no_probe_resp_if_seen_on);
return;
}
if (hapd->conf->no_probe_resp_if_max_sta &&
is_multicast_ether_addr(mgmt->da) &&
is_multicast_ether_addr(mgmt->bssid) &&
hapd->num_sta >= hapd->conf->max_num_sta &&
!ap_get_sta(hapd, mgmt->sa)) {
wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
" since no room for additional STA",
hapd->conf->iface, MAC2STR(mgmt->sa));
return;
}
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->iconf->ignore_probe_probability > 0.0 &&
drand48() < hapd->iconf->ignore_probe_probability) {
wpa_printf(MSG_INFO,
"TESTING: ignoring probe request from " MACSTR,
MAC2STR(mgmt->sa));
return;
}
#endif /* CONFIG_TESTING_OPTIONS */
wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR
" signal=%d", MAC2STR(mgmt->sa), ssi_signal);
resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
&resp_len);
if (resp == NULL)
return;
/*
* If this is a broadcast probe request, apply no ack policy to avoid
* excessive retries.
*/
noack = !!(res == WILDCARD_SSID_MATCH &&
is_broadcast_ether_addr(mgmt->da));
csa_offs_len = 0;
if (hapd->csa_in_progress) {
if (hapd->cs_c_off_proberesp)
csa_offs[csa_offs_len++] =
hapd->cs_c_off_proberesp;
if (hapd->cs_c_off_ecsa_proberesp)
csa_offs[csa_offs_len++] =
hapd->cs_c_off_ecsa_proberesp;
}
ret = hostapd_drv_send_mlme(hapd, resp, resp_len, noack,
csa_offs_len ? csa_offs : NULL,
csa_offs_len, 0);
if (ret < 0)
wpa_printf(MSG_INFO, "handle_probe_req: send failed");
os_free(resp);
wpa_printf(MSG_EXCESSIVE, "STA " MACSTR " sent probe request for %s "
"SSID", MAC2STR(mgmt->sa),
elems.ssid_len == 0 ? "broadcast" : "our");
}
static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
size_t *resp_len)
{
/* check probe response offloading caps and print warnings */
if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD))
return NULL;
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && hapd->wps_probe_resp_ie &&
(!(hapd->iface->probe_resp_offloads &
(WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS |
WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2))))
wpa_printf(MSG_WARNING, "Device is trying to offload WPS "
"Probe Response while not supporting this");
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_probe_resp_ie &&
!(hapd->iface->probe_resp_offloads &
WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P))
wpa_printf(MSG_WARNING, "Device is trying to offload P2P "
"Probe Response while not supporting this");
#endif /* CONFIG_P2P */
if (hapd->conf->interworking &&
!(hapd->iface->probe_resp_offloads &
WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING))
wpa_printf(MSG_WARNING, "Device is trying to offload "
"Interworking Probe Response while not supporting "
"this");
/* Generate a Probe Response template for the non-P2P case */
return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len);
}
#endif /* NEED_AP_MLME */
#ifdef CONFIG_IEEE80211AX
/* Unsolicited broadcast Probe Response transmission, 6 GHz only */
static u8 * hostapd_unsol_bcast_probe_resp(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params)
{
if (!is_6ghz_op_class(hapd->iconf->op_class))
return NULL;
params->unsol_bcast_probe_resp_interval =
hapd->conf->unsol_bcast_probe_resp_interval;
return hostapd_gen_probe_resp(hapd, NULL, 0,
&params->unsol_bcast_probe_resp_tmpl_len);
}
#endif /* CONFIG_IEEE80211AX */
void sta_track_del(struct hostapd_sta_info *info)
{
#ifdef CONFIG_TAXONOMY
wpabuf_free(info->probe_ie_taxonomy);
info->probe_ie_taxonomy = NULL;
#endif /* CONFIG_TAXONOMY */
os_free(info);
}
#ifdef CONFIG_FILS
static u16 hostapd_fils_discovery_cap(struct hostapd_data *hapd)
{
u16 cap_info, phy_index = 0;
u8 chwidth = FD_CAP_BSS_CHWIDTH_20, mcs_nss_size = 4;
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
cap_info = FD_CAP_ESS;
if (hapd->conf->wpa)
cap_info |= FD_CAP_PRIVACY;
if (is_6ghz_op_class(hapd->iconf->op_class)) {
phy_index = FD_CAP_PHY_INDEX_HE;
switch (hapd->iconf->op_class) {
case 135:
mcs_nss_size += 4;
/* fallthrough */
case 134:
mcs_nss_size += 4;
chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
break;
case 133:
chwidth = FD_CAP_BSS_CHWIDTH_80;
break;
case 132:
chwidth = FD_CAP_BSS_CHWIDTH_40;
break;
}
} else {
switch (hostapd_get_oper_chwidth(hapd->iconf)) {
case CHANWIDTH_80P80MHZ:
mcs_nss_size += 4;
/* fallthrough */
case CHANWIDTH_160MHZ:
mcs_nss_size += 4;
chwidth = FD_CAP_BSS_CHWIDTH_160_80_80;
break;
case CHANWIDTH_80MHZ:
chwidth = FD_CAP_BSS_CHWIDTH_80;
break;
case CHANWIDTH_USE_HT:
if (hapd->iconf->secondary_channel)
chwidth = FD_CAP_BSS_CHWIDTH_40;
else
chwidth = FD_CAP_BSS_CHWIDTH_20;
break;
}
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax)
phy_index = FD_CAP_PHY_INDEX_HE;
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_IEEE80211AC
if (!phy_index &&
hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac)
phy_index = FD_CAP_PHY_INDEX_VHT;
#endif /* CONFIG_IEEE80211AC */
if (!phy_index &&
hapd->iconf->ieee80211n && !hapd->conf->disable_11n)
phy_index = FD_CAP_PHY_INDEX_HT;
}
cap_info |= phy_index << FD_CAP_PHY_INDEX_SHIFT;
cap_info |= chwidth << FD_CAP_BSS_CHWIDTH_SHIFT;
if (mode) {
u16 *mcs = (u16 *) mode->he_capab[IEEE80211_MODE_AP].mcs;
int i;
u16 nss = 0;
for (i = 0; i < HE_NSS_MAX_STREAMS; i++) {
u16 nss_mask = 0x3 << (i * 2);
if (mcs_nss_size == 4 &&
(((mcs[0] & nss_mask) == nss_mask) ||
((mcs[1] & nss_mask) == nss_mask)))
continue;
if (mcs_nss_size == 8 &&
(((mcs[2] & nss_mask) == nss_mask) ||
((mcs[3] & nss_mask) == nss_mask)))
continue;
if (mcs_nss_size == 12 &&
(((mcs[4] & nss_mask) == nss_mask) ||
((mcs[5] & nss_mask) == nss_mask)))
continue;
nss++;
}
if (nss > 4)
cap_info |= FD_CAP_NSS_5_8 << FD_CAP_NSS_SHIFT;
else if (nss)
cap_info |= (nss - 1) << FD_CAP_NSS_SHIFT;
}
return cap_info;
}
static u8 * hostapd_gen_fils_discovery(struct hostapd_data *hapd, size_t *len)
{
struct ieee80211_mgmt *head;
const u8 *mobility_domain;
u8 *pos, *length_pos, buf[200];
u16 ctl = 0;
u8 fd_rsn_info[5];
size_t total_len, buf_len;
total_len = 24 + 2 + 12;
/* FILS Discovery Frame Control */
ctl = (sizeof(hapd->conf->ssid.short_ssid) - 1) |
FD_FRAME_CTL_SHORT_SSID_PRESENT |
FD_FRAME_CTL_LENGTH_PRESENT |
FD_FRAME_CTL_CAP_PRESENT;
total_len += 4 + 1 + 2;
/* Check for optional subfields and calculate length */
if (wpa_auth_write_fd_rsn_info(hapd->wpa_auth, fd_rsn_info)) {
ctl |= FD_FRAME_CTL_RSN_INFO_PRESENT;
total_len += sizeof(fd_rsn_info);
}
mobility_domain = hostapd_wpa_ie(hapd, WLAN_EID_MOBILITY_DOMAIN);
if (mobility_domain) {
ctl |= FD_FRAME_CTL_MD_PRESENT;
total_len += 3;
}
+ total_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_ACTION);
+
pos = hostapd_eid_fils_indic(hapd, buf, 0);
buf_len = pos - buf;
total_len += buf_len;
head = os_zalloc(total_len);
if (!head)
return NULL;
head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memset(head->da, 0xff, ETH_ALEN);
os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
head->u.action.category = WLAN_ACTION_PUBLIC;
head->u.action.u.public_action.action = WLAN_PA_FILS_DISCOVERY;
pos = &head->u.action.u.public_action.variable[0];
/* FILS Discovery Information field */
/* FILS Discovery Frame Control */
WPA_PUT_LE16(pos, ctl);
pos += 2;
/* Hardware or low-level driver will fill in the Timestamp value */
pos += 8;
/* Beacon Interval */
WPA_PUT_LE16(pos, hapd->iconf->beacon_int);
pos += 2;
/* Short SSID */
WPA_PUT_LE32(pos, hapd->conf->ssid.short_ssid);
pos += sizeof(hapd->conf->ssid.short_ssid);
/* Store position of FILS discovery information element Length field */
length_pos = pos++;
/* FD Capability */
WPA_PUT_LE16(pos, hostapd_fils_discovery_cap(hapd));
pos += 2;
/* Operating Class - not present */
/* Primary Channel - not present */
/* AP Configuration Sequence Number - not present */
/* Access Network Options - not present */
/* FD RSN Information */
if (ctl & FD_FRAME_CTL_RSN_INFO_PRESENT) {
os_memcpy(pos, fd_rsn_info, sizeof(fd_rsn_info));
pos += sizeof(fd_rsn_info);
}
/* Channel Center Frequency Segment 1 - not present */
/* Mobility Domain */
if (ctl & FD_FRAME_CTL_MD_PRESENT) {
os_memcpy(pos, &mobility_domain[2], 3);
pos += 3;
}
/* Fill in the Length field value */
*length_pos = pos - (length_pos + 1);
+ pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_ACTION);
+
/* FILS Indication element */
if (buf_len) {
os_memcpy(pos, buf, buf_len);
pos += buf_len;
}
*len = pos - (u8 *) head;
wpa_hexdump(MSG_DEBUG, "FILS Discovery frame template",
head, pos - (u8 *) head);
return (u8 *) head;
}
/* Configure FILS Discovery frame transmission parameters */
static u8 * hostapd_fils_discovery(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params)
{
params->fd_max_int = hapd->conf->fils_discovery_max_int;
if (is_6ghz_op_class(hapd->iconf->op_class) &&
params->fd_max_int > FD_MAX_INTERVAL_6GHZ)
params->fd_max_int = FD_MAX_INTERVAL_6GHZ;
params->fd_min_int = hapd->conf->fils_discovery_min_int;
if (params->fd_min_int > params->fd_max_int)
params->fd_min_int = params->fd_max_int;
if (params->fd_max_int)
return hostapd_gen_fils_discovery(hapd,
&params->fd_frame_tmpl_len);
return NULL;
}
#endif /* CONFIG_FILS */
int ieee802_11_build_ap_params(struct hostapd_data *hapd,
struct wpa_driver_ap_params *params)
{
struct ieee80211_mgmt *head = NULL;
u8 *tail = NULL;
size_t head_len = 0, tail_len = 0;
u8 *resp = NULL;
size_t resp_len = 0;
#ifdef NEED_AP_MLME
u16 capab_info;
u8 *pos, *tailpos, *tailend, *csa_pos;
#define BEACON_HEAD_BUF_SIZE 256
#define BEACON_TAIL_BUF_SIZE 512
head = os_zalloc(BEACON_HEAD_BUF_SIZE);
tail_len = BEACON_TAIL_BUF_SIZE;
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && hapd->wps_beacon_ie)
tail_len += wpabuf_len(hapd->wps_beacon_ie);
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
if (hapd->p2p_beacon_ie)
tail_len += wpabuf_len(hapd->p2p_beacon_ie);
#endif /* CONFIG_P2P */
#ifdef CONFIG_FST
if (hapd->iface->fst_ies)
tail_len += wpabuf_len(hapd->iface->fst_ies);
#endif /* CONFIG_FST */
if (hapd->conf->vendor_elements)
tail_len += wpabuf_len(hapd->conf->vendor_elements);
#ifdef CONFIG_IEEE80211AC
if (hapd->conf->vendor_vht) {
tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
2 + sizeof(struct ieee80211_vht_operation);
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
3 + sizeof(struct ieee80211_he_operation) +
3 + sizeof(struct ieee80211_he_mu_edca_parameter_set) +
3 + sizeof(struct ieee80211_spatial_reuse);
if (is_6ghz_op_class(hapd->iconf->op_class))
tail_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
3 + sizeof(struct ieee80211_he_6ghz_band_cap);
}
#endif /* CONFIG_IEEE80211AX */
+ tail_len += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_BEACON);
tail_len += hostapd_mbo_ie_len(hapd);
tail_len += hostapd_eid_owe_trans_len(hapd);
tail_len += hostapd_eid_dpp_cc_len(hapd);
tailpos = tail = os_malloc(tail_len);
if (head == NULL || tail == NULL) {
wpa_printf(MSG_ERROR, "Failed to set beacon data");
os_free(head);
os_free(tail);
return -1;
}
tailend = tail + tail_len;
head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_BEACON);
head->duration = host_to_le16(0);
os_memset(head->da, 0xff, ETH_ALEN);
os_memcpy(head->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN);
head->u.beacon.beacon_int =
host_to_le16(hapd->iconf->beacon_int);
/* hardware or low-level driver will setup seq_ctrl and timestamp */
capab_info = hostapd_own_capab_info(hapd);
head->u.beacon.capab_info = host_to_le16(capab_info);
pos = &head->u.beacon.variable[0];
/* SSID */
*pos++ = WLAN_EID_SSID;
if (hapd->conf->ignore_broadcast_ssid == 2) {
/* clear the data, but keep the correct length of the SSID */
*pos++ = hapd->conf->ssid.ssid_len;
os_memset(pos, 0, hapd->conf->ssid.ssid_len);
pos += hapd->conf->ssid.ssid_len;
} else if (hapd->conf->ignore_broadcast_ssid) {
*pos++ = 0; /* empty SSID */
} else {
*pos++ = hapd->conf->ssid.ssid_len;
os_memcpy(pos, hapd->conf->ssid.ssid,
hapd->conf->ssid.ssid_len);
pos += hapd->conf->ssid.ssid_len;
}
/* Supported rates */
pos = hostapd_eid_supp_rates(hapd, pos);
/* DS Params */
pos = hostapd_eid_ds_params(hapd, pos);
head_len = pos - (u8 *) head;
tailpos = hostapd_eid_country(hapd, tailpos, tailend - tailpos);
/* Power Constraint element */
tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
/* CSA IE */
csa_pos = hostapd_eid_csa(hapd, tailpos);
if (csa_pos != tailpos)
hapd->cs_c_off_beacon = csa_pos - tail - 1;
tailpos = csa_pos;
/* ERP Information element */
tailpos = hostapd_eid_erp_info(hapd, tailpos);
/* Extended supported rates */
tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos);
tailpos = hostapd_get_rsne(hapd, tailpos, tailend - tailpos);
tailpos = hostapd_eid_bss_load(hapd, tailpos, tailend - tailpos);
tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos,
tailend - tailpos);
tailpos = hostapd_get_mde(hapd, tailpos, tailend - tailpos);
/* eCSA IE */
csa_pos = hostapd_eid_ecsa(hapd, tailpos);
if (csa_pos != tailpos)
hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1;
tailpos = csa_pos;
tailpos = hostapd_eid_supported_op_classes(hapd, tailpos);
tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
tailpos = hostapd_eid_ht_operation(hapd, tailpos);
tailpos = hostapd_eid_ext_capab(hapd, tailpos);
/*
* TODO: Time Advertisement element should only be included in some
* DTIM Beacon frames.
*/
tailpos = hostapd_eid_time_adv(hapd, tailpos);
tailpos = hostapd_eid_interworking(hapd, tailpos);
tailpos = hostapd_eid_adv_proto(hapd, tailpos);
tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
#ifdef CONFIG_FST
if (hapd->iface->fst_ies) {
os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies),
wpabuf_len(hapd->iface->fst_ies));
tailpos += wpabuf_len(hapd->iface->fst_ies);
}
#endif /* CONFIG_FST */
#ifdef CONFIG_IEEE80211AC
if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
!is_6ghz_op_class(hapd->iconf->op_class)) {
tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0);
tailpos = hostapd_eid_vht_operation(hapd, tailpos);
tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax &&
is_6ghz_op_class(hapd->iconf->op_class))
tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
#endif /* CONFIG_IEEE80211AX */
if ((hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) ||
(hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax))
tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
+ tailpos = hostapd_eid_rnr(hapd, tailpos, WLAN_FC_STYPE_BEACON);
tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
tailpos = hostapd_get_rsnxe(hapd, tailpos, tailend - tailpos);
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
tailpos = hostapd_eid_he_capab(hapd, tailpos,
IEEE80211_MODE_AP);
tailpos = hostapd_eid_he_operation(hapd, tailpos);
tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
tailpos = hostapd_eid_he_6ghz_band_cap(hapd, tailpos);
}
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_IEEE80211AC
if (hapd->conf->vendor_vht)
tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
#endif /* CONFIG_IEEE80211AC */
/* WPA / OSEN */
tailpos = hostapd_get_wpa_ie(hapd, tailpos, tailend - tailpos);
tailpos = hostapd_get_osen_ie(hapd, tailpos, tailend - tailpos);
/* Wi-Fi Alliance WMM */
tailpos = hostapd_eid_wmm(hapd, tailpos);
#ifdef CONFIG_WPS
if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie),
wpabuf_len(hapd->wps_beacon_ie));
tailpos += wpabuf_len(hapd->wps_beacon_ie);
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
if ((hapd->conf->p2p & P2P_ENABLED) && hapd->p2p_beacon_ie) {
os_memcpy(tailpos, wpabuf_head(hapd->p2p_beacon_ie),
wpabuf_len(hapd->p2p_beacon_ie));
tailpos += wpabuf_len(hapd->p2p_beacon_ie);
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_P2P_MANAGER
if ((hapd->conf->p2p & (P2P_MANAGE | P2P_ENABLED | P2P_GROUP_OWNER)) ==
P2P_MANAGE)
tailpos = hostapd_eid_p2p_manage(hapd, tailpos);
#endif /* CONFIG_P2P_MANAGER */
#ifdef CONFIG_HS20
tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
#endif /* CONFIG_HS20 */
tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos);
tailpos = hostapd_eid_owe_trans(hapd, tailpos,
tail + tail_len - tailpos);
tailpos = hostapd_eid_dpp_cc(hapd, tailpos, tail + tail_len - tailpos);
if (hapd->conf->vendor_elements) {
os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
wpabuf_len(hapd->conf->vendor_elements));
tailpos += wpabuf_len(hapd->conf->vendor_elements);
}
tail_len = tailpos > tail ? tailpos - tail : 0;
resp = hostapd_probe_resp_offloads(hapd, &resp_len);
#endif /* NEED_AP_MLME */
os_memset(params, 0, sizeof(*params));
params->head = (u8 *) head;
params->head_len = head_len;
params->tail = tail;
params->tail_len = tail_len;
params->proberesp = resp;
params->proberesp_len = resp_len;
params->dtim_period = hapd->conf->dtim_period;
params->beacon_int = hapd->iconf->beacon_int;
params->basic_rates = hapd->iface->basic_rates;
params->beacon_rate = hapd->iconf->beacon_rate;
params->rate_type = hapd->iconf->rate_type;
params->ssid = hapd->conf->ssid.ssid;
params->ssid_len = hapd->conf->ssid.ssid_len;
if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
(WPA_PROTO_WPA | WPA_PROTO_RSN))
params->pairwise_ciphers = hapd->conf->wpa_pairwise |
hapd->conf->rsn_pairwise;
else if (hapd->conf->wpa & WPA_PROTO_RSN)
params->pairwise_ciphers = hapd->conf->rsn_pairwise;
else if (hapd->conf->wpa & WPA_PROTO_WPA)
params->pairwise_ciphers = hapd->conf->wpa_pairwise;
params->group_cipher = hapd->conf->wpa_group;
params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
params->auth_algs = hapd->conf->auth_algs;
params->wpa_version = hapd->conf->wpa;
params->privacy = hapd->conf->wpa;
#ifdef CONFIG_WEP
params->privacy |= hapd->conf->ssid.wep.keys_set ||
(hapd->conf->ieee802_1x &&
(hapd->conf->default_wep_key_len ||
hapd->conf->individual_wep_key_len));
#endif /* CONFIG_WEP */
switch (hapd->conf->ignore_broadcast_ssid) {
case 0:
params->hide_ssid = NO_SSID_HIDING;
break;
case 1:
params->hide_ssid = HIDDEN_SSID_ZERO_LEN;
break;
case 2:
params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
break;
}
params->isolate = hapd->conf->isolate;
#ifdef NEED_AP_MLME
params->cts_protect = !!(ieee802_11_erp_info(hapd) &
ERP_INFO_USE_PROTECTION);
params->preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
hapd->iconf->preamble == SHORT_PREAMBLE;
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
params->short_slot_time =
hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
else
params->short_slot_time = -1;
if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
params->ht_opmode = -1;
else
params->ht_opmode = hapd->iface->ht_op_mode;
#endif /* NEED_AP_MLME */
params->interworking = hapd->conf->interworking;
if (hapd->conf->interworking &&
!is_zero_ether_addr(hapd->conf->hessid))
params->hessid = hapd->conf->hessid;
params->access_network_type = hapd->conf->access_network_type;
params->ap_max_inactivity = hapd->conf->ap_max_inactivity;
#ifdef CONFIG_P2P
params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow;
#endif /* CONFIG_P2P */
#ifdef CONFIG_HS20
params->disable_dgaf = hapd->conf->disable_dgaf;
if (hapd->conf->osen) {
params->privacy = 1;
params->osen = 1;
}
#endif /* CONFIG_HS20 */
params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
params->pbss = hapd->conf->pbss;
if (hapd->conf->ftm_responder) {
if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) {
params->ftm_responder = 1;
params->lci = hapd->iface->conf->lci;
params->civic = hapd->iface->conf->civic;
} else {
wpa_printf(MSG_WARNING,
"Not configuring FTM responder as the driver doesn't advertise support for it");
}
}
return 0;
}
void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params)
{
os_free(params->tail);
params->tail = NULL;
os_free(params->head);
params->head = NULL;
os_free(params->proberesp);
params->proberesp = NULL;
#ifdef CONFIG_FILS
os_free(params->fd_frame_tmpl);
params->fd_frame_tmpl = NULL;
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211AX
os_free(params->unsol_bcast_probe_resp_tmpl);
params->unsol_bcast_probe_resp_tmpl = NULL;
#endif /* CONFIG_IEEE80211AX */
}
-int ieee802_11_set_beacon(struct hostapd_data *hapd)
+static int __ieee802_11_set_beacon(struct hostapd_data *hapd)
{
struct wpa_driver_ap_params params;
struct hostapd_freq_params freq;
struct hostapd_iface *iface = hapd->iface;
struct hostapd_config *iconf = iface->conf;
struct hostapd_hw_modes *cmode = iface->current_mode;
struct wpabuf *beacon, *proberesp, *assocresp;
int res, ret = -1;
if (!hapd->drv_priv) {
wpa_printf(MSG_ERROR, "Interface is disabled");
return -1;
}
if (hapd->csa_in_progress) {
wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period");
return -1;
}
hapd->beacon_set_done = 1;
if (ieee802_11_build_ap_params(hapd, &params) < 0)
return -1;
if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
0)
goto fail;
params.beacon_ies = beacon;
params.proberesp_ies = proberesp;
params.assocresp_ies = assocresp;
params.reenable = hapd->reenable_beacon;
#ifdef CONFIG_IEEE80211AX
params.he_spr_ctrl = hapd->iface->conf->spr.sr_control;
params.he_spr_non_srg_obss_pd_max_offset =
hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
params.he_spr_srg_obss_pd_min_offset =
hapd->iface->conf->spr.srg_obss_pd_min_offset;
params.he_spr_srg_obss_pd_max_offset =
hapd->iface->conf->spr.srg_obss_pd_max_offset;
os_memcpy(params.he_spr_bss_color_bitmap,
hapd->iface->conf->spr.srg_bss_color_bitmap, 8);
os_memcpy(params.he_spr_partial_bssid_bitmap,
hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8);
params.he_bss_color_disabled =
hapd->iface->conf->he_op.he_bss_color_disabled;
params.he_bss_color_partial =
hapd->iface->conf->he_op.he_bss_color_partial;
params.he_bss_color = hapd->iface->conf->he_op.he_bss_color;
params.twt_responder = hostapd_get_he_twt_responder(hapd,
IEEE80211_MODE_AP);
params.unsol_bcast_probe_resp_tmpl =
hostapd_unsol_bcast_probe_resp(hapd, &params);
#endif /* CONFIG_IEEE80211AX */
hapd->reenable_beacon = 0;
#ifdef CONFIG_SAE
params.sae_pwe = hapd->conf->sae_pwe;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
params.fd_frame_tmpl = hostapd_fils_discovery(hapd, &params);
#endif /* CONFIG_FILS */
if (cmode &&
hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
iconf->channel, iconf->enable_edmg,
iconf->edmg_channel, iconf->ieee80211n,
iconf->ieee80211ac, iconf->ieee80211ax,
iconf->secondary_channel,
hostapd_get_oper_chwidth(iconf),
hostapd_get_oper_centr_freq_seg0_idx(iconf),
hostapd_get_oper_centr_freq_seg1_idx(iconf),
cmode->vht_capab,
&cmode->he_capab[IEEE80211_MODE_AP]) == 0)
params.freq = &freq;
res = hostapd_drv_set_ap(hapd, &params);
hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
if (res)
wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
else
ret = 0;
fail:
ieee802_11_free_ap_params(&params);
return ret;
}
+int ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+ struct hostapd_iface *iface = hapd->iface;
+ int ret;
+ size_t i, j;
+ bool is_6g;
+
+ ret = __ieee802_11_set_beacon(hapd);
+ if (ret != 0)
+ return ret;
+
+ if (!iface->interfaces || iface->interfaces->count <= 1)
+ return 0;
+
+ /* Update Beacon frames in case of 6 GHz colocation */
+ is_6g = is_6ghz_op_class(iface->conf->op_class);
+ for (j = 0; j < iface->interfaces->count; j++) {
+ struct hostapd_iface *colocated;
+
+ colocated = iface->interfaces->iface[j];
+ if (colocated == iface || !colocated || !colocated->conf)
+ continue;
+
+ if (is_6g == is_6ghz_op_class(colocated->conf->op_class))
+ continue;
+
+ for (i = 0; i < colocated->num_bss; i++) {
+ if (colocated->bss[i] && colocated->bss[i]->started)
+ __ieee802_11_set_beacon(colocated->bss[i]);
+ }
+ }
+
+ return 0;
+}
+
+
int ieee802_11_set_beacons(struct hostapd_iface *iface)
{
size_t i;
int ret = 0;
for (i = 0; i < iface->num_bss; i++) {
if (iface->bss[i]->started &&
ieee802_11_set_beacon(iface->bss[i]) < 0)
ret = -1;
}
return ret;
}
/* only update beacons if started */
int ieee802_11_update_beacons(struct hostapd_iface *iface)
{
size_t i;
int ret = 0;
for (i = 0; i < iface->num_bss; i++) {
if (iface->bss[i]->beacon_set_done && iface->bss[i]->started &&
ieee802_11_set_beacon(iface->bss[i]) < 0)
ret = -1;
}
return ret;
}
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.c b/contrib/wpa/src/ap/ctrl_iface_ap.c
index 28e40ba9cede..1d8fb8246581 100644
--- a/contrib/wpa/src/ap/ctrl_iface_ap.c
+++ b/contrib/wpa/src/ap/ctrl_iface_ap.c
@@ -1,1041 +1,1049 @@
/*
* Control interface for shared AP commands
* Copyright (c) 2004-2019, 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"
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "fst/fst_ctrl_iface.h"
#include "hostapd.h"
#include "ieee802_1x.h"
#include "wpa_auth.h"
#include "ieee802_11.h"
#include "sta_info.h"
#include "wps_hostapd.h"
#include "p2p_hostapd.h"
#include "ctrl_iface_ap.h"
#include "ap_drv_ops.h"
#include "mbo_ap.h"
#include "taxonomy.h"
static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen,
size_t curr_len, const u8 *mcs_set)
{
int ret;
size_t len = curr_len;
ret = os_snprintf(buf + len, buflen - len,
"ht_mcs_bitmask=");
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
/* 77 first bits (+ 3 reserved bits) */
len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10);
ret = os_snprintf(buf + len, buflen - len, "\n");
if (os_snprintf_error(buflen - len, ret))
return curr_len;
len += ret;
return len;
}
-static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
- struct sta_info *sta,
- char *buf, size_t buflen)
+static int hostapd_get_sta_conn_time(struct sta_info *sta,
+ struct hostap_sta_driver_data *data,
+ char *buf, size_t buflen)
+{
+ struct os_reltime age;
+ unsigned long secs;
+ int ret;
+
+ if (sta->connected_time.sec) {
+ /* Locally maintained time in AP mode */
+ os_reltime_age(&sta->connected_time, &age);
+ secs = (unsigned long) age.sec;
+ } else if (data->flags & STA_DRV_DATA_CONN_TIME) {
+ /* Time from the driver in mesh mode */
+ secs = data->connected_sec;
+ } else {
+ return 0;
+ }
+
+ ret = os_snprintf(buf, buflen, "connected_time=%lu\n", secs);
+ if (os_snprintf_error(buflen, ret))
+ return 0;
+ return ret;
+}
+
+
+static int hostapd_get_sta_info(struct hostapd_data *hapd,
+ struct sta_info *sta,
+ char *buf, size_t buflen)
{
struct hostap_sta_driver_data data;
int ret;
int len = 0;
if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
return 0;
ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
"rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n"
"signal=%d\n",
data.rx_packets, data.tx_packets,
data.rx_bytes, data.tx_bytes, data.inactive_msec,
data.signal);
if (os_snprintf_error(buflen, ret))
return 0;
len += ret;
ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu",
data.current_rx_rate);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
if (data.flags & STA_DRV_DATA_RX_MCS) {
ret = os_snprintf(buf + len, buflen - len, " mcs %u",
data.rx_mcs);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
if (data.flags & STA_DRV_DATA_RX_VHT_MCS) {
ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
data.rx_vhtmcs);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
if (data.flags & STA_DRV_DATA_RX_VHT_NSS) {
ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
data.rx_vht_nss);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
if (data.flags & STA_DRV_DATA_RX_SHORT_GI) {
ret = os_snprintf(buf + len, buflen - len, " shortGI");
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
ret = os_snprintf(buf + len, buflen - len, "\n");
if (!os_snprintf_error(buflen - len, ret))
len += ret;
ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu",
data.current_tx_rate);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
if (data.flags & STA_DRV_DATA_TX_MCS) {
ret = os_snprintf(buf + len, buflen - len, " mcs %u",
data.tx_mcs);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
if (data.flags & STA_DRV_DATA_TX_VHT_MCS) {
ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u",
data.tx_vhtmcs);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
if (data.flags & STA_DRV_DATA_TX_VHT_NSS) {
ret = os_snprintf(buf + len, buflen - len, " vhtnss %u",
data.tx_vht_nss);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
if (data.flags & STA_DRV_DATA_TX_SHORT_GI) {
ret = os_snprintf(buf + len, buflen - len, " shortGI");
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
ret = os_snprintf(buf + len, buflen - len, "\n");
if (!os_snprintf_error(buflen - len, ret))
len += ret;
if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
ret = os_snprintf(buf + len, buflen - len,
"rx_vht_mcs_map=%04x\n"
"tx_vht_mcs_map=%04x\n",
le_to_host16(sta->vht_capabilities->
vht_supported_mcs_set.rx_map),
le_to_host16(sta->vht_capabilities->
vht_supported_mcs_set.tx_map));
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
sta->ht_capabilities->
supported_mcs_set);
}
if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) {
ret = os_snprintf(buf + len, buflen - len,
"last_ack_signal=%d\n", data.last_ack_rssi);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
- return len;
-}
+ len += hostapd_get_sta_conn_time(sta, &data, buf + len, buflen - len);
-
-static int hostapd_get_sta_conn_time(struct sta_info *sta,
- char *buf, size_t buflen)
-{
- struct os_reltime age;
- int ret;
-
- if (!sta->connected_time.sec)
- return 0;
-
- os_reltime_age(&sta->connected_time, &age);
-
- ret = os_snprintf(buf, buflen, "connected_time=%u\n",
- (unsigned int) age.sec);
- if (os_snprintf_error(buflen, ret))
- return 0;
- return ret;
+ return len;
}
static const char * timeout_next_str(int val)
{
switch (val) {
case STA_NULLFUNC:
return "NULLFUNC POLL";
case STA_DISASSOC:
return "DISASSOC";
case STA_DEAUTH:
return "DEAUTH";
case STA_REMOVE:
return "REMOVE";
case STA_DISASSOC_FROM_CLI:
return "DISASSOC_FROM_CLI";
}
return "?";
}
static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
struct sta_info *sta,
char *buf, size_t buflen)
{
int len, res, ret, i;
const char *keyid;
if (!sta)
return 0;
len = 0;
ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
MAC2STR(sta->addr));
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len);
if (ret < 0)
return len;
len += ret;
ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
"listen_interval=%d\nsupported_rates=",
sta->aid, sta->capability, sta->listen_interval);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
for (i = 0; i < sta->supported_rates_len; i++) {
ret = os_snprintf(buf + len, buflen - len, "%02x%s",
sta->supported_rates[i],
i + 1 < sta->supported_rates_len ? " " : "");
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
timeout_next_str(sta->timeout_next));
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len);
if (res >= 0)
len += res;
res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len);
if (res >= 0)
len += res;
res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len);
if (res >= 0)
len += res;
res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
buflen - len);
if (res >= 0)
len += res;
res = hostapd_p2p_get_mib_sta(hapd, sta, buf + len, buflen - len);
if (res >= 0)
len += res;
- len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len);
- len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
+ len += hostapd_get_sta_info(hapd, sta, buf + len, buflen - len);
#ifdef CONFIG_SAE
if (sta->sae && sta->sae->state == SAE_ACCEPTED) {
res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n",
sta->sae->group);
if (!os_snprintf_error(buflen - len, res))
len += res;
}
if (sta->sae && sta->sae->tmp) {
const u8 *pos;
unsigned int j, count;
struct wpabuf *groups = sta->sae->tmp->peer_rejected_groups;
res = os_snprintf(buf + len, buflen - len,
"sae_rejected_groups=");
if (!os_snprintf_error(buflen - len, res))
len += res;
if (groups) {
pos = wpabuf_head(groups);
count = wpabuf_len(groups) / 2;
} else {
pos = NULL;
count = 0;
}
for (j = 0; pos && j < count; j++) {
res = os_snprintf(buf + len, buflen - len, "%s%d",
j == 0 ? "" : " ", WPA_GET_LE16(pos));
if (!os_snprintf_error(buflen - len, res))
len += res;
pos += 2;
}
res = os_snprintf(buf + len, buflen - len, "\n");
if (!os_snprintf_error(buflen - len, res))
len += res;
}
#endif /* CONFIG_SAE */
if (sta->vlan_id > 0) {
res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n",
sta->vlan_id);
if (!os_snprintf_error(buflen - len, res))
len += res;
}
res = mbo_ap_get_info(sta, buf + len, buflen - len);
if (res >= 0)
len += res;
if (sta->supp_op_classes &&
buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) {
len += os_snprintf(buf + len, buflen - len, "supp_op_classes=");
len += wpa_snprintf_hex(buf + len, buflen - len,
sta->supp_op_classes + 1,
sta->supp_op_classes[0]);
len += os_snprintf(buf + len, buflen - len, "\n");
}
if (sta->power_capab) {
ret = os_snprintf(buf + len, buflen - len,
"min_txpower=%d\n"
"max_txpower=%d\n",
sta->min_tx_power, sta->max_tx_power);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
#ifdef CONFIG_IEEE80211AC
if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) {
res = os_snprintf(buf + len, buflen - len,
"vht_caps_info=0x%08x\n",
le_to_host32(sta->vht_capabilities->
vht_capabilities_info));
if (!os_snprintf_error(buflen - len, res))
len += res;
}
#endif /* CONFIG_IEEE80211AC */
if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) {
res = os_snprintf(buf + len, buflen - len,
"ht_caps_info=0x%04x\n",
le_to_host16(sta->ht_capabilities->
ht_capabilities_info));
if (!os_snprintf_error(buflen - len, res))
len += res;
}
if (sta->ext_capability &&
buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) {
len += os_snprintf(buf + len, buflen - len, "ext_capab=");
len += wpa_snprintf_hex(buf + len, buflen - len,
sta->ext_capability + 1,
sta->ext_capability[0]);
len += os_snprintf(buf + len, buflen - len, "\n");
}
if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) {
ret = os_snprintf(buf + len, buflen - len,
"wds_sta_ifname=%s\n", sta->ifname_wds);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
keyid = ap_sta_wpa_get_keyid(hapd, sta);
if (keyid) {
ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
}
return len;
}
int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen);
}
int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
char *buf, size_t buflen)
{
u8 addr[ETH_ALEN];
int ret;
const char *pos;
struct sta_info *sta;
if (hwaddr_aton(txtaddr, addr)) {
ret = os_snprintf(buf, buflen, "FAIL\n");
if (os_snprintf_error(buflen, ret))
return 0;
return ret;
}
sta = ap_get_sta(hapd, addr);
if (sta == NULL)
return -1;
pos = os_strchr(txtaddr, ' ');
if (pos) {
pos++;
#ifdef HOSTAPD_DUMP_STATE
if (os_strcmp(pos, "eapol") == 0) {
if (sta->eapol_sm == NULL)
return -1;
return eapol_auth_dump_state(sta->eapol_sm, buf,
buflen);
}
#endif /* HOSTAPD_DUMP_STATE */
return -1;
}
ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
return ret;
}
int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
char *buf, size_t buflen)
{
u8 addr[ETH_ALEN];
struct sta_info *sta;
int ret;
if (hwaddr_aton(txtaddr, addr) ||
(sta = ap_get_sta(hapd, addr)) == NULL) {
ret = os_snprintf(buf, buflen, "FAIL\n");
if (os_snprintf_error(buflen, ret))
return 0;
return ret;
}
if (!sta->next)
return 0;
return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
}
#ifdef CONFIG_P2P_MANAGER
static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
u8 minor_reason_code, const u8 *addr)
{
struct ieee80211_mgmt *mgmt;
int ret;
u8 *pos;
mgmt = os_zalloc(sizeof(*mgmt) + 100);
if (mgmt == NULL)
return -1;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
" with minor reason code %u (stype=%u (%s))",
MAC2STR(addr), minor_reason_code, stype,
fc2str(le_to_host16(mgmt->frame_control)));
os_memcpy(mgmt->da, addr, ETH_ALEN);
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
if (stype == WLAN_FC_STYPE_DEAUTH) {
mgmt->u.deauth.reason_code =
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
pos = mgmt->u.deauth.variable;
} else {
mgmt->u.disassoc.reason_code =
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
pos = mgmt->u.disassoc.variable;
}
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 4 + 3 + 1;
WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
pos += 4;
*pos++ = P2P_ATTR_MINOR_REASON_CODE;
WPA_PUT_LE16(pos, 1);
pos += 2;
*pos++ = minor_reason_code;
ret = hostapd_drv_send_mlme(hapd, mgmt, pos - (u8 *) mgmt, 0, NULL, 0,
0);
os_free(mgmt);
return ret < 0 ? -1 : 0;
}
#endif /* CONFIG_P2P_MANAGER */
int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
const char *txtaddr)
{
u8 addr[ETH_ALEN];
struct sta_info *sta;
const char *pos;
u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DEAUTHENTICATE %s",
txtaddr);
if (hwaddr_aton(txtaddr, addr))
return -1;
pos = os_strstr(txtaddr, " reason=");
if (pos)
reason = atoi(pos + 8);
pos = os_strstr(txtaddr, " test=");
if (pos) {
struct ieee80211_mgmt mgmt;
int encrypt;
pos += 6;
encrypt = atoi(pos);
os_memset(&mgmt, 0, sizeof(mgmt));
mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_DEAUTH);
os_memcpy(mgmt.da, addr, ETH_ALEN);
os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
mgmt.u.deauth.reason_code = host_to_le16(reason);
if (hostapd_drv_send_mlme(hapd, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth),
0, NULL, 0, !encrypt) < 0)
return -1;
return 0;
}
#ifdef CONFIG_P2P_MANAGER
pos = os_strstr(txtaddr, " p2p=");
if (pos) {
return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DEAUTH,
atoi(pos + 5), addr);
}
#endif /* CONFIG_P2P_MANAGER */
if (os_strstr(txtaddr, " tx=0"))
hostapd_drv_sta_remove(hapd, addr);
else
hostapd_drv_sta_deauth(hapd, addr, reason);
sta = ap_get_sta(hapd, addr);
if (sta)
ap_sta_deauthenticate(hapd, sta, reason);
else if (addr[0] == 0xff)
hostapd_free_stas(hapd);
return 0;
}
int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
const char *txtaddr)
{
u8 addr[ETH_ALEN];
struct sta_info *sta;
const char *pos;
u16 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE DISASSOCIATE %s",
txtaddr);
if (hwaddr_aton(txtaddr, addr))
return -1;
pos = os_strstr(txtaddr, " reason=");
if (pos)
reason = atoi(pos + 8);
pos = os_strstr(txtaddr, " test=");
if (pos) {
struct ieee80211_mgmt mgmt;
int encrypt;
pos += 6;
encrypt = atoi(pos);
os_memset(&mgmt, 0, sizeof(mgmt));
mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_DISASSOC);
os_memcpy(mgmt.da, addr, ETH_ALEN);
os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
mgmt.u.disassoc.reason_code = host_to_le16(reason);
if (hostapd_drv_send_mlme(hapd, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth),
0, NULL, 0, !encrypt) < 0)
return -1;
return 0;
}
#ifdef CONFIG_P2P_MANAGER
pos = os_strstr(txtaddr, " p2p=");
if (pos) {
return p2p_manager_disconnect(hapd, WLAN_FC_STYPE_DISASSOC,
atoi(pos + 5), addr);
}
#endif /* CONFIG_P2P_MANAGER */
if (os_strstr(txtaddr, " tx=0"))
hostapd_drv_sta_remove(hapd, addr);
else
hostapd_drv_sta_disassoc(hapd, addr, reason);
sta = ap_get_sta(hapd, addr);
if (sta)
ap_sta_disassociate(hapd, sta, reason);
else if (addr[0] == 0xff)
hostapd_free_stas(hapd);
return 0;
}
#ifdef CONFIG_TAXONOMY
int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
const char *txtaddr,
char *buf, size_t buflen)
{
u8 addr[ETH_ALEN];
struct sta_info *sta;
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr);
if (hwaddr_aton(txtaddr, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (!sta)
return -1;
return retrieve_sta_taxonomy(hapd, sta, buf, buflen);
}
#endif /* CONFIG_TAXONOMY */
int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
const char *txtaddr)
{
u8 addr[ETH_ALEN];
struct sta_info *sta;
wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr);
if (hwaddr_aton(txtaddr, addr))
return -1;
sta = ap_get_sta(hapd, addr);
if (!sta)
return -1;
hostapd_drv_poll_client(hapd, hapd->own_addr, addr,
sta->flags & WLAN_STA_WMM);
return 0;
}
int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
size_t buflen)
{
struct hostapd_iface *iface = hapd->iface;
struct hostapd_hw_modes *mode = iface->current_mode;
int len = 0, ret, j;
size_t i;
ret = os_snprintf(buf + len, buflen - len,
"state=%s\n"
"phy=%s\n"
"freq=%d\n"
"num_sta_non_erp=%d\n"
"num_sta_no_short_slot_time=%d\n"
"num_sta_no_short_preamble=%d\n"
"olbc=%d\n"
"num_sta_ht_no_gf=%d\n"
"num_sta_no_ht=%d\n"
"num_sta_ht_20_mhz=%d\n"
"num_sta_ht40_intolerant=%d\n"
"olbc_ht=%d\n"
"ht_op_mode=0x%x\n",
hostapd_state_text(iface->state),
iface->phy,
iface->freq,
iface->num_sta_non_erp,
iface->num_sta_no_short_slot_time,
iface->num_sta_no_short_preamble,
iface->olbc,
iface->num_sta_ht_no_gf,
iface->num_sta_no_ht,
iface->num_sta_ht_20mhz,
iface->num_sta_ht40_intolerant,
iface->olbc_ht,
iface->ht_op_mode);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
if (!iface->cac_started || !iface->dfs_cac_ms) {
ret = os_snprintf(buf + len, buflen - len,
"cac_time_seconds=%d\n"
"cac_time_left_seconds=N/A\n",
iface->dfs_cac_ms / 1000);
} else {
/* CAC started and CAC time set - calculate remaining time */
struct os_reltime now;
unsigned int left_time;
os_reltime_age(&iface->dfs_cac_start, &now);
left_time = iface->dfs_cac_ms / 1000 - now.sec;
ret = os_snprintf(buf + len, buflen - len,
"cac_time_seconds=%u\n"
"cac_time_left_seconds=%u\n",
iface->dfs_cac_ms / 1000,
left_time);
}
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
ret = os_snprintf(buf + len, buflen - len,
"channel=%u\n"
"edmg_enable=%d\n"
"edmg_channel=%d\n"
"secondary_channel=%d\n"
"ieee80211n=%d\n"
"ieee80211ac=%d\n"
"ieee80211ax=%d\n"
"beacon_int=%u\n"
"dtim_period=%d\n",
iface->conf->channel,
iface->conf->enable_edmg,
iface->conf->edmg_channel,
iface->conf->ieee80211n && !hapd->conf->disable_11n ?
iface->conf->secondary_channel : 0,
iface->conf->ieee80211n && !hapd->conf->disable_11n,
iface->conf->ieee80211ac &&
!hapd->conf->disable_11ac,
iface->conf->ieee80211ax &&
!hapd->conf->disable_11ax,
iface->conf->beacon_int,
hapd->conf->dtim_period);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
#ifdef CONFIG_IEEE80211AX
if (iface->conf->ieee80211ax && !hapd->conf->disable_11ax) {
ret = os_snprintf(buf + len, buflen - len,
"he_oper_chwidth=%d\n"
"he_oper_centr_freq_seg0_idx=%d\n"
"he_oper_centr_freq_seg1_idx=%d\n",
iface->conf->he_oper_chwidth,
iface->conf->he_oper_centr_freq_seg0_idx,
iface->conf->he_oper_centr_freq_seg1_idx);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
#endif /* CONFIG_IEEE80211AX */
if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) {
ret = os_snprintf(buf + len, buflen - len,
"vht_oper_chwidth=%d\n"
"vht_oper_centr_freq_seg0_idx=%d\n"
"vht_oper_centr_freq_seg1_idx=%d\n"
"vht_caps_info=%08x\n",
iface->conf->vht_oper_chwidth,
iface->conf->vht_oper_centr_freq_seg0_idx,
iface->conf->vht_oper_centr_freq_seg1_idx,
iface->conf->vht_capab);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) {
u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]);
u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]);
ret = os_snprintf(buf + len, buflen - len,
"rx_vht_mcs_map=%04x\n"
"tx_vht_mcs_map=%04x\n",
rxmap, txmap);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
if (iface->conf->ieee80211n && !hapd->conf->disable_11n) {
ret = os_snprintf(buf + len, buflen - len,
"ht_caps_info=%04x\n",
hapd->iconf->ht_capab);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) {
len = hostapd_write_ht_mcs_bitmask(buf, buflen, len,
mode->mcs_set);
}
if (iface->current_rates && iface->num_rates) {
ret = os_snprintf(buf + len, buflen - len, "supported_rates=");
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
for (j = 0; j < iface->num_rates; j++) {
ret = os_snprintf(buf + len, buflen - len, "%s%02x",
j > 0 ? " " : "",
iface->current_rates[j].rate / 5);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
ret = os_snprintf(buf + len, buflen - len, "\n");
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
for (j = 0; mode && j < mode->num_channels; j++) {
if (mode->channels[j].freq == iface->freq) {
ret = os_snprintf(buf + len, buflen - len,
"max_txpower=%u\n",
mode->channels[j].max_tx_power);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
break;
}
}
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *bss = iface->bss[i];
ret = os_snprintf(buf + len, buflen - len,
"bss[%d]=%s\n"
"bssid[%d]=" MACSTR "\n"
"ssid[%d]=%s\n"
"num_sta[%d]=%d\n",
(int) i, bss->conf->iface,
(int) i, MAC2STR(bss->own_addr),
(int) i,
wpa_ssid_txt(bss->conf->ssid.ssid,
bss->conf->ssid.ssid_len),
(int) i, bss->num_sta);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
if (hapd->conf->chan_util_avg_period) {
ret = os_snprintf(buf + len, buflen - len,
"chan_util_avg=%u\n",
iface->chan_util_average);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
return len;
}
int hostapd_parse_csa_settings(const char *pos,
struct csa_settings *settings)
{
char *end;
os_memset(settings, 0, sizeof(*settings));
settings->cs_count = strtol(pos, &end, 10);
if (pos == end) {
wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
return -1;
}
settings->freq_params.freq = atoi(end);
if (settings->freq_params.freq == 0) {
wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
return -1;
}
#define SET_CSA_SETTING(str) \
do { \
const char *pos2 = os_strstr(pos, " " #str "="); \
if (pos2) { \
pos2 += sizeof(" " #str "=") - 1; \
settings->freq_params.str = atoi(pos2); \
} \
} while (0)
SET_CSA_SETTING(center_freq1);
SET_CSA_SETTING(center_freq2);
SET_CSA_SETTING(bandwidth);
SET_CSA_SETTING(sec_channel_offset);
settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
settings->freq_params.he_enabled = !!os_strstr(pos, " he");
settings->block_tx = !!os_strstr(pos, " blocktx");
#undef SET_CSA_SETTING
return 0;
}
int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
{
return hostapd_drv_stop_ap(hapd);
}
int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
size_t len)
{
return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
}
void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd)
{
wpa_auth_pmksa_flush(hapd->wpa_auth);
}
int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd)
{
u8 spa[ETH_ALEN];
u8 pmkid[PMKID_LEN];
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
char *pos, *pos2;
int akmp = 0, expiration = 0;
/*
* Entry format:
* <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp>
*/
if (hwaddr_aton(cmd, spa))
return -1;
pos = os_strchr(cmd, ' ');
if (!pos)
return -1;
pos++;
if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
return -1;
pos = os_strchr(pos, ' ');
if (!pos)
return -1;
pos++;
pos2 = os_strchr(pos, ' ');
if (!pos2)
return -1;
pmk_len = (pos2 - pos) / 2;
if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX ||
hexstr2bin(pos, pmk, pmk_len) < 0)
return -1;
pos = pos2 + 1;
if (sscanf(pos, "%d %d", &expiration, &akmp) != 2)
return -1;
return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len,
pmkid, expiration, akmp);
}
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
#ifdef CONFIG_MESH
int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
const u8 *addr, char *buf, size_t len)
{
return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len);
}
void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd)
{
u8 spa[ETH_ALEN];
u8 pmkid[PMKID_LEN];
u8 pmk[PMK_LEN_MAX];
char *pos;
int expiration;
/*
* Entry format:
* <BSSID> <PMKID> <PMK> <expiration in seconds>
*/
if (hwaddr_aton(cmd, spa))
return NULL;
pos = os_strchr(cmd, ' ');
if (!pos)
return NULL;
pos++;
if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
return NULL;
pos = os_strchr(pos, ' ');
if (!pos)
return NULL;
pos++;
if (hexstr2bin(pos, pmk, PMK_LEN) < 0)
return NULL;
pos = os_strchr(pos, ' ');
if (!pos)
return NULL;
pos++;
if (sscanf(pos, "%d", &expiration) != 1)
return NULL;
return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration);
}
#endif /* CONFIG_MESH */
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
diff --git a/contrib/wpa/src/ap/dhcp_snoop.c b/contrib/wpa/src/ap/dhcp_snoop.c
index edc77da2e797..551936b8e43c 100644
--- a/contrib/wpa/src/ap/dhcp_snoop.c
+++ b/contrib/wpa/src/ap/dhcp_snoop.c
@@ -1,158 +1,160 @@
/*
* DHCP snooping for Proxy ARP
* Copyright (c) 2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/dhcp.h"
#include "l2_packet/l2_packet.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ap_drv_ops.h"
#include "x_snoop.h"
#include "dhcp_snoop.h"
static const char * ipaddr_str(u32 addr)
{
static char buf[17];
os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
(addr >> 24) & 0xff, (addr >> 16) & 0xff,
(addr >> 8) & 0xff, addr & 0xff);
return buf;
}
static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
struct hostapd_data *hapd = ctx;
const struct bootp_pkt *b;
struct sta_info *sta;
int exten_len;
const u8 *end, *pos;
int res, msgtype = 0, prefixlen = 32;
u32 subnet_mask = 0;
u16 ip_len;
exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
if (exten_len < 4)
return;
b = (const struct bootp_pkt *) &buf[ETH_HLEN];
ip_len = ntohs(b->iph.ip_len);
if (ip_len > (unsigned int) (len - ETH_HLEN))
return;
if (WPA_GET_BE32(b->exten) != DHCP_MAGIC)
return;
/* Parse DHCP options */
end = (const u8 *) b + ip_len;
pos = &b->exten[4];
while (pos < end && *pos != DHCP_OPT_END) {
const u8 *opt = pos++;
if (*opt == DHCP_OPT_PAD)
continue;
if (pos >= end || 1 + *pos > end - pos)
break;
pos += *pos + 1;
if (pos >= end)
break;
switch (*opt) {
case DHCP_OPT_SUBNET_MASK:
if (opt[1] == 4)
subnet_mask = WPA_GET_BE32(&opt[2]);
if (subnet_mask == 0)
return;
while (!(subnet_mask & 0x1)) {
subnet_mask >>= 1;
prefixlen--;
}
break;
case DHCP_OPT_MSG_TYPE:
if (opt[1])
msgtype = opt[2];
break;
default:
break;
}
}
+#ifdef CONFIG_HS20
if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (!(sta->flags & WLAN_STA_AUTHORIZED))
continue;
x_snoop_mcast_to_ucast_convert_send(hapd, sta,
(u8 *) buf, len);
}
}
+#endif /* CONFIG_HS20 */
if (msgtype == DHCPACK) {
if (b->your_ip == 0)
return;
/* DHCPACK for DHCPREQUEST */
sta = ap_get_sta(hapd, b->hw_addr);
if (!sta)
return;
wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
" @ IPv4 address %s/%d",
MAC2STR(sta->addr),
ipaddr_str(be_to_host32(b->your_ip)),
prefixlen);
if (sta->ipaddr == b->your_ip)
return;
if (sta->ipaddr != 0) {
wpa_printf(MSG_DEBUG,
"dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
ipaddr_str(be_to_host32(sta->ipaddr)));
hostapd_drv_br_delete_ip_neigh(hapd, 4,
(u8 *) &sta->ipaddr);
}
res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip,
prefixlen, sta->addr);
if (res) {
wpa_printf(MSG_DEBUG,
"dhcp_snoop: Adding ip neigh table failed: %d",
res);
return;
}
sta->ipaddr = b->your_ip;
}
}
int dhcp_snoop_init(struct hostapd_data *hapd)
{
hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp,
L2_PACKET_FILTER_DHCP);
if (hapd->sock_dhcp == NULL) {
wpa_printf(MSG_DEBUG,
"dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
strerror(errno));
return -1;
}
return 0;
}
void dhcp_snoop_deinit(struct hostapd_data *hapd)
{
l2_packet_deinit(hapd->sock_dhcp);
hapd->sock_dhcp = NULL;
}
diff --git a/contrib/wpa/src/ap/dpp_hostapd.c b/contrib/wpa/src/ap/dpp_hostapd.c
index 93ffd8cf7c36..41769f475544 100644
--- a/contrib/wpa/src/ap/dpp_hostapd.c
+++ b/contrib/wpa/src/ap/dpp_hostapd.c
@@ -1,2642 +1,2659 @@
/*
* hostapd / DPP integration
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/dpp.h"
#include "common/gas.h"
#include "common/wpa_ctrl.h"
#include "hostapd.h"
#include "ap_drv_ops.h"
#include "gas_query_ap.h"
#include "gas_serv.h"
#include "wpa_auth.h"
#include "dpp_hostapd.h"
static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
static void hostapd_dpp_auth_conf_wait_timeout(void *eloop_ctx,
void *timeout_ctx);
static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator);
static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx);
static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
#ifdef CONFIG_DPP2
static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
void *timeout_ctx);
static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
struct dpp_authentication *auth,
struct dpp_config_obj *conf);
#endif /* CONFIG_DPP2 */
static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/**
* hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
* @hapd: Pointer to hostapd_data
* @cmd: DPP URI read from a QR Code
* Returns: Identifier of the stored info or -1 on failure
*/
int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd)
{
struct dpp_bootstrap_info *bi;
struct dpp_authentication *auth = hapd->dpp_auth;
bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd);
if (!bi)
return -1;
if (auth && auth->response_pending &&
dpp_notify_new_qr_code(auth, bi) == 1) {
wpa_printf(MSG_DEBUG,
"DPP: Sending out pending authentication response");
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d",
MAC2STR(auth->peer_mac_addr), auth->curr_freq,
DPP_PA_AUTHENTICATION_RESP);
hostapd_drv_send_action(hapd, auth->curr_freq, 0,
auth->peer_mac_addr,
wpabuf_head(hapd->dpp_auth->resp_msg),
wpabuf_len(hapd->dpp_auth->resp_msg));
}
#ifdef CONFIG_DPP2
dpp_controller_new_qr_code(hapd->iface->interfaces->dpp, bi);
#endif /* CONFIG_DPP2 */
return bi->id;
}
/**
* hostapd_dpp_nfc_uri - Parse and add DPP bootstrapping info from NFC Tag (URI)
* @hapd: Pointer to hostapd_data
* @cmd: DPP URI read from a NFC Tag (URI NDEF message)
* Returns: Identifier of the stored info or -1 on failure
*/
int hostapd_dpp_nfc_uri(struct hostapd_data *hapd, const char *cmd)
{
struct dpp_bootstrap_info *bi;
bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, cmd);
if (!bi)
return -1;
return bi->id;
}
int hostapd_dpp_nfc_handover_req(struct hostapd_data *hapd, const char *cmd)
{
const char *pos;
struct dpp_bootstrap_info *peer_bi, *own_bi;
pos = os_strstr(cmd, " own=");
if (!pos)
return -1;
pos += 5;
own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
if (!own_bi)
return -1;
pos = os_strstr(cmd, " uri=");
if (!pos)
return -1;
pos += 5;
peer_bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, pos);
if (!peer_bi) {
wpa_printf(MSG_INFO,
"DPP: Failed to parse URI from NFC Handover Request");
return -1;
}
if (dpp_nfc_update_bi(own_bi, peer_bi) < 0)
return -1;
return peer_bi->id;
}
int hostapd_dpp_nfc_handover_sel(struct hostapd_data *hapd, const char *cmd)
{
const char *pos;
struct dpp_bootstrap_info *peer_bi, *own_bi;
pos = os_strstr(cmd, " own=");
if (!pos)
return -1;
pos += 5;
own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
if (!own_bi)
return -1;
pos = os_strstr(cmd, " uri=");
if (!pos)
return -1;
pos += 5;
peer_bi = dpp_add_nfc_uri(hapd->iface->interfaces->dpp, pos);
if (!peer_bi) {
wpa_printf(MSG_INFO,
"DPP: Failed to parse URI from NFC Handover Select");
return -1;
}
if (peer_bi->curve != own_bi->curve) {
wpa_printf(MSG_INFO,
"DPP: Peer (NFC Handover Selector) used different curve");
return -1;
}
return peer_bi->id;
}
static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct dpp_authentication *auth = hapd->dpp_auth;
if (!auth || !auth->resp_msg)
return;
wpa_printf(MSG_DEBUG,
"DPP: Retry Authentication Response after timeout");
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d",
MAC2STR(auth->peer_mac_addr), auth->curr_freq,
DPP_PA_AUTHENTICATION_RESP);
hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr,
wpabuf_head(auth->resp_msg),
wpabuf_len(auth->resp_msg));
}
static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd)
{
struct dpp_authentication *auth = hapd->dpp_auth;
unsigned int wait_time, max_tries;
if (!auth || !auth->resp_msg)
return;
if (hapd->dpp_resp_max_tries)
max_tries = hapd->dpp_resp_max_tries;
else
max_tries = 5;
auth->auth_resp_tries++;
if (auth->auth_resp_tries >= max_tries) {
wpa_printf(MSG_INFO,
"DPP: No confirm received from initiator - stopping exchange");
hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
return;
}
if (hapd->dpp_resp_retry_time)
wait_time = hapd->dpp_resp_retry_time;
else
wait_time = 1000;
wpa_printf(MSG_DEBUG,
"DPP: Schedule retransmission of Authentication Response frame in %u ms",
wait_time);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
eloop_register_timeout(wait_time / 1000,
(wait_time % 1000) * 1000,
hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
}
void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
const u8 *data, size_t data_len, int ok)
{
struct dpp_authentication *auth = hapd->dpp_auth;
wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d",
MAC2STR(dst), ok);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
" result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED");
if (!hapd->dpp_auth) {
wpa_printf(MSG_DEBUG,
"DPP: Ignore TX status since there is no ongoing authentication exchange");
return;
}
#ifdef CONFIG_DPP2
if (auth->connect_on_tx_status) {
wpa_printf(MSG_DEBUG,
"DPP: Complete exchange on configuration result");
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
return;
}
#endif /* CONFIG_DPP2 */
if (hapd->dpp_auth->remove_on_tx_status) {
wpa_printf(MSG_DEBUG,
"DPP: Terminate authentication exchange due to an earlier error");
eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout,
hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
NULL);
#ifdef CONFIG_DPP2
eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
hapd, NULL);
#endif /* CONFIG_DPP2 */
hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
return;
}
if (hapd->dpp_auth_ok_on_ack)
hostapd_dpp_auth_success(hapd, 1);
if (!is_broadcast_ether_addr(dst) && !ok) {
wpa_printf(MSG_DEBUG,
"DPP: Unicast DPP Action frame was not ACKed");
if (auth->waiting_auth_resp) {
/* In case of DPP Authentication Request frame, move to
* the next channel immediately. */
hostapd_drv_send_action_cancel_wait(hapd);
hostapd_dpp_auth_init_next(hapd);
return;
}
if (auth->waiting_auth_conf) {
hostapd_dpp_auth_resp_retry(hapd);
return;
}
}
if (auth->waiting_auth_conf &&
auth->auth_resp_status == DPP_STATUS_OK) {
/* Make sure we do not get stuck waiting for Auth Confirm
* indefinitely after successfully transmitted Auth Response to
* allow new authentication exchanges to be started. */
eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd,
NULL);
eloop_register_timeout(1, 0, hostapd_dpp_auth_conf_wait_timeout,
hapd, NULL);
}
if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) {
/* Allow timeout handling to stop iteration if no response is
* received from a peer that has ACKed a request. */
auth->auth_req_ack = 1;
}
if (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 &&
hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) {
wpa_printf(MSG_DEBUG,
"DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response",
hapd->dpp_auth->curr_freq,
hapd->dpp_auth->neg_freq);
hostapd_drv_send_action_cancel_wait(hapd);
if (hapd->dpp_auth->neg_freq !=
(unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
/* TODO: Listen operation on non-operating channel */
wpa_printf(MSG_INFO,
"DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
hapd->dpp_auth->neg_freq, hapd->iface->freq);
}
}
if (hapd->dpp_auth_ok_on_ack)
hapd->dpp_auth_ok_on_ack = 0;
}
static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct dpp_authentication *auth = hapd->dpp_auth;
unsigned int freq;
struct os_reltime now, diff;
unsigned int wait_time, diff_ms;
if (!auth || !auth->waiting_auth_resp)
return;
wait_time = hapd->dpp_resp_wait_time ?
hapd->dpp_resp_wait_time : 2000;
os_get_reltime(&now);
os_reltime_sub(&now, &hapd->dpp_last_init, &diff);
diff_ms = diff.sec * 1000 + diff.usec / 1000;
wpa_printf(MSG_DEBUG,
"DPP: Reply wait timeout - wait_time=%u diff_ms=%u",
wait_time, diff_ms);
if (auth->auth_req_ack && diff_ms >= wait_time) {
/* Peer ACK'ed Authentication Request frame, but did not reply
* with Authentication Response frame within two seconds. */
wpa_printf(MSG_INFO,
"DPP: No response received from responder - stopping initiation attempt");
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED);
hostapd_drv_send_action_cancel_wait(hapd);
hostapd_dpp_listen_stop(hapd);
dpp_auth_deinit(auth);
hapd->dpp_auth = NULL;
return;
}
if (diff_ms >= wait_time) {
/* Authentication Request frame was not ACK'ed and no reply
* was receiving within two seconds. */
wpa_printf(MSG_DEBUG,
"DPP: Continue Initiator channel iteration");
hostapd_drv_send_action_cancel_wait(hapd);
hostapd_dpp_listen_stop(hapd);
hostapd_dpp_auth_init_next(hapd);
return;
}
/* Driver did not support 2000 ms long wait_time with TX command, so
* schedule listen operation to continue waiting for the response.
*
* DPP listen operations continue until stopped, so simply schedule a
* new call to this function at the point when the two second reply
* wait has expired. */
wait_time -= diff_ms;
freq = auth->curr_freq;
if (auth->neg_freq > 0)
freq = auth->neg_freq;
wpa_printf(MSG_DEBUG,
"DPP: Continue reply wait on channel %u MHz for %u ms",
freq, wait_time);
hapd->dpp_in_response_listen = 1;
if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) {
/* TODO: Listen operation on non-operating channel */
wpa_printf(MSG_INFO,
"DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
freq, hapd->iface->freq);
}
eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
hostapd_dpp_reply_wait_timeout, hapd, NULL);
}
static void hostapd_dpp_auth_conf_wait_timeout(void *eloop_ctx,
void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct dpp_authentication *auth = hapd->dpp_auth;
if (!auth || !auth->waiting_auth_conf)
return;
wpa_printf(MSG_DEBUG,
"DPP: Terminate authentication exchange due to Auth Confirm timeout");
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"No Auth Confirm received");
hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(auth);
hapd->dpp_auth = NULL;
}
static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
struct dpp_authentication *auth)
{
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->dpp_config_obj_override)
auth->config_obj_override =
os_strdup(hapd->dpp_config_obj_override);
if (hapd->dpp_discovery_override)
auth->discovery_override =
os_strdup(hapd->dpp_discovery_override);
if (hapd->dpp_groups_override)
auth->groups_override = os_strdup(hapd->dpp_groups_override);
auth->ignore_netaccesskey_mismatch =
hapd->dpp_ignore_netaccesskey_mismatch;
#endif /* CONFIG_TESTING_OPTIONS */
}
static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
if (!hapd->dpp_auth)
return;
wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout");
hostapd_dpp_auth_init_next(hapd);
}
static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd)
{
struct dpp_authentication *auth = hapd->dpp_auth;
const u8 *dst;
unsigned int wait_time, max_wait_time, freq, max_tries, used;
struct os_reltime now, diff;
if (!auth)
return -1;
if (auth->freq_idx == 0)
os_get_reltime(&hapd->dpp_init_iter_start);
if (auth->freq_idx >= auth->num_freq) {
auth->num_freq_iters++;
if (hapd->dpp_init_max_tries)
max_tries = hapd->dpp_init_max_tries;
else
max_tries = 5;
if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) {
wpa_printf(MSG_INFO,
"DPP: No response received from responder - stopping initiation attempt");
wpa_msg(hapd->msg_ctx, MSG_INFO,
DPP_EVENT_AUTH_INIT_FAILED);
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
hapd, NULL);
hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
return -1;
}
auth->freq_idx = 0;
eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
if (hapd->dpp_init_retry_time)
wait_time = hapd->dpp_init_retry_time;
else
wait_time = 10000;
os_get_reltime(&now);
os_reltime_sub(&now, &hapd->dpp_init_iter_start, &diff);
used = diff.sec * 1000 + diff.usec / 1000;
if (used > wait_time)
wait_time = 0;
else
wait_time -= used;
wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms",
wait_time);
eloop_register_timeout(wait_time / 1000,
(wait_time % 1000) * 1000,
hostapd_dpp_init_timeout, hapd,
NULL);
return 0;
}
freq = auth->freq[auth->freq_idx++];
auth->curr_freq = freq;
if (!is_zero_ether_addr(auth->peer_mac_addr))
dst = auth->peer_mac_addr;
else if (is_zero_ether_addr(auth->peer_bi->mac_addr))
dst = broadcast;
else
dst = auth->peer_bi->mac_addr;
hapd->dpp_auth_ok_on_ack = 0;
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
max_wait_time = hapd->dpp_resp_wait_time ?
hapd->dpp_resp_wait_time : 2000;
if (wait_time > max_wait_time)
wait_time = max_wait_time;
wait_time += 10; /* give the driver some extra time to complete */
eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
hostapd_dpp_reply_wait_timeout, hapd, NULL);
wait_time -= 10;
if (auth->neg_freq > 0 && freq != auth->neg_freq) {
wpa_printf(MSG_DEBUG,
"DPP: Initiate on %u MHz and move to neg_freq %u MHz for response",
freq, auth->neg_freq);
}
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d",
MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ);
auth->auth_req_ack = 0;
os_get_reltime(&hapd->dpp_last_init);
return hostapd_drv_send_action(hapd, freq, wait_time,
dst,
wpabuf_head(hapd->dpp_auth->req_msg),
wpabuf_len(hapd->dpp_auth->req_msg));
}
#ifdef CONFIG_DPP2
static int hostapd_dpp_process_conf_obj(void *ctx,
struct dpp_authentication *auth)
{
struct hostapd_data *hapd = ctx;
unsigned int i;
for (i = 0; i < auth->num_conf_obj; i++)
hostapd_dpp_handle_config_obj(hapd, auth,
&auth->conf_obj[i]);
return 0;
}
#endif /* CONFIG_DPP2 */
int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
{
const char *pos;
struct dpp_bootstrap_info *peer_bi, *own_bi = NULL;
struct dpp_authentication *auth;
u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
unsigned int neg_freq = 0;
int tcp = 0;
#ifdef CONFIG_DPP2
int tcp_port = DPP_TCP_PORT;
struct hostapd_ip_addr ipaddr;
char *addr;
#endif /* CONFIG_DPP2 */
pos = os_strstr(cmd, " peer=");
if (!pos)
return -1;
pos += 6;
peer_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
if (!peer_bi) {
wpa_printf(MSG_INFO,
"DPP: Could not find bootstrapping info for the identified peer");
return -1;
}
#ifdef CONFIG_DPP2
pos = os_strstr(cmd, " tcp_port=");
if (pos) {
pos += 10;
tcp_port = atoi(pos);
}
addr = get_param(cmd, " tcp_addr=");
if (addr) {
int res;
res = hostapd_parse_ip_addr(addr, &ipaddr);
os_free(addr);
if (res)
return -1;
tcp = 1;
}
#endif /* CONFIG_DPP2 */
pos = os_strstr(cmd, " own=");
if (pos) {
pos += 5;
own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp,
atoi(pos));
if (!own_bi) {
wpa_printf(MSG_INFO,
"DPP: Could not find bootstrapping info for the identified local entry");
return -1;
}
if (peer_bi->curve != own_bi->curve) {
wpa_printf(MSG_INFO,
"DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)",
peer_bi->curve->name, own_bi->curve->name);
return -1;
}
}
pos = os_strstr(cmd, " role=");
if (pos) {
pos += 6;
if (os_strncmp(pos, "configurator", 12) == 0)
allowed_roles = DPP_CAPAB_CONFIGURATOR;
else if (os_strncmp(pos, "enrollee", 8) == 0)
allowed_roles = DPP_CAPAB_ENROLLEE;
else if (os_strncmp(pos, "either", 6) == 0)
allowed_roles = DPP_CAPAB_CONFIGURATOR |
DPP_CAPAB_ENROLLEE;
else
goto fail;
}
pos = os_strstr(cmd, " neg_freq=");
if (pos)
neg_freq = atoi(pos + 10);
if (!tcp && hapd->dpp_auth) {
eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout,
hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout,
hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd,
NULL);
#ifdef CONFIG_DPP2
eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
hapd, NULL);
#endif /* CONFIG_DPP2 */
hostapd_drv_send_action_cancel_wait(hapd);
dpp_auth_deinit(hapd->dpp_auth);
}
auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
peer_bi, own_bi, allowed_roles, neg_freq,
hapd->iface->hw_features,
hapd->iface->num_hw_features);
if (!auth)
goto fail;
hostapd_dpp_set_testing_options(hapd, auth);
if (dpp_set_configurator(auth, cmd) < 0) {
dpp_auth_deinit(auth);
goto fail;
}
auth->neg_freq = neg_freq;
if (!is_zero_ether_addr(peer_bi->mac_addr))
os_memcpy(auth->peer_mac_addr, peer_bi->mac_addr, ETH_ALEN);
#ifdef CONFIG_DPP2
if (tcp)
return dpp_tcp_init(hapd->iface->interfaces->dpp, auth,
&ipaddr, tcp_port, hapd->conf->dpp_name,
DPP_NETROLE_AP, hapd->msg_ctx, hapd,
hostapd_dpp_process_conf_obj);
#endif /* CONFIG_DPP2 */
hapd->dpp_auth = auth;
return hostapd_dpp_auth_init_next(hapd);
fail:
return -1;
}
int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd)
{
int freq;
freq = atoi(cmd);
if (freq <= 0)
return -1;
if (os_strstr(cmd, " role=configurator"))
hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR;
else if (os_strstr(cmd, " role=enrollee"))
hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
else
hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR |
DPP_CAPAB_ENROLLEE;
hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
if (freq != hapd->iface->freq && hapd->iface->freq > 0) {
/* TODO: Listen operation on non-operating channel */
wpa_printf(MSG_INFO,
"DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)",
freq, hapd->iface->freq);
return -1;
}
hostapd_drv_dpp_listen(hapd, true);
return 0;
}
void hostapd_dpp_listen_stop(struct hostapd_data *hapd)
{
hostapd_drv_dpp_listen(hapd, false);
/* TODO: Stop listen operation on non-operating channel */
}
static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len,
unsigned int freq)
{
const u8 *r_bootstrap, *i_bootstrap;
u16 r_bootstrap_len, i_bootstrap_len;
struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
if (!hapd->iface->interfaces->dpp)
return;
wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
MAC2STR(src));
#ifdef CONFIG_DPP2
hostapd_dpp_chirp_stop(hapd);
#endif /* CONFIG_DPP2 */
r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing or invalid required Responder Bootstrapping Key Hash attribute");
return;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
r_bootstrap, r_bootstrap_len);
i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
&i_bootstrap_len);
if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing or invalid required Initiator Bootstrapping Key Hash attribute");
return;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
i_bootstrap, i_bootstrap_len);
/* Try to find own and peer bootstrapping key matches based on the
* received hash values */
dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap,
r_bootstrap, &own_bi, &peer_bi);
#ifdef CONFIG_DPP2
if (!own_bi) {
if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
src, hdr, buf, len, freq, i_bootstrap,
r_bootstrap, hapd) == 0)
return;
}
#endif /* CONFIG_DPP2 */
if (!own_bi) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"No matching own bootstrapping key found - ignore message");
return;
}
if (hapd->dpp_auth) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Already in DPP authentication exchange - ignore new one");
return;
}
hapd->dpp_auth_ok_on_ack = 0;
hapd->dpp_auth = dpp_auth_req_rx(hapd->iface->interfaces->dpp,
hapd->msg_ctx, hapd->dpp_allowed_roles,
hapd->dpp_qr_mutual,
peer_bi, own_bi, freq, hdr, buf, len);
if (!hapd->dpp_auth) {
wpa_printf(MSG_DEBUG, "DPP: No response generated");
return;
}
hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
if (dpp_set_configurator(hapd->dpp_auth,
hapd->dpp_configurator_params) < 0) {
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
return;
}
os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d",
MAC2STR(src), hapd->dpp_auth->curr_freq,
DPP_PA_AUTHENTICATION_RESP);
hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0,
src, wpabuf_head(hapd->dpp_auth->resp_msg),
wpabuf_len(hapd->dpp_auth->resp_msg));
}
static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd,
struct dpp_authentication *auth,
struct dpp_config_obj *conf)
{
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s",
dpp_akm_str(conf->akm));
if (conf->ssid_len)
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s",
wpa_ssid_txt(conf->ssid, conf->ssid_len));
if (conf->connector) {
/* TODO: Save the Connector and consider using a command
* to fetch the value instead of sending an event with
* it. The Connector could end up being larger than what
* most clients are ready to receive as an event
* message. */
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s",
conf->connector);
}
if (conf->passphrase[0]) {
char hex[64 * 2 + 1];
wpa_snprintf_hex(hex, sizeof(hex),
(const u8 *) conf->passphrase,
os_strlen(conf->passphrase));
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s",
hex);
} else if (conf->psk_set) {
char hex[PMK_LEN * 2 + 1];
wpa_snprintf_hex(hex, sizeof(hex), conf->psk, PMK_LEN);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s",
hex);
}
if (conf->c_sign_key) {
char *hex;
size_t hexlen;
hexlen = 2 * wpabuf_len(conf->c_sign_key) + 1;
hex = os_malloc(hexlen);
if (hex) {
wpa_snprintf_hex(hex, hexlen,
wpabuf_head(conf->c_sign_key),
wpabuf_len(conf->c_sign_key));
wpa_msg(hapd->msg_ctx, MSG_INFO,
DPP_EVENT_C_SIGN_KEY "%s", hex);
os_free(hex);
}
}
if (auth->net_access_key) {
char *hex;
size_t hexlen;
hexlen = 2 * wpabuf_len(auth->net_access_key) + 1;
hex = os_malloc(hexlen);
if (hex) {
wpa_snprintf_hex(hex, hexlen,
wpabuf_head(auth->net_access_key),
wpabuf_len(auth->net_access_key));
if (auth->net_access_key_expiry)
wpa_msg(hapd->msg_ctx, MSG_INFO,
DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex,
(unsigned long)
auth->net_access_key_expiry);
else
wpa_msg(hapd->msg_ctx, MSG_INFO,
DPP_EVENT_NET_ACCESS_KEY "%s", hex);
os_free(hex);
}
}
}
static int hostapd_dpp_handle_key_pkg(struct hostapd_data *hapd,
struct dpp_asymmetric_key *key)
{
#ifdef CONFIG_DPP2
int res;
if (!key)
return 0;
wpa_printf(MSG_DEBUG, "DPP: Received Configurator backup");
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED);
while (key) {
res = dpp_configurator_from_backup(
hapd->iface->interfaces->dpp, key);
if (res < 0)
return -1;
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFIGURATOR_ID "%d",
res);
key = key->next;
}
#endif /* CONFIG_DPP2 */
return 0;
}
static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
enum gas_query_ap_result result,
const struct wpabuf *adv_proto,
const struct wpabuf *resp, u16 status_code)
{
struct hostapd_data *hapd = ctx;
const u8 *pos;
struct dpp_authentication *auth = hapd->dpp_auth;
enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
if (!auth || !auth->auth_success) {
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
return;
}
if (result != GAS_QUERY_AP_SUCCESS ||
!resp || status_code != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed");
goto fail;
}
wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto",
adv_proto);
wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)",
resp);
if (wpabuf_len(adv_proto) != 10 ||
!(pos = wpabuf_head(adv_proto)) ||
pos[0] != WLAN_EID_ADV_PROTO ||
pos[1] != 8 ||
pos[3] != WLAN_EID_VENDOR_SPECIFIC ||
pos[4] != 5 ||
WPA_GET_BE24(&pos[5]) != OUI_WFA ||
pos[8] != 0x1a ||
pos[9] != 1) {
wpa_printf(MSG_DEBUG,
"DPP: Not a DPP Advertisement Protocol ID");
goto fail;
}
if (dpp_conf_resp_rx(auth, resp) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
goto fail;
}
hostapd_dpp_handle_config_obj(hapd, auth, &auth->conf_obj[0]);
if (hostapd_dpp_handle_key_pkg(hapd, auth->conf_key_pkg) < 0)
goto fail;
status = DPP_STATUS_OK;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_REJECT_CONFIG) {
wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object");
status = DPP_STATUS_CONFIG_REJECTED;
}
#endif /* CONFIG_TESTING_OPTIONS */
fail:
if (status != DPP_STATUS_OK)
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
#ifdef CONFIG_DPP2
if (auth->peer_version >= 2 &&
auth->conf_resp_status == DPP_STATUS_OK) {
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
msg = dpp_build_conf_result(auth, status);
if (!msg)
goto fail2;
wpa_msg(hapd->msg_ctx, MSG_INFO,
DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
MAC2STR(addr), auth->curr_freq,
DPP_PA_CONFIGURATION_RESULT);
hostapd_drv_send_action(hapd, auth->curr_freq, 0,
addr, wpabuf_head(msg),
wpabuf_len(msg));
wpabuf_free(msg);
/* This exchange will be terminated in the TX status handler */
auth->connect_on_tx_status = 1;
return;
}
fail2:
#endif /* CONFIG_DPP2 */
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
}
static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
{
struct dpp_authentication *auth = hapd->dpp_auth;
struct wpabuf *buf;
int res;
buf = dpp_build_conf_req_helper(auth, hapd->conf->dpp_name,
DPP_NETROLE_AP,
hapd->conf->dpp_mud_url, NULL);
if (!buf) {
wpa_printf(MSG_DEBUG,
"DPP: No configuration request data available");
return;
}
wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)",
MAC2STR(auth->peer_mac_addr), auth->curr_freq);
res = gas_query_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq,
buf, hostapd_dpp_gas_resp_cb, hapd);
if (res < 0) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"GAS: Failed to send Query Request");
wpabuf_free(buf);
} else {
wpa_printf(MSG_DEBUG,
"DPP: GAS query started with dialog token %u", res);
}
}
static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator)
{
wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d",
initiator);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
wpa_printf(MSG_INFO,
"DPP: TESTING - stop at Authentication Confirm");
if (hapd->dpp_auth->configurator) {
/* Prevent GAS response */
hapd->dpp_auth->auth_success = 0;
}
return;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (!hapd->dpp_auth->configurator)
hostapd_dpp_start_gas_client(hapd);
}
static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len,
unsigned int freq)
{
struct dpp_authentication *auth = hapd->dpp_auth;
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR,
MAC2STR(src));
if (!auth) {
wpa_printf(MSG_DEBUG,
"DPP: No DPP Authentication in progress - drop");
return;
}
if (!is_zero_ether_addr(auth->peer_mac_addr) &&
os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
}
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
if (auth->curr_freq != freq && auth->neg_freq == freq) {
wpa_printf(MSG_DEBUG,
"DPP: Responder accepted request for different negotiation channel");
auth->curr_freq = freq;
}
eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
msg = dpp_auth_resp_rx(auth, hdr, buf, len);
if (!msg) {
if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
wpa_printf(MSG_DEBUG, "DPP: Wait for full response");
return;
}
wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
return;
}
os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d", MAC2STR(src), auth->curr_freq,
DPP_PA_AUTHENTICATION_CONF);
hostapd_drv_send_action(hapd, auth->curr_freq, 0, src,
wpabuf_head(msg), wpabuf_len(msg));
wpabuf_free(msg);
hapd->dpp_auth_ok_on_ack = 1;
}
static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len)
{
struct dpp_authentication *auth = hapd->dpp_auth;
wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR,
MAC2STR(src));
if (!auth) {
wpa_printf(MSG_DEBUG,
"DPP: No DPP Authentication in progress - drop");
return;
}
if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
}
if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
return;
}
hostapd_dpp_auth_success(hapd, 0);
}
#ifdef CONFIG_DPP2
static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx,
void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct dpp_authentication *auth = hapd->dpp_auth;
if (!auth || !auth->waiting_conf_result)
return;
wpa_printf(MSG_DEBUG,
"DPP: Timeout while waiting for Configuration Result");
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
dpp_auth_deinit(auth);
hapd->dpp_auth = NULL;
}
static void hostapd_dpp_conn_status_result_wait_timeout(void *eloop_ctx,
void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct dpp_authentication *auth = hapd->dpp_auth;
if (!auth || !auth->waiting_conf_result)
return;
wpa_printf(MSG_DEBUG,
"DPP: Timeout while waiting for Connection Status Result");
wpa_msg(hapd->msg_ctx, MSG_INFO,
DPP_EVENT_CONN_STATUS_RESULT "timeout");
dpp_auth_deinit(auth);
hapd->dpp_auth = NULL;
}
static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len)
{
struct dpp_authentication *auth = hapd->dpp_auth;
enum dpp_status_error status;
wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
MAC2STR(src));
if (!auth || !auth->waiting_conf_result) {
wpa_printf(MSG_DEBUG,
"DPP: No DPP Configuration waiting for result - drop");
return;
}
if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
}
status = dpp_conf_result_rx(auth, hdr, buf, len);
if (status == DPP_STATUS_OK && auth->send_conn_status) {
wpa_msg(hapd->msg_ctx, MSG_INFO,
DPP_EVENT_CONF_SENT "wait_conn_status=1");
wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
hapd, NULL);
auth->waiting_conn_status_result = 1;
eloop_cancel_timeout(
hostapd_dpp_conn_status_result_wait_timeout,
hapd, NULL);
eloop_register_timeout(
16, 0, hostapd_dpp_conn_status_result_wait_timeout,
hapd, NULL);
return;
}
hostapd_drv_send_action_cancel_wait(hapd);
hostapd_dpp_listen_stop(hapd);
if (status == DPP_STATUS_OK)
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
else
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
dpp_auth_deinit(auth);
hapd->dpp_auth = NULL;
eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
NULL);
}
static void hostapd_dpp_rx_conn_status_result(struct hostapd_data *hapd,
const u8 *src, const u8 *hdr,
const u8 *buf, size_t len)
{
struct dpp_authentication *auth = hapd->dpp_auth;
enum dpp_status_error status;
u8 ssid[SSID_MAX_LEN];
size_t ssid_len = 0;
char *channel_list = NULL;
wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
if (!auth || !auth->waiting_conn_status_result) {
wpa_printf(MSG_DEBUG,
"DPP: No DPP Configuration waiting for connection status result - drop");
return;
}
status = dpp_conn_status_result_rx(auth, hdr, buf, len,
ssid, &ssid_len, &channel_list);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT
"result=%d ssid=%s channel_list=%s",
status, wpa_ssid_txt(ssid, ssid_len),
channel_list ? channel_list : "N/A");
os_free(channel_list);
hostapd_drv_send_action_cancel_wait(hapd);
hostapd_dpp_listen_stop(hapd);
dpp_auth_deinit(auth);
hapd->dpp_auth = NULL;
eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout,
hapd, NULL);
}
static void
hostapd_dpp_rx_presence_announcement(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len,
unsigned int freq)
{
const u8 *r_bootstrap;
u16 r_bootstrap_len;
struct dpp_bootstrap_info *peer_bi;
struct dpp_authentication *auth;
wpa_printf(MSG_DEBUG, "DPP: Presence Announcement from " MACSTR,
MAC2STR(src));
r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing or invalid required Responder Bootstrapping Key Hash attribute");
return;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
r_bootstrap, r_bootstrap_len);
peer_bi = dpp_bootstrap_find_chirp(hapd->iface->interfaces->dpp,
r_bootstrap);
dpp_notify_chirp_received(hapd->msg_ctx,
peer_bi ? (int) peer_bi->id : -1,
src, freq, r_bootstrap);
if (!peer_bi) {
if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
src, hdr, buf, len, freq, NULL,
r_bootstrap, hapd) == 0)
return;
wpa_printf(MSG_DEBUG,
"DPP: No matching bootstrapping information found");
return;
}
if (hapd->dpp_auth) {
wpa_printf(MSG_DEBUG,
"DPP: Ignore Presence Announcement during ongoing Authentication");
return;
}
auth = dpp_auth_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
peer_bi, NULL, DPP_CAPAB_CONFIGURATOR, freq, NULL,
0);
if (!auth)
return;
hostapd_dpp_set_testing_options(hapd, auth);
if (dpp_set_configurator(auth,
hapd->dpp_configurator_params) < 0) {
dpp_auth_deinit(auth);
return;
}
auth->neg_freq = freq;
/* The source address of the Presence Announcement frame overrides any
* MAC address information from the bootstrapping information. */
os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
hapd->dpp_auth = auth;
if (hostapd_dpp_auth_init_next(hapd) < 0) {
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
}
}
static void hostapd_dpp_reconfig_reply_wait_timeout(void *eloop_ctx,
void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct dpp_authentication *auth = hapd->dpp_auth;
if (!auth)
return;
wpa_printf(MSG_DEBUG, "DPP: Reconfig Reply wait timeout");
hostapd_dpp_listen_stop(hapd);
dpp_auth_deinit(auth);
hapd->dpp_auth = NULL;
}
static void
hostapd_dpp_rx_reconfig_announcement(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len,
unsigned int freq)
{
const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
struct dpp_configurator *conf;
struct dpp_authentication *auth;
unsigned int wait_time, max_wait_time;
u16 group;
if (hapd->dpp_auth) {
wpa_printf(MSG_DEBUG,
"DPP: Ignore Reconfig Announcement during ongoing Authentication");
return;
}
wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement from " MACSTR,
MAC2STR(src));
csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
&csign_hash_len);
if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing or invalid required Configurator C-sign key Hash attribute");
return;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
csign_hash, csign_hash_len);
conf = dpp_configurator_find_kid(hapd->iface->interfaces->dpp,
csign_hash);
if (!conf) {
if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
src, hdr, buf, len, freq, NULL,
NULL, hapd) == 0)
return;
wpa_printf(MSG_DEBUG,
"DPP: No matching Configurator information found");
return;
}
fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
&fcgroup_len);
if (!fcgroup || fcgroup_len != 2) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing or invalid required Finite Cyclic Group attribute");
return;
}
group = WPA_GET_LE16(fcgroup);
wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
auth = dpp_reconfig_init(hapd->iface->interfaces->dpp, hapd->msg_ctx,
conf, freq, group, a_nonce, a_nonce_len,
e_id, e_id_len);
if (!auth)
return;
hostapd_dpp_set_testing_options(hapd, auth);
if (dpp_set_configurator(auth, hapd->dpp_configurator_params) < 0) {
dpp_auth_deinit(auth);
return;
}
os_memcpy(auth->peer_mac_addr, src, ETH_ALEN);
hapd->dpp_auth = auth;
hapd->dpp_in_response_listen = 0;
hapd->dpp_auth_ok_on_ack = 0;
wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
max_wait_time = hapd->dpp_resp_wait_time ?
hapd->dpp_resp_wait_time : 2000;
if (wait_time > max_wait_time)
wait_time = max_wait_time;
wait_time += 10; /* give the driver some extra time to complete */
eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000,
hostapd_dpp_reconfig_reply_wait_timeout,
hapd, NULL);
wait_time -= 10;
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d",
MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_REQ);
if (hostapd_drv_send_action(hapd, freq, wait_time, src,
wpabuf_head(auth->reconfig_req_msg),
wpabuf_len(auth->reconfig_req_msg)) < 0) {
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
}
}
static void
hostapd_dpp_rx_reconfig_auth_resp(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len,
unsigned int freq)
{
struct dpp_authentication *auth = hapd->dpp_auth;
struct wpabuf *conf;
wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response from "
MACSTR, MAC2STR(src));
if (!auth || !auth->reconfig || !auth->configurator) {
wpa_printf(MSG_DEBUG,
"DPP: No DPP Reconfig Authentication in progress - drop");
return;
}
if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
return;
}
conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
if (!conf)
return;
eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
hapd, NULL);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d",
MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_CONF);
if (hostapd_drv_send_action(hapd, freq, 500, src,
wpabuf_head(conf), wpabuf_len(conf)) < 0) {
wpabuf_free(conf);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
return;
}
wpabuf_free(conf);
}
#endif /* CONFIG_DPP2 */
static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd,
const u8 *src, unsigned int freq,
u8 trans_id,
enum dpp_status_error status)
{
struct wpabuf *msg;
size_t len;
len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
#ifdef CONFIG_DPP2
len += 5;
#endif /* CONFIG_DPP2 */
msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP, len);
if (!msg)
return;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID");
goto skip_trans_id;
}
if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID");
trans_id ^= 0x01;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* Transaction ID */
wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, trans_id);
#ifdef CONFIG_TESTING_OPTIONS
skip_trans_id:
if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
goto skip_status;
}
if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
status = 254;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* DPP Status */
wpabuf_put_le16(msg, DPP_ATTR_STATUS);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, status);
#ifdef CONFIG_TESTING_OPTIONS
skip_status:
if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Connector");
goto skip_connector;
}
if (status == DPP_STATUS_OK &&
dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) {
char *connector;
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector");
connector = dpp_corrupt_connector_signature(
hapd->conf->dpp_connector);
if (!connector) {
wpabuf_free(msg);
return;
}
wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
wpabuf_put_le16(msg, os_strlen(connector));
wpabuf_put_str(msg, connector);
os_free(connector);
goto skip_connector;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* DPP Connector */
if (status == DPP_STATUS_OK) {
wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
wpabuf_put_str(msg, hapd->conf->dpp_connector);
}
#ifdef CONFIG_TESTING_OPTIONS
skip_connector:
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_DPP2
if (DPP_VERSION > 1) {
/* Protocol Version */
wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, DPP_VERSION);
}
#endif /* CONFIG_DPP2 */
wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR
" status=%d", MAC2STR(src), status);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d status=%d", MAC2STR(src), freq,
DPP_PA_PEER_DISCOVERY_RESP, status);
hostapd_drv_send_action(hapd, freq, 0, src,
wpabuf_head(msg), wpabuf_len(msg));
wpabuf_free(msg);
}
static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
const u8 *src,
const u8 *buf, size_t len,
unsigned int freq)
{
const u8 *connector, *trans_id;
u16 connector_len, trans_id_len;
struct os_time now;
struct dpp_introduction intro;
os_time_t expire;
int expiration;
enum dpp_status_error res;
wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
MAC2STR(src));
if (!hapd->wpa_auth ||
!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
!(hapd->conf->wpa & WPA_PROTO_RSN)) {
wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
return;
}
if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
!hapd->conf->dpp_csign) {
wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
return;
}
os_get_time(&now);
if (hapd->conf->dpp_netaccesskey_expiry &&
(os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
return;
}
trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
&trans_id_len);
if (!trans_id || trans_id_len != 1) {
wpa_printf(MSG_DEBUG,
"DPP: Peer did not include Transaction ID");
return;
}
connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
if (!connector) {
wpa_printf(MSG_DEBUG,
"DPP: Peer did not include its Connector");
return;
}
res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
wpabuf_head(hapd->conf->dpp_netaccesskey),
wpabuf_len(hapd->conf->dpp_netaccesskey),
wpabuf_head(hapd->conf->dpp_csign),
wpabuf_len(hapd->conf->dpp_csign),
connector, connector_len, &expire);
if (res == 255) {
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in internal failure (peer "
MACSTR ")", MAC2STR(src));
return;
}
if (res != DPP_STATUS_OK) {
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in failure (peer "
MACSTR " status %d)", MAC2STR(src), res);
hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
res);
return;
}
if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
expire = hapd->conf->dpp_netaccesskey_expiry;
if (expire)
expiration = expire - now.sec;
else
expiration = 0;
if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
intro.pmkid, expiration,
WPA_KEY_MGMT_DPP) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
return;
}
hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
DPP_STATUS_OK);
}
static void
hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src,
const u8 *buf, size_t len,
unsigned int freq)
{
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR,
MAC2STR(src));
/* TODO: Support multiple PKEX codes by iterating over all the enabled
* values here */
if (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) {
wpa_printf(MSG_DEBUG,
"DPP: No PKEX code configured - ignore request");
return;
}
if (hapd->dpp_pkex) {
/* TODO: Support parallel operations */
wpa_printf(MSG_DEBUG,
"DPP: Already in PKEX session - ignore new request");
return;
}
hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx,
hapd->dpp_pkex_bi,
hapd->own_addr, src,
hapd->dpp_pkex_identifier,
hapd->dpp_pkex_code,
buf, len);
if (!hapd->dpp_pkex) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to process the request - ignore it");
return;
}
msg = hapd->dpp_pkex->exchange_resp;
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d", MAC2STR(src), freq,
DPP_PA_PKEX_EXCHANGE_RESP);
hostapd_drv_send_action(hapd, freq, 0, src,
wpabuf_head(msg), wpabuf_len(msg));
if (hapd->dpp_pkex->failed) {
wpa_printf(MSG_DEBUG,
"DPP: Terminate PKEX exchange due to an earlier error");
if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t;
dpp_pkex_free(hapd->dpp_pkex);
hapd->dpp_pkex = NULL;
}
}
static void
hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src,
const u8 *buf, size_t len, unsigned int freq)
{
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR,
MAC2STR(src));
/* TODO: Support multiple PKEX codes by iterating over all the enabled
* values here */
if (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator ||
hapd->dpp_pkex->exchange_done) {
wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
return;
}
msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len);
if (!msg) {
wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
return;
}
wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR,
MAC2STR(src));
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d", MAC2STR(src), freq,
DPP_PA_PKEX_COMMIT_REVEAL_REQ);
hostapd_drv_send_action(hapd, freq, 0, src,
wpabuf_head(msg), wpabuf_len(msg));
wpabuf_free(msg);
}
static void
hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len,
unsigned int freq)
{
struct wpabuf *msg;
struct dpp_pkex *pkex = hapd->dpp_pkex;
struct dpp_bootstrap_info *bi;
wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR,
MAC2STR(src));
if (!pkex || pkex->initiator || !pkex->exchange_done) {
wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
return;
}
msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len);
if (!msg) {
wpa_printf(MSG_DEBUG, "DPP: Failed to process the request");
if (hapd->dpp_pkex->failed) {
wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange");
if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t)
hapd->dpp_pkex->own_bi->pkex_t =
hapd->dpp_pkex->t;
dpp_pkex_free(hapd->dpp_pkex);
hapd->dpp_pkex = NULL;
}
return;
}
wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to "
MACSTR, MAC2STR(src));
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d", MAC2STR(src), freq,
DPP_PA_PKEX_COMMIT_REVEAL_RESP);
hostapd_drv_send_action(hapd, freq, 0, src,
wpabuf_head(msg), wpabuf_len(msg));
wpabuf_free(msg);
bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
if (!bi)
return;
hapd->dpp_pkex = NULL;
}
static void
hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len,
unsigned int freq)
{
int res;
struct dpp_bootstrap_info *bi;
struct dpp_pkex *pkex = hapd->dpp_pkex;
char cmd[500];
wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR,
MAC2STR(src));
if (!pkex || !pkex->initiator || !pkex->exchange_done) {
wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session");
return;
}
res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len);
if (res < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
return;
}
bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
if (!bi)
return;
hapd->dpp_pkex = NULL;
os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
bi->id,
hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
wpa_printf(MSG_DEBUG,
"DPP: Start authentication after PKEX with parameters: %s",
cmd);
if (hostapd_dpp_auth_init(hapd, cmd) < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Authentication initialization failed");
return;
}
}
void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
const u8 *buf, size_t len, unsigned int freq)
{
u8 crypto_suite;
enum dpp_public_action_frame_type type;
const u8 *hdr;
unsigned int pkex_t;
if (len < DPP_HDR_LEN)
return;
if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE)
return;
hdr = buf;
buf += 4;
len -= 4;
crypto_suite = *buf++;
type = *buf++;
len -= 2;
wpa_printf(MSG_DEBUG,
"DPP: Received DPP Public Action frame crypto suite %u type %d from "
MACSTR " freq=%u",
crypto_suite, type, MAC2STR(src), freq);
if (crypto_suite != 1) {
wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u",
crypto_suite);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
" freq=%u type=%d ignore=unsupported-crypto-suite",
MAC2STR(src), freq, type);
return;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
if (dpp_check_attrs(buf, len) < 0) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
" freq=%u type=%d ignore=invalid-attributes",
MAC2STR(src), freq, type);
return;
}
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
" freq=%u type=%d", MAC2STR(src), freq, type);
#ifdef CONFIG_DPP2
if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
src, hdr, buf, len, freq, NULL, NULL,
hapd) == 0)
return;
#endif /* CONFIG_DPP2 */
switch (type) {
case DPP_PA_AUTHENTICATION_REQ:
hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq);
break;
case DPP_PA_AUTHENTICATION_RESP:
hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq);
break;
case DPP_PA_AUTHENTICATION_CONF:
hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len);
break;
case DPP_PA_PEER_DISCOVERY_REQ:
hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq);
break;
case DPP_PA_PKEX_EXCHANGE_REQ:
hostapd_dpp_rx_pkex_exchange_req(hapd, src, buf, len, freq);
break;
case DPP_PA_PKEX_EXCHANGE_RESP:
hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq);
break;
case DPP_PA_PKEX_COMMIT_REVEAL_REQ:
hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len,
freq);
break;
case DPP_PA_PKEX_COMMIT_REVEAL_RESP:
hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len,
freq);
break;
#ifdef CONFIG_DPP2
case DPP_PA_CONFIGURATION_RESULT:
hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len);
break;
case DPP_PA_CONNECTION_STATUS_RESULT:
hostapd_dpp_rx_conn_status_result(hapd, src, hdr, buf, len);
break;
case DPP_PA_PRESENCE_ANNOUNCEMENT:
hostapd_dpp_rx_presence_announcement(hapd, src, hdr, buf, len,
freq);
break;
case DPP_PA_RECONFIG_ANNOUNCEMENT:
hostapd_dpp_rx_reconfig_announcement(hapd, src, hdr, buf, len,
freq);
break;
case DPP_PA_RECONFIG_AUTH_RESP:
hostapd_dpp_rx_reconfig_auth_resp(hapd, src, hdr, buf, len,
freq);
break;
#endif /* CONFIG_DPP2 */
default:
wpa_printf(MSG_DEBUG,
"DPP: Ignored unsupported frame subtype %d", type);
break;
}
if (hapd->dpp_pkex)
pkex_t = hapd->dpp_pkex->t;
else if (hapd->dpp_pkex_bi)
pkex_t = hapd->dpp_pkex_bi->pkex_t;
else
pkex_t = 0;
if (pkex_t >= PKEX_COUNTER_T_LIMIT) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0");
hostapd_dpp_pkex_remove(hapd, "*");
}
}
struct wpabuf *
hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
const u8 *query, size_t query_len,
const u8 *data, size_t data_len)
{
struct dpp_authentication *auth = hapd->dpp_auth;
struct wpabuf *resp;
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
#ifdef CONFIG_DPP2
if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
data_len) == 0) {
/* Response will be forwarded once received over TCP */
return NULL;
}
#endif /* CONFIG_DPP2 */
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
return NULL;
}
if (hapd->dpp_auth_ok_on_ack && auth->configurator) {
wpa_printf(MSG_DEBUG,
"DPP: Have not received ACK for Auth Confirm yet - assume it was received based on this GAS request");
/* hostapd_dpp_auth_success() would normally have been called
* from TX status handler, but since there was no such handler
* call yet, simply send out the event message and proceed with
* exchange. */
wpa_msg(hapd->msg_ctx, MSG_INFO,
DPP_EVENT_AUTH_SUCCESS "init=1");
hapd->dpp_auth_ok_on_ack = 0;
}
wpa_hexdump(MSG_DEBUG,
"DPP: Received Configuration Request (GAS Query Request)",
query, query_len);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR,
MAC2STR(sa));
resp = dpp_conf_req_rx(auth, query, query_len);
if (!resp)
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
return resp;
}
void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
{
struct dpp_authentication *auth = hapd->dpp_auth;
if (!auth)
return;
wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
ok);
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
#ifdef CONFIG_DPP2
eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
hapd, NULL);
if (ok && auth->peer_version >= 2 &&
auth->conf_resp_status == DPP_STATUS_OK) {
wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
auth->waiting_conf_result = 1;
eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
hapd, NULL);
eloop_register_timeout(2, 0,
hostapd_dpp_config_result_wait_timeout,
hapd, NULL);
return;
}
#endif /* CONFIG_DPP2 */
hostapd_drv_send_action_cancel_wait(hapd);
if (ok)
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
else
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
}
int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd)
{
struct dpp_authentication *auth;
int ret = -1;
char *curve = NULL;
auth = dpp_alloc_auth(hapd->iface->interfaces->dpp, hapd->msg_ctx);
if (!auth)
return -1;
curve = get_param(cmd, " curve=");
hostapd_dpp_set_testing_options(hapd, auth);
if (dpp_set_configurator(auth, cmd) == 0 &&
dpp_configurator_own_config(auth, curve, 1) == 0) {
hostapd_dpp_handle_config_obj(hapd, auth, &auth->conf_obj[0]);
ret = 0;
}
dpp_auth_deinit(auth);
os_free(curve);
return ret;
}
int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
{
struct dpp_bootstrap_info *own_bi;
const char *pos, *end;
pos = os_strstr(cmd, " own=");
if (!pos)
return -1;
pos += 5;
own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
if (!own_bi) {
wpa_printf(MSG_DEBUG,
"DPP: Identified bootstrap info not found");
return -1;
}
if (own_bi->type != DPP_BOOTSTRAP_PKEX) {
wpa_printf(MSG_DEBUG,
"DPP: Identified bootstrap info not for PKEX");
return -1;
}
hapd->dpp_pkex_bi = own_bi;
own_bi->pkex_t = 0; /* clear pending errors on new code */
os_free(hapd->dpp_pkex_identifier);
hapd->dpp_pkex_identifier = NULL;
pos = os_strstr(cmd, " identifier=");
if (pos) {
pos += 12;
end = os_strchr(pos, ' ');
if (!end)
return -1;
hapd->dpp_pkex_identifier = os_malloc(end - pos + 1);
if (!hapd->dpp_pkex_identifier)
return -1;
os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos);
hapd->dpp_pkex_identifier[end - pos] = '\0';
}
pos = os_strstr(cmd, " code=");
if (!pos)
return -1;
os_free(hapd->dpp_pkex_code);
hapd->dpp_pkex_code = os_strdup(pos + 6);
if (!hapd->dpp_pkex_code)
return -1;
if (os_strstr(cmd, " init=1")) {
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
dpp_pkex_free(hapd->dpp_pkex);
hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, own_bi,
hapd->own_addr,
hapd->dpp_pkex_identifier,
hapd->dpp_pkex_code);
if (!hapd->dpp_pkex)
return -1;
msg = hapd->dpp_pkex->exchange_req;
/* TODO: Which channel to use? */
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d", MAC2STR(broadcast), 2437,
DPP_PA_PKEX_EXCHANGE_REQ);
hostapd_drv_send_action(hapd, 2437, 0, broadcast,
wpabuf_head(msg), wpabuf_len(msg));
}
/* TODO: Support multiple PKEX info entries */
os_free(hapd->dpp_pkex_auth_cmd);
hapd->dpp_pkex_auth_cmd = os_strdup(cmd);
return 1;
}
int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id)
{
unsigned int id_val;
if (os_strcmp(id, "*") == 0) {
id_val = 0;
} else {
id_val = atoi(id);
if (id_val == 0)
return -1;
}
if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code)
return -1;
/* TODO: Support multiple PKEX entries */
os_free(hapd->dpp_pkex_code);
hapd->dpp_pkex_code = NULL;
os_free(hapd->dpp_pkex_identifier);
hapd->dpp_pkex_identifier = NULL;
os_free(hapd->dpp_pkex_auth_cmd);
hapd->dpp_pkex_auth_cmd = NULL;
hapd->dpp_pkex_bi = NULL;
/* TODO: Remove dpp_pkex only if it is for the identified PKEX code */
dpp_pkex_free(hapd->dpp_pkex);
hapd->dpp_pkex = NULL;
return 0;
}
void hostapd_dpp_stop(struct hostapd_data *hapd)
{
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
dpp_pkex_free(hapd->dpp_pkex);
hapd->dpp_pkex = NULL;
}
#ifdef CONFIG_DPP2
static void hostapd_dpp_relay_tx(void *ctx, const u8 *addr, unsigned int freq,
const u8 *msg, size_t len)
{
struct hostapd_data *hapd = ctx;
u8 *buf;
wpa_printf(MSG_DEBUG, "DPP: Send action frame dst=" MACSTR " freq=%u",
MAC2STR(addr), freq);
buf = os_malloc(2 + len);
if (!buf)
return;
buf[0] = WLAN_ACTION_PUBLIC;
buf[1] = WLAN_PA_VENDOR_SPECIFIC;
os_memcpy(buf + 2, msg, len);
hostapd_drv_send_action(hapd, freq, 0, addr, buf, 2 + len);
os_free(buf);
}
static void hostapd_dpp_relay_gas_resp_tx(void *ctx, const u8 *addr,
u8 dialog_token, int prot,
struct wpabuf *buf)
{
struct hostapd_data *hapd = ctx;
gas_serv_req_dpp_processing(hapd, addr, dialog_token, prot, buf);
}
#endif /* CONFIG_DPP2 */
static int hostapd_dpp_add_controllers(struct hostapd_data *hapd)
{
#ifdef CONFIG_DPP2
struct dpp_controller_conf *ctrl;
struct dpp_relay_config config;
os_memset(&config, 0, sizeof(config));
config.cb_ctx = hapd;
config.tx = hostapd_dpp_relay_tx;
config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
for (ctrl = hapd->conf->dpp_controller; ctrl; ctrl = ctrl->next) {
config.ipaddr = &ctrl->ipaddr;
config.pkhash = ctrl->pkhash;
if (dpp_relay_add_controller(hapd->iface->interfaces->dpp,
&config) < 0)
return -1;
}
#endif /* CONFIG_DPP2 */
return 0;
}
int hostapd_dpp_init(struct hostapd_data *hapd)
{
hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
hapd->dpp_init_done = 1;
return hostapd_dpp_add_controllers(hapd);
}
void hostapd_dpp_deinit(struct hostapd_data *hapd)
{
#ifdef CONFIG_TESTING_OPTIONS
os_free(hapd->dpp_config_obj_override);
hapd->dpp_config_obj_override = NULL;
os_free(hapd->dpp_discovery_override);
hapd->dpp_discovery_override = NULL;
os_free(hapd->dpp_groups_override);
hapd->dpp_groups_override = NULL;
hapd->dpp_ignore_netaccesskey_mismatch = 0;
#endif /* CONFIG_TESTING_OPTIONS */
if (!hapd->dpp_init_done)
return;
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
#ifdef CONFIG_DPP2
eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
NULL);
eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd,
NULL);
hostapd_dpp_chirp_stop(hapd);
+ if (hapd->iface->interfaces)
+ dpp_controller_stop_for_ctx(hapd->iface->interfaces->dpp, hapd);
#endif /* CONFIG_DPP2 */
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
hostapd_dpp_pkex_remove(hapd, "*");
hapd->dpp_pkex = NULL;
os_free(hapd->dpp_configurator_params);
hapd->dpp_configurator_params = NULL;
}
#ifdef CONFIG_DPP2
int hostapd_dpp_controller_start(struct hostapd_data *hapd, const char *cmd)
{
struct dpp_controller_config config;
const char *pos;
os_memset(&config, 0, sizeof(config));
config.allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR;
config.netrole = DPP_NETROLE_AP;
config.msg_ctx = hapd->msg_ctx;
config.cb_ctx = hapd;
config.process_conf_obj = hostapd_dpp_process_conf_obj;
if (cmd) {
pos = os_strstr(cmd, " tcp_port=");
if (pos) {
pos += 10;
config.tcp_port = atoi(pos);
}
pos = os_strstr(cmd, " role=");
if (pos) {
pos += 6;
if (os_strncmp(pos, "configurator", 12) == 0)
config.allowed_roles = DPP_CAPAB_CONFIGURATOR;
else if (os_strncmp(pos, "enrollee", 8) == 0)
config.allowed_roles = DPP_CAPAB_ENROLLEE;
else if (os_strncmp(pos, "either", 6) == 0)
config.allowed_roles = DPP_CAPAB_CONFIGURATOR |
DPP_CAPAB_ENROLLEE;
else
return -1;
}
config.qr_mutual = os_strstr(cmd, " qr=mutual") != NULL;
}
config.configurator_params = hapd->dpp_configurator_params;
return dpp_controller_start(hapd->iface->interfaces->dpp, &config);
}
static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx);
static void hostapd_dpp_chirp_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
wpa_printf(MSG_DEBUG, "DPP: No chirp response received");
hostapd_drv_send_action_cancel_wait(hapd);
hostapd_dpp_chirp_next(hapd, NULL);
}
static void hostapd_dpp_chirp_start(struct hostapd_data *hapd)
{
struct wpabuf *msg;
int type;
msg = hapd->dpp_presence_announcement;
type = DPP_PA_PRESENCE_ANNOUNCEMENT;
wpa_printf(MSG_DEBUG, "DPP: Chirp on %d MHz", hapd->dpp_chirp_freq);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
" freq=%u type=%d",
MAC2STR(broadcast), hapd->dpp_chirp_freq, type);
if (hostapd_drv_send_action(
hapd, hapd->dpp_chirp_freq, 2000, broadcast,
wpabuf_head(msg), wpabuf_len(msg)) < 0 ||
eloop_register_timeout(2, 0, hostapd_dpp_chirp_timeout,
hapd, NULL) < 0)
hostapd_dpp_chirp_stop(hapd);
}
static struct hostapd_hw_modes *
dpp_get_mode(struct hostapd_data *hapd,
enum hostapd_hw_mode mode)
{
struct hostapd_hw_modes *modes = hapd->iface->hw_features;
u16 num_modes = hapd->iface->num_hw_features;
u16 i;
for (i = 0; i < num_modes; i++) {
if (modes[i].mode != mode ||
!modes[i].num_channels || !modes[i].channels)
continue;
return &modes[i];
}
return NULL;
}
static void
hostapd_dpp_chirp_scan_res_handler(struct hostapd_iface *iface)
{
struct hostapd_data *hapd = iface->bss[0];
struct wpa_scan_results *scan_res;
struct dpp_bootstrap_info *bi = hapd->dpp_chirp_bi;
unsigned int i;
struct hostapd_hw_modes *mode;
int c;
+ bool chan6 = hapd->iface->hw_features == NULL;
if (!bi)
return;
hapd->dpp_chirp_scan_done = 1;
scan_res = hostapd_driver_get_scan_results(hapd);
os_free(hapd->dpp_chirp_freqs);
hapd->dpp_chirp_freqs = NULL;
/* Channels from own bootstrapping info */
if (bi) {
for (i = 0; i < bi->num_freq; i++)
int_array_add_unique(&hapd->dpp_chirp_freqs,
bi->freq[i]);
}
/* Preferred chirping channels */
- int_array_add_unique(&hapd->dpp_chirp_freqs, 2437);
+ mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211G);
+ if (mode) {
+ for (c = 0; c < mode->num_channels; c++) {
+ struct hostapd_channel_data *chan = &mode->channels[c];
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_RADAR) ||
+ chan->freq != 2437)
+ continue;
+ chan6 = true;
+ break;
+ }
+ }
+ if (chan6)
+ int_array_add_unique(&hapd->dpp_chirp_freqs, 2437);
mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211A);
if (mode) {
int chan44 = 0, chan149 = 0;
for (c = 0; c < mode->num_channels; c++) {
struct hostapd_channel_data *chan = &mode->channels[c];
if (chan->flag & (HOSTAPD_CHAN_DISABLED |
HOSTAPD_CHAN_RADAR))
continue;
if (chan->freq == 5220)
chan44 = 1;
if (chan->freq == 5745)
chan149 = 1;
}
if (chan149)
int_array_add_unique(&hapd->dpp_chirp_freqs, 5745);
else if (chan44)
int_array_add_unique(&hapd->dpp_chirp_freqs, 5220);
}
mode = dpp_get_mode(hapd, HOSTAPD_MODE_IEEE80211AD);
if (mode) {
for (c = 0; c < mode->num_channels; c++) {
struct hostapd_channel_data *chan = &mode->channels[c];
if ((chan->flag & (HOSTAPD_CHAN_DISABLED |
HOSTAPD_CHAN_RADAR)) ||
chan->freq != 60480)
continue;
int_array_add_unique(&hapd->dpp_chirp_freqs, 60480);
break;
}
}
/* Add channels from scan results for APs that advertise Configurator
* Connectivity element */
for (i = 0; scan_res && i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
size_t ie_len = bss->ie_len;
if (!ie_len)
ie_len = bss->beacon_ie_len;
if (get_vendor_ie((const u8 *) (bss + 1), ie_len,
DPP_CC_IE_VENDOR_TYPE))
int_array_add_unique(&hapd->dpp_chirp_freqs,
bss->freq);
}
if (!hapd->dpp_chirp_freqs ||
eloop_register_timeout(0, 0, hostapd_dpp_chirp_next,
hapd, NULL) < 0)
hostapd_dpp_chirp_stop(hapd);
wpa_scan_results_free(scan_res);
}
static void hostapd_dpp_chirp_next(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
int i;
if (hapd->dpp_chirp_listen)
hostapd_dpp_listen_stop(hapd);
if (hapd->dpp_chirp_freq == 0) {
if (hapd->dpp_chirp_round % 4 == 0 &&
!hapd->dpp_chirp_scan_done) {
struct wpa_driver_scan_params params;
int ret;
wpa_printf(MSG_DEBUG,
"DPP: Update channel list for chirping");
os_memset(&params, 0, sizeof(params));
ret = hostapd_driver_scan(hapd, &params);
if (ret < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to request a scan ret=%d (%s)",
ret, strerror(-ret));
hostapd_dpp_chirp_scan_res_handler(hapd->iface);
} else {
hapd->iface->scan_cb =
hostapd_dpp_chirp_scan_res_handler;
}
return;
}
hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[0];
hapd->dpp_chirp_round++;
wpa_printf(MSG_DEBUG, "DPP: Start chirping round %d",
hapd->dpp_chirp_round);
} else {
for (i = 0; hapd->dpp_chirp_freqs[i]; i++)
if (hapd->dpp_chirp_freqs[i] == hapd->dpp_chirp_freq)
break;
if (!hapd->dpp_chirp_freqs[i]) {
wpa_printf(MSG_DEBUG,
"DPP: Previous chirp freq %d not found",
hapd->dpp_chirp_freq);
return;
}
i++;
if (hapd->dpp_chirp_freqs[i]) {
hapd->dpp_chirp_freq = hapd->dpp_chirp_freqs[i];
} else {
hapd->dpp_chirp_iter--;
if (hapd->dpp_chirp_iter <= 0) {
wpa_printf(MSG_DEBUG,
"DPP: Chirping iterations completed");
hostapd_dpp_chirp_stop(hapd);
return;
}
hapd->dpp_chirp_freq = 0;
hapd->dpp_chirp_scan_done = 0;
if (eloop_register_timeout(30, 0,
hostapd_dpp_chirp_next,
hapd, NULL) < 0) {
hostapd_dpp_chirp_stop(hapd);
return;
}
if (hapd->dpp_chirp_listen) {
wpa_printf(MSG_DEBUG,
"DPP: Listen on %d MHz during chirp 30 second wait",
hapd->dpp_chirp_listen);
/* TODO: start listen on the channel */
} else {
wpa_printf(MSG_DEBUG,
"DPP: Wait 30 seconds before starting the next chirping round");
}
return;
}
}
hostapd_dpp_chirp_start(hapd);
}
int hostapd_dpp_chirp(struct hostapd_data *hapd, const char *cmd)
{
const char *pos;
int iter = 1, listen_freq = 0;
struct dpp_bootstrap_info *bi;
pos = os_strstr(cmd, " own=");
if (!pos)
return -1;
pos += 5;
bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
if (!bi) {
wpa_printf(MSG_DEBUG,
"DPP: Identified bootstrap info not found");
return -1;
}
pos = os_strstr(cmd, " iter=");
if (pos) {
iter = atoi(pos + 6);
if (iter <= 0)
return -1;
}
pos = os_strstr(cmd, " listen=");
if (pos) {
listen_freq = atoi(pos + 8);
if (listen_freq <= 0)
return -1;
}
hostapd_dpp_chirp_stop(hapd);
hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE;
hapd->dpp_qr_mutual = 0;
hapd->dpp_chirp_bi = bi;
hapd->dpp_presence_announcement = dpp_build_presence_announcement(bi);
if (!hapd->dpp_presence_announcement)
return -1;
hapd->dpp_chirp_iter = iter;
hapd->dpp_chirp_round = 0;
hapd->dpp_chirp_scan_done = 0;
hapd->dpp_chirp_listen = listen_freq;
return eloop_register_timeout(0, 0, hostapd_dpp_chirp_next, hapd, NULL);
}
void hostapd_dpp_chirp_stop(struct hostapd_data *hapd)
{
if (hapd->dpp_presence_announcement) {
hostapd_drv_send_action_cancel_wait(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CHIRP_STOPPED);
}
hapd->dpp_chirp_bi = NULL;
wpabuf_free(hapd->dpp_presence_announcement);
hapd->dpp_presence_announcement = NULL;
if (hapd->dpp_chirp_listen)
hostapd_dpp_listen_stop(hapd);
hapd->dpp_chirp_listen = 0;
hapd->dpp_chirp_freq = 0;
os_free(hapd->dpp_chirp_freqs);
hapd->dpp_chirp_freqs = NULL;
eloop_cancel_timeout(hostapd_dpp_chirp_next, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_chirp_timeout, hapd, NULL);
if (hapd->iface->scan_cb == hostapd_dpp_chirp_scan_res_handler) {
/* TODO: abort ongoing scan */
hapd->iface->scan_cb = NULL;
}
}
static int handle_dpp_remove_bi(struct hostapd_iface *iface, void *ctx)
{
struct dpp_bootstrap_info *bi = ctx;
size_t i;
for (i = 0; i < iface->num_bss; i++) {
struct hostapd_data *hapd = iface->bss[i];
if (bi == hapd->dpp_chirp_bi)
hostapd_dpp_chirp_stop(hapd);
}
return 0;
}
void hostapd_dpp_remove_bi(void *ctx, struct dpp_bootstrap_info *bi)
{
struct hapd_interfaces *interfaces = ctx;
hostapd_for_each_interface(interfaces, handle_dpp_remove_bi, bi);
}
#endif /* CONFIG_DPP2 */
diff --git a/contrib/wpa/src/ap/hostapd.h b/contrib/wpa/src/ap/hostapd.h
index 07d0aaa92100..f3ca7529ac96 100644
--- a/contrib/wpa/src/ap/hostapd.h
+++ b/contrib/wpa/src/ap/hostapd.h
@@ -1,693 +1,695 @@
/*
* hostapd / Initialization and configuration
* Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef HOSTAPD_H
#define HOSTAPD_H
#ifdef CONFIG_SQLITE
#include <sqlite3.h>
#endif /* CONFIG_SQLITE */
#include "common/defs.h"
#include "utils/list.h"
#include "ap_config.h"
#include "drivers/driver.h"
#define OCE_STA_CFON_ENABLED(hapd) \
((hapd->conf->oce & OCE_STA_CFON) && \
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON))
#define OCE_AP_ENABLED(hapd) \
((hapd->conf->oce & OCE_AP) && \
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP))
struct wpa_ctrl_dst;
struct radius_server_data;
struct upnp_wps_device_sm;
struct hostapd_data;
struct sta_info;
struct ieee80211_ht_capabilities;
struct full_dynamic_vlan;
enum wps_event;
union wps_event_data;
#ifdef CONFIG_MESH
struct mesh_conf;
#endif /* CONFIG_MESH */
#ifdef CONFIG_CTRL_IFACE_UDP
#define CTRL_IFACE_COOKIE_LEN 8
#endif /* CONFIG_CTRL_IFACE_UDP */
struct hostapd_iface;
struct hapd_interfaces {
int (*reload_config)(struct hostapd_iface *iface);
struct hostapd_config * (*config_read_cb)(const char *config_fname);
int (*ctrl_iface_init)(struct hostapd_data *hapd);
void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
int (*for_each_interface)(struct hapd_interfaces *interfaces,
int (*cb)(struct hostapd_iface *iface,
void *ctx), void *ctx);
int (*driver_init)(struct hostapd_iface *iface);
size_t count;
int global_ctrl_sock;
struct dl_list global_ctrl_dst;
char *global_iface_path;
char *global_iface_name;
#ifndef CONFIG_NATIVE_WINDOWS
gid_t ctrl_iface_group;
#endif /* CONFIG_NATIVE_WINDOWS */
struct hostapd_iface **iface;
size_t terminate_on_error;
#ifndef CONFIG_NO_VLAN
struct dynamic_iface *vlan_priv;
#endif /* CONFIG_NO_VLAN */
#ifdef CONFIG_ETH_P_OUI
struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */
#endif /* CONFIG_ETH_P_OUI */
int eloop_initialized;
#ifdef CONFIG_DPP
struct dpp_global *dpp;
#endif /* CONFIG_DPP */
#ifdef CONFIG_CTRL_IFACE_UDP
unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
};
enum hostapd_chan_status {
HOSTAPD_CHAN_VALID = 0, /* channel is ready */
HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
};
struct hostapd_probereq_cb {
int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
const u8 *ie, size_t ie_len, int ssi_signal);
void *ctx;
};
#define HOSTAPD_RATE_BASIC 0x00000001
struct hostapd_rate_data {
int rate; /* rate in 100 kbps */
int flags; /* HOSTAPD_RATE_ flags */
};
struct hostapd_frame_info {
unsigned int freq;
u32 channel;
u32 datarate;
int ssi_signal; /* dBm */
};
enum wps_status {
WPS_STATUS_SUCCESS = 1,
WPS_STATUS_FAILURE
};
enum pbc_status {
WPS_PBC_STATUS_DISABLE,
WPS_PBC_STATUS_ACTIVE,
WPS_PBC_STATUS_TIMEOUT,
WPS_PBC_STATUS_OVERLAP
};
struct wps_stat {
enum wps_status status;
enum wps_error_indication failure_reason;
enum pbc_status pbc_status;
u8 peer_addr[ETH_ALEN];
};
struct hostapd_neighbor_entry {
struct dl_list list;
u8 bssid[ETH_ALEN];
struct wpa_ssid_value ssid;
struct wpabuf *nr;
struct wpabuf *lci;
struct wpabuf *civic;
/* LCI update time */
struct os_time lci_date;
int stationary;
+ u32 short_ssid;
+ u8 bss_parameters;
};
struct hostapd_sae_commit_queue {
struct dl_list list;
int rssi;
size_t len;
u8 msg[];
};
/**
* struct hostapd_data - hostapd per-BSS data structure
*/
struct hostapd_data {
struct hostapd_iface *iface;
struct hostapd_config *iconf;
struct hostapd_bss_config *conf;
int interface_added; /* virtual interface added for this BSS */
unsigned int started:1;
unsigned int disabled:1;
unsigned int reenable_beacon:1;
u8 own_addr[ETH_ALEN];
int num_sta; /* number of entries in sta_list */
struct sta_info *sta_list; /* STA info list head */
#define STA_HASH_SIZE 256
#define STA_HASH(sta) (sta[5])
struct sta_info *sta_hash[STA_HASH_SIZE];
/*
* Bitfield for indicating which AIDs are allocated. Only AID values
* 1-2007 are used and as such, the bit at index 0 corresponds to AID
* 1.
*/
#define AID_WORDS ((2008 + 31) / 32)
u32 sta_aid[AID_WORDS];
const struct wpa_driver_ops *driver;
void *drv_priv;
void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
struct sta_info *sta, int reassoc);
void *msg_ctx; /* ctx for wpa_msg() calls */
void *msg_ctx_parent; /* parent interface ctx for wpa_msg() calls */
struct radius_client_data *radius;
u64 acct_session_id;
struct radius_das_data *radius_das;
struct hostapd_cached_radius_acl *acl_cache;
struct hostapd_acl_query_data *acl_queries;
struct wpa_authenticator *wpa_auth;
struct eapol_authenticator *eapol_auth;
struct eap_config *eap_cfg;
struct rsn_preauth_interface *preauth_iface;
struct os_reltime michael_mic_failure;
int michael_mic_failures;
int tkip_countermeasures;
int ctrl_sock;
struct dl_list ctrl_dst;
void *ssl_ctx;
void *eap_sim_db_priv;
struct radius_server_data *radius_srv;
struct dl_list erp_keys; /* struct eap_server_erp_key */
int parameter_set_count;
/* Time Advertisement */
u8 time_update_counter;
struct wpabuf *time_adv;
#ifdef CONFIG_FULL_DYNAMIC_VLAN
struct full_dynamic_vlan *full_dynamic_vlan;
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
struct l2_packet_data *l2;
#ifdef CONFIG_IEEE80211R_AP
struct dl_list l2_queue;
struct dl_list l2_oui_queue;
struct eth_p_oui_ctx *oui_pull;
struct eth_p_oui_ctx *oui_resp;
struct eth_p_oui_ctx *oui_push;
struct eth_p_oui_ctx *oui_sreq;
struct eth_p_oui_ctx *oui_sresp;
#endif /* CONFIG_IEEE80211R_AP */
struct wps_context *wps;
int beacon_set_done;
struct wpabuf *wps_beacon_ie;
struct wpabuf *wps_probe_resp_ie;
#ifdef CONFIG_WPS
unsigned int ap_pin_failures;
unsigned int ap_pin_failures_consecutive;
struct upnp_wps_device_sm *wps_upnp;
unsigned int ap_pin_lockout_time;
struct wps_stat wps_stats;
#endif /* CONFIG_WPS */
#ifdef CONFIG_MACSEC
struct ieee802_1x_kay *kay;
#endif /* CONFIG_MACSEC */
struct hostapd_probereq_cb *probereq_cb;
size_t num_probereq_cb;
void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
int freq);
void *public_action_cb_ctx;
void (*public_action_cb2)(void *ctx, const u8 *buf, size_t len,
int freq);
void *public_action_cb2_ctx;
int (*vendor_action_cb)(void *ctx, const u8 *buf, size_t len,
int freq);
void *vendor_action_cb_ctx;
void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
const u8 *uuid_e);
void *wps_reg_success_cb_ctx;
void (*wps_event_cb)(void *ctx, enum wps_event event,
union wps_event_data *data);
void *wps_event_cb_ctx;
void (*sta_authorized_cb)(void *ctx, const u8 *mac_addr,
int authorized, const u8 *p2p_dev_addr);
void *sta_authorized_cb_ctx;
void (*setup_complete_cb)(void *ctx);
void *setup_complete_cb_ctx;
void (*new_psk_cb)(void *ctx, const u8 *mac_addr,
const u8 *p2p_dev_addr, const u8 *psk,
size_t psk_len);
void *new_psk_cb_ctx;
/* channel switch parameters */
struct hostapd_freq_params cs_freq_params;
u8 cs_count;
int cs_block_tx;
unsigned int cs_c_off_beacon;
unsigned int cs_c_off_proberesp;
int csa_in_progress;
unsigned int cs_c_off_ecsa_beacon;
unsigned int cs_c_off_ecsa_proberesp;
#ifdef CONFIG_P2P
struct p2p_data *p2p;
struct p2p_group *p2p_group;
struct wpabuf *p2p_beacon_ie;
struct wpabuf *p2p_probe_resp_ie;
/* Number of non-P2P association stations */
int num_sta_no_p2p;
/* Periodic NoA (used only when no non-P2P clients in the group) */
int noa_enabled;
int noa_start;
int noa_duration;
#endif /* CONFIG_P2P */
#ifdef CONFIG_PROXYARP
struct l2_packet_data *sock_dhcp;
struct l2_packet_data *sock_ndisc;
#endif /* CONFIG_PROXYARP */
#ifdef CONFIG_MESH
int num_plinks;
int max_plinks;
void (*mesh_sta_free_cb)(struct hostapd_data *hapd,
struct sta_info *sta);
struct wpabuf *mesh_pending_auth;
struct os_reltime mesh_pending_auth_time;
u8 mesh_required_peer[ETH_ALEN];
#endif /* CONFIG_MESH */
#ifdef CONFIG_SQLITE
struct hostapd_eap_user tmp_eap_user;
#endif /* CONFIG_SQLITE */
#ifdef CONFIG_SAE
/** Key used for generating SAE anti-clogging tokens */
u8 comeback_key[8];
struct os_reltime last_comeback_key_update;
u16 comeback_idx;
u16 comeback_pending_idx[256];
int dot11RSNASAERetransPeriod; /* msec */
struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
#endif /* CONFIG_SAE */
#ifdef CONFIG_TESTING_OPTIONS
unsigned int ext_mgmt_frame_handling:1;
unsigned int ext_eapol_frame_io:1;
struct l2_packet_data *l2_test;
enum wpa_alg last_gtk_alg;
int last_gtk_key_idx;
u8 last_gtk[WPA_GTK_MAX_LEN];
size_t last_gtk_len;
enum wpa_alg last_igtk_alg;
int last_igtk_key_idx;
u8 last_igtk[WPA_IGTK_MAX_LEN];
size_t last_igtk_len;
enum wpa_alg last_bigtk_alg;
int last_bigtk_key_idx;
u8 last_bigtk[WPA_BIGTK_MAX_LEN];
size_t last_bigtk_len;
bool force_backlog_bytes;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_MBO
unsigned int mbo_assoc_disallow;
#endif /* CONFIG_MBO */
struct dl_list nr_db;
u8 beacon_req_token;
u8 lci_req_token;
u8 range_req_token;
unsigned int lci_req_active:1;
unsigned int range_req_active:1;
int dhcp_sock; /* UDP socket used with the DHCP server */
struct ptksa_cache *ptksa;
#ifdef CONFIG_DPP
int dpp_init_done;
struct dpp_authentication *dpp_auth;
u8 dpp_allowed_roles;
int dpp_qr_mutual;
int dpp_auth_ok_on_ack;
int dpp_in_response_listen;
struct gas_query_ap *gas;
struct dpp_pkex *dpp_pkex;
struct dpp_bootstrap_info *dpp_pkex_bi;
char *dpp_pkex_code;
char *dpp_pkex_identifier;
char *dpp_pkex_auth_cmd;
char *dpp_configurator_params;
struct os_reltime dpp_last_init;
struct os_reltime dpp_init_iter_start;
unsigned int dpp_init_max_tries;
unsigned int dpp_init_retry_time;
unsigned int dpp_resp_wait_time;
unsigned int dpp_resp_max_tries;
unsigned int dpp_resp_retry_time;
#ifdef CONFIG_DPP2
struct wpabuf *dpp_presence_announcement;
struct dpp_bootstrap_info *dpp_chirp_bi;
int dpp_chirp_freq;
int *dpp_chirp_freqs;
int dpp_chirp_iter;
int dpp_chirp_round;
int dpp_chirp_scan_done;
int dpp_chirp_listen;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
char *dpp_config_obj_override;
char *dpp_discovery_override;
char *dpp_groups_override;
unsigned int dpp_ignore_netaccesskey_mismatch:1;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_DPP */
#ifdef CONFIG_AIRTIME_POLICY
unsigned int num_backlogged_sta;
unsigned int airtime_weight;
#endif /* CONFIG_AIRTIME_POLICY */
u8 last_1x_eapol_key_replay_counter[8];
#ifdef CONFIG_SQLITE
sqlite3 *rad_attr_db;
#endif /* CONFIG_SQLITE */
#ifdef CONFIG_CTRL_IFACE_UDP
unsigned char ctrl_iface_cookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
};
struct hostapd_sta_info {
struct dl_list list;
u8 addr[ETH_ALEN];
struct os_reltime last_seen;
int ssi_signal;
#ifdef CONFIG_TAXONOMY
struct wpabuf *probe_ie_taxonomy;
#endif /* CONFIG_TAXONOMY */
};
/**
* struct hostapd_iface - hostapd per-interface data structure
*/
struct hostapd_iface {
struct hapd_interfaces *interfaces;
void *owner;
char *config_fname;
struct hostapd_config *conf;
char phy[16]; /* Name of the PHY (radio) */
enum hostapd_iface_state {
HAPD_IFACE_UNINITIALIZED,
HAPD_IFACE_DISABLED,
HAPD_IFACE_COUNTRY_UPDATE,
HAPD_IFACE_ACS,
HAPD_IFACE_HT_SCAN,
HAPD_IFACE_DFS,
HAPD_IFACE_ENABLED
} state;
#ifdef CONFIG_MESH
struct mesh_conf *mconf;
#endif /* CONFIG_MESH */
size_t num_bss;
struct hostapd_data **bss;
unsigned int wait_channel_update:1;
unsigned int cac_started:1;
#ifdef CONFIG_FST
struct fst_iface *fst;
const struct wpabuf *fst_ies;
#endif /* CONFIG_FST */
/*
* When set, indicates that the driver will handle the AP
* teardown: delete global keys, station keys, and stations.
*/
unsigned int driver_ap_teardown:1;
/*
* When set, indicates that this interface is part of list of
* interfaces that need to be started together (synchronously).
*/
unsigned int need_to_start_in_sync:1;
/* Ready to start but waiting for other interfaces to become ready. */
unsigned int ready_to_start_in_sync:1;
int num_ap; /* number of entries in ap_list */
struct ap_info *ap_list; /* AP info list head */
struct ap_info *ap_hash[STA_HASH_SIZE];
u64 drv_flags;
u64 drv_flags2;
/*
* A bitmap of supported protocols for probe response offload. See
* struct wpa_driver_capa in driver.h
*/
unsigned int probe_resp_offloads;
/* extended capabilities supported by the driver */
const u8 *extended_capa, *extended_capa_mask;
unsigned int extended_capa_len;
unsigned int drv_max_acl_mac_addrs;
struct hostapd_hw_modes *hw_features;
int num_hw_features;
struct hostapd_hw_modes *current_mode;
/* Rates that are currently used (i.e., filtered copy of
* current_mode->channels */
int num_rates;
struct hostapd_rate_data *current_rates;
int *basic_rates;
int freq;
u16 hw_flags;
/* Number of associated Non-ERP stations (i.e., stations using 802.11b
* in 802.11g BSS) */
int num_sta_non_erp;
/* Number of associated stations that do not support Short Slot Time */
int num_sta_no_short_slot_time;
/* Number of associated stations that do not support Short Preamble */
int num_sta_no_short_preamble;
int olbc; /* Overlapping Legacy BSS Condition */
/* Number of HT associated stations that do not support greenfield */
int num_sta_ht_no_gf;
/* Number of associated non-HT stations */
int num_sta_no_ht;
/* Number of HT associated stations 20 MHz */
int num_sta_ht_20mhz;
/* Number of HT40 intolerant stations */
int num_sta_ht40_intolerant;
/* Overlapping BSS information */
int olbc_ht;
u16 ht_op_mode;
/* surveying helpers */
/* number of channels surveyed */
unsigned int chans_surveyed;
/* lowest observed noise floor in dBm */
s8 lowest_nf;
/* channel utilization calculation */
u64 last_channel_time;
u64 last_channel_time_busy;
u8 channel_utilization;
unsigned int chan_util_samples_sum;
unsigned int chan_util_num_sample_periods;
unsigned int chan_util_average;
/* eCSA IE will be added only if operating class is specified */
u8 cs_oper_class;
unsigned int dfs_cac_ms;
struct os_reltime dfs_cac_start;
/* Latched with the actual secondary channel information and will be
* used while juggling between HT20 and HT40 modes. */
int secondary_ch;
#ifdef CONFIG_ACS
unsigned int acs_num_completed_scans;
#endif /* CONFIG_ACS */
void (*scan_cb)(struct hostapd_iface *iface);
int num_ht40_scan_tries;
struct dl_list sta_seen; /* struct hostapd_sta_info */
unsigned int num_sta_seen;
u8 dfs_domain;
#ifdef CONFIG_AIRTIME_POLICY
unsigned int airtime_quantum;
#endif /* CONFIG_AIRTIME_POLICY */
/* Previous WMM element information */
struct hostapd_wmm_ac_params prev_wmm[WMM_AC_NUM];
int (*enable_iface_cb)(struct hostapd_iface *iface);
int (*disable_iface_cb)(struct hostapd_iface *iface);
};
/* hostapd.c */
int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
int (*cb)(struct hostapd_iface *iface,
void *ctx), void *ctx);
int hostapd_reload_config(struct hostapd_iface *iface);
void hostapd_reconfig_encryption(struct hostapd_data *hapd);
struct hostapd_data *
hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
struct hostapd_config *conf,
struct hostapd_bss_config *bss);
int hostapd_setup_interface(struct hostapd_iface *iface);
int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
void hostapd_interface_deinit(struct hostapd_iface *iface);
void hostapd_interface_free(struct hostapd_iface *iface);
struct hostapd_iface * hostapd_alloc_iface(void);
struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
const char *config_file);
struct hostapd_iface *
hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
const char *config_fname, int debug);
void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc);
void hostapd_interface_deinit_free(struct hostapd_iface *iface);
int hostapd_enable_iface(struct hostapd_iface *hapd_iface);
int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
void hostapd_bss_deinit_no_free(struct hostapd_data *hapd);
void hostapd_free_hapd_data(struct hostapd_data *hapd);
void hostapd_cleanup_iface_partial(struct hostapd_iface *iface);
int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
const char * hostapd_state_text(enum hostapd_iface_state s);
int hostapd_csa_in_progress(struct hostapd_iface *iface);
void hostapd_chan_switch_config(struct hostapd_data *hapd,
struct hostapd_freq_params *freq_params);
int hostapd_switch_channel(struct hostapd_data *hapd,
struct csa_settings *settings);
void
hostapd_switch_channel_fallback(struct hostapd_iface *iface,
const struct hostapd_freq_params *freq_params);
void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
void hostapd_periodic_iface(struct hostapd_iface *iface);
int hostapd_owe_trans_get_info(struct hostapd_data *hapd);
void hostapd_ocv_check_csa_sa_query(void *eloop_ctx, void *timeout_ctx);
/* utils.c */
int hostapd_register_probereq_cb(struct hostapd_data *hapd,
int (*cb)(void *ctx, const u8 *sa,
const u8 *da, const u8 *bssid,
const u8 *ie, size_t ie_len,
int ssi_signal),
void *ctx);
void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
/* drv_callbacks.c (TODO: move to somewhere else?) */
void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd,
struct sta_info *sta);
int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
const u8 *ie, size_t ielen, int reassoc);
void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr);
void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
const u8 *addr, int reason_code);
int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
const u8 *bssid, const u8 *ie, size_t ie_len,
int ssi_signal);
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
int offset, int width, int cf1, int cf2,
int finished);
struct survey_results;
void hostapd_event_get_survey(struct hostapd_iface *iface,
struct survey_results *survey_results);
void hostapd_acs_channel_selected(struct hostapd_data *hapd,
struct acs_selected_channels *acs_res);
const struct hostapd_eap_user *
hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
size_t identity_len, int phase2);
struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
const char *ifname);
void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
enum smps_mode smps_mode,
enum chan_width chan_width, u8 rx_nss);
#ifdef CONFIG_FST
void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
struct fst_wpa_obj *iface_obj);
#endif /* CONFIG_FST */
#endif /* HOSTAPD_H */
diff --git a/contrib/wpa/src/ap/hw_features.c b/contrib/wpa/src/ap/hw_features.c
index bb5404fa7dd4..4b66b02f4e16 100644
--- a/contrib/wpa/src/ap/hw_features.c
+++ b/contrib/wpa/src/ap/hw_features.c
@@ -1,1212 +1,1214 @@
/*
* hostapd / Hardware feature query and different modes
* Copyright 2002-2003, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2008-2012, 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"
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "common/hw_features_common.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
#include "acs.h"
#include "ieee802_11.h"
#include "beacon.h"
#include "hw_features.h"
void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
size_t num_hw_features)
{
size_t i;
if (hw_features == NULL)
return;
for (i = 0; i < num_hw_features; i++) {
os_free(hw_features[i].channels);
os_free(hw_features[i].rates);
}
os_free(hw_features);
}
#ifndef CONFIG_NO_STDOUT_DEBUG
static char * dfs_info(struct hostapd_channel_data *chan)
{
static char info[256];
char *state;
switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
case HOSTAPD_CHAN_DFS_UNKNOWN:
state = "unknown";
break;
case HOSTAPD_CHAN_DFS_USABLE:
state = "usable";
break;
case HOSTAPD_CHAN_DFS_UNAVAILABLE:
state = "unavailable";
break;
case HOSTAPD_CHAN_DFS_AVAILABLE:
state = "available";
break;
default:
return "";
}
os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
info[sizeof(info) - 1] = '\0';
return info;
}
#endif /* CONFIG_NO_STDOUT_DEBUG */
int hostapd_get_hw_features(struct hostapd_iface *iface)
{
struct hostapd_data *hapd = iface->bss[0];
int i, j;
u16 num_modes, flags;
struct hostapd_hw_modes *modes;
u8 dfs_domain;
if (hostapd_drv_none(hapd))
return -1;
modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags,
&dfs_domain);
if (modes == NULL) {
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Fetching hardware channel/rate support not "
"supported.");
return -1;
}
iface->hw_flags = flags;
iface->dfs_domain = dfs_domain;
hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
iface->hw_features = modes;
iface->num_hw_features = num_modes;
for (i = 0; i < num_modes; i++) {
struct hostapd_hw_modes *feature = &modes[i];
int dfs_enabled = hapd->iconf->ieee80211h &&
(iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
/* set flag for channels we can use in current regulatory
* domain */
for (j = 0; j < feature->num_channels; j++) {
int dfs = 0;
/*
* Disable all channels that are marked not to allow
* to initiate radiation (a.k.a. passive scan and no
* IBSS).
* Use radar channels only if the driver supports DFS.
*/
if ((feature->channels[j].flag &
HOSTAPD_CHAN_RADAR) && dfs_enabled) {
dfs = 1;
} else if (((feature->channels[j].flag &
HOSTAPD_CHAN_RADAR) &&
!(iface->drv_flags &
WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
(feature->channels[j].flag &
HOSTAPD_CHAN_NO_IR)) {
feature->channels[j].flag |=
HOSTAPD_CHAN_DISABLED;
}
if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
continue;
wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
"chan=%d freq=%d MHz max_tx_power=%d dBm%s",
feature->mode,
feature->channels[j].chan,
feature->channels[j].freq,
feature->channels[j].max_tx_power,
dfs ? dfs_info(&feature->channels[j]) : "");
}
}
return 0;
}
int hostapd_prepare_rates(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode)
{
int i, num_basic_rates = 0;
int basic_rates_a[] = { 60, 120, 240, -1 };
int basic_rates_b[] = { 10, 20, -1 };
int basic_rates_g[] = { 10, 20, 55, 110, -1 };
int *basic_rates;
if (iface->conf->basic_rates)
basic_rates = iface->conf->basic_rates;
else switch (mode->mode) {
case HOSTAPD_MODE_IEEE80211A:
basic_rates = basic_rates_a;
break;
case HOSTAPD_MODE_IEEE80211B:
basic_rates = basic_rates_b;
break;
case HOSTAPD_MODE_IEEE80211G:
basic_rates = basic_rates_g;
break;
case HOSTAPD_MODE_IEEE80211AD:
return 0; /* No basic rates for 11ad */
default:
return -1;
}
i = 0;
while (basic_rates[i] >= 0)
i++;
if (i)
i++; /* -1 termination */
os_free(iface->basic_rates);
iface->basic_rates = os_malloc(i * sizeof(int));
if (iface->basic_rates)
os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
os_free(iface->current_rates);
iface->num_rates = 0;
iface->current_rates =
os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
if (!iface->current_rates) {
wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
"table.");
return -1;
}
for (i = 0; i < mode->num_rates; i++) {
struct hostapd_rate_data *rate;
if (iface->conf->supported_rates &&
!hostapd_rate_found(iface->conf->supported_rates,
mode->rates[i]))
continue;
rate = &iface->current_rates[iface->num_rates];
rate->rate = mode->rates[i];
if (hostapd_rate_found(basic_rates, rate->rate)) {
rate->flags |= HOSTAPD_RATE_BASIC;
num_basic_rates++;
}
wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
iface->num_rates, rate->rate, rate->flags);
iface->num_rates++;
}
if ((iface->num_rates == 0 || num_basic_rates == 0) &&
(!iface->conf->ieee80211n || !iface->conf->require_ht)) {
wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
"rate sets (%d,%d).",
iface->num_rates, num_basic_rates);
return -1;
}
return 0;
}
static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
{
int pri_freq, sec_freq;
struct hostapd_channel_data *p_chan, *s_chan;
pri_freq = iface->freq;
sec_freq = pri_freq + iface->conf->secondary_channel * 20;
if (!iface->current_mode)
return 0;
p_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq, NULL,
iface->hw_features,
iface->num_hw_features);
s_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq, NULL,
iface->hw_features,
iface->num_hw_features);
return allowed_ht40_channel_pair(iface->current_mode->mode,
p_chan, s_chan);
}
static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
{
if (iface->conf->secondary_channel > 0) {
iface->conf->channel += 4;
iface->freq += 20;
iface->conf->secondary_channel = -1;
} else {
iface->conf->channel -= 4;
iface->freq -= 20;
iface->conf->secondary_channel = 1;
}
}
static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
struct wpa_scan_results *scan_res)
{
unsigned int pri_freq, sec_freq;
int res;
struct hostapd_channel_data *pri_chan, *sec_chan;
pri_freq = iface->freq;
sec_freq = pri_freq + iface->conf->secondary_channel * 20;
if (!iface->current_mode)
return 0;
pri_chan = hw_get_channel_freq(iface->current_mode->mode, pri_freq,
NULL, iface->hw_features,
iface->num_hw_features);
sec_chan = hw_get_channel_freq(iface->current_mode->mode, sec_freq,
NULL, iface->hw_features,
iface->num_hw_features);
res = check_40mhz_5g(scan_res, pri_chan, sec_chan);
if (res == 2) {
if (iface->conf->no_pri_sec_switch) {
wpa_printf(MSG_DEBUG,
"Cannot switch PRI/SEC channels due to local constraint");
} else {
ieee80211n_switch_pri_sec(iface);
}
}
return !!res;
}
static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
struct wpa_scan_results *scan_res)
{
int pri_chan, sec_chan;
pri_chan = iface->conf->channel;
sec_chan = pri_chan + iface->conf->secondary_channel * 4;
return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
sec_chan);
}
static void ieee80211n_check_scan(struct hostapd_iface *iface)
{
struct wpa_scan_results *scan_res;
int oper40;
int res = 0;
/* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
* allowed per IEEE Std 802.11-2012, 10.15.3.2 */
iface->scan_cb = NULL;
scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
if (scan_res == NULL) {
hostapd_setup_interface_complete(iface, 1);
return;
}
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
else
oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
wpa_scan_results_free(scan_res);
iface->secondary_ch = iface->conf->secondary_channel;
if (!oper40) {
wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
"channel pri=%d sec=%d based on overlapping BSSes",
iface->conf->channel,
iface->conf->channel +
iface->conf->secondary_channel * 4);
iface->conf->secondary_channel = 0;
if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
/*
* TODO: Could consider scheduling another scan to check
* if channel width can be changed if no coex reports
* are received from associating stations.
*/
}
}
#ifdef CONFIG_IEEE80211AX
if (iface->conf->secondary_channel &&
iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
iface->conf->ieee80211ax) {
struct he_capabilities *he_cap;
he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP];
if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G)) {
wpa_printf(MSG_DEBUG,
"HE: 40 MHz channel width is not supported in 2.4 GHz; clear secondary channel configuration");
iface->conf->secondary_channel = 0;
}
}
#endif /* CONFIG_IEEE80211AX */
if (iface->conf->secondary_channel)
res = ieee80211n_allowed_ht40_channel_pair(iface);
if (!res) {
iface->conf->secondary_channel = 0;
hostapd_set_oper_centr_freq_seg0_idx(iface->conf, 0);
hostapd_set_oper_centr_freq_seg1_idx(iface->conf, 0);
hostapd_set_oper_chwidth(iface->conf, CHANWIDTH_USE_HT);
res = 1;
wpa_printf(MSG_INFO, "Fallback to 20 MHz");
}
hostapd_setup_interface_complete(iface, !res);
}
static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
struct wpa_driver_scan_params *params)
{
/* Scan only the affected frequency range */
int pri_freq, sec_freq;
int affected_start, affected_end;
int i, pos;
struct hostapd_hw_modes *mode;
if (iface->current_mode == NULL)
return;
pri_freq = iface->freq;
if (iface->conf->secondary_channel > 0)
sec_freq = pri_freq + 20;
else
sec_freq = pri_freq - 20;
/*
* Note: Need to find the PRI channel also in cases where the affected
* channel is the SEC channel of a 40 MHz BSS, so need to include the
* scanning coverage here to be 40 MHz from the center frequency.
*/
affected_start = (pri_freq + sec_freq) / 2 - 40;
affected_end = (pri_freq + sec_freq) / 2 + 40;
wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
affected_start, affected_end);
mode = iface->current_mode;
params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
if (params->freqs == NULL)
return;
pos = 0;
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *chan = &mode->channels[i];
if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
if (chan->freq < affected_start ||
chan->freq > affected_end)
continue;
params->freqs[pos++] = chan->freq;
}
}
static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
struct wpa_driver_scan_params *params)
{
/* Scan only the affected frequency range */
int pri_freq;
int affected_start, affected_end;
int i, pos;
struct hostapd_hw_modes *mode;
if (iface->current_mode == NULL)
return;
pri_freq = iface->freq;
if (iface->conf->secondary_channel > 0) {
affected_start = pri_freq - 10;
affected_end = pri_freq + 30;
} else {
affected_start = pri_freq - 30;
affected_end = pri_freq + 10;
}
wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
affected_start, affected_end);
mode = iface->current_mode;
params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
if (params->freqs == NULL)
return;
pos = 0;
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *chan = &mode->channels[i];
if (chan->flag & HOSTAPD_CHAN_DISABLED)
continue;
if (chan->freq < affected_start ||
chan->freq > affected_end)
continue;
params->freqs[pos++] = chan->freq;
}
}
static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
{
#define HT2040_COEX_SCAN_RETRY 15
struct hostapd_iface *iface = eloop_data;
struct wpa_driver_scan_params params;
int ret;
os_memset(&params, 0, sizeof(params));
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
ieee80211n_scan_channels_2g4(iface, &params);
else
ieee80211n_scan_channels_5g(iface, &params);
ret = hostapd_driver_scan(iface->bss[0], &params);
iface->num_ht40_scan_tries++;
os_free(params.freqs);
if (ret == -EBUSY &&
iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
wpa_printf(MSG_ERROR,
"Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
ret, strerror(-ret), iface->num_ht40_scan_tries);
eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
return;
}
if (ret == 0) {
iface->scan_cb = ieee80211n_check_scan;
return;
}
wpa_printf(MSG_DEBUG,
"Failed to request a scan in device, bringing up in HT20 mode");
iface->conf->secondary_channel = 0;
iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
hostapd_setup_interface_complete(iface, 0);
}
void hostapd_stop_setup_timers(struct hostapd_iface *iface)
{
eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
}
static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
{
struct wpa_driver_scan_params params;
int ret;
/* Check that HT40 is used and PRI / SEC switch is allowed */
if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
return 0;
hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
"40 MHz channel");
os_memset(&params, 0, sizeof(params));
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
ieee80211n_scan_channels_2g4(iface, &params);
else
ieee80211n_scan_channels_5g(iface, &params);
ret = hostapd_driver_scan(iface->bss[0], &params);
os_free(params.freqs);
if (ret == -EBUSY) {
wpa_printf(MSG_ERROR,
"Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
ret, strerror(-ret));
iface->num_ht40_scan_tries = 1;
eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
return 1;
}
if (ret < 0) {
wpa_printf(MSG_ERROR,
"Failed to request a scan of neighboring BSSes ret=%d (%s)",
ret, strerror(-ret));
return -1;
}
iface->scan_cb = ieee80211n_check_scan;
return 1;
}
static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
{
u16 hw = iface->current_mode->ht_capab;
u16 conf = iface->conf->ht_capab;
if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
!(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [LDPC]");
return 0;
}
/*
* Driver ACS chosen channel may not be HT40 due to internal driver
* restrictions.
*/
if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
!(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [HT40*]");
return 0;
}
if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
!(hw & HT_CAP_INFO_GREEN_FIELD)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [GF]");
return 0;
}
if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
!(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [SHORT-GI-20]");
return 0;
}
if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
!(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [SHORT-GI-40]");
return 0;
}
if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [TX-STBC]");
return 0;
}
if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
(hw & HT_CAP_INFO_RX_STBC_MASK)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [RX-STBC*]");
return 0;
}
if ((conf & HT_CAP_INFO_DELAYED_BA) &&
!(hw & HT_CAP_INFO_DELAYED_BA)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [DELAYED-BA]");
return 0;
}
if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
!(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [MAX-AMSDU-7935]");
return 0;
}
if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
!(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [DSSS_CCK-40]");
return 0;
}
if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
!(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
wpa_printf(MSG_ERROR, "Driver does not support configured "
"HT capability [LSIG-TXOP-PROT]");
return 0;
}
return 1;
}
#ifdef CONFIG_IEEE80211AC
static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
{
struct hostapd_hw_modes *mode = iface->current_mode;
u32 hw = mode->vht_capab;
u32 conf = iface->conf->vht_capab;
wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
hw, conf);
if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
iface->conf->bss[0]->vendor_vht &&
mode->vht_capab == 0 && iface->hw_features) {
int i;
for (i = 0; i < iface->num_hw_features; i++) {
if (iface->hw_features[i].mode ==
HOSTAPD_MODE_IEEE80211A) {
mode = &iface->hw_features[i];
hw = mode->vht_capab;
wpa_printf(MSG_DEBUG,
"update hw vht capab based on 5 GHz band: 0x%x",
hw);
break;
}
}
}
return ieee80211ac_cap_check(hw, conf);
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
static int ieee80211ax_supported_he_capab(struct hostapd_iface *iface)
{
return 1;
}
#endif /* CONFIG_IEEE80211AX */
int hostapd_check_ht_capab(struct hostapd_iface *iface)
{
int ret;
if (is_6ghz_freq(iface->freq))
return 0;
if (!iface->conf->ieee80211n)
return 0;
if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B &&
iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G &&
(iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) {
wpa_printf(MSG_DEBUG,
"Disable HT capability [DSSS_CCK-40] on 5 GHz band");
iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
}
if (!ieee80211n_supported_ht_capab(iface))
return -1;
#ifdef CONFIG_IEEE80211AX
if (iface->conf->ieee80211ax &&
!ieee80211ax_supported_he_capab(iface))
return -1;
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_IEEE80211AC
if (iface->conf->ieee80211ac &&
!ieee80211ac_supported_vht_capab(iface))
return -1;
#endif /* CONFIG_IEEE80211AC */
ret = ieee80211n_check_40mhz(iface);
if (ret)
return ret;
if (!ieee80211n_allowed_ht40_channel_pair(iface))
return -1;
return 0;
}
int hostapd_check_edmg_capab(struct hostapd_iface *iface)
{
struct hostapd_hw_modes *mode = iface->hw_features;
struct ieee80211_edmg_config edmg;
if (!iface->conf->enable_edmg)
return 0;
hostapd_encode_edmg_chan(iface->conf->enable_edmg,
iface->conf->edmg_channel,
iface->conf->channel,
&edmg);
if (mode->edmg.channels && ieee802_edmg_is_allowed(mode->edmg, edmg))
return 0;
wpa_printf(MSG_WARNING, "Requested EDMG configuration is not valid");
wpa_printf(MSG_INFO, "EDMG capab: channels 0x%x, bw_config %d",
mode->edmg.channels, mode->edmg.bw_config);
wpa_printf(MSG_INFO,
"Requested EDMG configuration: channels 0x%x, bw_config %d",
edmg.channels, edmg.bw_config);
return -1;
}
int hostapd_check_he_6ghz_capab(struct hostapd_iface *iface)
{
#ifdef CONFIG_IEEE80211AX
struct he_capabilities *he_cap;
u16 hw;
if (!iface->current_mode || !is_6ghz_freq(iface->freq))
return 0;
he_cap = &iface->current_mode->he_capab[IEEE80211_MODE_AP];
hw = he_cap->he_6ghz_capa;
if (iface->conf->he_6ghz_max_mpdu >
((hw & HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK) >>
HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT)) {
wpa_printf(MSG_ERROR,
"The driver does not support the configured HE 6 GHz Max MPDU length");
return -1;
}
if (iface->conf->he_6ghz_max_ampdu_len_exp >
((hw & HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK) >>
HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT)) {
wpa_printf(MSG_ERROR,
"The driver does not support the configured HE 6 GHz Max AMPDU Length Exponent");
return -1;
}
if (iface->conf->he_6ghz_rx_ant_pat &&
!(hw & HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS)) {
wpa_printf(MSG_ERROR,
"The driver does not support the configured HE 6 GHz Rx Antenna Pattern");
return -1;
}
if (iface->conf->he_6ghz_tx_ant_pat &&
!(hw & HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS)) {
wpa_printf(MSG_ERROR,
"The driver does not support the configured HE 6 GHz Tx Antenna Pattern");
return -1;
}
#endif /* CONFIG_IEEE80211AX */
return 0;
}
static int hostapd_is_usable_chan(struct hostapd_iface *iface,
int frequency, int primary)
{
struct hostapd_channel_data *chan;
if (!iface->current_mode)
return 0;
chan = hw_get_channel_freq(iface->current_mode->mode, frequency, NULL,
iface->hw_features, iface->num_hw_features);
if (!chan)
return 0;
if ((primary && chan_pri_allowed(chan)) ||
(!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
return 1;
wpa_printf(MSG_INFO,
"Frequency %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
frequency, primary ? "primary" : "secondary",
chan->flag,
chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
return 0;
}
static int hostapd_is_usable_edmg(struct hostapd_iface *iface)
{
int i, contiguous = 0;
int num_of_enabled = 0;
int max_contiguous = 0;
struct ieee80211_edmg_config edmg;
struct hostapd_channel_data *pri_chan;
if (!iface->conf->enable_edmg)
return 1;
if (!iface->current_mode)
return 0;
pri_chan = hw_get_channel_freq(iface->current_mode->mode,
iface->freq, NULL,
iface->hw_features,
iface->num_hw_features);
+ if (!pri_chan)
+ return 0;
hostapd_encode_edmg_chan(iface->conf->enable_edmg,
iface->conf->edmg_channel,
pri_chan->chan,
&edmg);
if (!(edmg.channels & BIT(pri_chan->chan - 1)))
return 0;
/* 60 GHz channels 1..6 */
for (i = 0; i < 6; i++) {
int freq = 56160 + 2160 * (i + 1);
if (edmg.channels & BIT(i)) {
contiguous++;
num_of_enabled++;
} else {
contiguous = 0;
continue;
}
/* P802.11ay defines that the total number of subfields
* set to one does not exceed 4.
*/
if (num_of_enabled > 4)
return 0;
if (!hostapd_is_usable_chan(iface, freq, 1))
return 0;
if (contiguous > max_contiguous)
max_contiguous = contiguous;
}
/* Check if the EDMG configuration is valid under the limitations
* of P802.11ay.
*/
/* check bw_config against contiguous EDMG channels */
switch (edmg.bw_config) {
case EDMG_BW_CONFIG_4:
if (!max_contiguous)
return 0;
break;
case EDMG_BW_CONFIG_5:
if (max_contiguous < 2)
return 0;
break;
default:
return 0;
}
return 1;
}
static int hostapd_is_usable_chans(struct hostapd_iface *iface)
{
int secondary_freq;
struct hostapd_channel_data *pri_chan;
if (!iface->current_mode)
return 0;
pri_chan = hw_get_channel_freq(iface->current_mode->mode,
iface->freq, NULL,
iface->hw_features,
iface->num_hw_features);
if (!pri_chan) {
wpa_printf(MSG_ERROR, "Primary frequency not present");
return 0;
}
if (!hostapd_is_usable_chan(iface, pri_chan->freq, 1)) {
wpa_printf(MSG_ERROR, "Primary frequency not allowed");
return 0;
}
if (!hostapd_is_usable_edmg(iface))
return 0;
if (!iface->conf->secondary_channel)
return 1;
if (hostapd_is_usable_chan(iface, iface->freq +
iface->conf->secondary_channel * 20, 0)) {
if (iface->conf->secondary_channel == 1 &&
(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))
return 1;
if (iface->conf->secondary_channel == -1 &&
(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))
return 1;
}
if (!iface->conf->ht40_plus_minus_allowed)
return 0;
/* Both HT40+ and HT40- are set, pick a valid secondary channel */
secondary_freq = iface->freq + 20;
if (hostapd_is_usable_chan(iface, secondary_freq, 0) &&
(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
iface->conf->secondary_channel = 1;
return 1;
}
secondary_freq = iface->freq - 20;
if (hostapd_is_usable_chan(iface, secondary_freq, 0) &&
(pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
iface->conf->secondary_channel = -1;
return 1;
}
return 0;
}
static void hostapd_determine_mode(struct hostapd_iface *iface)
{
int i;
enum hostapd_hw_mode target_mode;
if (iface->current_mode ||
iface->conf->hw_mode != HOSTAPD_MODE_IEEE80211ANY)
return;
if (iface->freq < 4000)
target_mode = HOSTAPD_MODE_IEEE80211G;
else if (iface->freq > 50000)
target_mode = HOSTAPD_MODE_IEEE80211AD;
else
target_mode = HOSTAPD_MODE_IEEE80211A;
for (i = 0; i < iface->num_hw_features; i++) {
struct hostapd_hw_modes *mode;
mode = &iface->hw_features[i];
if (mode->mode == target_mode) {
iface->current_mode = mode;
iface->conf->hw_mode = mode->mode;
break;
}
}
if (!iface->current_mode)
wpa_printf(MSG_ERROR, "ACS: Cannot decide mode");
}
static enum hostapd_chan_status
hostapd_check_chans(struct hostapd_iface *iface)
{
if (iface->freq) {
hostapd_determine_mode(iface);
if (hostapd_is_usable_chans(iface))
return HOSTAPD_CHAN_VALID;
else
return HOSTAPD_CHAN_INVALID;
}
/*
* The user set channel=0 or channel=acs_survey
* which is used to trigger ACS.
*/
switch (acs_init(iface)) {
case HOSTAPD_CHAN_ACS:
return HOSTAPD_CHAN_ACS;
case HOSTAPD_CHAN_VALID:
case HOSTAPD_CHAN_INVALID:
default:
return HOSTAPD_CHAN_INVALID;
}
}
static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
{
if (!iface->current_mode) {
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Hardware does not support configured mode");
return;
}
hostapd_logger(iface->bss[0], NULL,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Configured channel (%d) or frequency (%d) (secondary_channel=%d) not found from the channel list of the current mode (%d) %s",
iface->conf->channel,
iface->freq, iface->conf->secondary_channel,
iface->current_mode->mode,
hostapd_hw_mode_txt(iface->current_mode->mode));
hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Hardware does not support configured channel");
}
int hostapd_acs_completed(struct hostapd_iface *iface, int err)
{
int ret = -1;
if (err)
goto out;
switch (hostapd_check_chans(iface)) {
case HOSTAPD_CHAN_VALID:
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
ACS_EVENT_COMPLETED "freq=%d channel=%d",
iface->freq, iface->conf->channel);
break;
case HOSTAPD_CHAN_ACS:
wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
hostapd_notify_bad_chans(iface);
goto out;
case HOSTAPD_CHAN_INVALID:
default:
wpa_printf(MSG_ERROR, "ACS picked unusable channels");
wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
hostapd_notify_bad_chans(iface);
goto out;
}
ret = hostapd_check_ht_capab(iface);
if (ret < 0)
goto out;
if (ret == 1) {
wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
return 0;
}
ret = 0;
out:
return hostapd_setup_interface_complete(iface, ret);
}
/**
* hostapd_select_hw_mode - Select the hardware mode
* @iface: Pointer to interface data.
* Returns: 0 on success, < 0 on failure
*
* Sets up the hardware mode, channel, rates, and passive scanning
* based on the configuration.
*/
int hostapd_select_hw_mode(struct hostapd_iface *iface)
{
int i;
if (iface->num_hw_features < 1)
return -1;
if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
iface->conf->ieee80211n || iface->conf->ieee80211ac ||
iface->conf->ieee80211ax) &&
iface->conf->channel == 14) {
wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT/HE on channel 14");
iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
iface->conf->ieee80211n = 0;
iface->conf->ieee80211ac = 0;
iface->conf->ieee80211ax = 0;
}
iface->current_mode = NULL;
for (i = 0; i < iface->num_hw_features; i++) {
struct hostapd_hw_modes *mode = &iface->hw_features[i];
int chan;
if (mode->mode == iface->conf->hw_mode) {
if (iface->freq > 0 &&
!hw_mode_get_channel(mode, iface->freq, &chan))
continue;
iface->current_mode = mode;
break;
}
}
if (iface->current_mode == NULL) {
if ((iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) &&
(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY)) {
wpa_printf(MSG_DEBUG,
"Using offloaded hw_mode=any ACS");
} else if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) &&
iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211ANY) {
wpa_printf(MSG_DEBUG,
"Using internal ACS for hw_mode=any");
} else {
wpa_printf(MSG_ERROR,
"Hardware does not support configured mode");
hostapd_logger(iface->bss[0], NULL,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_WARNING,
"Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)",
(int) iface->conf->hw_mode);
return -2;
}
}
switch (hostapd_check_chans(iface)) {
case HOSTAPD_CHAN_VALID:
return 0;
case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
return 1;
case HOSTAPD_CHAN_INVALID:
default:
hostapd_notify_bad_chans(iface);
return -3;
}
}
const char * hostapd_hw_mode_txt(int mode)
{
switch (mode) {
case HOSTAPD_MODE_IEEE80211A:
return "IEEE 802.11a";
case HOSTAPD_MODE_IEEE80211B:
return "IEEE 802.11b";
case HOSTAPD_MODE_IEEE80211G:
return "IEEE 802.11g";
case HOSTAPD_MODE_IEEE80211AD:
return "IEEE 802.11ad";
default:
return "UNKNOWN";
}
}
int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
{
return hw_get_freq(hapd->iface->current_mode, chan);
}
int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
{
int i, channel;
struct hostapd_hw_modes *mode;
if (hapd->iface->current_mode) {
channel = hw_get_chan(hapd->iface->current_mode->mode, freq,
hapd->iface->hw_features,
hapd->iface->num_hw_features);
if (channel)
return channel;
}
/* Check other available modes since the channel list for the current
* mode did not include the specified frequency. */
if (!hapd->iface->hw_features)
return 0;
for (i = 0; i < hapd->iface->num_hw_features; i++) {
mode = &hapd->iface->hw_features[i];
channel = hw_get_chan(mode->mode, freq,
hapd->iface->hw_features,
hapd->iface->num_hw_features);
if (channel)
return channel;
}
return 0;
}
int hostapd_hw_skip_mode(struct hostapd_iface *iface,
struct hostapd_hw_modes *mode)
{
int i;
if (iface->current_mode)
return mode != iface->current_mode;
if (mode->mode != HOSTAPD_MODE_IEEE80211B)
return 0;
for (i = 0; i < iface->num_hw_features; i++) {
if (iface->hw_features[i].mode == HOSTAPD_MODE_IEEE80211G)
return 1;
}
return 0;
}
diff --git a/contrib/wpa/src/ap/ieee802_11.c b/contrib/wpa/src/ap/ieee802_11.c
index 22cce961063e..db41049287fc 100644
--- a/contrib/wpa/src/ap/ieee802_11.c
+++ b/contrib/wpa/src/ap/ieee802_11.c
@@ -1,7074 +1,7456 @@
/*
* hostapd / IEEE 802.11 Management
* Copyright (c) 2002-2017, 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"
#ifndef CONFIG_NATIVE_WINDOWS
#include "utils/common.h"
#include "utils/eloop.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
#include "crypto/sha384.h"
#include "crypto/sha512.h"
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "common/sae.h"
#include "common/dpp.h"
#include "common/ocv.h"
#include "common/wpa_common.h"
#include "common/wpa_ctrl.h"
#include "common/ptksa_cache.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "p2p/p2p.h"
#include "wps/wps.h"
#include "fst/fst.h"
#include "hostapd.h"
#include "beacon.h"
#include "ieee802_11_auth.h"
#include "sta_info.h"
#include "ieee802_1x.h"
#include "wpa_auth.h"
#include "pmksa_cache_auth.h"
#include "wmm.h"
#include "ap_list.h"
#include "accounting.h"
#include "ap_config.h"
#include "ap_mlme.h"
#include "p2p_hostapd.h"
#include "ap_drv_ops.h"
#include "wnm_ap.h"
#include "hw_features.h"
#include "ieee802_11.h"
#include "dfs.h"
#include "mbo_ap.h"
#include "rrm.h"
#include "taxonomy.h"
#include "fils_hlp.h"
#include "dpp_hostapd.h"
#include "gas_query_ap.h"
#ifdef CONFIG_FILS
static struct wpabuf *
prepare_auth_resp_fils(struct hostapd_data *hapd,
struct sta_info *sta, u16 *resp,
struct rsn_pmksa_cache_entry *pmksa,
struct wpabuf *erp_resp,
const u8 *msk, size_t msk_len,
int *is_pub);
#endif /* CONFIG_FILS */
#ifdef CONFIG_PASN
static int handle_auth_pasn_resp(struct hostapd_data *hapd,
struct sta_info *sta,
struct rsn_pmksa_cache_entry *pmksa,
u16 status);
#ifdef CONFIG_FILS
static void pasn_fils_auth_resp(struct hostapd_data *hapd,
struct sta_info *sta, u16 status,
struct wpabuf *erp_resp,
const u8 *msk, size_t msk_len);
#endif /* CONFIG_FILS */
#endif /* CONFIG_PASN */
static void handle_auth(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int rssi, int from_queue);
u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
{
u8 multi_ap_val = 0;
if (!hapd->conf->multi_ap)
return eid;
if (hapd->conf->multi_ap & BACKHAUL_BSS)
multi_ap_val |= MULTI_AP_BACKHAUL_BSS;
if (hapd->conf->multi_ap & FRONTHAUL_BSS)
multi_ap_val |= MULTI_AP_FRONTHAUL_BSS;
return eid + add_multi_ap_ie(eid, 9, multi_ap_val);
}
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
int i, num, count;
int h2e_required;
if (hapd->iface->current_rates == NULL)
return eid;
*pos++ = WLAN_EID_SUPP_RATES;
num = hapd->iface->num_rates;
if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
num++;
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
num++;
h2e_required = (hapd->conf->sae_pwe == 1 ||
hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
hapd->conf->sae_pwe != 3 &&
wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
if (h2e_required)
num++;
if (num > 8) {
/* rest of the rates are encoded in Extended supported
* rates element */
num = 8;
}
*pos++ = num;
for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
i++) {
count++;
*pos = hapd->iface->current_rates[i].rate / 5;
if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
*pos |= 0x80;
pos++;
}
if (hapd->iconf->ieee80211n && hapd->iconf->require_ht && count < 8) {
count++;
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
}
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && count < 8) {
count++;
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
}
if (h2e_required && count < 8) {
count++;
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
}
return pos;
}
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
{
u8 *pos = eid;
int i, num, count;
int h2e_required;
if (hapd->iface->current_rates == NULL)
return eid;
num = hapd->iface->num_rates;
if (hapd->iconf->ieee80211n && hapd->iconf->require_ht)
num++;
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht)
num++;
h2e_required = (hapd->conf->sae_pwe == 1 ||
hostapd_sae_pw_id_in_use(hapd->conf) == 2) &&
hapd->conf->sae_pwe != 3 &&
wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt);
if (h2e_required)
num++;
if (num <= 8)
return eid;
num -= 8;
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = num;
for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
i++) {
count++;
if (count <= 8)
continue; /* already in SuppRates IE */
*pos = hapd->iface->current_rates[i].rate / 5;
if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC)
*pos |= 0x80;
pos++;
}
if (hapd->iconf->ieee80211n && hapd->iconf->require_ht) {
count++;
if (count > 8)
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY;
}
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht) {
count++;
if (count > 8)
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY;
}
if (h2e_required) {
count++;
if (count > 8)
*pos++ = 0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY;
}
return pos;
}
u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
size_t len)
{
size_t i;
for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
if (hapd->conf->radio_measurements[i])
break;
}
if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN)
return eid;
*eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
*eid++ = RRM_CAPABILITIES_IE_LEN;
os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN);
return eid + RRM_CAPABILITIES_IE_LEN;
}
u16 hostapd_own_capab_info(struct hostapd_data *hapd)
{
int capab = WLAN_CAPABILITY_ESS;
int privacy = 0;
int dfs;
int i;
/* Check if any of configured channels require DFS */
dfs = hostapd_is_dfs_required(hapd->iface);
if (dfs < 0) {
wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
dfs);
dfs = 0;
}
if (hapd->iface->num_sta_no_short_preamble == 0 &&
hapd->iconf->preamble == SHORT_PREAMBLE)
capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
#ifdef CONFIG_WEP
privacy = hapd->conf->ssid.wep.keys_set;
if (hapd->conf->ieee802_1x &&
(hapd->conf->default_wep_key_len ||
hapd->conf->individual_wep_key_len))
privacy = 1;
#endif /* CONFIG_WEP */
if (hapd->conf->wpa)
privacy = 1;
#ifdef CONFIG_HS20
if (hapd->conf->osen)
privacy = 1;
#endif /* CONFIG_HS20 */
if (privacy)
capab |= WLAN_CAPABILITY_PRIVACY;
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G &&
hapd->iface->num_sta_no_short_slot_time == 0)
capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
/*
* Currently, Spectrum Management capability bit is set when directly
* requested in configuration by spectrum_mgmt_required or when AP is
* running on DFS channel.
* TODO: Also consider driver support for TPC to set Spectrum Mgmt bit
*/
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
(hapd->iconf->spectrum_mgmt_required || dfs))
capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
if (hapd->conf->radio_measurements[i]) {
capab |= IEEE80211_CAP_RRM;
break;
}
}
return capab;
}
#ifdef CONFIG_WEP
#ifndef CONFIG_NO_RC4
static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
u16 auth_transaction, const u8 *challenge,
int iswep)
{
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"authentication (shared key, transaction %d)",
auth_transaction);
if (auth_transaction == 1) {
if (!sta->challenge) {
/* Generate a pseudo-random challenge */
u8 key[8];
sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN);
if (sta->challenge == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
if (os_get_random(key, sizeof(key)) < 0) {
os_free(sta->challenge);
sta->challenge = NULL;
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
rc4_skip(key, sizeof(key), 0,
sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
}
return 0;
}
if (auth_transaction != 3)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
/* Transaction 3 */
if (!iswep || !sta->challenge || !challenge ||
os_memcmp_const(sta->challenge, challenge,
WLAN_AUTH_CHALLENGE_LEN)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"shared key authentication - invalid "
"challenge-response");
return WLAN_STATUS_CHALLENGE_FAIL;
}
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"authentication OK (shared key)");
sta->flags |= WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
os_free(sta->challenge);
sta->challenge = NULL;
return 0;
}
#endif /* CONFIG_NO_RC4 */
#endif /* CONFIG_WEP */
static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *dst, const u8 *bssid,
u16 auth_alg, u16 auth_transaction, u16 resp,
const u8 *ies, size_t ies_len, const char *dbg)
{
struct ieee80211_mgmt *reply;
u8 *buf;
size_t rlen;
int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
buf = os_zalloc(rlen);
if (buf == NULL)
return -1;
reply = (struct ieee80211_mgmt *) buf;
reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_AUTH);
os_memcpy(reply->da, dst, ETH_ALEN);
os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(reply->bssid, bssid, ETH_ALEN);
reply->u.auth.auth_alg = host_to_le16(auth_alg);
reply->u.auth.auth_transaction = host_to_le16(auth_transaction);
reply->u.auth.status_code = host_to_le16(resp);
if (ies && ies_len)
os_memcpy(reply->u.auth.variable, ies, ies_len);
wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR
" auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)",
MAC2STR(dst), auth_alg, auth_transaction,
resp, (unsigned long) ies_len, dbg);
#ifdef CONFIG_TESTING_OPTIONS
#ifdef CONFIG_SAE
if (hapd->conf->sae_confirm_immediate == 2 &&
auth_alg == WLAN_AUTH_SAE) {
if (auth_transaction == 1 && sta &&
(resp == WLAN_STATUS_SUCCESS ||
resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
resp == WLAN_STATUS_SAE_PK)) {
wpa_printf(MSG_DEBUG,
"TESTING: Postpone SAE Commit transmission until Confirm is ready");
os_free(sta->sae_postponed_commit);
sta->sae_postponed_commit = buf;
sta->sae_postponed_commit_len = rlen;
return WLAN_STATUS_SUCCESS;
}
if (auth_transaction == 2 && sta && sta->sae_postponed_commit) {
wpa_printf(MSG_DEBUG,
"TESTING: Send postponed SAE Commit first, immediately followed by SAE Confirm");
if (hostapd_drv_send_mlme(hapd,
sta->sae_postponed_commit,
sta->sae_postponed_commit_len,
0, NULL, 0, 0) < 0)
wpa_printf(MSG_INFO, "send_auth_reply: send failed");
os_free(sta->sae_postponed_commit);
sta->sae_postponed_commit = NULL;
sta->sae_postponed_commit_len = 0;
}
}
#endif /* CONFIG_SAE */
#endif /* CONFIG_TESTING_OPTIONS */
if (hostapd_drv_send_mlme(hapd, reply, rlen, 0, NULL, 0, 0) < 0)
wpa_printf(MSG_INFO, "send_auth_reply: send failed");
else
reply_res = WLAN_STATUS_SUCCESS;
os_free(buf);
return reply_res;
}
#ifdef CONFIG_IEEE80211R_AP
static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
int reply_res;
reply_res = send_auth_reply(hapd, NULL, dst, bssid, WLAN_AUTH_FT,
auth_transaction, status, ies, ies_len,
"auth-ft-finish");
sta = ap_get_sta(hapd, dst);
if (sta == NULL)
return;
if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS ||
status != WLAN_STATUS_SUCCESS)) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
return;
}
if (status != WLAN_STATUS_SUCCESS)
return;
hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
sta->flags |= WLAN_STA_AUTH;
mlme_authenticate_indication(hapd, sta);
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
static void sae_set_state(struct sta_info *sta, enum sae_state state,
const char *reason)
{
wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
sae_state_txt(sta->sae->state), sae_state_txt(state),
MAC2STR(sta->addr), reason);
sta->sae->state = state;
}
static const char * sae_get_password(struct hostapd_data *hapd,
struct sta_info *sta,
const char *rx_id,
struct sae_password_entry **pw_entry,
struct sae_pt **s_pt,
const struct sae_pk **s_pk)
{
const char *password = NULL;
struct sae_password_entry *pw;
struct sae_pt *pt = NULL;
const struct sae_pk *pk = NULL;
for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
if (!is_broadcast_ether_addr(pw->peer_addr) &&
os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0)
continue;
if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier))
continue;
if (rx_id && pw->identifier &&
os_strcmp(rx_id, pw->identifier) != 0)
continue;
password = pw->password;
pt = pw->pt;
if (!(hapd->conf->mesh & MESH_ENABLED))
pk = pw->pk;
break;
}
if (!password) {
password = hapd->conf->ssid.wpa_passphrase;
pt = hapd->conf->ssid.pt;
}
if (pw_entry)
*pw_entry = pw;
if (s_pt)
*s_pt = pt;
if (s_pk)
*s_pk = pk;
return password;
}
static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
struct sta_info *sta, int update,
int status_code)
{
struct wpabuf *buf;
const char *password = NULL;
struct sae_password_entry *pw;
const char *rx_id = NULL;
int use_pt = 0;
struct sae_pt *pt = NULL;
const struct sae_pk *pk = NULL;
if (sta->sae->tmp) {
rx_id = sta->sae->tmp->pw_id;
use_pt = sta->sae->h2e;
#ifdef CONFIG_SAE_PK
os_memcpy(sta->sae->tmp->own_addr, hapd->own_addr, ETH_ALEN);
os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
#endif /* CONFIG_SAE_PK */
}
if (rx_id && hapd->conf->sae_pwe != 3)
use_pt = 1;
else if (status_code == WLAN_STATUS_SUCCESS)
use_pt = 0;
else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK)
use_pt = 1;
password = sae_get_password(hapd, sta, rx_id, &pw, &pt, &pk);
if (!password || (use_pt && !pt)) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
return NULL;
}
if (update && use_pt &&
sae_prepare_commit_pt(sta->sae, pt, hapd->own_addr, sta->addr,
NULL, pk) < 0)
return NULL;
if (update && !use_pt &&
sae_prepare_commit(hapd->own_addr, sta->addr,
(u8 *) password, os_strlen(password),
sta->sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
return NULL;
}
if (pw && pw->vlan_id) {
if (!sta->sae->tmp) {
wpa_printf(MSG_INFO,
"SAE: No temporary data allocated - cannot store VLAN ID");
return NULL;
}
sta->sae->tmp->vlan_id = pw->vlan_id;
}
buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
(rx_id ? 3 + os_strlen(rx_id) : 0));
if (buf &&
sae_write_commit(sta->sae, buf, sta->sae->tmp ?
sta->sae->tmp->anti_clogging_token : NULL,
rx_id) < 0) {
wpabuf_free(buf);
buf = NULL;
}
return buf;
}
static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
struct sta_info *sta)
{
struct wpabuf *buf;
buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
if (buf == NULL)
return NULL;
#ifdef CONFIG_SAE_PK
#ifdef CONFIG_TESTING_OPTIONS
if (sta->sae->tmp)
sta->sae->tmp->omit_pk_elem = hapd->conf->sae_pk_omit;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_SAE_PK */
if (sae_write_confirm(sta->sae, buf) < 0) {
wpabuf_free(buf);
return NULL;
}
return buf;
}
static int auth_sae_send_commit(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *bssid, int update, int status_code)
{
struct wpabuf *data;
int reply_res;
u16 status;
data = auth_build_sae_commit(hapd, sta, update, status_code);
if (!data && sta->sae->tmp && sta->sae->tmp->pw_id)
return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
if (sta->sae->tmp && sta->sae->pk)
status = WLAN_STATUS_SAE_PK;
else if (sta->sae->tmp && sta->sae->h2e)
status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
else
status = WLAN_STATUS_SUCCESS;
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->sae_commit_status >= 0 &&
hapd->conf->sae_commit_status != status) {
wpa_printf(MSG_INFO,
"TESTING: Override SAE commit status code %u --> %d",
status, hapd->conf->sae_commit_status);
status = hapd->conf->sae_commit_status;
}
#endif /* CONFIG_TESTING_OPTIONS */
reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
WLAN_AUTH_SAE, 1,
status, wpabuf_head(data),
wpabuf_len(data), "sae-send-commit");
wpabuf_free(data);
return reply_res;
}
static int auth_sae_send_confirm(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *bssid)
{
struct wpabuf *data;
int reply_res;
data = auth_build_sae_confirm(hapd, sta);
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
WLAN_AUTH_SAE, 2,
WLAN_STATUS_SUCCESS, wpabuf_head(data),
wpabuf_len(data), "sae-send-confirm");
wpabuf_free(data);
return reply_res;
}
#endif /* CONFIG_SAE */
#if defined(CONFIG_SAE) || defined(CONFIG_PASN)
static int use_anti_clogging(struct hostapd_data *hapd)
{
struct sta_info *sta;
unsigned int open = 0;
if (hapd->conf->anti_clogging_threshold == 0)
return 1;
for (sta = hapd->sta_list; sta; sta = sta->next) {
#ifdef CONFIG_SAE
if (sta->sae &&
(sta->sae->state == SAE_COMMITTED ||
sta->sae->state == SAE_CONFIRMED))
open++;
#endif /* CONFIG_SAE */
#ifdef CONFIG_PASN
if (sta->pasn && sta->pasn->ecdh)
open++;
#endif /* CONFIG_PASN */
if (open >= hapd->conf->anti_clogging_threshold)
return 1;
}
#ifdef CONFIG_SAE
/* In addition to already existing open SAE sessions, check whether
* there are enough pending commit messages in the processing queue to
* potentially result in too many open sessions. */
if (open + dl_list_len(&hapd->sae_commit_queue) >=
hapd->conf->anti_clogging_threshold)
return 1;
#endif /* CONFIG_SAE */
return 0;
}
static int comeback_token_hash(struct hostapd_data *hapd, const u8 *addr,
u8 *idx)
{
u8 hash[SHA256_MAC_LEN];
if (hmac_sha256(hapd->comeback_key, sizeof(hapd->comeback_key),
addr, ETH_ALEN, hash) < 0)
return -1;
*idx = hash[0];
return 0;
}
static int check_comeback_token(struct hostapd_data *hapd, const u8 *addr,
const u8 *token, size_t token_len)
{
u8 mac[SHA256_MAC_LEN];
const u8 *addrs[2];
size_t len[2];
u16 token_idx;
u8 idx;
if (token_len != SHA256_MAC_LEN ||
comeback_token_hash(hapd, addr, &idx) < 0)
return -1;
token_idx = hapd->comeback_pending_idx[idx];
if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
wpa_printf(MSG_DEBUG,
"Comeback: Invalid anti-clogging token from "
MACSTR " - token_idx 0x%04x, expected 0x%04x",
MAC2STR(addr), WPA_GET_BE16(token), token_idx);
return -1;
}
addrs[0] = addr;
len[0] = ETH_ALEN;
addrs[1] = token;
len[1] = 2;
if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key),
2, addrs, len, mac) < 0 ||
os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
return -1;
hapd->comeback_pending_idx[idx] = 0; /* invalidate used token */
return 0;
}
static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
int group, const u8 *addr, int h2e)
{
struct wpabuf *buf;
u8 *token;
struct os_reltime now;
u8 idx[2];
const u8 *addrs[2];
size_t len[2];
u8 p_idx;
u16 token_idx;
os_get_reltime(&now);
if (!os_reltime_initialized(&hapd->last_comeback_key_update) ||
os_reltime_expired(&now, &hapd->last_comeback_key_update, 60) ||
hapd->comeback_idx == 0xffff) {
if (random_get_bytes(hapd->comeback_key,
sizeof(hapd->comeback_key)) < 0)
return NULL;
wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
hapd->comeback_key, sizeof(hapd->comeback_key));
hapd->last_comeback_key_update = now;
hapd->comeback_idx = 0;
os_memset(hapd->comeback_pending_idx, 0,
sizeof(hapd->comeback_pending_idx));
}
buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
if (buf == NULL)
return NULL;
if (group)
wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
if (h2e) {
/* Encapsulate Anti-clogging Token field in a container IE */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
}
if (comeback_token_hash(hapd, addr, &p_idx) < 0) {
wpabuf_free(buf);
return NULL;
}
token_idx = hapd->comeback_pending_idx[p_idx];
if (!token_idx) {
hapd->comeback_idx++;
token_idx = hapd->comeback_idx;
hapd->comeback_pending_idx[p_idx] = token_idx;
}
WPA_PUT_BE16(idx, token_idx);
token = wpabuf_put(buf, SHA256_MAC_LEN);
addrs[0] = addr;
len[0] = ETH_ALEN;
addrs[1] = idx;
len[1] = sizeof(idx);
if (hmac_sha256_vector(hapd->comeback_key, sizeof(hapd->comeback_key),
2, addrs, len, token) < 0) {
wpabuf_free(buf);
return NULL;
}
WPA_PUT_BE16(token, token_idx);
return buf;
}
#endif /* defined(CONFIG_SAE) || defined(CONFIG_PASN) */
#ifdef CONFIG_SAE
static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
{
if (sta->sae->sync > hapd->conf->sae_sync) {
sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
sta->sae->sync = 0;
return -1;
}
return 0;
}
static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
{
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta = eloop_data;
int ret;
if (sae_check_big_sync(hapd, sta))
return;
sta->sae->sync++;
wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
" (sync=%d state=%s)",
MAC2STR(sta->addr), sta->sae->sync,
sae_state_txt(sta->sae->state));
switch (sta->sae->state) {
case SAE_COMMITTED:
ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
eloop_register_timeout(0,
hapd->dot11RSNASAERetransPeriod * 1000,
auth_sae_retransmit_timer, hapd, sta);
break;
case SAE_CONFIRMED:
ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
eloop_register_timeout(0,
hapd->dot11RSNASAERetransPeriod * 1000,
auth_sae_retransmit_timer, hapd, sta);
break;
default:
ret = -1;
break;
}
if (ret != WLAN_STATUS_SUCCESS)
wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
}
void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
{
eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
}
static void sae_set_retransmit_timer(struct hostapd_data *hapd,
struct sta_info *sta)
{
if (!(hapd->conf->mesh & MESH_ENABLED))
return;
eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000,
auth_sae_retransmit_timer, hapd, sta);
}
static void sae_sme_send_external_auth_status(struct hostapd_data *hapd,
struct sta_info *sta, u16 status)
{
struct external_auth params;
os_memset(&params, 0, sizeof(params));
params.status = status;
params.bssid = sta->addr;
if (status == WLAN_STATUS_SUCCESS && sta->sae &&
!hapd->conf->disable_pmksa_caching)
params.pmkid = sta->sae->pmkid;
hostapd_drv_send_external_auth_status(hapd, &params);
}
void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
#ifndef CONFIG_NO_VLAN
struct vlan_description vlan_desc;
if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) {
wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR
" to VLAN ID %d",
MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
os_memset(&vlan_desc, 0, sizeof(vlan_desc));
vlan_desc.notempty = 1;
vlan_desc.untagged = sta->sae->tmp->vlan_id;
if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
wpa_printf(MSG_INFO,
"Invalid VLAN ID %d in sae_password",
sta->sae->tmp->vlan_id);
return;
}
if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
ap_sta_bind_vlan(hapd, sta) < 0) {
wpa_printf(MSG_INFO,
"Failed to assign VLAN ID %d from sae_password to "
MACSTR, sta->sae->tmp->vlan_id,
MAC2STR(sta->addr));
return;
}
}
#endif /* CONFIG_NO_VLAN */
sta->flags |= WLAN_STA_AUTH;
sta->auth_alg = WLAN_AUTH_SAE;
mlme_authenticate_indication(hapd, sta);
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
crypto_bignum_deinit(sta->sae->peer_commit_scalar_accepted, 0);
sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar;
sta->sae->peer_commit_scalar = NULL;
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
sta->sae->pmk, sta->sae->pmkid);
sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
}
static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *bssid, u16 auth_transaction, u16 status_code,
int allow_reuse, int *sta_removed)
{
int ret;
*sta_removed = 0;
if (auth_transaction != 1 && auth_transaction != 2)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
auth_transaction);
switch (sta->sae->state) {
case SAE_NOTHING:
if (auth_transaction == 1) {
if (sta->sae->tmp) {
sta->sae->h2e =
(status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK);
sta->sae->pk =
status_code == WLAN_STATUS_SAE_PK;
}
ret = auth_sae_send_commit(hapd, sta, bssid,
!allow_reuse, status_code);
if (ret)
return ret;
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
/*
* In mesh case, both Commit and Confirm are sent
* immediately. In infrastructure BSS, by default, only
* a single Authentication frame (Commit) is expected
* from the AP here and the second one (Confirm) will
* be sent once the STA has sent its second
* Authentication frame (Confirm). This behavior can be
* overridden with explicit configuration so that the
* infrastructure BSS case sends both frames together.
*/
if ((hapd->conf->mesh & MESH_ENABLED) ||
hapd->conf->sae_confirm_immediate) {
/*
* Send both Commit and Confirm immediately
* based on SAE finite state machine
* Nothing -> Confirm transition.
*/
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret)
return ret;
sae_set_state(sta, SAE_CONFIRMED,
"Sent Confirm (mesh)");
} else {
/*
* For infrastructure BSS, send only the Commit
* message now to get alternating sequence of
* Authentication frames between the AP and STA.
* Confirm will be sent in
* Committed -> Confirmed/Accepted transition
* when receiving Confirm from STA.
*/
}
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
} else {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"SAE confirm before commit");
}
break;
case SAE_COMMITTED:
sae_clear_retransmit_timer(hapd, sta);
if (auth_transaction == 1) {
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret)
return ret;
sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
} else if (hapd->conf->mesh & MESH_ENABLED) {
/*
* In mesh case, follow SAE finite state machine and
* send Commit now, if sync count allows.
*/
if (sae_check_big_sync(hapd, sta))
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
ret = auth_sae_send_commit(hapd, sta, bssid, 0,
status_code);
if (ret)
return ret;
sae_set_retransmit_timer(hapd, sta);
} else {
/*
* For instructure BSS, send the postponed Confirm from
* Nothing -> Confirmed transition that was reduced to
* Nothing -> Committed above.
*/
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret)
return ret;
sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
/*
* Since this was triggered on Confirm RX, run another
* step to get to Accepted without waiting for
* additional events.
*/
return sae_sm_step(hapd, sta, bssid, auth_transaction,
WLAN_STATUS_SUCCESS, 0, sta_removed);
}
break;
case SAE_CONFIRMED:
sae_clear_retransmit_timer(hapd, sta);
if (auth_transaction == 1) {
if (sae_check_big_sync(hapd, sta))
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
ret = auth_sae_send_commit(hapd, sta, bssid, 1,
status_code);
if (ret)
return ret;
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret)
return ret;
sae_set_retransmit_timer(hapd, sta);
} else {
sta->sae->send_confirm = 0xffff;
sae_accept_sta(hapd, sta);
}
break;
case SAE_ACCEPTED:
if (auth_transaction == 1 &&
(hapd->conf->mesh & MESH_ENABLED)) {
wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
") doing reauthentication",
MAC2STR(sta->addr));
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
ap_free_sta(hapd, sta);
*sta_removed = 1;
} else if (auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
ret = auth_sae_send_commit(hapd, sta, bssid, 1,
status_code);
if (ret)
return ret;
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
if (sae_process_commit(sta->sae) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
} else {
if (sae_check_big_sync(hapd, sta))
return WLAN_STATUS_SUCCESS;
sta->sae->sync++;
ret = auth_sae_send_confirm(hapd, sta, bssid);
sae_clear_temp_data(sta->sae);
if (ret)
return ret;
}
break;
default:
wpa_printf(MSG_ERROR, "SAE: invalid state %d",
sta->sae->state);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
return WLAN_STATUS_SUCCESS;
}
static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta)
{
struct sae_data *sae = sta->sae;
int i, *groups = hapd->conf->sae_groups;
int default_groups[] = { 19, 0 };
if (sae->state != SAE_COMMITTED)
return;
wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
if (!groups)
groups = default_groups;
for (i = 0; groups[i] > 0; i++) {
if (sae->group == groups[i])
break;
}
if (groups[i] <= 0) {
wpa_printf(MSG_DEBUG,
"SAE: Previously selected group not found from the current configuration");
return;
}
for (;;) {
i++;
if (groups[i] <= 0) {
wpa_printf(MSG_DEBUG,
"SAE: No alternative group enabled");
return;
}
if (sae_set_group(sae, groups[i]) < 0)
continue;
break;
}
wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]);
}
static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
{
int sae_pwe = hapd->conf->sae_pwe;
int id_in_use;
bool sae_pk = false;
id_in_use = hostapd_sae_pw_id_in_use(hapd->conf);
if (id_in_use == 2 && sae_pwe != 3)
sae_pwe = 1;
else if (id_in_use == 1 && sae_pwe == 0)
sae_pwe = 2;
#ifdef CONFIG_SAE_PK
sae_pk = hostapd_sae_pk_in_use(hapd->conf);
if (sae_pwe == 0 && sae_pk)
sae_pwe = 2;
#endif /* CONFIG_SAE_PK */
return ((sae_pwe == 0 || sae_pwe == 3) &&
status_code == WLAN_STATUS_SUCCESS) ||
(sae_pwe == 1 &&
(status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
(sae_pk && status_code == WLAN_STATUS_SAE_PK))) ||
(sae_pwe == 2 &&
(status_code == WLAN_STATUS_SUCCESS ||
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
(sae_pk && status_code == WLAN_STATUS_SAE_PK)));
}
static int sae_is_group_enabled(struct hostapd_data *hapd, int group)
{
int *groups = hapd->conf->sae_groups;
int default_groups[] = { 19, 0 };
int i;
if (!groups)
groups = default_groups;
for (i = 0; groups[i] > 0; i++) {
if (groups[i] == group)
return 1;
}
return 0;
}
static int check_sae_rejected_groups(struct hostapd_data *hapd,
struct sae_data *sae)
{
const struct wpabuf *groups;
size_t i, count;
const u8 *pos;
if (!sae->tmp)
return 0;
groups = sae->tmp->peer_rejected_groups;
if (!groups)
return 0;
pos = wpabuf_head(groups);
count = wpabuf_len(groups) / 2;
for (i = 0; i < count; i++) {
int enabled;
u16 group;
group = WPA_GET_LE16(pos);
pos += 2;
enabled = sae_is_group_enabled(hapd, group);
wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s",
group, enabled ? "enabled" : "disabled");
if (enabled)
return 1;
}
return 0;
}
static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
const struct ieee80211_mgmt *mgmt, size_t len,
u16 auth_transaction, u16 status_code)
{
int resp = WLAN_STATUS_SUCCESS;
struct wpabuf *data = NULL;
int *groups = hapd->conf->sae_groups;
int default_groups[] = { 19, 0 };
const u8 *pos, *end;
int sta_removed = 0;
bool success_status;
if (!groups)
groups = default_groups;
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->sae_reflection_attack && auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
resp = status_code;
send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
auth_transaction, resp, pos, end - pos,
"auth-sae-reflection-attack");
goto remove_sta;
}
if (hapd->conf->sae_commit_override && auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override");
send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
auth_transaction, resp,
wpabuf_head(hapd->conf->sae_commit_override),
wpabuf_len(hapd->conf->sae_commit_override),
"sae-commit-override");
goto remove_sta;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (!sta->sae) {
if (auth_transaction != 1 ||
!sae_status_success(hapd, status_code)) {
wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u",
status_code);
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
sta->sae = os_zalloc(sizeof(*sta->sae));
if (!sta->sae) {
resp = -1;
goto remove_sta;
}
sae_set_state(sta, SAE_NOTHING, "Init");
sta->sae->sync = 0;
}
if (sta->mesh_sae_pmksa_caching) {
wpa_printf(MSG_DEBUG,
"SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication");
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
sta->mesh_sae_pmksa_caching = 0;
}
if (auth_transaction == 1) {
const u8 *token = NULL;
size_t token_len = 0;
int allow_reuse = 0;
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"start SAE authentication (RX commit, status=%u (%s))",
status_code, status2str(status_code));
if ((hapd->conf->mesh & MESH_ENABLED) &&
status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
sta->sae->tmp) {
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
if (pos + sizeof(le16) > end) {
wpa_printf(MSG_ERROR,
"SAE: Too short anti-clogging token request");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
resp = sae_group_allowed(sta->sae, groups,
WPA_GET_LE16(pos));
if (resp != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_ERROR,
"SAE: Invalid group in anti-clogging token request");
goto reply;
}
pos += sizeof(le16);
wpabuf_free(sta->sae->tmp->anti_clogging_token);
sta->sae->tmp->anti_clogging_token =
wpabuf_alloc_copy(pos, end - pos);
if (sta->sae->tmp->anti_clogging_token == NULL) {
wpa_printf(MSG_ERROR,
"SAE: Failed to alloc for anti-clogging token");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto remove_sta;
}
/*
* IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code
* is 76, a new Commit Message shall be constructed
* with the Anti-Clogging Token from the received
* Authentication frame, and the commit-scalar and
* COMMIT-ELEMENT previously sent.
*/
resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0,
status_code);
if (resp != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_ERROR,
"SAE: Failed to send commit message");
goto remove_sta;
}
sae_set_state(sta, SAE_COMMITTED,
"Sent Commit (anti-clogging token case in mesh)");
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
return;
}
if ((hapd->conf->mesh & MESH_ENABLED) &&
status_code ==
WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
sta->sae->tmp) {
wpa_printf(MSG_DEBUG,
"SAE: Peer did not accept our SAE group");
sae_pick_next_group(hapd, sta);
goto remove_sta;
}
if (!sae_status_success(hapd, status_code))
goto remove_sta;
if (!(hapd->conf->mesh & MESH_ENABLED) &&
sta->sae->state == SAE_COMMITTED) {
/* This is needed in the infrastructure BSS case to
* address a sequence where a STA entry may remain in
* hostapd across two attempts to do SAE authentication
* by the same STA. The second attempt may end up trying
* to use a different group and that would not be
* allowed if we remain in Committed state with the
* previously set parameters. */
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
if (end - pos >= (int) sizeof(le16) &&
sae_group_allowed(sta->sae, groups,
WPA_GET_LE16(pos)) ==
WLAN_STATUS_SUCCESS) {
/* Do not waste resources deriving the same PWE
* again since the same group is reused. */
sae_set_state(sta, SAE_NOTHING,
"Allow previous PWE to be reused");
allow_reuse = 1;
} else {
sae_set_state(sta, SAE_NOTHING,
"Clear existing state to allow restart");
sae_clear_data(sta->sae);
}
}
resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
((const u8 *) mgmt) + len -
mgmt->u.auth.variable, &token,
&token_len, groups, status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK);
if (resp == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message from " MACSTR " due to reflection attack",
MAC2STR(sta->addr));
goto remove_sta;
}
if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
wpa_msg(hapd->msg_ctx, MSG_INFO,
WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER
MACSTR, MAC2STR(sta->addr));
sae_clear_retransmit_timer(hapd, sta);
sae_set_state(sta, SAE_NOTHING,
"Unknown Password Identifier");
goto remove_sta;
}
if (token &&
check_comeback_token(hapd, sta->addr, token, token_len)
< 0) {
wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
"incorrect token from " MACSTR,
MAC2STR(sta->addr));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto remove_sta;
}
if (resp != WLAN_STATUS_SUCCESS)
goto reply;
if (check_sae_rejected_groups(hapd, sta->sae)) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
if (!token && use_anti_clogging(hapd) && !allow_reuse) {
int h2e = 0;
wpa_printf(MSG_DEBUG,
"SAE: Request anti-clogging token from "
MACSTR, MAC2STR(sta->addr));
if (sta->sae->tmp)
h2e = sta->sae->h2e;
if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK)
h2e = 1;
data = auth_build_token_req(hapd, sta->sae->group,
sta->addr, h2e);
resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
if (hapd->conf->mesh & MESH_ENABLED)
sae_set_state(sta, SAE_NOTHING,
"Request anti-clogging token case in mesh");
goto reply;
}
resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
status_code, allow_reuse, &sta_removed);
} else if (auth_transaction == 2) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"SAE authentication (RX confirm, status=%u (%s))",
status_code, status2str(status_code));
if (status_code != WLAN_STATUS_SUCCESS)
goto remove_sta;
if (sta->sae->state >= SAE_CONFIRMED ||
!(hapd->conf->mesh & MESH_ENABLED)) {
const u8 *var;
size_t var_len;
u16 peer_send_confirm;
var = mgmt->u.auth.variable;
var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable;
if (var_len < 2) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
peer_send_confirm = WPA_GET_LE16(var);
if (sta->sae->state == SAE_ACCEPTED &&
(peer_send_confirm <= sta->sae->rc ||
peer_send_confirm == 0xffff)) {
wpa_printf(MSG_DEBUG,
"SAE: Silently ignore unexpected Confirm from peer "
MACSTR
" (peer-send-confirm=%u Rc=%u)",
MAC2STR(sta->addr),
peer_send_confirm, sta->sae->rc);
return;
}
if (sae_check_confirm(sta->sae, var, var_len) < 0) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
sta->sae->rc = peer_send_confirm;
}
resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
status_code, 0, &sta_removed);
} else {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"unexpected SAE authentication transaction %u (status=%u (%s))",
auth_transaction, status_code,
status2str(status_code));
if (status_code != WLAN_STATUS_SUCCESS)
goto remove_sta;
resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
}
reply:
if (!sta_removed && resp != WLAN_STATUS_SUCCESS) {
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
/* Copy the Finite Cyclic Group field from the request if we
* rejected it as unsupported group. */
if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
!data && end - pos >= 2)
data = wpabuf_alloc_copy(pos, 2);
sae_sme_send_external_auth_status(hapd, sta, resp);
send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0, "auth-sae");
}
remove_sta:
if (auth_transaction == 1)
success_status = sae_status_success(hapd, status_code);
else
success_status = status_code == WLAN_STATUS_SUCCESS;
if (!sta_removed && sta->added_unassoc &&
(resp != WLAN_STATUS_SUCCESS || !success_status)) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
}
wpabuf_free(data);
}
/**
* auth_sae_init_committed - Send COMMIT and start SAE in committed state
* @hapd: BSS data for the device initiating the authentication
* @sta: the peer to which commit authentication frame is sent
*
* This function implements Init event handling (IEEE Std 802.11-2012,
* 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the
* sta->sae structure should be initialized appropriately via a call to
* sae_prepare_commit().
*/
int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
{
int ret;
if (!sta->sae || !sta->sae->tmp)
return -1;
if (sta->sae->state != SAE_NOTHING)
return -1;
ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0, -1);
if (ret)
return -1;
sae_set_state(sta, SAE_COMMITTED, "Init and sent commit");
sta->sae->sync = 0;
sae_set_retransmit_timer(hapd, sta);
return 0;
}
void auth_sae_process_commit(void *eloop_ctx, void *user_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct hostapd_sae_commit_queue *q;
unsigned int queue_len;
q = dl_list_first(&hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list);
if (!q)
return;
wpa_printf(MSG_DEBUG,
"SAE: Process next available message from queue");
dl_list_del(&q->list);
handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len,
q->rssi, 1);
os_free(q);
if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
return;
queue_len = dl_list_len(&hapd->sae_commit_queue);
eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
hapd, NULL);
}
static void auth_sae_queue(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int rssi)
{
struct hostapd_sae_commit_queue *q, *q2;
unsigned int queue_len;
const struct ieee80211_mgmt *mgmt2;
queue_len = dl_list_len(&hapd->sae_commit_queue);
if (queue_len >= 15) {
wpa_printf(MSG_DEBUG,
"SAE: No more room in message queue - drop the new frame from "
MACSTR, MAC2STR(mgmt->sa));
return;
}
wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from "
MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa),
queue_len);
q = os_zalloc(sizeof(*q) + len);
if (!q)
return;
q->rssi = rssi;
q->len = len;
os_memcpy(q->msg, mgmt, len);
/* Check whether there is already a queued Authentication frame from the
* same station with the same transaction number and if so, replace that
* queue entry with the new one. This avoids issues with a peer that
* sends multiple times (e.g., due to frequent SAE retries). There is no
* point in us trying to process the old attempts after a new one has
* obsoleted them. */
dl_list_for_each(q2, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 &&
mgmt->u.auth.auth_transaction ==
mgmt2->u.auth.auth_transaction) {
wpa_printf(MSG_DEBUG,
"SAE: Replace queued message from same STA with same transaction number");
dl_list_add(&q2->list, &q->list);
dl_list_del(&q2->list);
os_free(q2);
goto queued;
}
}
/* No pending identical entry, so add to the end of the queue */
dl_list_add_tail(&hapd->sae_commit_queue, &q->list);
queued:
if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
return;
eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
hapd, NULL);
}
static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr)
{
struct hostapd_sae_commit_queue *q;
const struct ieee80211_mgmt *mgmt;
dl_list_for_each(q, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
mgmt = (const struct ieee80211_mgmt *) q->msg;
if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0)
return 1;
}
return 0;
}
#endif /* CONFIG_SAE */
static u16 wpa_res_to_status_code(enum wpa_validate_result res)
{
switch (res) {
case WPA_IE_OK:
return WLAN_STATUS_SUCCESS;
case WPA_INVALID_IE:
return WLAN_STATUS_INVALID_IE;
case WPA_INVALID_GROUP:
return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
case WPA_INVALID_PAIRWISE:
return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
case WPA_INVALID_AKMP:
return WLAN_STATUS_AKMP_NOT_VALID;
case WPA_NOT_ENABLED:
return WLAN_STATUS_INVALID_IE;
case WPA_ALLOC_FAIL:
return WLAN_STATUS_UNSPECIFIED_FAILURE;
case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
case WPA_INVALID_MGMT_GROUP_CIPHER:
return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
case WPA_INVALID_MDIE:
return WLAN_STATUS_INVALID_MDIE;
case WPA_INVALID_PROTO:
return WLAN_STATUS_INVALID_IE;
case WPA_INVALID_PMKID:
return WLAN_STATUS_INVALID_PMKID;
case WPA_DENIED_OTHER_REASON:
return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
}
return WLAN_STATUS_INVALID_IE;
}
#ifdef CONFIG_FILS
static void handle_auth_fils_finish(struct hostapd_data *hapd,
struct sta_info *sta, u16 resp,
struct wpabuf *data, int pub);
void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *pos, size_t len, u16 auth_alg,
u16 auth_transaction, u16 status_code,
void (*cb)(struct hostapd_data *hapd,
struct sta_info *sta, u16 resp,
struct wpabuf *data, int pub))
{
u16 resp = WLAN_STATUS_SUCCESS;
const u8 *end;
struct ieee802_11_elems elems;
enum wpa_validate_result res;
struct wpa_ie_data rsn;
struct rsn_pmksa_cache_entry *pmksa = NULL;
if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
return;
end = pos + len;
wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
pos, end - pos);
/* TODO: FILS PK */
#ifdef CONFIG_FILS_SK_PFS
if (auth_alg == WLAN_AUTH_FILS_SK_PFS) {
u16 group;
struct wpabuf *pub;
size_t elem_len;
/* Using FILS PFS */
/* Finite Cyclic Group */
if (end - pos < 2) {
wpa_printf(MSG_DEBUG,
"FILS: No room for Finite Cyclic Group");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
group = WPA_GET_LE16(pos);
pos += 2;
if (group != hapd->conf->fils_dh_group) {
wpa_printf(MSG_DEBUG,
"FILS: Unsupported Finite Cyclic Group: %u (expected %u)",
group, hapd->conf->fils_dh_group);
resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
goto fail;
}
crypto_ecdh_deinit(sta->fils_ecdh);
sta->fils_ecdh = crypto_ecdh_init(group);
if (!sta->fils_ecdh) {
wpa_printf(MSG_INFO,
"FILS: Could not initialize ECDH with group %d",
group);
resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
goto fail;
}
pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
if (!pub) {
wpa_printf(MSG_DEBUG,
"FILS: Failed to derive ECDH public key");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
elem_len = wpabuf_len(pub);
wpabuf_free(pub);
/* Element */
if ((size_t) (end - pos) < elem_len) {
wpa_printf(MSG_DEBUG, "FILS: No room for Element");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpabuf_free(sta->fils_g_sta);
sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len);
wpabuf_clear_free(sta->fils_dh_ss);
sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1,
pos, elem_len);
if (!sta->fils_dh_ss) {
wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss);
pos += elem_len;
} else {
crypto_ecdh_deinit(sta->fils_ecdh);
sta->fils_ecdh = NULL;
wpabuf_clear_free(sta->fils_dh_ss);
sta->fils_dh_ss = NULL;
}
#endif /* CONFIG_FILS_SK_PFS */
wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
/* RSNE */
wpa_hexdump(MSG_DEBUG, "FILS: RSN element",
elems.rsn_ie, elems.rsn_ie_len);
if (!elems.rsn_ie ||
wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
&rsn) < 0) {
wpa_printf(MSG_DEBUG, "FILS: No valid RSN element");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
if (!sta->wpa_sm)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
NULL);
if (!sta->wpa_sm) {
wpa_printf(MSG_DEBUG,
"FILS: Failed to initialize RSN state machine");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
elems.rsn_ie - 2, elems.rsn_ie_len + 2,
elems.rsnxe ? elems.rsnxe - 2 : NULL,
elems.rsnxe ? elems.rsnxe_len + 2 : 0,
elems.mdie, elems.mdie_len, NULL, 0);
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
if (!elems.fils_nonce) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce,
FILS_NONCE_LEN);
os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN);
/* PMKID List */
if (rsn.pmkid && rsn.num_pmkid > 0) {
u8 num;
const u8 *pmkid;
wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
pmkid = rsn.pmkid;
num = rsn.num_pmkid;
while (num) {
wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
pmkid);
if (pmksa)
break;
pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth,
sta->addr,
pmkid);
if (pmksa)
break;
pmkid += PMKID_LEN;
num--;
}
}
if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) {
wpa_printf(MSG_DEBUG,
"FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore",
wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp);
pmksa = NULL;
}
if (pmksa)
wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry");
/* FILS Session */
if (!elems.fils_session) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
FILS_SESSION_LEN);
os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN);
/* Wrapped Data */
if (elems.wrapped_data) {
wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
elems.wrapped_data,
elems.wrapped_data_len);
if (!pmksa) {
#ifndef CONFIG_NO_RADIUS
if (!sta->eapol_sm) {
sta->eapol_sm =
ieee802_1x_alloc_eapol_sm(hapd, sta);
}
wpa_printf(MSG_DEBUG,
"FILS: Forward EAP-Initiate/Re-auth to authentication server");
ieee802_1x_encapsulate_radius(
hapd, sta, elems.wrapped_data,
elems.wrapped_data_len);
sta->fils_pending_cb = cb;
wpa_printf(MSG_DEBUG,
"FILS: Will send Authentication frame once the response from authentication server is available");
sta->flags |= WLAN_STA_PENDING_FILS_ERP;
/* Calculate pending PMKID here so that we do not need
* to maintain a copy of the EAP-Initiate/Reauth
* message. */
if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm),
elems.wrapped_data,
elems.wrapped_data_len,
sta->fils_erp_pmkid) == 0)
sta->fils_erp_pmkid_set = 1;
return;
#else /* CONFIG_NO_RADIUS */
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
#endif /* CONFIG_NO_RADIUS */
}
}
fail:
if (cb) {
struct wpabuf *data;
int pub = 0;
data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL,
NULL, 0, &pub);
if (!data) {
wpa_printf(MSG_DEBUG,
"%s: prepare_auth_resp_fils() returned failure",
__func__);
}
cb(hapd, sta, resp, data, pub);
}
}
static struct wpabuf *
prepare_auth_resp_fils(struct hostapd_data *hapd,
struct sta_info *sta, u16 *resp,
struct rsn_pmksa_cache_entry *pmksa,
struct wpabuf *erp_resp,
const u8 *msk, size_t msk_len,
int *is_pub)
{
u8 fils_nonce[FILS_NONCE_LEN];
size_t ielen;
struct wpabuf *data = NULL;
const u8 *ie;
u8 *ie_buf = NULL;
const u8 *pmk = NULL;
size_t pmk_len = 0;
u8 pmk_buf[PMK_LEN_MAX];
struct wpabuf *pub = NULL;
if (*resp != WLAN_STATUS_SUCCESS)
goto fail;
ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
if (!ie) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
if (pmksa) {
/* Add PMKID of the selected PMKSA into RSNE */
ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
if (!ie_buf) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
os_memcpy(ie_buf, ie, ielen);
if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
ie = ie_buf;
}
if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
fils_nonce, FILS_NONCE_LEN);
#ifdef CONFIG_FILS_SK_PFS
if (sta->fils_dh_ss && sta->fils_ecdh) {
pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1);
if (!pub) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
}
#endif /* CONFIG_FILS_SK_PFS */
data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0));
if (!data) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
/* TODO: FILS PK */
#ifdef CONFIG_FILS_SK_PFS
if (pub) {
/* Finite Cyclic Group */
wpabuf_put_le16(data, hapd->conf->fils_dh_group);
/* Element */
wpabuf_put_buf(data, pub);
}
#endif /* CONFIG_FILS_SK_PFS */
/* RSNE */
wpabuf_put_data(data, ie, ielen);
/* MDE when using FILS+FT (already included in ie,ielen with RSNE) */
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) {
/* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */
int res;
int use_sha384 = wpa_key_mgmt_sha384(
wpa_auth_sta_key_mgmt(sta->wpa_sm));
res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384,
wpabuf_put(data, 0),
wpabuf_tailroom(data));
if (res < 0) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpabuf_put(data, res);
}
#endif /* CONFIG_IEEE80211R_AP */
/* FILS Nonce */
wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */
/* Element ID Extension */
wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE);
wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN);
/* FILS Session */
wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */
/* Element ID Extension */
wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION);
wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN);
/* Wrapped Data */
if (!pmksa && erp_resp) {
wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */
/* Element ID Extension */
wpabuf_put_u8(data, WLAN_EID_EXT_WRAPPED_DATA);
wpabuf_put_buf(data, erp_resp);
if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
msk, msk_len, sta->fils_snonce, fils_nonce,
sta->fils_dh_ss ?
wpabuf_head(sta->fils_dh_ss) : NULL,
sta->fils_dh_ss ?
wpabuf_len(sta->fils_dh_ss) : 0,
pmk_buf, &pmk_len)) {
wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
wpabuf_free(data);
data = NULL;
goto fail;
}
pmk = pmk_buf;
/* Don't use DHss in PTK derivation if PMKSA caching is not
* used. */
wpabuf_clear_free(sta->fils_dh_ss);
sta->fils_dh_ss = NULL;
if (sta->fils_erp_pmkid_set) {
/* TODO: get PMKLifetime from WPA parameters */
unsigned int dot11RSNAConfigPMKLifetime = 43200;
int session_timeout;
session_timeout = dot11RSNAConfigPMKLifetime;
if (sta->session_timeout_set) {
struct os_reltime now, diff;
os_get_reltime(&now);
os_reltime_sub(&sta->session_timeout, &now,
&diff);
session_timeout = diff.sec;
}
sta->fils_erp_pmkid_set = 0;
wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len,
sta->fils_erp_pmkid);
if (!hapd->conf->disable_pmksa_caching &&
wpa_auth_pmksa_add2(
hapd->wpa_auth, sta->addr,
pmk, pmk_len,
sta->fils_erp_pmkid,
session_timeout,
wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) {
wpa_printf(MSG_ERROR,
"FILS: Failed to add PMKSA cache entry based on ERP");
}
}
} else if (pmksa) {
pmk = pmksa->pmk;
pmk_len = pmksa->pmk_len;
}
if (!pmk) {
wpa_printf(MSG_DEBUG, "FILS: No PMK available");
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
wpabuf_free(data);
data = NULL;
goto fail;
}
if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
sta->fils_snonce, fils_nonce,
sta->fils_dh_ss ?
wpabuf_head(sta->fils_dh_ss) : NULL,
sta->fils_dh_ss ?
wpabuf_len(sta->fils_dh_ss) : 0,
sta->fils_g_sta, pub) < 0) {
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
wpabuf_free(data);
data = NULL;
goto fail;
}
fail:
if (is_pub)
*is_pub = pub != NULL;
os_free(ie_buf);
wpabuf_free(pub);
wpabuf_clear_free(sta->fils_dh_ss);
sta->fils_dh_ss = NULL;
#ifdef CONFIG_FILS_SK_PFS
crypto_ecdh_deinit(sta->fils_ecdh);
sta->fils_ecdh = NULL;
#endif /* CONFIG_FILS_SK_PFS */
return data;
}
static void handle_auth_fils_finish(struct hostapd_data *hapd,
struct sta_info *sta, u16 resp,
struct wpabuf *data, int pub)
{
u16 auth_alg;
auth_alg = (pub ||
resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ?
WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
send_auth_reply(hapd, sta, sta->addr, hapd->own_addr, auth_alg, 2, resp,
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0, "auth-fils-finish");
wpabuf_free(data);
if (resp == WLAN_STATUS_SUCCESS) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"authentication OK (FILS)");
sta->flags |= WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK;
mlme_authenticate_indication(hapd, sta);
}
}
void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
struct sta_info *sta, int success,
struct wpabuf *erp_resp,
const u8 *msk, size_t msk_len)
{
u16 resp;
u32 flags = sta->flags;
sta->flags &= ~(WLAN_STA_PENDING_FILS_ERP |
WLAN_STA_PENDING_PASN_FILS_ERP);
resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE;
if (flags & WLAN_STA_PENDING_FILS_ERP) {
struct wpabuf *data;
int pub = 0;
if (!sta->fils_pending_cb)
return;
data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp,
msk, msk_len, &pub);
if (!data) {
wpa_printf(MSG_DEBUG,
"%s: prepare_auth_resp_fils() failure",
__func__);
}
sta->fils_pending_cb(hapd, sta, resp, data, pub);
#ifdef CONFIG_PASN
} else if (flags & WLAN_STA_PENDING_PASN_FILS_ERP) {
pasn_fils_auth_resp(hapd, sta, resp, erp_resp,
msk, msk_len);
#endif /* CONFIG_PASN */
}
}
#endif /* CONFIG_FILS */
static int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
const u8 *msg, size_t len,
struct radius_sta *info)
{
int res;
res = hostapd_allowed_address(hapd, addr, msg, len, info, 0);
if (res == HOSTAPD_ACL_REJECT) {
wpa_printf(MSG_DEBUG, "Station " MACSTR
" not allowed to authenticate",
MAC2STR(addr));
return HOSTAPD_ACL_REJECT;
}
if (res == HOSTAPD_ACL_PENDING) {
wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
" waiting for an external authentication",
MAC2STR(addr));
/* Authentication code will re-send the authentication frame
* after it has received (and cached) information from the
* external source. */
return HOSTAPD_ACL_PENDING;
}
return res;
}
static int
ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
int res, struct radius_sta *info)
{
u32 session_timeout = info->session_timeout;
u32 acct_interim_interval = info->acct_interim_interval;
struct vlan_description *vlan_id = &info->vlan_id;
struct hostapd_sta_wpa_psk_short *psk = info->psk;
char *identity = info->identity;
char *radius_cui = info->radius_cui;
if (vlan_id->notempty &&
!hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
"Invalid VLAN %d%s received from RADIUS server",
vlan_id->untagged,
vlan_id->tagged[0] ? "+" : "");
return -1;
}
if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0)
return -1;
if (sta->vlan_id)
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
hostapd_free_psk_list(sta->psk);
if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED)
hostapd_copy_psk_list(&sta->psk, psk);
else
sta->psk = NULL;
os_free(sta->identity);
if (identity)
sta->identity = os_strdup(identity);
else
sta->identity = NULL;
os_free(sta->radius_cui);
if (radius_cui)
sta->radius_cui = os_strdup(radius_cui);
else
sta->radius_cui = NULL;
if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
sta->acct_interim_interval = acct_interim_interval;
if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) {
sta->session_timeout_set = 1;
os_get_reltime(&sta->session_timeout);
sta->session_timeout.sec += session_timeout;
ap_sta_session_timeout(hapd, sta, session_timeout);
} else {
sta->session_timeout_set = 0;
ap_sta_no_session_timeout(hapd, sta);
}
return 0;
}
#ifdef CONFIG_PASN
#ifdef CONFIG_SAE
static int pasn_wd_handle_sae_commit(struct hostapd_data *hapd,
struct sta_info *sta,
struct wpabuf *wd)
{
struct pasn_data *pasn = sta->pasn;
const char *password;
const u8 *data;
size_t buf_len;
u16 res, alg, seq, status;
int groups[] = { pasn->group, 0 };
struct sae_pt *pt = NULL;
int ret;
if (!wd)
return -1;
data = wpabuf_head_u8(wd);
buf_len = wpabuf_len(wd);
if (buf_len < 6) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%lu",
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
buf_len);
return -1;
}
alg = WPA_GET_LE16(data);
seq = WPA_GET_LE16(data + 2);
status = WPA_GET_LE16(data + 4);
wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u",
alg, seq, status);
if (alg != WLAN_AUTH_SAE || seq != 1 ||
status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit");
return -1;
}
sae_clear_data(&pasn->sae);
pasn->sae.state = SAE_NOTHING;
ret = sae_set_group(&pasn->sae, pasn->group);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
return -1;
}
password = sae_get_password(hapd, sta, NULL, NULL, &pt, NULL);
if (!password || !pt) {
wpa_printf(MSG_DEBUG, "PASN: No SAE PT found");
return -1;
}
ret = sae_prepare_commit_pt(&pasn->sae, pt, hapd->own_addr, sta->addr,
NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
return -1;
}
res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0,
groups, 0);
if (res != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit");
return -1;
}
/* Process the commit message and derive the PMK */
ret = sae_process_commit(&pasn->sae);
if (ret) {
wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
return -1;
}
pasn->sae.state = SAE_COMMITTED;
return 0;
}
static int pasn_wd_handle_sae_confirm(struct hostapd_data *hapd,
struct sta_info *sta,
struct wpabuf *wd)
{
struct pasn_data *pasn = sta->pasn;
const u8 *data;
size_t buf_len;
u16 res, alg, seq, status;
if (!wd)
return -1;
data = wpabuf_head_u8(wd);
buf_len = wpabuf_len(wd);
if (buf_len < 6) {
- wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%lu",
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu",
buf_len);
return -1;
}
alg = WPA_GET_LE16(data);
seq = WPA_GET_LE16(data + 2);
status = WPA_GET_LE16(data + 4);
wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
alg, seq, status);
if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
return -1;
}
res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6);
if (res != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
return -1;
}
pasn->sae.state = SAE_ACCEPTED;
/*
* TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with
* PASN/SAE should only be allowed with future PASN only. For now do not
* restrict this only for PASN.
*/
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
pasn->sae.pmk, pasn->sae.pmkid);
return 0;
}
static struct wpabuf * pasn_get_sae_wd(struct hostapd_data *hapd,
struct sta_info *sta)
{
struct pasn_data *pasn = sta->pasn;
struct wpabuf *buf = NULL;
u8 *len_ptr;
size_t len;
/* Need to add the entire Authentication frame body */
buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN);
if (!buf) {
wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
return NULL;
}
/* Need to add the entire authentication frame body for the commit */
len_ptr = wpabuf_put(buf, 2);
wpabuf_put_le16(buf, WLAN_AUTH_SAE);
wpabuf_put_le16(buf, 1);
wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
/* Write the actual commit and update the length accordingly */
sae_write_commit(&pasn->sae, buf, NULL, 0);
len = wpabuf_len(buf);
WPA_PUT_LE16(len_ptr, len - 2);
/* Need to add the entire Authentication frame body for the confirm */
len_ptr = wpabuf_put(buf, 2);
wpabuf_put_le16(buf, WLAN_AUTH_SAE);
wpabuf_put_le16(buf, 2);
wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
sae_write_confirm(&pasn->sae, buf);
WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2);
pasn->sae.state = SAE_CONFIRMED;
return buf;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
static struct wpabuf * pasn_get_fils_wd(struct hostapd_data *hapd,
struct sta_info *sta)
{
struct pasn_data *pasn = sta->pasn;
struct pasn_fils_data *fils = &pasn->fils;
struct wpabuf *buf = NULL;
if (!fils->erp_resp) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp");
return NULL;
}
buf = wpabuf_alloc(1500);
if (!buf)
return NULL;
/* Add the authentication algorithm */
wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
/* Authentication Transaction seq# */
wpabuf_put_le16(buf, 2);
/* Status Code */
wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
/* Own RSNE */
wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
/* FILS Nonce */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN);
/* FILS Session */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN);
/* Wrapped Data */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp));
wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
wpabuf_put_buf(buf, fils->erp_resp);
return buf;
}
static void pasn_fils_auth_resp(struct hostapd_data *hapd,
struct sta_info *sta, u16 status,
struct wpabuf *erp_resp,
const u8 *msk, size_t msk_len)
{
struct pasn_data *pasn = sta->pasn;
struct pasn_fils_data *fils = &pasn->fils;
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
int ret;
wpa_printf(MSG_DEBUG, "PASN: FILS: Handle AS response - status=%u",
status);
if (status != WLAN_STATUS_SUCCESS)
goto fail;
if (!pasn->secret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Missing secret");
goto fail;
}
if (random_get_bytes(fils->anonce, FILS_NONCE_LEN) < 0) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ANonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS ANonce",
fils->anonce, FILS_NONCE_LEN);
ret = fils_rmsk_to_pmk(pasn->akmp, msk, msk_len, fils->nonce,
fils->anonce, NULL, 0, pmk, &pmk_len);
if (ret) {
wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
goto fail;
}
ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
wpabuf_head(pasn->secret),
wpabuf_len(pasn->secret),
&sta->pasn->ptk, sta->pasn->akmp,
sta->pasn->cipher, sta->pasn->kdk_len);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PTK");
goto fail;
}
wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
wpabuf_free(pasn->secret);
pasn->secret = NULL;
fils->erp_resp = erp_resp;
ret = handle_auth_pasn_resp(hapd, sta, NULL, WLAN_STATUS_SUCCESS);
fils->erp_resp = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to send response");
goto fail;
}
fils->state = PASN_FILS_STATE_COMPLETE;
return;
fail:
ap_free_sta(hapd, sta);
}
static int pasn_wd_handle_fils(struct hostapd_data *hapd, struct sta_info *sta,
struct wpabuf *wd)
{
#ifdef CONFIG_NO_RADIUS
wpa_printf(MSG_DEBUG, "PASN: FILS: RADIUS is not configured. Fail");
return -1;
#else /* CONFIG_NO_RADIUS */
struct pasn_data *pasn = sta->pasn;
struct pasn_fils_data *fils = &pasn->fils;
struct ieee802_11_elems elems;
struct wpa_ie_data rsne_data;
struct wpabuf *fils_wd;
const u8 *data;
size_t buf_len;
u16 alg, seq, status;
int ret;
if (fils->state != PASN_FILS_STATE_NONE) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Not expecting wrapped data");
return -1;
}
if (!wd) {
wpa_printf(MSG_DEBUG, "PASN: FILS: No wrapped data");
return -1;
}
data = wpabuf_head_u8(wd);
buf_len = wpabuf_len(wd);
if (buf_len < 6) {
- wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short. len=%lu",
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short. len=%zu",
buf_len);
return -1;
}
alg = WPA_GET_LE16(data);
seq = WPA_GET_LE16(data + 2);
status = WPA_GET_LE16(data + 4);
wpa_printf(MSG_DEBUG, "PASN: FILS: alg=%u, seq=%u, status=%u",
alg, seq, status);
if (alg != WLAN_AUTH_FILS_SK || seq != 1 ||
status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG,
"PASN: FILS: Dropping peer authentication");
return -1;
}
data += 6;
buf_len -= 6;
if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements");
return -1;
}
if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
!elems.wrapped_data) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
return -1;
}
ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
&rsne_data);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RNSE");
return -1;
}
ret = wpa_pasn_validate_rsne(&rsne_data);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
return -1;
}
if (rsne_data.num_pmkid) {
wpa_printf(MSG_DEBUG,
"PASN: FILS: Not expecting PMKID in RSNE");
return -1;
}
wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", elems.fils_nonce,
FILS_NONCE_LEN);
os_memcpy(fils->nonce, elems.fils_nonce, FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", elems.fils_session,
FILS_SESSION_LEN);
os_memcpy(fils->session, elems.fils_session, FILS_SESSION_LEN);
fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
WLAN_EID_EXT_WRAPPED_DATA);
if (!fils_wd) {
wpa_printf(MSG_DEBUG, "PASN: FILS: Missing wrapped data");
return -1;
}
if (!sta->eapol_sm)
sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta);
wpa_printf(MSG_DEBUG,
"PASN: FILS: Forward EAP-Initiate/Re-auth to AS");
ieee802_1x_encapsulate_radius(hapd, sta, wpabuf_head(fils_wd),
wpabuf_len(fils_wd));
sta->flags |= WLAN_STA_PENDING_PASN_FILS_ERP;
fils->state = PASN_FILS_STATE_PENDING_AS;
/*
* Calculate pending PMKID here so that we do not need to maintain a
* copy of the EAP-Initiate/Reautt message.
*/
fils_pmkid_erp(pasn->akmp, wpabuf_head(fils_wd), wpabuf_len(fils_wd),
fils->erp_pmkid);
wpabuf_free(fils_wd);
return 0;
#endif /* CONFIG_NO_RADIUS */
}
#endif /* CONFIG_FILS */
static struct wpabuf * pasn_get_wrapped_data(struct hostapd_data *hapd,
struct sta_info *sta)
{
switch (sta->pasn->akmp) {
case WPA_KEY_MGMT_PASN:
/* no wrapped data */
return NULL;
case WPA_KEY_MGMT_SAE:
#ifdef CONFIG_SAE
return pasn_get_sae_wd(hapd, sta);
#else /* CONFIG_SAE */
wpa_printf(MSG_ERROR,
"PASN: SAE: Cannot derive wrapped data");
return NULL;
#endif /* CONFIG_SAE */
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FILS_SHA384:
#ifdef CONFIG_FILS
return pasn_get_fils_wd(hapd, sta);
#endif /* CONFIG_FILS */
/* fall through */
case WPA_KEY_MGMT_FT_PSK:
case WPA_KEY_MGMT_FT_IEEE8021X:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
default:
wpa_printf(MSG_ERROR,
"PASN: TODO: Wrapped data for akmp=0x%x",
sta->pasn->akmp);
return NULL;
}
}
static int
pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *cached_pmk, size_t cached_pmk_len,
struct wpa_pasn_params_data *pasn_data,
struct wpabuf *wrapped_data,
struct wpabuf *secret)
{
static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
u8 pmk[PMK_LEN_MAX];
u8 pmk_len;
int ret;
os_memset(pmk, 0, sizeof(pmk));
pmk_len = 0;
if (!cached_pmk || !cached_pmk_len)
wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry");
if (sta->pasn->akmp == WPA_KEY_MGMT_PASN) {
wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
pmk_len = WPA_PASN_PMK_LEN;
os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk));
} else if (cached_pmk && cached_pmk_len) {
wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry");
pmk_len = cached_pmk_len;
os_memcpy(pmk, cached_pmk, cached_pmk_len);
} else {
switch (sta->pasn->akmp) {
#ifdef CONFIG_SAE
case WPA_KEY_MGMT_SAE:
if (sta->pasn->sae.state == SAE_COMMITTED) {
pmk_len = PMK_LEN;
os_memcpy(pmk, sta->pasn->sae.pmk, PMK_LEN);
break;
}
#endif /* CONFIG_SAE */
/* fall through */
default:
/* TODO: Derive PMK based on wrapped data */
wpa_printf(MSG_DEBUG,
"PASN: Missing PMK derivation");
return -1;
}
}
ret = pasn_pmk_to_ptk(pmk, pmk_len, sta->addr, hapd->own_addr,
wpabuf_head(secret), wpabuf_len(secret),
&sta->pasn->ptk, sta->pasn->akmp,
sta->pasn->cipher, sta->pasn->kdk_len);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
return -1;
}
wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
return 0;
}
static void handle_auth_pasn_comeback(struct hostapd_data *hapd,
struct sta_info *sta, u16 group)
{
struct wpabuf *buf, *comeback;
int ret;
wpa_printf(MSG_DEBUG,
"PASN: Building comeback frame 2. Comeback after=%u",
hapd->conf->pasn_comeback_after);
buf = wpabuf_alloc(1500);
if (!buf)
return;
wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr,
sta->addr, 2,
WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY);
/*
* Do not include the group as a part of the token since it is not going
* to be used.
*/
comeback = auth_build_token_req(hapd, 0, sta->addr, 0);
if (!comeback) {
wpa_printf(MSG_DEBUG,
"PASN: Failed sending auth with comeback");
wpabuf_free(buf);
return;
}
wpa_pasn_add_parameter_ie(buf, group,
WPA_PASN_WRAPPED_DATA_NO,
NULL, 0, comeback,
hapd->conf->pasn_comeback_after);
wpabuf_free(comeback);
wpa_printf(MSG_DEBUG,
"PASN: comeback: STA=" MACSTR, MAC2STR(sta->addr));
ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0,
NULL, 0, 0);
if (ret)
wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2");
wpabuf_free(buf);
}
static int handle_auth_pasn_resp(struct hostapd_data *hapd,
struct sta_info *sta,
struct rsn_pmksa_cache_entry *pmksa,
u16 status)
{
struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
u8 mic[WPA_PASN_MAX_MIC_LEN];
u8 mic_len;
u8 *ptr;
const u8 *frame, *data, *rsn_ie, *rsnxe_ie;
u8 *data_buf = NULL;
size_t rsn_ie_len, frame_len, data_len;
int ret;
const u8 *pmkid = NULL;
wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status);
buf = wpabuf_alloc(1500);
if (!buf)
goto fail;
wpa_pasn_build_auth_header(buf, hapd->own_addr, hapd->own_addr,
sta->addr, 2, status);
if (status != WLAN_STATUS_SUCCESS)
goto done;
if (pmksa) {
pmkid = pmksa->pmkid;
#ifdef CONFIG_SAE
} else if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID");
pmkid = sta->pasn->sae.pmkid;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
} else if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID");
pmkid = sta->pasn->fils.erp_pmkid;
#endif /* CONFIG_FILS */
}
if (wpa_pasn_add_rsne(buf, pmkid,
sta->pasn->akmp, sta->pasn->cipher) < 0)
goto fail;
/* No need to derive PMK if PMKSA is given */
if (!pmksa)
wrapped_data_buf = pasn_get_wrapped_data(hapd, sta);
else
sta->pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO;
/* Get public key */
pubkey = crypto_ecdh_get_pubkey(sta->pasn->ecdh, 0);
pubkey = wpabuf_zeropad(pubkey,
crypto_ecdh_prime_len(sta->pasn->ecdh));
if (!pubkey) {
wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
goto fail;
}
wpa_pasn_add_parameter_ie(buf, sta->pasn->group,
sta->pasn->wrapped_data_format,
pubkey, true, NULL, 0);
if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
goto fail;
wpabuf_free(wrapped_data_buf);
wrapped_data_buf = NULL;
wpabuf_free(pubkey);
pubkey = NULL;
/* Add RSNXE if needed */
rsnxe_ie = hostapd_wpa_ie(hapd, WLAN_EID_RSNX);
if (rsnxe_ie)
wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]);
/* Add the mic */
mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher);
wpabuf_put_u8(buf, WLAN_EID_MIC);
wpabuf_put_u8(buf, mic_len);
ptr = wpabuf_put(buf, mic_len);
os_memset(ptr, 0, mic_len);
frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
rsn_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &rsn_ie_len);
if (!rsn_ie || !rsn_ie_len)
goto fail;
/*
* Note: wpa_auth_get_wpa_ie() might return not only the RSNE but also
* MDE, etc. Thus, do not use the returned length but instead use the
* length specified in the IE header.
*/
data_len = rsn_ie[1] + 2;
if (rsnxe_ie) {
data_buf = os_zalloc(rsn_ie[1] + 2 + rsnxe_ie[1] + 2);
if (!data_buf)
goto fail;
os_memcpy(data_buf, rsn_ie, rsn_ie[1] + 2);
os_memcpy(data_buf + rsn_ie[1] + 2, rsnxe_ie, rsnxe_ie[1] + 2);
data_len += rsnxe_ie[1] + 2;
data = data_buf;
} else {
data = rsn_ie;
}
ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher,
hapd->own_addr, sta->addr, data, data_len,
frame, frame_len, mic);
os_free(data_buf);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Frame 3: Failed MIC calculation");
goto fail;
}
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->pasn_corrupt_mic) {
wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC");
mic[0] = ~mic[0];
}
#endif /* CONFIG_TESTING_OPTIONS */
os_memcpy(ptr, mic, mic_len);
done:
wpa_printf(MSG_DEBUG,
"PASN: Building frame 2: success; resp STA=" MACSTR,
MAC2STR(sta->addr));
ret = hostapd_drv_send_mlme(hapd, wpabuf_head(buf), wpabuf_len(buf), 0,
NULL, 0, 0);
if (ret)
wpa_printf(MSG_INFO, "send_auth_reply: Send failed");
wpabuf_free(buf);
return ret;
fail:
wpabuf_free(wrapped_data_buf);
wpabuf_free(pubkey);
wpabuf_free(buf);
return -1;
}
static void handle_auth_pasn_1(struct hostapd_data *hapd, struct sta_info *sta,
const struct ieee80211_mgmt *mgmt, size_t len)
{
struct ieee802_11_elems elems;
struct wpa_ie_data rsn_data;
struct wpa_pasn_params_data pasn_params;
struct rsn_pmksa_cache_entry *pmksa = NULL;
const u8 *cached_pmk = NULL;
size_t cached_pmk_len = 0;
#ifdef CONFIG_IEEE80211R_AP
u8 pmk_r1[PMK_LEN_MAX];
size_t pmk_r1_len;
#endif /* CONFIG_IEEE80211R_AP */
struct wpabuf *wrapped_data = NULL, *secret = NULL;
const int *groups = hapd->conf->pasn_groups;
static const int default_groups[] = { 19, 0 };
u16 status = WLAN_STATUS_SUCCESS;
int ret, inc_y;
bool derive_keys;
u32 i;
if (!groups)
groups = default_groups;
if (ieee802_11_parse_elems(mgmt->u.auth.variable,
len - offsetof(struct ieee80211_mgmt,
u.auth.variable),
&elems, 0) == ParseFailed) {
wpa_printf(MSG_DEBUG,
"PASN: Failed parsing Authentication frame");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
&rsn_data);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE");
status = WLAN_STATUS_INVALID_RSNIE;
goto send_resp;
}
ret = wpa_pasn_validate_rsne(&rsn_data);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
status = WLAN_STATUS_INVALID_RSNIE;
goto send_resp;
}
if (!(rsn_data.key_mgmt & hapd->conf->wpa_key_mgmt) ||
!(rsn_data.pairwise_cipher & hapd->conf->rsn_pairwise)) {
wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
status = WLAN_STATUS_INVALID_RSNIE;
goto send_resp;
}
sta->pasn->akmp = rsn_data.key_mgmt;
sta->pasn->cipher = rsn_data.pairwise_cipher;
if (hapd->conf->force_kdk_derivation ||
((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) &&
ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
WLAN_RSNX_CAPAB_SECURE_LTF)))
sta->pasn->kdk_len = WPA_KDK_MAX_LEN;
else
sta->pasn->kdk_len = 0;
wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", sta->pasn->kdk_len);
if (!elems.pasn_params || !elems.pasn_params_len) {
wpa_printf(MSG_DEBUG,
"PASN: No PASN Parameters element found");
status = WLAN_STATUS_INVALID_PARAMETERS;
goto send_resp;
}
ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
elems.pasn_params_len + 3,
false, &pasn_params);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed validation of PASN Parameters IE");
status = WLAN_STATUS_INVALID_PARAMETERS;
goto send_resp;
}
for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++)
;
if (!pasn_params.group || groups[i] != pasn_params.group) {
wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed",
pasn_params.group);
status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
goto send_resp;
}
if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
if (pasn_params.comeback) {
wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token");
ret = check_comeback_token(hapd, sta->addr,
pasn_params.comeback,
pasn_params.comeback_len);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
} else if (use_anti_clogging(hapd)) {
wpa_printf(MSG_DEBUG, "PASN: Respond with comeback");
handle_auth_pasn_comeback(hapd, sta, pasn_params.group);
ap_free_sta(hapd, sta);
return;
}
sta->pasn->ecdh = crypto_ecdh_init(pasn_params.group);
if (!sta->pasn->ecdh) {
wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
sta->pasn->group = pasn_params.group;
if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
inc_y = 1;
} else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
inc_y = 0;
} else {
wpa_printf(MSG_DEBUG,
"PASN: Invalid first octet in pubkey=0x%x",
pasn_params.pubkey[0]);
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
secret = crypto_ecdh_set_peerkey(sta->pasn->ecdh, inc_y,
pasn_params.pubkey + 1,
pasn_params.pubkey_len - 1);
if (!secret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
derive_keys = true;
if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
wrapped_data = ieee802_11_defrag(&elems,
WLAN_EID_EXTENSION,
WLAN_EID_EXT_WRAPPED_DATA);
if (!wrapped_data) {
wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
#ifdef CONFIG_SAE
if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
ret = pasn_wd_handle_sae_commit(hapd, sta,
wrapped_data);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed processing SAE commit");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
ret = pasn_wd_handle_fils(hapd, sta, wrapped_data);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed processing FILS wrapped data");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
wpa_printf(MSG_DEBUG,
"PASN: FILS: Pending AS response");
/*
* With PASN/FILS, keys can be derived only after a
* response from the AS is processed.
*/
derive_keys = false;
}
#endif /* CONFIG_FILS */
}
sta->pasn->wrapped_data_format = pasn_params.wrapped_data_format;
ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher,
((const u8 *) mgmt) + IEEE80211_HDRLEN,
len - IEEE80211_HDRLEN, sta->pasn->hash);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
if (!derive_keys) {
wpa_printf(MSG_DEBUG, "PASN: Storing secret");
sta->pasn->secret = secret;
wpabuf_free(wrapped_data);
return;
}
if (rsn_data.num_pmkid) {
if (wpa_key_mgmt_ft(sta->pasn->akmp)) {
#ifdef CONFIG_IEEE80211R_AP
wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1");
ret = wpa_ft_fetch_pmk_r1(hapd->wpa_auth, sta->addr,
rsn_data.pmkid,
pmk_r1, &pmk_r1_len, NULL,
NULL, NULL, NULL,
NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: FT: Failed getting PMK-R1");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
cached_pmk = pmk_r1;
cached_pmk_len = pmk_r1_len;
#else /* CONFIG_IEEE80211R_AP */
wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
#endif /* CONFIG_IEEE80211R_AP */
} else {
wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry");
pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
rsn_data.pmkid);
if (pmksa) {
cached_pmk = pmksa->pmk;
cached_pmk_len = pmksa->pmk_len;
}
}
} else {
wpa_printf(MSG_DEBUG, "PASN: No PMKID specified");
}
ret = pasn_derive_keys(hapd, sta, cached_pmk, cached_pmk_len,
&pasn_params, wrapped_data, secret);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto send_resp;
}
ret = pasn_auth_frame_hash(sta->pasn->akmp, sta->pasn->cipher,
((const u8 *) mgmt) + IEEE80211_HDRLEN,
len - IEEE80211_HDRLEN, sta->pasn->hash);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
}
send_resp:
ret = handle_auth_pasn_resp(hapd, sta, pmksa, status);
if (ret) {
wpa_printf(MSG_DEBUG, "PASN: Failed to send response");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
} else {
wpa_printf(MSG_DEBUG,
"PASN: Success handling transaction == 1");
}
wpabuf_free(secret);
wpabuf_free(wrapped_data);
if (status != WLAN_STATUS_SUCCESS)
ap_free_sta(hapd, sta);
}
static void handle_auth_pasn_3(struct hostapd_data *hapd, struct sta_info *sta,
const struct ieee80211_mgmt *mgmt, size_t len)
{
struct ieee802_11_elems elems;
struct wpa_pasn_params_data pasn_params;
struct wpabuf *wrapped_data = NULL;
u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
u8 mic_len;
int ret;
if (ieee802_11_parse_elems(mgmt->u.auth.variable,
len - offsetof(struct ieee80211_mgmt,
u.auth.variable),
&elems, 0) == ParseFailed) {
wpa_printf(MSG_DEBUG,
"PASN: Failed parsing Authentication frame");
goto fail;
}
/* Check that the MIC IE exists. Save it and zero out the memory. */
mic_len = pasn_mic_len(sta->pasn->akmp, sta->pasn->cipher);
if (!elems.mic || elems.mic_len != mic_len) {
wpa_printf(MSG_DEBUG,
"PASN: Invalid MIC. Expecting len=%u", mic_len);
goto fail;
} else {
os_memcpy(mic, elems.mic, mic_len);
/* TODO: Clean this up.. Should not modify received frame
* buffer. */
os_memset((u8 *) elems.mic, 0, mic_len);
}
if (!elems.pasn_params || !elems.pasn_params_len) {
wpa_printf(MSG_DEBUG,
"PASN: No PASN Parameters element found");
goto fail;
}
ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
elems.pasn_params_len + 3,
false, &pasn_params);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed validation of PASN Parameters IE");
goto fail;
}
if (pasn_params.pubkey || pasn_params.pubkey_len) {
wpa_printf(MSG_DEBUG,
"PASN: Public key should not be included");
goto fail;
}
/* Verify the MIC */
ret = pasn_mic(sta->pasn->ptk.kck, sta->pasn->akmp, sta->pasn->cipher,
sta->addr, hapd->own_addr,
sta->pasn->hash, mic_len * 2,
(u8 *) &mgmt->u.auth,
len - offsetof(struct ieee80211_mgmt, u.auth),
out_mic);
wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
goto fail;
}
if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
wrapped_data = ieee802_11_defrag(&elems,
WLAN_EID_EXTENSION,
WLAN_EID_EXT_WRAPPED_DATA);
if (!wrapped_data) {
wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
goto fail;
}
#ifdef CONFIG_SAE
if (sta->pasn->akmp == WPA_KEY_MGMT_SAE) {
ret = pasn_wd_handle_sae_confirm(hapd, sta,
wrapped_data);
if (ret) {
wpa_printf(MSG_DEBUG,
"PASN: Failed processing SAE confirm");
wpabuf_free(wrapped_data);
goto fail;
}
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
if (sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
sta->pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
if (wrapped_data) {
wpa_printf(MSG_DEBUG,
"PASN: FILS: Ignore wrapped data");
}
}
#endif /* CONFIG_FILS */
wpabuf_free(wrapped_data);
}
wpa_printf(MSG_INFO,
"PASN: Success handling transaction == 3. Store PTK");
ptksa_cache_add(hapd->ptksa, sta->addr, sta->pasn->cipher, 43200,
&sta->pasn->ptk);
fail:
ap_free_sta(hapd, sta);
}
static void handle_auth_pasn(struct hostapd_data *hapd, struct sta_info *sta,
const struct ieee80211_mgmt *mgmt, size_t len,
u16 trans_seq, u16 status)
{
if (hapd->conf->wpa != WPA_PROTO_RSN) {
wpa_printf(MSG_INFO, "PASN: RSN is not configured");
return;
}
wpa_printf(MSG_INFO, "PASN authentication: sta=" MACSTR,
MAC2STR(sta->addr));
if (trans_seq == 1) {
if (sta->pasn) {
wpa_printf(MSG_DEBUG,
"PASN: Not expecting transaction == 1");
return;
}
if (status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG,
"PASN: Failure status in transaction == 1");
return;
}
sta->pasn = os_zalloc(sizeof(*sta->pasn));
if (!sta->pasn) {
wpa_printf(MSG_DEBUG,
"PASN: Failed to allocate PASN context");
return;
}
handle_auth_pasn_1(hapd, sta, mgmt, len);
} else if (trans_seq == 3) {
if (!sta->pasn) {
wpa_printf(MSG_DEBUG,
"PASN: Not expecting transaction == 3");
return;
}
if (status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG,
"PASN: Failure status in transaction == 3");
ap_free_sta_pasn(hapd, sta);
return;
}
handle_auth_pasn_3(hapd, sta, mgmt, len);
} else {
wpa_printf(MSG_DEBUG,
"PASN: Invalid transaction %u - ignore", trans_seq);
}
}
#endif /* CONFIG_PASN */
static void handle_auth(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int rssi, int from_queue)
{
u16 auth_alg, auth_transaction, status_code;
u16 resp = WLAN_STATUS_SUCCESS;
struct sta_info *sta = NULL;
int res, reply_res;
u16 fc;
const u8 *challenge = NULL;
u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
size_t resp_ies_len = 0;
u16 seq_ctrl;
struct radius_sta rad_info;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
(unsigned long) len);
return;
}
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->iconf->ignore_auth_probability > 0.0 &&
drand48() < hapd->iconf->ignore_auth_probability) {
wpa_printf(MSG_INFO,
"TESTING: ignoring auth frame from " MACSTR,
MAC2STR(mgmt->sa));
return;
}
#endif /* CONFIG_TESTING_OPTIONS */
auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
status_code = le_to_host16(mgmt->u.auth.status_code);
fc = le_to_host16(mgmt->frame_control);
seq_ctrl = le_to_host16(mgmt->seq_ctrl);
if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
2 + WLAN_AUTH_CHALLENGE_LEN &&
mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE &&
mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN)
challenge = &mgmt->u.auth.variable[2];
wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
"auth_transaction=%d status_code=%d wep=%d%s "
"seq_ctrl=0x%x%s%s",
MAC2STR(mgmt->sa), auth_alg, auth_transaction,
status_code, !!(fc & WLAN_FC_ISWEP),
challenge ? " challenge" : "",
seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
from_queue ? " (from queue)" : "");
#ifdef CONFIG_NO_RC4
if (auth_alg == WLAN_AUTH_SHARED_KEY) {
wpa_printf(MSG_INFO,
"Unsupported authentication algorithm (%d)",
auth_alg);
resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
goto fail;
}
#endif /* CONFIG_NO_RC4 */
if (hapd->tkip_countermeasures) {
wpa_printf(MSG_DEBUG,
"Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
auth_alg == WLAN_AUTH_OPEN) ||
#ifdef CONFIG_IEEE80211R_AP
(hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
auth_alg == WLAN_AUTH_FT) ||
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
(hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
auth_alg == WLAN_AUTH_SAE) ||
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
(hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
auth_alg == WLAN_AUTH_FILS_SK) ||
(hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
hapd->conf->fils_dh_group &&
auth_alg == WLAN_AUTH_FILS_SK_PFS) ||
#endif /* CONFIG_FILS */
#ifdef CONFIG_PASN
(hapd->conf->wpa &&
(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PASN) &&
auth_alg == WLAN_AUTH_PASN) ||
#endif /* CONFIG_PASN */
((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
auth_alg == WLAN_AUTH_SHARED_KEY))) {
wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
auth_alg);
resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
goto fail;
}
if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
#ifdef CONFIG_PASN
(auth_alg == WLAN_AUTH_PASN && auth_transaction == 3) ||
#endif /* CONFIG_PASN */
(auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)",
auth_transaction);
resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
goto fail;
}
if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
MAC2STR(mgmt->sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
if (hapd->conf->no_auth_if_seen_on) {
struct hostapd_data *other;
other = sta_track_seen_on(hapd->iface, mgmt->sa,
hapd->conf->no_auth_if_seen_on);
if (other) {
u8 *pos;
u32 info;
u8 op_class, channel, phytype;
wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
MACSTR " since STA has been seen on %s",
hapd->conf->iface, MAC2STR(mgmt->sa),
hapd->conf->no_auth_if_seen_on);
resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
pos = &resp_ies[0];
*pos++ = WLAN_EID_NEIGHBOR_REPORT;
*pos++ = 13;
os_memcpy(pos, other->own_addr, ETH_ALEN);
pos += ETH_ALEN;
info = 0; /* TODO: BSSID Information */
WPA_PUT_LE32(pos, info);
pos += 4;
if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
phytype = 8; /* dmg */
else if (other->iconf->ieee80211ac)
phytype = 9; /* vht */
else if (other->iconf->ieee80211n)
phytype = 7; /* ht */
else if (other->iconf->hw_mode ==
HOSTAPD_MODE_IEEE80211A)
phytype = 4; /* ofdm */
else if (other->iconf->hw_mode ==
HOSTAPD_MODE_IEEE80211G)
phytype = 6; /* erp */
else
phytype = 5; /* hrdsss */
if (ieee80211_freq_to_channel_ext(
hostapd_hw_get_freq(other,
other->iconf->channel),
other->iconf->secondary_channel,
other->iconf->ieee80211ac,
&op_class, &channel) == NUM_HOSTAPD_MODES) {
op_class = 0;
channel = other->iconf->channel;
}
*pos++ = op_class;
*pos++ = channel;
*pos++ = phytype;
resp_ies_len = pos - &resp_ies[0];
goto fail;
}
}
res = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len,
&rad_info);
if (res == HOSTAPD_ACL_REJECT) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Ignore Authentication frame from " MACSTR
" due to ACL reject", MAC2STR(mgmt->sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
if (res == HOSTAPD_ACL_PENDING)
return;
#ifdef CONFIG_SAE
if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
(auth_transaction == 1 ||
(auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) {
/* Handle SAE Authentication commit message through a queue to
* provide more control for postponing the needed heavy
* processing under a possible DoS attack scenario. In addition,
* queue SAE Authentication confirm message if there happens to
* be a queued commit message from the same peer. This is needed
* to avoid reordering Authentication frames within the same
* SAE exchange. */
auth_sae_queue(hapd, mgmt, len, rssi);
return;
}
#endif /* CONFIG_SAE */
sta = ap_get_sta(hapd, mgmt->sa);
if (sta) {
sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
sta->ft_over_ds = 0;
if ((fc & WLAN_FC_RETRY) &&
sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
sta->last_seq_ctrl == seq_ctrl &&
sta->last_subtype == WLAN_FC_STYPE_AUTH) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Drop repeated authentication frame seq_ctrl=0x%x",
seq_ctrl);
return;
}
#ifdef CONFIG_MESH
if ((hapd->conf->mesh & MESH_ENABLED) &&
sta->plink_state == PLINK_BLOCKED) {
wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
" is blocked - drop Authentication frame",
MAC2STR(mgmt->sa));
return;
}
#endif /* CONFIG_MESH */
#ifdef CONFIG_PASN
if (auth_alg == WLAN_AUTH_PASN &&
(sta->flags & WLAN_STA_ASSOC)) {
wpa_printf(MSG_DEBUG,
"PASN: auth: Existing station: " MACSTR,
MAC2STR(sta->addr));
return;
}
#endif /* CONFIG_PASN */
} else {
#ifdef CONFIG_MESH
if (hapd->conf->mesh & MESH_ENABLED) {
/* if the mesh peer is not available, we don't do auth.
*/
wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
" not yet known - drop Authentication frame",
MAC2STR(mgmt->sa));
/*
* Save a copy of the frame so that it can be processed
* if a new peer entry is added shortly after this.
*/
wpabuf_free(hapd->mesh_pending_auth);
hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len);
os_get_reltime(&hapd->mesh_pending_auth_time);
return;
}
#endif /* CONFIG_MESH */
sta = ap_sta_add(hapd, mgmt->sa);
if (!sta) {
wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
}
sta->last_seq_ctrl = seq_ctrl;
sta->last_subtype = WLAN_FC_STYPE_AUTH;
#ifdef CONFIG_MBO
sta->auth_rssi = rssi;
#endif /* CONFIG_MBO */
res = ieee802_11_set_radius_info(hapd, sta, res, &rad_info);
if (res) {
wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
sta->flags &= ~WLAN_STA_PREAUTH;
ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
/*
* If the driver supports full AP client state, add a station to the
* driver before sending authentication reply to make sure the driver
* has resources, and not to go through the entire authentication and
* association handshake, and fail it at the end.
*
* If this is not the first transaction, in a multi-step authentication
* algorithm, the station already exists in the driver
* (sta->added_unassoc = 1) so skip it.
*
* In mesh mode, the station was already added to the driver when the
* NEW_PEER_CANDIDATE event is received.
*
* If PMF was negotiated for the existing association, skip this to
* avoid dropping the STA entry and the associated keys. This is needed
* to allow the original connection work until the attempt can complete
* (re)association, so that unprotected Authentication frame cannot be
* used to bypass PMF protection.
*
* PASN authentication does not require adding/removing station to the
* driver so skip this flow in case of PASN authentication.
*/
if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
(!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) &&
!(hapd->conf->mesh & MESH_ENABLED) &&
!(sta->added_unassoc) && auth_alg != WLAN_AUTH_PASN) {
if (ap_sta_re_add(hapd, sta) < 0) {
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
}
switch (auth_alg) {
case WLAN_AUTH_OPEN:
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"authentication OK (open system)");
sta->flags |= WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
sta->auth_alg = WLAN_AUTH_OPEN;
mlme_authenticate_indication(hapd, sta);
break;
#ifdef CONFIG_WEP
#ifndef CONFIG_NO_RC4
case WLAN_AUTH_SHARED_KEY:
resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
fc & WLAN_FC_ISWEP);
if (resp != 0)
wpa_printf(MSG_DEBUG,
"auth_shared_key() failed: status=%d", resp);
sta->auth_alg = WLAN_AUTH_SHARED_KEY;
mlme_authenticate_indication(hapd, sta);
if (sta->challenge && auth_transaction == 1) {
resp_ies[0] = WLAN_EID_CHALLENGE;
resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN;
os_memcpy(resp_ies + 2, sta->challenge,
WLAN_AUTH_CHALLENGE_LEN);
resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
}
break;
#endif /* CONFIG_NO_RC4 */
#endif /* CONFIG_WEP */
#ifdef CONFIG_IEEE80211R_AP
case WLAN_AUTH_FT:
sta->auth_alg = WLAN_AUTH_FT;
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr, NULL);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
"state machine");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid,
auth_transaction, mgmt->u.auth.variable,
len - IEEE80211_HDRLEN -
sizeof(mgmt->u.auth),
handle_auth_ft_finish, hapd);
/* handle_auth_ft_finish() callback will complete auth. */
return;
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
case WLAN_AUTH_SAE:
#ifdef CONFIG_MESH
if (status_code == WLAN_STATUS_SUCCESS &&
hapd->conf->mesh & MESH_ENABLED) {
if (sta->wpa_sm == NULL)
sta->wpa_sm =
wpa_auth_sta_init(hapd->wpa_auth,
sta->addr, NULL);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_DEBUG,
"SAE: Failed to initialize WPA state machine");
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
}
#endif /* CONFIG_MESH */
handle_auth_sae(hapd, sta, mgmt, len, auth_transaction,
status_code);
return;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
case WLAN_AUTH_FILS_SK:
case WLAN_AUTH_FILS_SK_PFS:
handle_auth_fils(hapd, sta, mgmt->u.auth.variable,
len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
auth_alg, auth_transaction, status_code,
handle_auth_fils_finish);
return;
#endif /* CONFIG_FILS */
#ifdef CONFIG_PASN
case WLAN_AUTH_PASN:
handle_auth_pasn(hapd, sta, mgmt, len, auth_transaction,
status_code);
return;
#endif /* CONFIG_PASN */
}
fail:
reply_res = send_auth_reply(hapd, sta, mgmt->sa, mgmt->bssid, auth_alg,
auth_alg == WLAN_AUTH_SAE ?
auth_transaction : auth_transaction + 1,
resp, resp_ies, resp_ies_len,
"handle-auth");
if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
reply_res != WLAN_STATUS_SUCCESS)) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
}
}
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
{
int i, j = 32, aid;
/* get a unique AID */
if (sta->aid > 0) {
wpa_printf(MSG_DEBUG, " old AID %d", sta->aid);
return 0;
}
if (TEST_FAIL())
return -1;
for (i = 0; i < AID_WORDS; i++) {
if (hapd->sta_aid[i] == (u32) -1)
continue;
for (j = 0; j < 32; j++) {
if (!(hapd->sta_aid[i] & BIT(j)))
break;
}
if (j < 32)
break;
}
if (j == 32)
return -1;
aid = i * 32 + j + 1;
if (aid > 2007)
return -1;
sta->aid = aid;
hapd->sta_aid[i] |= BIT(j);
wpa_printf(MSG_DEBUG, " new AID %d", sta->aid);
return 0;
}
static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ssid_ie, size_t ssid_ie_len)
{
if (ssid_ie == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Station tried to associate with unknown SSID "
"'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
return WLAN_STATUS_SUCCESS;
}
static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *wmm_ie, size_t wmm_ie_len)
{
sta->flags &= ~WLAN_STA_WMM;
sta->qosinfo = 0;
if (wmm_ie && hapd->conf->wmm_enabled) {
struct wmm_information_element *wmm;
if (!hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_WPA,
HOSTAPD_LEVEL_DEBUG,
"invalid WMM element in association "
"request");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sta->flags |= WLAN_STA_WMM;
wmm = (struct wmm_information_element *) wmm_ie;
sta->qosinfo = wmm->qos_info;
}
return WLAN_STATUS_SUCCESS;
}
static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *multi_ap_ie, size_t multi_ap_len)
{
u8 multi_ap_value = 0;
sta->flags &= ~WLAN_STA_MULTI_AP;
if (!hapd->conf->multi_ap)
return WLAN_STATUS_SUCCESS;
if (multi_ap_ie) {
const u8 *multi_ap_subelem;
multi_ap_subelem = get_ie(multi_ap_ie + 4,
multi_ap_len - 4,
MULTI_AP_SUB_ELEM_TYPE);
if (multi_ap_subelem && multi_ap_subelem[1] == 1) {
multi_ap_value = multi_ap_subelem[2];
} else {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Multi-AP IE has missing or invalid Multi-AP subelement");
return WLAN_STATUS_INVALID_IE;
}
}
if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA)
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Multi-AP IE with unexpected value 0x%02x",
multi_ap_value);
if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) {
if (hapd->conf->multi_ap & FRONTHAUL_BSS)
return WLAN_STATUS_SUCCESS;
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Non-Multi-AP STA tries to associate with backhaul-only BSS");
return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
}
if (!(hapd->conf->multi_ap & BACKHAUL_BSS))
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Backhaul STA tries to associate with fronthaul-only BSS");
sta->flags |= WLAN_STA_MULTI_AP;
return WLAN_STATUS_SUCCESS;
}
static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
struct ieee802_11_elems *elems)
{
/* Supported rates not used in IEEE 802.11ad/DMG */
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
return WLAN_STATUS_SUCCESS;
if (!elems->supp_rates) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"No supported rates element in AssocReq");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (elems->supp_rates_len + elems->ext_supp_rates_len >
sizeof(sta->supported_rates)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Invalid supported rates element length %d+%d",
elems->supp_rates_len,
elems->ext_supp_rates_len);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sta->supported_rates_len = merge_byte_arrays(
sta->supported_rates, sizeof(sta->supported_rates),
elems->supp_rates, elems->supp_rates_len,
elems->ext_supp_rates, elems->ext_supp_rates_len);
return WLAN_STATUS_SUCCESS;
}
static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ext_capab_ie, size_t ext_capab_ie_len)
{
#ifdef CONFIG_INTERWORKING
/* check for QoS Map support */
if (ext_capab_ie_len >= 5) {
if (ext_capab_ie[4] & 0x01)
sta->qos_map_enabled = 1;
}
#endif /* CONFIG_INTERWORKING */
if (ext_capab_ie_len > 0) {
sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
os_free(sta->ext_capability);
sta->ext_capability = os_malloc(1 + ext_capab_ie_len);
if (sta->ext_capability) {
sta->ext_capability[0] = ext_capab_ie_len;
os_memcpy(sta->ext_capability + 1, ext_capab_ie,
ext_capab_ie_len);
}
}
return WLAN_STATUS_SUCCESS;
}
#ifdef CONFIG_OWE
static int owe_group_supported(struct hostapd_data *hapd, u16 group)
{
int i;
int *groups = hapd->conf->owe_groups;
if (group != 19 && group != 20 && group != 21)
return 0;
if (!groups)
return 1;
for (i = 0; groups[i] > 0; i++) {
if (groups[i] == group)
return 1;
}
return 0;
}
static u16 owe_process_assoc_req(struct hostapd_data *hapd,
struct sta_info *sta, const u8 *owe_dh,
u8 owe_dh_len)
{
struct wpabuf *secret, *pub, *hkey;
int res;
u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
const char *info = "OWE Key Generation";
const u8 *addr[2];
size_t len[2];
u16 group;
size_t hash_len, prime_len;
if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
return WLAN_STATUS_SUCCESS;
}
group = WPA_GET_LE16(owe_dh);
if (!owe_group_supported(hapd, group)) {
wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group);
return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
}
if (group == 19)
prime_len = 32;
else if (group == 20)
prime_len = 48;
else if (group == 21)
prime_len = 66;
else
return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
crypto_ecdh_deinit(sta->owe_ecdh);
sta->owe_ecdh = crypto_ecdh_init(group);
if (!sta->owe_ecdh)
return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
sta->owe_group = group;
secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2,
owe_dh_len - 2);
secret = wpabuf_zeropad(secret, prime_len);
if (!secret) {
wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
/* prk = HKDF-extract(C | A | group, z) */
pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
if (!pub) {
wpabuf_clear_free(secret);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
/* PMKID = Truncate-128(Hash(C | A)) */
addr[0] = owe_dh + 2;
len[0] = owe_dh_len - 2;
addr[1] = wpabuf_head(pub);
len[1] = wpabuf_len(pub);
if (group == 19) {
res = sha256_vector(2, addr, len, pmkid);
hash_len = SHA256_MAC_LEN;
} else if (group == 20) {
res = sha384_vector(2, addr, len, pmkid);
hash_len = SHA384_MAC_LEN;
} else if (group == 21) {
res = sha512_vector(2, addr, len, pmkid);
hash_len = SHA512_MAC_LEN;
} else {
wpabuf_free(pub);
wpabuf_clear_free(secret);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
pub = wpabuf_zeropad(pub, prime_len);
if (res < 0 || !pub) {
wpabuf_free(pub);
wpabuf_clear_free(secret);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2);
if (!hkey) {
wpabuf_free(pub);
wpabuf_clear_free(secret);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */
wpabuf_put_buf(hkey, pub); /* A */
wpabuf_free(pub);
wpabuf_put_le16(hkey, group); /* group */
if (group == 19)
res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
wpabuf_head(secret), wpabuf_len(secret), prk);
else if (group == 20)
res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
wpabuf_head(secret), wpabuf_len(secret), prk);
else if (group == 21)
res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
wpabuf_head(secret), wpabuf_len(secret), prk);
wpabuf_clear_free(hkey);
wpabuf_clear_free(secret);
if (res < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
/* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
os_free(sta->owe_pmk);
sta->owe_pmk = os_malloc(hash_len);
if (!sta->owe_pmk) {
os_memset(prk, 0, SHA512_MAC_LEN);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (group == 19)
res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
os_strlen(info), sta->owe_pmk, hash_len);
else if (group == 20)
res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
os_strlen(info), sta->owe_pmk, hash_len);
else if (group == 21)
res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
os_strlen(info), sta->owe_pmk, hash_len);
os_memset(prk, 0, SHA512_MAC_LEN);
if (res < 0) {
os_free(sta->owe_pmk);
sta->owe_pmk = NULL;
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sta->owe_pmk_len = hash_len;
wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len);
wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk,
sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE);
return WLAN_STATUS_SUCCESS;
}
u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
const u8 *rsn_ie, size_t rsn_ie_len,
const u8 *owe_dh, size_t owe_dh_len)
{
struct wpa_ie_data data;
int res;
if (!rsn_ie || rsn_ie_len < 2) {
wpa_printf(MSG_DEBUG, "OWE: Invalid RSNE from " MACSTR,
MAC2STR(peer));
return WLAN_STATUS_INVALID_IE;
}
rsn_ie -= 2;
rsn_ie_len += 2;
res = wpa_parse_wpa_ie_rsn(rsn_ie, rsn_ie_len, &data);
if (res) {
wpa_printf(MSG_DEBUG, "Failed to parse RSNE from " MACSTR
" (res=%d)", MAC2STR(peer), res);
wpa_hexdump(MSG_DEBUG, "RSNE", rsn_ie, rsn_ie_len);
return wpa_res_to_status_code(res);
}
if (!(data.key_mgmt & WPA_KEY_MGMT_OWE)) {
wpa_printf(MSG_DEBUG,
"OWE: Unexpected key mgmt 0x%x from " MACSTR,
(unsigned int) data.key_mgmt, MAC2STR(peer));
return WLAN_STATUS_AKMP_NOT_VALID;
}
if (!owe_dh) {
wpa_printf(MSG_DEBUG,
"OWE: No Diffie-Hellman Parameter element from "
MACSTR, MAC2STR(peer));
return WLAN_STATUS_AKMP_NOT_VALID;
}
return WLAN_STATUS_SUCCESS;
}
u16 owe_process_rsn_ie(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *rsn_ie, size_t rsn_ie_len,
const u8 *owe_dh, size_t owe_dh_len)
{
u16 status;
u8 *owe_buf, ie[256 * 2];
size_t ie_len = 0;
enum wpa_validate_result res;
if (!rsn_ie || rsn_ie_len < 2) {
wpa_printf(MSG_DEBUG, "OWE: No RSNE in (Re)AssocReq");
status = WLAN_STATUS_INVALID_IE;
goto end;
}
if (!sta->wpa_sm)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
NULL);
if (!sta->wpa_sm) {
wpa_printf(MSG_WARNING,
"OWE: Failed to initialize WPA state machine");
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto end;
}
rsn_ie -= 2;
rsn_ie_len += 2;
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq, rsn_ie, rsn_ie_len,
NULL, 0, NULL, 0, owe_dh, owe_dh_len);
status = wpa_res_to_status_code(res);
if (status != WLAN_STATUS_SUCCESS)
goto end;
status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
if (status != WLAN_STATUS_SUCCESS)
goto end;
owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, ie, sizeof(ie),
NULL, 0);
if (!owe_buf) {
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto end;
}
if (sta->owe_ecdh) {
struct wpabuf *pub;
pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
if (!pub) {
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto end;
}
/* OWE Diffie-Hellman Parameter element */
*owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
*owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
*owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
*/
WPA_PUT_LE16(owe_buf, sta->owe_group);
owe_buf += 2;
os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
owe_buf += wpabuf_len(pub);
wpabuf_free(pub);
sta->external_dh_updated = 1;
}
ie_len = owe_buf - ie;
end:
wpa_printf(MSG_DEBUG, "OWE: Update status %d, ie len %d for peer "
MACSTR, status, (unsigned int) ie_len,
MAC2STR(sta->addr));
hostapd_drv_update_dh_ie(hapd, sta->addr, status,
status == WLAN_STATUS_SUCCESS ? ie : NULL,
ie_len);
return status;
}
#endif /* CONFIG_OWE */
static bool check_sa_query(struct hostapd_data *hapd, struct sta_info *sta,
int reassoc)
{
if ((sta->flags &
(WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED)) !=
(WLAN_STA_ASSOC | WLAN_STA_MFP | WLAN_STA_AUTHORIZED))
return false;
if (!sta->sa_query_timed_out && sta->sa_query_count > 0)
ap_check_sa_query_timeout(hapd, sta);
if (!sta->sa_query_timed_out &&
(!reassoc || sta->auth_alg != WLAN_AUTH_FT)) {
/*
* STA has already been associated with MFP and SA Query timeout
* has not been reached. Reject the association attempt
* temporarily and start SA Query, if one is not pending.
*/
if (sta->sa_query_count == 0)
ap_sta_start_sa_query(hapd, sta);
return true;
}
return false;
}
static int check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ies, size_t ies_len, int reassoc)
{
struct ieee802_11_elems elems;
int resp;
const u8 *wpa_ie;
size_t wpa_ie_len;
const u8 *p2p_dev_addr = NULL;
if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "Station sent an invalid "
"association request");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
resp = copy_supp_rates(hapd, sta, &elems);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
!(sta->flags & WLAN_STA_HT)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "Station does not support "
"mandatory HT PHY - reject association");
return WLAN_STATUS_ASSOC_DENIED_NO_HT;
}
#ifdef CONFIG_IEEE80211AC
if (hapd->iconf->ieee80211ac) {
resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
}
if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
!(sta->flags & WLAN_STA_VHT)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "Station does not support "
"mandatory VHT PHY - reject association");
return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
}
if (hapd->conf->vendor_vht && !elems.vht_capabilities) {
resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht,
elems.vendor_vht_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP,
elems.he_capabilities,
elems.he_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
if (is_6ghz_op_class(hapd->iconf->op_class)) {
if (!(sta->flags & WLAN_STA_HE)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Station does not support mandatory HE PHY - reject association");
return WLAN_STATUS_DENIED_HE_NOT_SUPPORTED;
}
resp = copy_sta_he_6ghz_capab(hapd, sta,
elems.he_6ghz_band_cap);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
}
}
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_P2P
if (elems.p2p) {
wpabuf_free(sta->p2p_ie);
sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
P2P_IE_VENDOR_TYPE);
if (sta->p2p_ie)
p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
} else {
wpabuf_free(sta->p2p_ie);
sta->p2p_ie = NULL;
}
#endif /* CONFIG_P2P */
if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
wpa_ie = elems.rsn_ie;
wpa_ie_len = elems.rsn_ie_len;
} else if ((hapd->conf->wpa & WPA_PROTO_WPA) &&
elems.wpa_ie) {
wpa_ie = elems.wpa_ie;
wpa_ie_len = elems.wpa_ie_len;
} else {
wpa_ie = NULL;
wpa_ie_len = 0;
}
#ifdef CONFIG_WPS
sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
if (hapd->conf->wps_state && elems.wps_ie) {
wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association "
"Request - assume WPS is used");
if (check_sa_query(hapd, sta, reassoc))
return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
sta->flags |= WLAN_STA_WPS;
wpabuf_free(sta->wps_ie);
sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
WPS_IE_VENDOR_TYPE);
if (sta->wps_ie && wps_is_20(sta->wps_ie)) {
wpa_printf(MSG_DEBUG, "WPS: STA supports WPS 2.0");
sta->flags |= WLAN_STA_WPS2;
}
wpa_ie = NULL;
wpa_ie_len = 0;
if (sta->wps_ie && wps_validate_assoc_req(sta->wps_ie) < 0) {
wpa_printf(MSG_DEBUG, "WPS: Invalid WPS IE in "
"(Re)Association Request - reject");
return WLAN_STATUS_INVALID_IE;
}
} else if (hapd->conf->wps_state && wpa_ie == NULL) {
wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in "
"(Re)Association Request - possible WPS use");
sta->flags |= WLAN_STA_MAYBE_WPS;
} else
#endif /* CONFIG_WPS */
if (hapd->conf->wpa && wpa_ie == NULL) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"No WPA/RSN IE in association request");
return WLAN_STATUS_INVALID_IE;
}
if (hapd->conf->wpa && wpa_ie) {
enum wpa_validate_result res;
wpa_ie -= 2;
wpa_ie_len += 2;
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr,
p2p_dev_addr);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_WARNING, "Failed to initialize WPA "
"state machine");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
hapd->iface->freq,
wpa_ie, wpa_ie_len,
elems.rsnxe ? elems.rsnxe - 2 : NULL,
elems.rsnxe ? elems.rsnxe_len + 2 : 0,
elems.mdie, elems.mdie_len,
elems.owe_dh, elems.owe_dh_len);
resp = wpa_res_to_status_code(res);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
if (check_sa_query(hapd, sta, reassoc))
return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
if (wpa_auth_uses_mfp(sta->wpa_sm))
sta->flags |= WLAN_STA_MFP;
else
sta->flags &= ~WLAN_STA_MFP;
#ifdef CONFIG_IEEE80211R_AP
if (sta->auth_alg == WLAN_AUTH_FT) {
if (!reassoc) {
wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
"to use association (not "
"re-association) with FT auth_alg",
MAC2STR(sta->addr));
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
ies_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae &&
sta->sae->state == SAE_ACCEPTED)
wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid);
if (wpa_auth_uses_sae(sta->wpa_sm) &&
sta->auth_alg == WLAN_AUTH_OPEN) {
struct rsn_pmksa_cache_entry *sa;
sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) {
wpa_printf(MSG_DEBUG,
"SAE: No PMKSA cache entry found for "
MACSTR, MAC2STR(sta->addr));
return WLAN_STATUS_INVALID_PMKID;
}
wpa_printf(MSG_DEBUG, "SAE: " MACSTR
" using PMKSA caching", MAC2STR(sta->addr));
} else if (wpa_auth_uses_sae(sta->wpa_sm) &&
sta->auth_alg != WLAN_AUTH_SAE &&
!(sta->auth_alg == WLAN_AUTH_FT &&
wpa_auth_uses_ft_sae(sta->wpa_sm))) {
wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
"SAE AKM after non-SAE auth_alg %u",
MAC2STR(sta->addr), sta->auth_alg);
return WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
}
if (hapd->conf->sae_pwe == 2 &&
sta->auth_alg == WLAN_AUTH_SAE &&
sta->sae && !sta->sae->h2e &&
ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
WLAN_RSNX_CAPAB_SAE_H2E)) {
wpa_printf(MSG_INFO, "SAE: " MACSTR
" indicates support for SAE H2E, but did not use it",
MAC2STR(sta->addr));
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_OWE
if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
elems.owe_dh) {
resp = owe_process_assoc_req(hapd, sta, elems.owe_dh,
elems.owe_dh_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
dpp_pfs_free(sta->dpp_pfs);
sta->dpp_pfs = NULL;
if (DPP_VERSION > 1 &&
(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
elems.owe_dh) {
sta->dpp_pfs = dpp_pfs_init(
wpabuf_head(hapd->conf->dpp_netaccesskey),
wpabuf_len(hapd->conf->dpp_netaccesskey));
if (!sta->dpp_pfs) {
wpa_printf(MSG_DEBUG,
"DPP: Could not initialize PFS");
/* Try to continue without PFS */
goto pfs_fail;
}
if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh,
elems.owe_dh_len) < 0) {
dpp_pfs_free(sta->dpp_pfs);
sta->dpp_pfs = NULL;
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
}
wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
sta->dpp_pfs->secret : NULL);
pfs_fail:
#endif /* CONFIG_DPP2 */
if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Station tried to use TKIP with HT "
"association");
return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
}
#ifdef CONFIG_HS20
} else if (hapd->conf->osen) {
if (elems.osen == NULL) {
hostapd_logger(
hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"No HS 2.0 OSEN element in association request");
return WLAN_STATUS_INVALID_IE;
}
wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
if (sta->wpa_sm == NULL)
sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
sta->addr, NULL);
if (sta->wpa_sm == NULL) {
wpa_printf(MSG_WARNING, "Failed to initialize WPA "
"state machine");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
elems.osen - 2, elems.osen_len + 2) < 0)
return WLAN_STATUS_INVALID_IE;
#endif /* CONFIG_HS20 */
} else
wpa_auth_sta_no_wpa(sta->wpa_sm);
#ifdef CONFIG_P2P
p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
#endif /* CONFIG_P2P */
#ifdef CONFIG_HS20
wpabuf_free(sta->hs20_ie);
if (elems.hs20 && elems.hs20_len > 4) {
int release;
sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
elems.hs20_len - 4);
release = ((elems.hs20[4] >> 4) & 0x0f) + 1;
if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm) &&
hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
wpa_printf(MSG_DEBUG,
"HS 2.0: PMF not negotiated by release %d station "
MACSTR, release, MAC2STR(sta->addr));
return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
}
} else {
sta->hs20_ie = NULL;
}
wpabuf_free(sta->roaming_consortium);
if (elems.roaming_cons_sel)
sta->roaming_consortium = wpabuf_alloc_copy(
elems.roaming_cons_sel + 4,
elems.roaming_cons_sel_len - 4);
else
sta->roaming_consortium = NULL;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_FST
wpabuf_free(sta->mb_ies);
if (hapd->iface->fst)
sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
else
sta->mb_ies = NULL;
#endif /* CONFIG_FST */
#ifdef CONFIG_MBO
mbo_ap_check_sta_assoc(hapd, sta, &elems);
if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
wpa_printf(MSG_INFO,
"MBO: Reject WPA2 association without PMF");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
#endif /* CONFIG_MBO */
#if defined(CONFIG_FILS) && defined(CONFIG_OCV)
if (wpa_auth_uses_ocv(sta->wpa_sm) &&
(sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK)) {
struct wpa_channel_info ci;
int tx_chanwidth;
int tx_seg1_idx;
enum oci_verify_result res;
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (get_sta_tx_parameters(sta->wpa_sm,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx, &tx_chanwidth,
&tx_seg1_idx) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
res = ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
tx_chanwidth, tx_seg1_idx);
if (wpa_auth_uses_ocv(sta->wpa_sm) == 2 &&
res == OCI_NOT_FOUND) {
/* Work around misbehaving STAs */
wpa_printf(MSG_INFO,
"FILS: Disable OCV with a STA that does not send OCI");
wpa_auth_set_ocv(sta->wpa_sm, 0);
} else if (res != OCI_SUCCESS) {
wpa_printf(MSG_WARNING, "FILS: OCV failed: %s",
ocv_errorstr);
wpa_msg(hapd->msg_ctx, MSG_INFO, OCV_FAILURE "addr="
MACSTR " frame=fils-reassoc-req error=%s",
MAC2STR(sta->addr), ocv_errorstr);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
}
#endif /* CONFIG_FILS && CONFIG_OCV */
ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
elems.supp_op_classes_len);
if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) &&
elems.rrm_enabled &&
elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
sizeof(sta->rrm_enabled_capa));
if (elems.power_capab) {
sta->min_tx_power = elems.power_capab[0];
sta->max_tx_power = elems.power_capab[1];
sta->power_capab = 1;
} else {
sta->power_capab = 0;
}
return WLAN_STATUS_SUCCESS;
}
static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
u16 reason_code)
{
int send_len;
struct ieee80211_mgmt reply;
os_memset(&reply, 0, sizeof(reply));
reply.frame_control =
IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH);
os_memcpy(reply.da, addr, ETH_ALEN);
os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN);
os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN);
send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
reply.u.deauth.reason_code = host_to_le16(reason_code);
if (hostapd_drv_send_mlme(hapd, &reply, send_len, 0, NULL, 0, 0) < 0)
wpa_printf(MSG_INFO, "Failed to send deauth: %s",
strerror(errno));
}
static int add_associated_sta(struct hostapd_data *hapd,
struct sta_info *sta, int reassoc)
{
struct ieee80211_ht_capabilities ht_cap;
struct ieee80211_vht_capabilities vht_cap;
struct ieee80211_he_capabilities he_cap;
int set = 1;
/*
* Remove the STA entry to ensure the STA PS state gets cleared and
* configuration gets updated. This is relevant for cases, such as
* FT-over-the-DS, where a station re-associates back to the same AP but
* skips the authentication flow, or if working with a driver that
* does not support full AP client state.
*
* Skip this if the STA has already completed FT reassociation and the
* TK has been configured since the TX/RX PN must not be reset to 0 for
* the same key.
*
* FT-over-the-DS has a special case where the STA entry (and as such,
* the TK) has not yet been configured to the driver depending on which
* driver interface is used. For that case, allow add-STA operation to
* be used (instead of set-STA). This is needed to allow mac80211-based
* drivers to accept the STA parameter configuration. Since this is
* after a new FT-over-DS exchange, a new TK has been derived, so key
* reinstallation is not a concern for this case.
*/
wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
" (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg,
sta->ft_over_ds, reassoc,
!!(sta->flags & WLAN_STA_AUTHORIZED),
wpa_auth_sta_ft_tk_already_set(sta->wpa_sm),
wpa_auth_sta_fils_tk_already_set(sta->wpa_sm));
if (!sta->added_unassoc &&
(!(sta->flags & WLAN_STA_AUTHORIZED) ||
(reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
(!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
!wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
hostapd_drv_sta_remove(hapd, sta->addr);
wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
set = 0;
/* Do not allow the FT-over-DS exception to be used more than
* once per authentication exchange to guarantee a new TK is
* used here */
sta->ft_over_ds = 0;
}
if (sta->flags & WLAN_STA_HT)
hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
#ifdef CONFIG_IEEE80211AC
if (sta->flags & WLAN_STA_VHT)
hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
if (sta->flags & WLAN_STA_HE) {
hostapd_get_he_capab(hapd, sta->he_capab, &he_cap,
sta->he_capab_len);
}
#endif /* CONFIG_IEEE80211AX */
/*
* Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
* will be set when the ACK frame for the (Re)Association Response frame
* is processed (TX status driver event).
*/
if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability,
sta->supported_rates, sta->supported_rates_len,
sta->listen_interval,
sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
sta->flags & WLAN_STA_HE ? &he_cap : NULL,
sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
sta->he_6ghz_capab,
sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
set)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
"Could not %s STA to kernel driver",
set ? "set" : "add");
if (sta->added_unassoc) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
}
return -1;
}
sta->added_unassoc = 0;
return 0;
}
static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 status_code, int reassoc,
const u8 *ies, size_t ies_len, int rssi,
int omit_rsnxe)
{
int send_len;
u8 *buf;
size_t buflen;
struct ieee80211_mgmt *reply;
u8 *p;
u16 res = WLAN_STATUS_SUCCESS;
buflen = sizeof(struct ieee80211_mgmt) + 1024;
#ifdef CONFIG_FILS
if (sta && sta->fils_hlp_resp)
buflen += wpabuf_len(sta->fils_hlp_resp);
if (sta)
buflen += 150;
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
buflen += 150;
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
if (sta && sta->dpp_pfs)
buflen += 5 + sta->dpp_pfs->curve->prime_len;
#endif /* CONFIG_DPP2 */
buf = os_zalloc(buflen);
if (!buf) {
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto done;
}
reply = (struct ieee80211_mgmt *) buf;
reply->frame_control =
IEEE80211_FC(WLAN_FC_TYPE_MGMT,
(reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
WLAN_FC_STYPE_ASSOC_RESP));
os_memcpy(reply->da, addr, ETH_ALEN);
os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
send_len = IEEE80211_HDRLEN;
send_len += sizeof(reply->u.assoc_resp);
reply->u.assoc_resp.capab_info =
host_to_le16(hostapd_own_capab_info(hapd));
reply->u.assoc_resp.status_code = host_to_le16(status_code);
reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
BIT(14) | BIT(15));
/* Supported rates */
p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
/* Extended supported rates */
p = hostapd_eid_ext_supp_rates(hapd, p);
/* Radio measurement capabilities */
p = hostapd_eid_rm_enabled_capab(hapd, p, buf + buflen - p);
#ifdef CONFIG_MBO
if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS &&
rssi != 0) {
int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi;
p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p,
delta);
}
#endif /* CONFIG_MBO */
#ifdef CONFIG_IEEE80211R_AP
if (sta && status_code == WLAN_STATUS_SUCCESS) {
/* IEEE 802.11r: Mobility Domain Information, Fast BSS
* Transition Information, RSN, [RIC Response] */
p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
buf + buflen - p,
sta->auth_alg, ies, ies_len,
omit_rsnxe);
if (!p) {
wpa_printf(MSG_DEBUG,
"FT: Failed to write AssocResp IEs");
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto done;
}
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_FILS
if (sta && status_code == WLAN_STATUS_SUCCESS &&
(sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK))
p = wpa_auth_write_assoc_resp_fils(sta->wpa_sm, p,
buf + buflen - p,
ies, ies_len);
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
if (sta && status_code == WLAN_STATUS_SUCCESS &&
(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p,
buf + buflen - p,
ies, ies_len);
#endif /* CONFIG_OWE */
if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
p = hostapd_eid_ht_capabilities(hapd, p);
p = hostapd_eid_ht_operation(hapd, p);
#ifdef CONFIG_IEEE80211AC
if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac &&
!is_6ghz_op_class(hapd->iconf->op_class)) {
u32 nsts = 0, sta_nsts;
if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) {
struct ieee80211_vht_capabilities *capa;
nsts = (hapd->iface->conf->vht_capab >>
VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
capa = sta->vht_capabilities;
sta_nsts = (le_to_host32(capa->vht_capabilities_info) >>
VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
if (nsts < sta_nsts)
nsts = 0;
else
nsts = sta_nsts;
}
p = hostapd_eid_vht_capabilities(hapd, p, nsts);
p = hostapd_eid_vht_operation(hapd, p);
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax) {
p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
p = hostapd_eid_he_operation(hapd, p);
p = hostapd_eid_spatial_reuse(hapd, p);
p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
p = hostapd_eid_he_6ghz_band_cap(hapd, p);
}
#endif /* CONFIG_IEEE80211AX */
p = hostapd_eid_ext_capab(hapd, p);
p = hostapd_eid_bss_max_idle_period(hapd, p);
if (sta && sta->qos_map_enabled)
p = hostapd_eid_qos_map_set(hapd, p);
#ifdef CONFIG_FST
if (hapd->iface->fst_ies) {
os_memcpy(p, wpabuf_head(hapd->iface->fst_ies),
wpabuf_len(hapd->iface->fst_ies));
p += wpabuf_len(hapd->iface->fst_ies);
}
#endif /* CONFIG_FST */
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->rsnxe_override_ft &&
buf + buflen - p >=
(long int) wpabuf_len(hapd->conf->rsnxe_override_ft) &&
sta && sta->auth_alg == WLAN_AUTH_FT) {
wpa_printf(MSG_DEBUG, "TESTING: RSNXE FT override");
os_memcpy(p, wpabuf_head(hapd->conf->rsnxe_override_ft),
wpabuf_len(hapd->conf->rsnxe_override_ft));
p += wpabuf_len(hapd->conf->rsnxe_override_ft);
goto rsnxe_done;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (!omit_rsnxe)
p = hostapd_eid_rsnxe(hapd, p, buf + buflen - p);
#ifdef CONFIG_TESTING_OPTIONS
rsnxe_done:
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_OWE
if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE &&
!wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
struct wpabuf *pub;
pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
if (!pub) {
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto done;
}
/* OWE Diffie-Hellman Parameter element */
*p++ = WLAN_EID_EXTENSION; /* Element ID */
*p++ = 1 + 2 + wpabuf_len(pub); /* Length */
*p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
WPA_PUT_LE16(p, sta->owe_group);
p += 2;
os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
p += wpabuf_len(pub);
wpabuf_free(pub);
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
if (DPP_VERSION > 1 && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) {
os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie),
wpabuf_len(sta->dpp_pfs->ie));
p += wpabuf_len(sta->dpp_pfs->ie);
}
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_IEEE80211AC
if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
p = hostapd_eid_vendor_vht(hapd, p);
#endif /* CONFIG_IEEE80211AC */
if (sta && (sta->flags & WLAN_STA_WMM))
p = hostapd_eid_wmm(hapd, p);
#ifdef CONFIG_WPS
if (sta &&
((sta->flags & WLAN_STA_WPS) ||
((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) {
struct wpabuf *wps = wps_build_assoc_resp_ie();
if (wps) {
os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
p += wpabuf_len(wps);
wpabuf_free(wps);
}
}
#endif /* CONFIG_WPS */
if (sta && (sta->flags & WLAN_STA_MULTI_AP))
p = hostapd_eid_multi_ap(hapd, p);
#ifdef CONFIG_P2P
if (sta && sta->p2p_ie && hapd->p2p_group) {
struct wpabuf *p2p_resp_ie;
enum p2p_status_code status;
switch (status_code) {
case WLAN_STATUS_SUCCESS:
status = P2P_SC_SUCCESS;
break;
case WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
status = P2P_SC_FAIL_LIMIT_REACHED;
break;
default:
status = P2P_SC_FAIL_INVALID_PARAMS;
break;
}
p2p_resp_ie = p2p_group_assoc_resp_ie(hapd->p2p_group, status);
if (p2p_resp_ie) {
os_memcpy(p, wpabuf_head(p2p_resp_ie),
wpabuf_len(p2p_resp_ie));
p += wpabuf_len(p2p_resp_ie);
wpabuf_free(p2p_resp_ie);
}
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_P2P_MANAGER
if (hapd->conf->p2p & P2P_MANAGE)
p = hostapd_eid_p2p_manage(hapd, p);
#endif /* CONFIG_P2P_MANAGER */
p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
if (hapd->conf->assocresp_elements &&
(size_t) (buf + buflen - p) >=
wpabuf_len(hapd->conf->assocresp_elements)) {
os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
wpabuf_len(hapd->conf->assocresp_elements));
p += wpabuf_len(hapd->conf->assocresp_elements);
}
send_len += p - reply->u.assoc_resp.variable;
#ifdef CONFIG_FILS
if (sta &&
(sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK) &&
status_code == WLAN_STATUS_SUCCESS) {
struct ieee802_11_elems elems;
if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
ParseFailed || !elems.fils_session) {
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto done;
}
/* FILS Session */
*p++ = WLAN_EID_EXTENSION; /* Element ID */
*p++ = 1 + FILS_SESSION_LEN; /* Length */
*p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
os_memcpy(p, elems.fils_session, FILS_SESSION_LEN);
send_len += 2 + 1 + FILS_SESSION_LEN;
send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
buflen, sta->fils_hlp_resp);
if (send_len < 0) {
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto done;
}
}
#endif /* CONFIG_FILS */
if (hostapd_drv_send_mlme(hapd, reply, send_len, 0, NULL, 0, 0) < 0) {
wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
strerror(errno));
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
}
done:
os_free(buf);
return res;
}
#ifdef CONFIG_OWE
u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *owe_dh, u8 owe_dh_len,
u8 *owe_buf, size_t owe_buf_len, u16 *status)
{
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->own_ie_override) {
wpa_printf(MSG_DEBUG, "OWE: Using IE override");
*status = WLAN_STATUS_SUCCESS;
return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
owe_buf_len, NULL, 0);
}
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) {
wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching");
owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
owe_buf_len, NULL, 0);
*status = WLAN_STATUS_SUCCESS;
return owe_buf;
}
if (sta->owe_pmk && sta->external_dh_updated) {
wpa_printf(MSG_DEBUG, "OWE: Using previously derived PMK");
*status = WLAN_STATUS_SUCCESS;
return owe_buf;
}
*status = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len);
if (*status != WLAN_STATUS_SUCCESS)
return NULL;
owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf,
owe_buf_len, NULL, 0);
if (sta->owe_ecdh && owe_buf) {
struct wpabuf *pub;
pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
if (!pub) {
*status = WLAN_STATUS_UNSPECIFIED_FAILURE;
return owe_buf;
}
/* OWE Diffie-Hellman Parameter element */
*owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */
*owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */
*owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension
*/
WPA_PUT_LE16(owe_buf, sta->owe_group);
owe_buf += 2;
os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub));
owe_buf += wpabuf_len(pub);
wpabuf_free(pub);
}
return owe_buf;
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_FILS
void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
{
u16 reply_res;
wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR,
MAC2STR(sta->addr));
eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
if (!sta->fils_pending_assoc_req)
return;
reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
sta->fils_pending_assoc_is_reassoc,
sta->fils_pending_assoc_req,
sta->fils_pending_assoc_req_len, 0, 0);
os_free(sta->fils_pending_assoc_req);
sta->fils_pending_assoc_req = NULL;
sta->fils_pending_assoc_req_len = 0;
wpabuf_free(sta->fils_hlp_resp);
sta->fils_hlp_resp = NULL;
wpabuf_free(sta->hlp_dhcp_discover);
sta->hlp_dhcp_discover = NULL;
/*
* Remove the station in case transmission of a success response fails.
* At this point the station was already added associated to the driver.
*/
if (reply_res != WLAN_STATUS_SUCCESS)
hostapd_drv_sta_remove(hapd, sta->addr);
}
void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
{
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta = eloop_data;
wpa_printf(MSG_DEBUG,
"FILS: HLP response timeout - continue with association response for "
MACSTR, MAC2STR(sta->addr));
if (sta->fils_drv_assoc_finish)
hostapd_notify_assoc_fils_finish(hapd, sta);
else
fils_hlp_finish_assoc(hapd, sta);
}
#endif /* CONFIG_FILS */
static void handle_assoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
int reassoc, int rssi)
{
u16 capab_info, listen_interval, seq_ctrl, fc;
int resp = WLAN_STATUS_SUCCESS;
u16 reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
const u8 *pos;
int left, i;
struct sta_info *sta;
u8 *tmp = NULL;
#ifdef CONFIG_FILS
int delay_assoc = 0;
#endif /* CONFIG_FILS */
int omit_rsnxe = 0;
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
sizeof(mgmt->u.assoc_req))) {
wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)",
reassoc, (unsigned long) len);
return;
}
#ifdef CONFIG_TESTING_OPTIONS
if (reassoc) {
if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
drand48() < hapd->iconf->ignore_reassoc_probability) {
wpa_printf(MSG_INFO,
"TESTING: ignoring reassoc request from "
MACSTR, MAC2STR(mgmt->sa));
return;
}
} else {
if (hapd->iconf->ignore_assoc_probability > 0.0 &&
drand48() < hapd->iconf->ignore_assoc_probability) {
wpa_printf(MSG_INFO,
"TESTING: ignoring assoc request from "
MACSTR, MAC2STR(mgmt->sa));
return;
}
}
#endif /* CONFIG_TESTING_OPTIONS */
fc = le_to_host16(mgmt->frame_control);
seq_ctrl = le_to_host16(mgmt->seq_ctrl);
if (reassoc) {
capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
listen_interval = le_to_host16(
mgmt->u.reassoc_req.listen_interval);
wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
" capab_info=0x%02x listen_interval=%d current_ap="
MACSTR " seq_ctrl=0x%x%s",
MAC2STR(mgmt->sa), capab_info, listen_interval,
MAC2STR(mgmt->u.reassoc_req.current_ap),
seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
pos = mgmt->u.reassoc_req.variable;
} else {
capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
listen_interval = le_to_host16(
mgmt->u.assoc_req.listen_interval);
wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
" capab_info=0x%02x listen_interval=%d "
"seq_ctrl=0x%x%s",
MAC2STR(mgmt->sa), capab_info, listen_interval,
seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
pos = mgmt->u.assoc_req.variable;
}
sta = ap_get_sta(hapd, mgmt->sa);
#ifdef CONFIG_IEEE80211R_AP
if (sta && sta->auth_alg == WLAN_AUTH_FT &&
(sta->flags & WLAN_STA_AUTH) == 0) {
wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
"prior to authentication since it is using "
"over-the-DS FT", MAC2STR(mgmt->sa));
/*
* Mark station as authenticated, to avoid adding station
* entry in the driver as associated and not authenticated
*/
sta->flags |= WLAN_STA_AUTH;
} else
#endif /* CONFIG_IEEE80211R_AP */
if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode ==
HOSTAPD_MODE_IEEE80211AD) {
int acl_res;
struct radius_sta info;
acl_res = ieee802_11_allowed_address(hapd, mgmt->sa,
(const u8 *) mgmt,
len, &info);
if (acl_res == HOSTAPD_ACL_REJECT) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG,
"Ignore Association Request frame from "
MACSTR " due to ACL reject",
MAC2STR(mgmt->sa));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
if (acl_res == HOSTAPD_ACL_PENDING)
return;
/* DMG/IEEE 802.11ad does not use authentication.
* Allocate sta entry upon association. */
sta = ap_sta_add(hapd, mgmt->sa);
if (!sta) {
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Failed to add STA");
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
acl_res = ieee802_11_set_radius_info(
hapd, sta, acl_res, &info);
if (acl_res) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Skip authentication for DMG/IEEE 802.11ad");
sta->flags |= WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
sta->auth_alg = WLAN_AUTH_OPEN;
} else {
hostapd_logger(hapd, mgmt->sa,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Station tried to associate before authentication (aid=%d flags=0x%x)",
sta ? sta->aid : -1,
sta ? sta->flags : 0);
send_deauth(hapd, mgmt->sa,
WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
return;
}
}
if ((fc & WLAN_FC_RETRY) &&
sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
sta->last_seq_ctrl == seq_ctrl &&
sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
WLAN_FC_STYPE_ASSOC_REQ)) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Drop repeated association frame seq_ctrl=0x%x",
seq_ctrl);
return;
}
sta->last_seq_ctrl = seq_ctrl;
sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
WLAN_FC_STYPE_ASSOC_REQ;
if (hapd->tkip_countermeasures) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
if (listen_interval > hapd->conf->max_listen_interval) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Too large Listen Interval (%d)",
listen_interval);
resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE;
goto fail;
}
#ifdef CONFIG_MBO
if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
if (hapd->iconf->rssi_reject_assoc_rssi && rssi &&
rssi < hapd->iconf->rssi_reject_assoc_rssi &&
(sta->auth_rssi == 0 ||
sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) {
resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS;
goto fail;
}
#endif /* CONFIG_MBO */
/*
* sta->capability is used in check_assoc_ies() for RRM enabled
* capability element.
*/
sta->capability = capab_info;
#ifdef CONFIG_FILS
if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK) {
int res;
/* The end of the payload is encrypted. Need to decrypt it
* before parsing. */
tmp = os_memdup(pos, left);
if (!tmp) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
len, tmp, left);
if (res < 0) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
pos = tmp;
left = res;
}
#endif /* CONFIG_FILS */
/* followed by SSID and Supported rates; and HT capabilities if 802.11n
* is used */
resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
omit_rsnxe = !get_ie(pos, left, WLAN_EID_RSNX);
if (hostapd_get_aid(hapd, sta) < 0) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "No room for more AIDs");
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
sta->listen_interval = listen_interval;
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
sta->flags |= WLAN_STA_NONERP;
for (i = 0; i < sta->supported_rates_len; i++) {
if ((sta->supported_rates[i] & 0x7f) > 22) {
sta->flags &= ~WLAN_STA_NONERP;
break;
}
}
if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) {
sta->nonerp_set = 1;
hapd->iface->num_sta_non_erp++;
if (hapd->iface->num_sta_non_erp == 1)
ieee802_11_set_beacons(hapd->iface);
}
if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) &&
!sta->no_short_slot_time_set) {
sta->no_short_slot_time_set = 1;
hapd->iface->num_sta_no_short_slot_time++;
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode ==
HOSTAPD_MODE_IEEE80211G &&
hapd->iface->num_sta_no_short_slot_time == 1)
ieee802_11_set_beacons(hapd->iface);
}
if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
sta->flags |= WLAN_STA_SHORT_PREAMBLE;
else
sta->flags &= ~WLAN_STA_SHORT_PREAMBLE;
if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
!sta->no_short_preamble_set) {
sta->no_short_preamble_set = 1;
hapd->iface->num_sta_no_short_preamble++;
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G
&& hapd->iface->num_sta_no_short_preamble == 1)
ieee802_11_set_beacons(hapd->iface);
}
update_ht_state(hapd, sta);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"association OK (aid %d)", sta->aid);
/* Station will be marked associated, after it acknowledges AssocResp
*/
sta->flags |= WLAN_STA_ASSOC_REQ_OK;
if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) {
wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out "
"SA Query procedure", reassoc ? "re" : "");
/* TODO: Send a protected Disassociate frame to the STA using
* the old key and Reason Code "Previous Authentication no
* longer valid". Make sure this is only sent protected since
* unprotected frame would be received by the STA that is now
* trying to associate.
*/
}
/* Make sure that the previously registered inactivity timer will not
* remove the STA immediately. */
sta->timeout_next = STA_NULLFUNC;
#ifdef CONFIG_TAXONOMY
taxonomy_sta_info_assoc_req(hapd, sta, pos, left);
#endif /* CONFIG_TAXONOMY */
sta->pending_wds_enable = 0;
#ifdef CONFIG_FILS
if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK) {
if (fils_process_hlp(hapd, sta, pos, left) > 0)
delay_assoc = 1;
}
#endif /* CONFIG_FILS */
fail:
/*
* In case of a successful response, add the station to the driver.
* Otherwise, the kernel may ignore Data frames before we process the
* ACK frame (TX status). In case of a failure, this station will be
* removed.
*
* Note that this is not compliant with the IEEE 802.11 standard that
* states that a non-AP station should transition into the
* authenticated/associated state only after the station acknowledges
* the (Re)Association Response frame. However, still do this as:
*
* 1. In case the station does not acknowledge the (Re)Association
* Response frame, it will be removed.
* 2. Data frames will be dropped in the kernel until the station is
* set into authorized state, and there are no significant known
* issues with processing other non-Data Class 3 frames during this
* window.
*/
if (resp == WLAN_STATUS_SUCCESS && sta &&
add_associated_sta(hapd, sta, reassoc))
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
#ifdef CONFIG_FILS
if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS &&
eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) &&
sta->fils_pending_assoc_req) {
/* Do not reschedule fils_hlp_timeout in case the station
* retransmits (Re)Association Request frame while waiting for
* the previously started FILS HLP wait, so that the timeout can
* be determined from the first pending attempt. */
wpa_printf(MSG_DEBUG,
"FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to "
MACSTR, MAC2STR(sta->addr));
os_free(tmp);
return;
}
if (sta) {
eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
os_free(sta->fils_pending_assoc_req);
sta->fils_pending_assoc_req = NULL;
sta->fils_pending_assoc_req_len = 0;
wpabuf_free(sta->fils_hlp_resp);
sta->fils_hlp_resp = NULL;
}
if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
sta->fils_pending_assoc_req = tmp;
sta->fils_pending_assoc_req_len = left;
sta->fils_pending_assoc_is_reassoc = reassoc;
sta->fils_drv_assoc_finish = 0;
wpa_printf(MSG_DEBUG,
"FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
MACSTR, MAC2STR(sta->addr));
eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024,
fils_hlp_timeout, hapd, sta);
return;
}
#endif /* CONFIG_FILS */
if (resp >= 0)
reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc,
pos, left, rssi, omit_rsnxe);
os_free(tmp);
/*
* Remove the station in case transmission of a success response fails
* (the STA was added associated to the driver) or if the station was
* previously added unassociated.
*/
if (sta && ((reply_res != WLAN_STATUS_SUCCESS &&
resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
}
}
static void handle_disassoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
struct sta_info *sta;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)",
(unsigned long) len);
return;
}
wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d",
MAC2STR(mgmt->sa),
le_to_host16(mgmt->u.disassoc.reason_code));
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated",
MAC2STR(mgmt->sa));
return;
}
ap_sta_set_authorized(hapd, sta, 0);
sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
hostapd_set_sta_flags(hapd, sta);
wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "disassociated");
sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
/* Stop Accounting and IEEE 802.1X sessions, but leave the STA
* authenticated. */
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(hapd, sta);
if (sta->ipaddr)
hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
ap_sta_ip6addr_del(hapd, sta);
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
if (sta->timeout_next == STA_NULLFUNC ||
sta->timeout_next == STA_DISASSOC) {
sta->timeout_next = STA_DEAUTH;
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
hapd, sta);
}
mlme_disassociate_indication(
hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
/* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon
* disassociation. */
if (hapd->iface->current_mode &&
hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
sta->flags &= ~WLAN_STA_AUTH;
wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "deauthenticated");
ap_free_sta(hapd, sta);
}
}
static void handle_deauth(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
struct sta_info *sta;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "handle_deauth - too short "
"payload (len=%lu)", (unsigned long) len);
return;
}
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "deauthentication: STA=" MACSTR
" reason_code=%d",
MAC2STR(mgmt->sa), le_to_host16(mgmt->u.deauth.reason_code));
/* Clear the PTKSA cache entries for PASN */
ptksa_cache_flush(hapd->ptksa, mgmt->sa, WPA_CIPHER_NONE);
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL) {
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Station " MACSTR " trying "
"to deauthenticate, but it is not authenticated",
MAC2STR(mgmt->sa));
return;
}
ap_sta_set_authorized(hapd, sta, 0);
sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
WLAN_STA_ASSOC_REQ_OK);
hostapd_set_sta_flags(hapd, sta);
wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "deauthenticated");
mlme_deauthenticate_indication(
hapd, sta, le_to_host16(mgmt->u.deauth.reason_code));
sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
ap_free_sta(hapd, sta);
}
static void handle_beacon(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
struct hostapd_frame_info *fi)
{
struct ieee802_11_elems elems;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
(unsigned long) len);
return;
}
(void) ieee802_11_parse_elems(mgmt->u.beacon.variable,
len - (IEEE80211_HDRLEN +
sizeof(mgmt->u.beacon)), &elems,
0);
ap_list_process_beacon(hapd->iface, mgmt, &elems, fi);
}
static int robust_action_frame(u8 category)
{
return category != WLAN_ACTION_PUBLIC &&
category != WLAN_ACTION_HT;
}
static int handle_action(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
unsigned int freq)
{
struct sta_info *sta;
u8 *action __maybe_unused;
if (len < IEEE80211_HDRLEN + 2 + 1) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"handle_action - too short payload (len=%lu)",
(unsigned long) len);
return 0;
}
action = (u8 *) &mgmt->u.action.u;
wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR
" da " MACSTR " len %d freq %u",
mgmt->u.action.category, *action,
MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq);
sta = ap_get_sta(hapd, mgmt->sa);
if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
(sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
"frame (category=%u) from unassociated STA " MACSTR,
mgmt->u.action.category, MAC2STR(mgmt->sa));
return 0;
}
if (sta && (sta->flags & WLAN_STA_MFP) &&
!(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) &&
robust_action_frame(mgmt->u.action.category)) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Dropped unprotected Robust Action frame from "
"an MFP STA");
return 0;
}
if (sta) {
u16 fc = le_to_host16(mgmt->frame_control);
u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
if ((fc & WLAN_FC_RETRY) &&
sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
sta->last_seq_ctrl == seq_ctrl &&
sta->last_subtype == WLAN_FC_STYPE_ACTION) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"Drop repeated action frame seq_ctrl=0x%x",
seq_ctrl);
return 1;
}
sta->last_seq_ctrl = seq_ctrl;
sta->last_subtype = WLAN_FC_STYPE_ACTION;
}
switch (mgmt->u.action.category) {
#ifdef CONFIG_IEEE80211R_AP
case WLAN_ACTION_FT:
if (!sta ||
wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
len - IEEE80211_HDRLEN))
break;
return 1;
#endif /* CONFIG_IEEE80211R_AP */
case WLAN_ACTION_WMM:
hostapd_wmm_action(hapd, mgmt, len);
return 1;
case WLAN_ACTION_SA_QUERY:
ieee802_11_sa_query_action(hapd, mgmt, len);
return 1;
#ifdef CONFIG_WNM_AP
case WLAN_ACTION_WNM:
ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
return 1;
#endif /* CONFIG_WNM_AP */
#ifdef CONFIG_FST
case WLAN_ACTION_FST:
if (hapd->iface->fst)
fst_rx_action(hapd->iface->fst, mgmt, len);
else
wpa_printf(MSG_DEBUG,
"FST: Ignore FST Action frame - no FST attached");
return 1;
#endif /* CONFIG_FST */
case WLAN_ACTION_PUBLIC:
case WLAN_ACTION_PROTECTED_DUAL:
if (len >= IEEE80211_HDRLEN + 2 &&
mgmt->u.action.u.public_action.action ==
WLAN_PA_20_40_BSS_COEX) {
hostapd_2040_coex_action(hapd, mgmt, len);
return 1;
}
#ifdef CONFIG_DPP
if (len >= IEEE80211_HDRLEN + 6 &&
mgmt->u.action.u.vs_public_action.action ==
WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
OUI_WFA &&
mgmt->u.action.u.vs_public_action.variable[0] ==
DPP_OUI_TYPE) {
const u8 *pos, *end;
pos = mgmt->u.action.u.vs_public_action.oui;
end = ((const u8 *) mgmt) + len;
hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos,
freq);
return 1;
}
if (len >= IEEE80211_HDRLEN + 2 &&
(mgmt->u.action.u.public_action.action ==
WLAN_PA_GAS_INITIAL_RESP ||
mgmt->u.action.u.public_action.action ==
WLAN_PA_GAS_COMEBACK_RESP)) {
const u8 *pos, *end;
pos = &mgmt->u.action.u.public_action.action;
end = ((const u8 *) mgmt) + len;
gas_query_ap_rx(hapd->gas, mgmt->sa,
mgmt->u.action.category,
pos, end - pos, hapd->iface->freq);
return 1;
}
#endif /* CONFIG_DPP */
if (hapd->public_action_cb) {
hapd->public_action_cb(hapd->public_action_cb_ctx,
(u8 *) mgmt, len,
hapd->iface->freq);
}
if (hapd->public_action_cb2) {
hapd->public_action_cb2(hapd->public_action_cb2_ctx,
(u8 *) mgmt, len,
hapd->iface->freq);
}
if (hapd->public_action_cb || hapd->public_action_cb2)
return 1;
break;
case WLAN_ACTION_VENDOR_SPECIFIC:
if (hapd->vendor_action_cb) {
if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
(u8 *) mgmt, len,
hapd->iface->freq) == 0)
return 1;
}
break;
case WLAN_ACTION_RADIO_MEASUREMENT:
hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
return 1;
}
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"handle_action - unknown action category %d or invalid "
"frame",
mgmt->u.action.category);
if (!is_multicast_ether_addr(mgmt->da) &&
!(mgmt->u.action.category & 0x80) &&
!is_multicast_ether_addr(mgmt->sa)) {
struct ieee80211_mgmt *resp;
/*
* IEEE 802.11-REVma/D9.0 - 7.3.1.11
* Return the Action frame to the source without change
* except that MSB of the Category set to 1.
*/
wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action "
"frame back to sender");
resp = os_memdup(mgmt, len);
if (resp == NULL)
return 0;
os_memcpy(resp->da, resp->sa, ETH_ALEN);
os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
resp->u.action.category |= 0x80;
if (hostapd_drv_send_mlme(hapd, resp, len, 0, NULL, 0, 0) < 0) {
wpa_printf(MSG_ERROR, "IEEE 802.11: Failed to send "
"Action frame");
}
os_free(resp);
}
return 1;
}
/**
* notify_mgmt_frame - Notify of Management frames on the control interface
* @hapd: hostapd BSS data structure (the BSS to which the Management frame was
* sent to)
* @buf: Management frame data (starting from the IEEE 802.11 header)
* @len: Length of frame data in octets
*
* Notify the control interface of any received Management frame.
*/
static void notify_mgmt_frame(struct hostapd_data *hapd, const u8 *buf,
size_t len)
{
int hex_len = len * 2 + 1;
char *hex = os_malloc(hex_len);
if (hex) {
wpa_snprintf_hex(hex, hex_len, buf, len);
wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO,
AP_MGMT_FRAME_RECEIVED "buf=%s", hex);
os_free(hex);
}
}
/**
* ieee802_11_mgmt - process incoming IEEE 802.11 management frames
* @hapd: hostapd BSS data structure (the BSS to which the management frame was
* sent to)
* @buf: management frame data (starting from IEEE 802.11 header)
* @len: length of frame data in octets
* @fi: meta data about received frame (signal level, etc.)
*
* Process all incoming IEEE 802.11 management frames. This will be called for
* each frame received from the kernel driver through wlan#ap interface. In
* addition, it can be called to re-inserted pending frames (e.g., when using
* external RADIUS server as an MAC ACL).
*/
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi)
{
struct ieee80211_mgmt *mgmt;
u16 fc, stype;
int ret = 0;
unsigned int freq;
int ssi_signal = fi ? fi->ssi_signal : 0;
if (len < 24)
return 0;
if (fi && fi->freq)
freq = fi->freq;
else
freq = hapd->iface->freq;
mgmt = (struct ieee80211_mgmt *) buf;
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
if (is_multicast_ether_addr(mgmt->sa) ||
is_zero_ether_addr(mgmt->sa) ||
os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
/* Do not process any frames with unexpected/invalid SA so that
* we do not add any state for unexpected STA addresses or end
* up sending out frames to unexpected destination. */
wpa_printf(MSG_DEBUG, "MGMT: Invalid SA=" MACSTR
" in received frame - ignore this frame silently",
MAC2STR(mgmt->sa));
return 0;
}
if (stype == WLAN_FC_STYPE_BEACON) {
handle_beacon(hapd, mgmt, len, fi);
return 1;
}
if (!is_broadcast_ether_addr(mgmt->bssid) &&
#ifdef CONFIG_P2P
/* Invitation responses can be sent with the peer MAC as BSSID */
!((hapd->conf->p2p & P2P_GROUP_OWNER) &&
stype == WLAN_FC_STYPE_ACTION) &&
#endif /* CONFIG_P2P */
#ifdef CONFIG_MESH
!(hapd->conf->mesh & MESH_ENABLED) &&
#endif /* CONFIG_MESH */
os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
MAC2STR(mgmt->bssid));
return 0;
}
if (hapd->iface->state != HAPD_IFACE_ENABLED) {
wpa_printf(MSG_DEBUG, "MGMT: Ignore management frame while interface is not enabled (SA=" MACSTR " DA=" MACSTR " subtype=%u)",
MAC2STR(mgmt->sa), MAC2STR(mgmt->da), stype);
return 1;
}
if (stype == WLAN_FC_STYPE_PROBE_REQ) {
handle_probe_req(hapd, mgmt, len, ssi_signal);
return 1;
}
if ((!is_broadcast_ether_addr(mgmt->da) ||
stype != WLAN_FC_STYPE_ACTION) &&
os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"MGMT: DA=" MACSTR " not our address",
MAC2STR(mgmt->da));
return 0;
}
if (hapd->iconf->track_sta_max_num)
sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
if (hapd->conf->notify_mgmt_frames)
notify_mgmt_frame(hapd, buf, len);
switch (stype) {
case WLAN_FC_STYPE_AUTH:
wpa_printf(MSG_DEBUG, "mgmt::auth");
handle_auth(hapd, mgmt, len, ssi_signal, 0);
ret = 1;
break;
case WLAN_FC_STYPE_ASSOC_REQ:
wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
handle_assoc(hapd, mgmt, len, 0, ssi_signal);
ret = 1;
break;
case WLAN_FC_STYPE_REASSOC_REQ:
wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
handle_assoc(hapd, mgmt, len, 1, ssi_signal);
ret = 1;
break;
case WLAN_FC_STYPE_DISASSOC:
wpa_printf(MSG_DEBUG, "mgmt::disassoc");
handle_disassoc(hapd, mgmt, len);
ret = 1;
break;
case WLAN_FC_STYPE_DEAUTH:
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
handle_deauth(hapd, mgmt, len);
ret = 1;
break;
case WLAN_FC_STYPE_ACTION:
wpa_printf(MSG_DEBUG, "mgmt::action");
ret = handle_action(hapd, mgmt, len, freq);
break;
default:
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"unknown mgmt frame subtype %d", stype);
break;
}
return ret;
}
static void handle_auth_cb(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len, int ok)
{
u16 auth_alg, auth_transaction, status_code;
struct sta_info *sta;
bool success_status;
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR
" not found",
MAC2STR(mgmt->da));
return;
}
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
(unsigned long) len);
auth_alg = 0;
auth_transaction = 0;
status_code = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto fail;
}
auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
status_code = le_to_host16(mgmt->u.auth.status_code);
if (!ok) {
hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE,
"did not acknowledge authentication response");
goto fail;
}
if (status_code == WLAN_STATUS_SUCCESS &&
((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) ||
(auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO, "authenticated");
sta->flags |= WLAN_STA_AUTH;
if (sta->added_unassoc)
hostapd_set_sta_flags(hapd, sta);
return;
}
fail:
success_status = status_code == WLAN_STATUS_SUCCESS;
#ifdef CONFIG_SAE
if (auth_alg == WLAN_AUTH_SAE && auth_transaction == 1)
success_status = sae_status_success(hapd, status_code);
#endif /* CONFIG_SAE */
if (!success_status && sta->added_unassoc) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
}
}
static void hostapd_set_wds_encryption(struct hostapd_data *hapd,
struct sta_info *sta,
char *ifname_wds)
{
#ifdef CONFIG_WEP
int i;
struct hostapd_ssid *ssid = &hapd->conf->ssid;
if (hapd->conf->ieee802_1x || hapd->conf->wpa)
return;
for (i = 0; i < 4; i++) {
if (ssid->wep.key[i] &&
hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i,
0, i == ssid->wep.idx, NULL, 0,
ssid->wep.key[i], ssid->wep.len[i],
i == ssid->wep.idx ?
KEY_FLAG_GROUP_RX_TX_DEFAULT :
KEY_FLAG_GROUP_RX_TX)) {
wpa_printf(MSG_WARNING,
"Could not set WEP keys for WDS interface; %s",
ifname_wds);
break;
}
}
#endif /* CONFIG_WEP */
}
static void handle_assoc_cb(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len, int reassoc, int ok)
{
u16 status;
struct sta_info *sta;
int new_assoc = 1;
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
MAC2STR(mgmt->da));
return;
}
if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
sizeof(mgmt->u.assoc_resp))) {
wpa_printf(MSG_INFO,
"handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
reassoc, (unsigned long) len);
hostapd_drv_sta_remove(hapd, sta->addr);
return;
}
if (reassoc)
status = le_to_host16(mgmt->u.reassoc_resp.status_code);
else
status = le_to_host16(mgmt->u.assoc_resp.status_code);
if (!ok) {
hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"did not acknowledge association response");
sta->flags &= ~WLAN_STA_ASSOC_REQ_OK;
/* The STA is added only in case of SUCCESS */
if (status == WLAN_STATUS_SUCCESS)
hostapd_drv_sta_remove(hapd, sta->addr);
return;
}
if (status != WLAN_STATUS_SUCCESS)
return;
/* Stop previous accounting session, if one is started, and allocate
* new session id for the new session. */
accounting_sta_stop(hapd, sta);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"associated (aid %d)",
sta->aid);
if (sta->flags & WLAN_STA_ASSOC)
new_assoc = 0;
sta->flags |= WLAN_STA_ASSOC;
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
!hapd->conf->osen) ||
sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK ||
sta->auth_alg == WLAN_AUTH_FT) {
/*
* Open, static WEP, FT protocol, or FILS; no separate
* authorization step.
*/
ap_sta_set_authorized(hapd, sta, 1);
}
if (reassoc)
mlme_reassociate_indication(hapd, sta);
else
mlme_associate_indication(hapd, sta);
sta->sa_query_timed_out = 0;
if (sta->eapol_sm == NULL) {
/*
* This STA does not use RADIUS server for EAP authentication,
* so bind it to the selected VLAN interface now, since the
* interface selection is not going to change anymore.
*/
if (ap_sta_bind_vlan(hapd, sta) < 0)
return;
} else if (sta->vlan_id) {
/* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
if (ap_sta_bind_vlan(hapd, sta) < 0)
return;
}
hostapd_set_sta_flags(hapd, sta);
if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) {
wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA "
MACSTR " based on pending request",
MAC2STR(sta->addr));
sta->pending_wds_enable = 0;
sta->flags |= WLAN_STA_WDS;
}
if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA "
MACSTR " (aid %u)",
MAC2STR(sta->addr), sta->aid);
ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
sta->aid, 1);
if (!ret)
hostapd_set_wds_encryption(hapd, sta, ifname_wds);
}
if (sta->auth_alg == WLAN_AUTH_FT)
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
else
wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
#ifdef CONFIG_FILS
if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
sta->auth_alg == WLAN_AUTH_FILS_PK) &&
fils_set_tk(sta->wpa_sm) < 0) {
wpa_printf(MSG_DEBUG, "FILS: TK configuration failed");
ap_sta_disconnect(hapd, sta, sta->addr,
WLAN_REASON_UNSPECIFIED);
return;
}
#endif /* CONFIG_FILS */
if (sta->pending_eapol_rx) {
struct os_reltime now, age;
os_get_reltime(&now);
os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age);
if (age.sec == 0 && age.usec < 200000) {
wpa_printf(MSG_DEBUG,
"Process pending EAPOL frame that was received from " MACSTR " just before association notification",
MAC2STR(sta->addr));
ieee802_1x_receive(
hapd, mgmt->da,
wpabuf_head(sta->pending_eapol_rx->buf),
wpabuf_len(sta->pending_eapol_rx->buf));
}
wpabuf_free(sta->pending_eapol_rx->buf);
os_free(sta->pending_eapol_rx);
sta->pending_eapol_rx = NULL;
}
}
static void handle_deauth_cb(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len, int ok)
{
struct sta_info *sta;
if (is_multicast_ether_addr(mgmt->da))
return;
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
wpa_printf(MSG_DEBUG, "handle_deauth_cb: STA " MACSTR
" not found", MAC2STR(mgmt->da));
return;
}
if (ok)
wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged deauth",
MAC2STR(sta->addr));
else
wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
"deauth", MAC2STR(sta->addr));
ap_sta_deauth_cb(hapd, sta);
}
static void handle_disassoc_cb(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len, int ok)
{
struct sta_info *sta;
if (is_multicast_ether_addr(mgmt->da))
return;
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
wpa_printf(MSG_DEBUG, "handle_disassoc_cb: STA " MACSTR
" not found", MAC2STR(mgmt->da));
return;
}
if (ok)
wpa_printf(MSG_DEBUG, "STA " MACSTR " acknowledged disassoc",
MAC2STR(sta->addr));
else
wpa_printf(MSG_DEBUG, "STA " MACSTR " did not acknowledge "
"disassoc", MAC2STR(sta->addr));
ap_sta_disassoc_cb(hapd, sta);
}
static void handle_action_cb(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len, int ok)
{
struct sta_info *sta;
const struct rrm_measurement_report_element *report;
if (is_multicast_ether_addr(mgmt->da))
return;
#ifdef CONFIG_DPP
if (len >= IEEE80211_HDRLEN + 6 &&
mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
mgmt->u.action.u.vs_public_action.action ==
WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
OUI_WFA &&
mgmt->u.action.u.vs_public_action.variable[0] ==
DPP_OUI_TYPE) {
const u8 *pos, *end;
pos = &mgmt->u.action.u.vs_public_action.variable[1];
end = ((const u8 *) mgmt) + len;
hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok);
return;
}
if (len >= IEEE80211_HDRLEN + 2 &&
mgmt->u.action.category == WLAN_ACTION_PUBLIC &&
(mgmt->u.action.u.public_action.action ==
WLAN_PA_GAS_INITIAL_REQ ||
mgmt->u.action.u.public_action.action ==
WLAN_PA_GAS_COMEBACK_REQ)) {
const u8 *pos, *end;
pos = mgmt->u.action.u.public_action.variable;
end = ((const u8 *) mgmt) + len;
gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok);
return;
}
#endif /* CONFIG_DPP */
sta = ap_get_sta(hapd, mgmt->da);
if (!sta) {
wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
" not found", MAC2STR(mgmt->da));
return;
}
if (len < 24 + 5 + sizeof(*report))
return;
report = (const struct rrm_measurement_report_element *)
&mgmt->u.action.u.rrm.variable[2];
if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT &&
mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST &&
report->eid == WLAN_EID_MEASURE_REQUEST &&
report->len >= 3 &&
report->type == MEASURE_TYPE_BEACON)
hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
}
/**
* ieee802_11_mgmt_cb - Process management frame TX status callback
* @hapd: hostapd BSS data structure (the BSS from which the management frame
* was sent from)
* @buf: management frame data (starting from IEEE 802.11 header)
* @len: length of frame data in octets
* @stype: management frame subtype from frame control field
* @ok: Whether the frame was ACK'ed
*/
void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
u16 stype, int ok)
{
const struct ieee80211_mgmt *mgmt;
mgmt = (const struct ieee80211_mgmt *) buf;
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->ext_mgmt_frame_handling) {
size_t hex_len = 2 * len + 1;
char *hex = os_malloc(hex_len);
if (hex) {
wpa_snprintf_hex(hex, hex_len, buf, len);
wpa_msg(hapd->msg_ctx, MSG_INFO,
"MGMT-TX-STATUS stype=%u ok=%d buf=%s",
stype, ok, hex);
os_free(hex);
}
return;
}
#endif /* CONFIG_TESTING_OPTIONS */
switch (stype) {
case WLAN_FC_STYPE_AUTH:
wpa_printf(MSG_DEBUG, "mgmt::auth cb");
handle_auth_cb(hapd, mgmt, len, ok);
break;
case WLAN_FC_STYPE_ASSOC_RESP:
wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb");
handle_assoc_cb(hapd, mgmt, len, 0, ok);
break;
case WLAN_FC_STYPE_REASSOC_RESP:
wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb");
handle_assoc_cb(hapd, mgmt, len, 1, ok);
break;
case WLAN_FC_STYPE_PROBE_RESP:
wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok);
break;
case WLAN_FC_STYPE_DEAUTH:
wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
handle_deauth_cb(hapd, mgmt, len, ok);
break;
case WLAN_FC_STYPE_DISASSOC:
wpa_printf(MSG_DEBUG, "mgmt::disassoc cb");
handle_disassoc_cb(hapd, mgmt, len, ok);
break;
case WLAN_FC_STYPE_ACTION:
wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
handle_action_cb(hapd, mgmt, len, ok);
break;
default:
wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
break;
}
}
int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen)
{
/* TODO */
return 0;
}
int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
char *buf, size_t buflen)
{
/* TODO */
return 0;
}
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len, int ack)
{
struct sta_info *sta;
struct hostapd_iface *iface = hapd->iface;
sta = ap_get_sta(hapd, addr);
if (sta == NULL && iface->num_bss > 1) {
size_t j;
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
sta = ap_get_sta(hapd, addr);
if (sta)
break;
}
}
if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))
return;
if (sta->flags & WLAN_STA_PENDING_POLL) {
wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
"activity poll", MAC2STR(sta->addr),
ack ? "ACKed" : "did not ACK");
if (ack)
sta->flags &= ~WLAN_STA_PENDING_POLL;
}
ieee802_1x_tx_status(hapd, sta, buf, len, ack);
}
void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
const u8 *data, size_t len, int ack)
{
struct sta_info *sta;
struct hostapd_iface *iface = hapd->iface;
sta = ap_get_sta(hapd, dst);
if (sta == NULL && iface->num_bss > 1) {
size_t j;
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
sta = ap_get_sta(hapd, dst);
if (sta)
break;
}
}
if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
wpa_printf(MSG_DEBUG, "Ignore TX status for Data frame to STA "
MACSTR " that is not currently associated",
MAC2STR(dst));
return;
}
ieee802_1x_eapol_tx_status(hapd, sta, data, len, ack);
}
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr)
{
struct sta_info *sta;
struct hostapd_iface *iface = hapd->iface;
sta = ap_get_sta(hapd, addr);
if (sta == NULL && iface->num_bss > 1) {
size_t j;
for (j = 0; j < iface->num_bss; j++) {
hapd = iface->bss[j];
sta = ap_get_sta(hapd, addr);
if (sta)
break;
}
}
if (sta == NULL)
return;
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR,
MAC2STR(sta->addr));
if (!(sta->flags & WLAN_STA_PENDING_POLL))
return;
wpa_printf(MSG_DEBUG, "STA " MACSTR " ACKed pending "
"activity poll", MAC2STR(sta->addr));
sta->flags &= ~WLAN_STA_PENDING_POLL;
}
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
int wds)
{
struct sta_info *sta;
sta = ap_get_sta(hapd, src);
if (sta &&
((sta->flags & WLAN_STA_ASSOC) ||
((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
if (!hapd->conf->wds_sta)
return;
if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) ==
WLAN_STA_ASSOC_REQ_OK) {
wpa_printf(MSG_DEBUG,
"Postpone 4-address WDS mode enabling for STA "
MACSTR " since TX status for AssocResp is not yet known",
MAC2STR(sta->addr));
sta->pending_wds_enable = 1;
return;
}
if (wds && !(sta->flags & WLAN_STA_WDS)) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
"STA " MACSTR " (aid %u)",
MAC2STR(sta->addr), sta->aid);
sta->flags |= WLAN_STA_WDS;
ret = hostapd_set_wds_sta(hapd, ifname_wds,
sta->addr, sta->aid, 1);
if (!ret)
hostapd_set_wds_encryption(hapd, sta,
ifname_wds);
}
return;
}
wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
MACSTR, MAC2STR(src));
if (is_multicast_ether_addr(src) || is_zero_ether_addr(src) ||
os_memcmp(src, hapd->own_addr, ETH_ALEN) == 0) {
/* Broadcast bit set in SA or unexpected SA?! Ignore the frame
* silently. */
return;
}
if (sta && (sta->flags & WLAN_STA_ASSOC_REQ_OK)) {
wpa_printf(MSG_DEBUG, "Association Response to the STA has "
"already been sent, but no TX status yet known - "
"ignore Class 3 frame issue with " MACSTR,
MAC2STR(src));
return;
}
if (sta && (sta->flags & WLAN_STA_AUTH))
hostapd_drv_sta_disassoc(
hapd, src,
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
else
hostapd_drv_sta_deauth(
hapd, src,
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
}
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
{
struct hostapd_iface *iface = hapd->iface;
struct hostapd_config *iconf = iface->conf;
struct hostapd_hw_modes *mode = iface->current_mode;
struct hostapd_channel_data *chan;
int dfs, i;
u8 channel, tx_pwr_count, local_pwr_constraint;
int max_tx_power;
u8 tx_pwr;
if (!mode)
return eid;
if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
return eid;
for (i = 0; i < mode->num_channels; i++) {
if (mode->channels[i].freq == iface->freq)
break;
}
if (i == mode->num_channels)
return eid;
switch (hostapd_get_oper_chwidth(iconf)) {
case CHANWIDTH_USE_HT:
if (iconf->secondary_channel == 0) {
/* Max Transmit Power count = 0 (20 MHz) */
tx_pwr_count = 0;
} else {
/* Max Transmit Power count = 1 (20, 40 MHz) */
tx_pwr_count = 1;
}
break;
case CHANWIDTH_80MHZ:
/* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
tx_pwr_count = 2;
break;
case CHANWIDTH_80P80MHZ:
case CHANWIDTH_160MHZ:
/* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
tx_pwr_count = 3;
break;
default:
return eid;
}
/*
* Below local_pwr_constraint logic is referred from
* hostapd_eid_pwr_constraint.
*
* Check if DFS is required by regulatory.
*/
dfs = hostapd_is_dfs_required(hapd->iface);
if (dfs < 0)
dfs = 0;
/*
* In order to meet regulations when TPC is not implemented using
* a transmit power that is below the legal maximum (including any
* mitigation factor) should help. In this case, indicate 3 dB below
* maximum allowed transmit power.
*/
if (hapd->iconf->local_pwr_constraint == -1)
local_pwr_constraint = (dfs == 0) ? 0 : 3;
else
local_pwr_constraint = hapd->iconf->local_pwr_constraint;
/*
* A STA that is not an AP shall use a transmit power less than or
* equal to the local maximum transmit power level for the channel.
* The local maximum transmit power can be calculated from the formula:
* local max TX pwr = max TX pwr - local pwr constraint
* Where max TX pwr is maximum transmit power level specified for
* channel in Country element and local pwr constraint is specified
* for channel in this Power Constraint element.
*/
chan = &mode->channels[i];
max_tx_power = chan->max_tx_power - local_pwr_constraint;
/*
* Local Maximum Transmit power is encoded as two's complement
* with a 0.5 dB step.
*/
max_tx_power *= 2; /* in 0.5 dB steps */
if (max_tx_power > 127) {
/* 63.5 has special meaning of 63.5 dBm or higher */
max_tx_power = 127;
}
if (max_tx_power < -128)
max_tx_power = -128;
if (max_tx_power < 0)
tx_pwr = 0x80 + max_tx_power + 128;
else
tx_pwr = max_tx_power;
*eid++ = WLAN_EID_TRANSMIT_POWER_ENVELOPE;
*eid++ = 2 + tx_pwr_count;
/*
* Max Transmit Power count and
* Max Transmit Power units = 0 (EIRP)
*/
*eid++ = tx_pwr_count;
for (i = 0; i <= tx_pwr_count; i++)
*eid++ = tx_pwr;
return eid;
}
u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
{
u8 bw, chan1, chan2 = 0;
int freq1;
if (!hapd->cs_freq_params.channel ||
(!hapd->cs_freq_params.vht_enabled &&
!hapd->cs_freq_params.he_enabled))
return eid;
/* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
switch (hapd->cs_freq_params.bandwidth) {
case 40:
bw = 0;
break;
case 80:
/* check if it's 80+80 */
if (!hapd->cs_freq_params.center_freq2)
bw = 1;
else
bw = 3;
break;
case 160:
bw = 2;
break;
default:
/* not valid VHT bandwidth or not in CSA */
return eid;
}
freq1 = hapd->cs_freq_params.center_freq1 ?
hapd->cs_freq_params.center_freq1 :
hapd->cs_freq_params.freq;
if (ieee80211_freq_to_chan(freq1, &chan1) !=
HOSTAPD_MODE_IEEE80211A)
return eid;
if (hapd->cs_freq_params.center_freq2 &&
ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
&chan2) != HOSTAPD_MODE_IEEE80211A)
return eid;
*eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
*eid++ = 5; /* Length of Channel Switch Wrapper */
*eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
*eid++ = bw; /* New Channel Width */
*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
return eid;
}
+
+static size_t hostapd_eid_nr_db_len(struct hostapd_data *hapd,
+ size_t *current_len)
+{
+ struct hostapd_neighbor_entry *nr;
+ size_t total_len = 0, len = *current_len;
+
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+ list) {
+ if (!nr->nr || wpabuf_len(nr->nr) < 12)
+ continue;
+
+ if (nr->short_ssid == hapd->conf->ssid.short_ssid)
+ continue;
+
+ /* Start a new element */
+ if (!len ||
+ len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+ len = RNR_HEADER_LEN;
+ total_len += RNR_HEADER_LEN;
+ }
+
+ len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN;
+ total_len += RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN;
+ }
+
+ *current_len = len;
+ return total_len;
+}
+
+
+static size_t hostapd_eid_rnr_iface_len(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ size_t *current_len)
+{
+ size_t total_len = 0, len = *current_len;
+ int tbtt_count = 0;
+ size_t i, start = 0;
+
+ while (start < hapd->iface->num_bss) {
+ if (!len ||
+ len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+ len = RNR_HEADER_LEN;
+ total_len += RNR_HEADER_LEN;
+ }
+
+ len += RNR_TBTT_HEADER_LEN;
+ total_len += RNR_TBTT_HEADER_LEN;
+
+ for (i = start; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
+
+ if (!bss || !bss->conf || !bss->started)
+ continue;
+
+ if (bss == reporting_hapd ||
+ bss->conf->ignore_broadcast_ssid)
+ continue;
+
+ if (len + RNR_TBTT_INFO_LEN > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ break;
+
+ len += RNR_TBTT_INFO_LEN;
+ total_len += RNR_TBTT_INFO_LEN;
+ tbtt_count++;
+ }
+ start = i;
+ }
+
+ if (!tbtt_count)
+ total_len = 0;
+ else
+ *current_len = len;
+
+ return total_len;
+}
+
+
+enum colocation_mode {
+ NO_COLOCATED_6GHZ,
+ STANDALONE_6GHZ,
+ COLOCATED_6GHZ,
+ COLOCATED_LOWER_BAND,
+};
+
+static enum colocation_mode get_colocation_mode(struct hostapd_data *hapd)
+{
+ u8 i;
+ bool is_6ghz = is_6ghz_op_class(hapd->iconf->op_class);
+
+ if (!hapd->iface || !hapd->iface->interfaces)
+ return NO_COLOCATED_6GHZ;
+
+ if (is_6ghz && hapd->iface->interfaces->count == 1)
+ return STANDALONE_6GHZ;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ struct hostapd_iface *iface;
+ bool is_colocated_6ghz;
+
+ iface = hapd->iface->interfaces->iface[i];
+ if (iface == hapd->iface || !iface || !iface->conf)
+ continue;
+
+ is_colocated_6ghz = is_6ghz_op_class(iface->conf->op_class);
+ if (!is_6ghz && is_colocated_6ghz)
+ return COLOCATED_LOWER_BAND;
+ if (is_6ghz && !is_colocated_6ghz)
+ return COLOCATED_6GHZ;
+ }
+
+ if (is_6ghz)
+ return STANDALONE_6GHZ;
+
+ return NO_COLOCATED_6GHZ;
+}
+
+
+static size_t hostapd_eid_rnr_colocation_len(struct hostapd_data *hapd,
+ size_t *current_len)
+{
+ struct hostapd_iface *iface;
+ size_t len = 0;
+ size_t i;
+
+ if (!hapd->iface || !hapd->iface->interfaces)
+ return 0;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+
+ if (iface == hapd->iface ||
+ !is_6ghz_op_class(iface->conf->op_class))
+ continue;
+
+ len += hostapd_eid_rnr_iface_len(iface->bss[0], hapd,
+ current_len);
+ }
+
+ return len;
+}
+
+
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
+{
+ size_t total_len = 0, current_len = 0;
+ enum colocation_mode mode = get_colocation_mode(hapd);
+
+ switch (type) {
+ case WLAN_FC_STYPE_BEACON:
+ if (hapd->conf->rnr)
+ total_len += hostapd_eid_nr_db_len(hapd, &current_len);
+ /* fallthrough */
+
+ case WLAN_FC_STYPE_PROBE_RESP:
+ if (mode == COLOCATED_LOWER_BAND)
+ total_len += hostapd_eid_rnr_colocation_len(
+ hapd, &current_len);
+
+ if (hapd->conf->rnr && hapd->iface->num_bss > 1)
+ total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
+ &current_len);
+ break;
+
+ case WLAN_FC_STYPE_ACTION:
+ if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
+ total_len += hostapd_eid_rnr_iface_len(hapd, hapd,
+ &current_len);
+ break;
+
+ default:
+ break;
+ }
+
+ return total_len;
+}
+
+
+static u8 * hostapd_eid_nr_db(struct hostapd_data *hapd, u8 *eid,
+ size_t *current_len)
+{
+ struct hostapd_neighbor_entry *nr;
+ size_t len = *current_len;
+ u8 *size_offset = (eid - len) + 1;
+
+ dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+ list) {
+ if (!nr->nr || wpabuf_len(nr->nr) < 12)
+ continue;
+
+ if (nr->short_ssid == hapd->conf->ssid.short_ssid)
+ continue;
+
+ /* Start a new element */
+ if (!len ||
+ len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+ *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
+ size_offset = eid++;
+ len = RNR_HEADER_LEN;
+ }
+
+ /* TBTT Information Header subfield (2 octets) */
+ *eid++ = 0;
+ /* TBTT Information Length */
+ *eid++ = RNR_TBTT_INFO_LEN;
+ /* Operating Class */
+ *eid++ = wpabuf_head_u8(nr->nr)[10];
+ /* Channel Number */
+ *eid++ = wpabuf_head_u8(nr->nr)[11];
+ len += RNR_TBTT_HEADER_LEN;
+ /* TBTT Information Set */
+ /* TBTT Information field */
+ /* Neighbor AP TBTT Offset */
+ *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+ /* BSSID */
+ os_memcpy(eid, nr->bssid, ETH_ALEN);
+ eid += ETH_ALEN;
+ /* Short SSID */
+ os_memcpy(eid, &nr->short_ssid, 4);
+ eid += 4;
+ /* BSS parameters */
+ *eid++ = nr->bss_parameters;
+ /* 20 MHz PSD */
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
+ len += RNR_TBTT_INFO_LEN;
+ *size_offset = (eid - size_offset) - 1;
+ }
+
+ *current_len = len;
+ return eid;
+}
+
+
+static u8 * hostapd_eid_rnr_iface(struct hostapd_data *hapd,
+ struct hostapd_data *reporting_hapd,
+ u8 *eid, size_t *current_len)
+{
+ struct hostapd_data *bss;
+ struct hostapd_iface *iface = hapd->iface;
+ size_t i, start = 0;
+ size_t len = *current_len;
+ u8 *tbtt_count_pos, *eid_start = eid, *size_offset = (eid - len) + 1;
+ u8 tbtt_count = 0, op_class, channel, bss_param;
+
+ if (!(iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) || !iface->freq)
+ return eid;
+
+ if (ieee80211_freq_to_channel_ext(iface->freq,
+ hapd->iconf->secondary_channel,
+ hostapd_get_oper_chwidth(hapd->iconf),
+ &op_class, &channel) ==
+ NUM_HOSTAPD_MODES)
+ return eid;
+
+ while (start < iface->num_bss) {
+ if (!len ||
+ len + RNR_TBTT_HEADER_LEN + RNR_TBTT_INFO_LEN > 255) {
+ eid_start = eid;
+ *eid++ = WLAN_EID_REDUCED_NEIGHBOR_REPORT;
+ size_offset = eid++;
+ len = RNR_HEADER_LEN;
+ tbtt_count = 0;
+ }
+
+ tbtt_count_pos = eid++;
+ *eid++ = RNR_TBTT_INFO_LEN;
+ *eid++ = op_class;
+ *eid++ = hapd->iconf->channel;
+ len += RNR_TBTT_HEADER_LEN;
+
+ for (i = start; i < iface->num_bss; i++) {
+ bss_param = 0;
+ bss = iface->bss[i];
+ if (!bss || !bss->conf || !bss->started)
+ continue;
+
+ if (bss == reporting_hapd ||
+ bss->conf->ignore_broadcast_ssid)
+ continue;
+
+ if (len + RNR_TBTT_INFO_LEN > 255 ||
+ tbtt_count >= RNR_TBTT_INFO_COUNT_MAX)
+ break;
+
+ *eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
+ os_memcpy(eid, bss->conf->bssid, ETH_ALEN);
+ eid += ETH_ALEN;
+ os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
+ eid += 4;
+ if (bss->conf->ssid.short_ssid ==
+ reporting_hapd->conf->ssid.short_ssid)
+ bss_param |= RNR_BSS_PARAM_SAME_SSID;
+
+ if (is_6ghz_op_class(hapd->iconf->op_class) &&
+ bss->conf->unsol_bcast_probe_resp_interval)
+ bss_param |=
+ RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE;
+
+ bss_param |= RNR_BSS_PARAM_CO_LOCATED;
+
+ *eid++ = bss_param;
+ *eid++ = RNR_20_MHZ_PSD_MAX_TXPOWER - 1;
+ len += RNR_TBTT_INFO_LEN;
+ tbtt_count += 1;
+ }
+
+ start = i;
+ *tbtt_count_pos = RNR_TBTT_INFO_COUNT(tbtt_count - 1);
+ *size_offset = (eid - size_offset) - 1;
+ }
+
+ if (tbtt_count == 0)
+ return eid_start;
+
+ *current_len = len;
+ return eid;
+}
+
+
+static u8 * hostapd_eid_rnr_colocation(struct hostapd_data *hapd, u8 *eid,
+ size_t *current_len)
+{
+ struct hostapd_iface *iface;
+ size_t i;
+
+ if (!hapd->iface || !hapd->iface->interfaces)
+ return eid;
+
+ for (i = 0; i < hapd->iface->interfaces->count; i++) {
+ iface = hapd->iface->interfaces->iface[i];
+
+ if (iface == hapd->iface ||
+ !is_6ghz_op_class(iface->conf->op_class))
+ continue;
+
+ eid = hostapd_eid_rnr_iface(iface->bss[0], hapd, eid,
+ current_len);
+ }
+
+ return eid;
+}
+
+
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+{
+ u8 *eid_start = eid;
+ size_t current_len = 0;
+ enum colocation_mode mode = get_colocation_mode(hapd);
+
+ switch (type) {
+ case WLAN_FC_STYPE_BEACON:
+ if (hapd->conf->rnr)
+ eid = hostapd_eid_nr_db(hapd, eid, &current_len);
+ /* fallthrough */
+
+ case WLAN_FC_STYPE_PROBE_RESP:
+ if (mode == COLOCATED_LOWER_BAND)
+ eid = hostapd_eid_rnr_colocation(hapd, eid,
+ &current_len);
+
+ if (hapd->conf->rnr && hapd->iface->num_bss > 1)
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+ &current_len);
+ break;
+
+ case WLAN_FC_STYPE_ACTION:
+ if (hapd->iface->num_bss > 1 && mode == STANDALONE_6GHZ)
+ eid = hostapd_eid_rnr_iface(hapd, hapd, eid,
+ &current_len);
+ break;
+
+ default:
+ return eid_start;
+ }
+
+ if (eid == eid_start + 2)
+ return eid_start;
+
+ return eid;
+}
+
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/contrib/wpa/src/ap/ieee802_11.h b/contrib/wpa/src/ap/ieee802_11.h
index ea8c60846936..c59ad5e38e92 100644
--- a/contrib/wpa/src/ap/ieee802_11.h
+++ b/contrib/wpa/src/ap/ieee802_11.h
@@ -1,198 +1,200 @@
/*
* hostapd / IEEE 802.11 Management
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_11_H
#define IEEE802_11_H
struct hostapd_iface;
struct hostapd_data;
struct sta_info;
struct hostapd_frame_info;
struct ieee80211_ht_capabilities;
struct ieee80211_vht_capabilities;
struct ieee80211_mgmt;
struct radius_sta;
enum ieee80211_op_mode;
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi);
void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
u16 stype, int ok);
void hostapd_2040_coex_action(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len);
#ifdef NEED_AP_MLME
int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
char *buf, size_t buflen);
#else /* NEED_AP_MLME */
static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
size_t buflen)
{
return 0;
}
static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
struct sta_info *sta,
char *buf, size_t buflen)
{
return 0;
}
#endif /* NEED_AP_MLME */
u16 hostapd_own_capab_info(struct hostapd_data *hapd);
void ap_ht2040_timeout(void *eloop_data, void *user_data);
u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
size_t len);
u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts);
u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
enum ieee80211_op_mode opmode);
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid);
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id);
void hostapd_get_ht_capab(struct hostapd_data *hapd,
struct ieee80211_ht_capabilities *ht_cap,
struct ieee80211_ht_capabilities *neg_ht_cap);
void hostapd_get_vht_capab(struct hostapd_data *hapd,
struct ieee80211_vht_capabilities *vht_cap,
struct ieee80211_vht_capabilities *neg_vht_cap);
void hostapd_get_he_capab(struct hostapd_data *hapd,
const struct ieee80211_he_capabilities *he_cap,
struct ieee80211_he_capabilities *neg_he_cap,
size_t he_capab_len);
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab);
u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ie, size_t len);
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_capab);
u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_oper);
u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_opmode);
u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
enum ieee80211_op_mode opmode, const u8 *he_capab,
size_t he_capab_len);
u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *he_6ghz_capab);
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
enum ieee80211_op_mode mode);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len, int ack);
void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
const u8 *data, size_t len, int ack);
void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
int wds);
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
struct sta_info *sta, u8 *eid);
void ieee802_11_sa_query_action(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt,
size_t len);
u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_time_adv(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
int hostapd_update_time_adv(struct hostapd_data *hapd);
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
#ifdef CONFIG_SAE
void sae_clear_retransmit_timer(struct hostapd_data *hapd,
struct sta_info *sta);
void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta);
#else /* CONFIG_SAE */
static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
struct sta_info *sta)
{
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_MBO
u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len);
u8 hostapd_mbo_ie_len(struct hostapd_data *hapd);
u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
size_t len, int delta);
#else /* CONFIG_MBO */
static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid,
size_t len)
{
return eid;
}
static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
{
return 0;
}
#endif /* CONFIG_MBO */
void ap_copy_sta_supp_op_classes(struct sta_info *sta,
const u8 *supp_op_classes,
size_t supp_op_classes_len);
u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid);
void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
struct sta_info *sta, int success,
struct wpabuf *erp_resp,
const u8 *msk, size_t msk_len);
u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *owe_dh, u8 owe_dh_len,
u8 *owe_buf, size_t owe_buf_len, u16 *status);
u16 owe_process_rsn_ie(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *rsn_ie, size_t rsn_ie_len,
const u8 *owe_dh, size_t owe_dh_len);
u16 owe_validate_request(struct hostapd_data *hapd, const u8 *peer,
const u8 *rsn_ie, size_t rsn_ie_len,
const u8 *owe_dh, size_t owe_dh_len);
void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *pos, size_t len, u16 auth_alg,
u16 auth_transaction, u16 status_code,
void (*cb)(struct hostapd_data *hapd,
struct sta_info *sta,
u16 resp, struct wpabuf *data, int pub));
size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd);
u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len);
size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd);
u8 * hostapd_eid_dpp_cc(struct hostapd_data *hapd, u8 *eid, size_t len);
int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
int ap_seg1_idx, int *bandwidth, int *seg1_idx);
void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len);
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
#endif /* IEEE802_11_H */
diff --git a/contrib/wpa/src/ap/ieee802_11_he.c b/contrib/wpa/src/ap/ieee802_11_he.c
index cbe5e639588f..6cd6c90dcf02 100644
--- a/contrib/wpa/src/ap/ieee802_11_he.c
+++ b/contrib/wpa/src/ap/ieee802_11_he.c
@@ -1,519 +1,522 @@
/*
* hostapd / IEEE 802.11ax HE
* Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
* Copyright (c) 2019 John Crispin <john@phrozen.org>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "hostapd.h"
#include "ap_config.h"
#include "beacon.h"
#include "sta_info.h"
#include "ieee802_11.h"
#include "dfs.h"
static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
{
u8 sz = 0, ru;
if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
return 0;
ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
while (ru) {
if (ru & 0x1)
sz++;
ru >>= 1;
}
sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
sz = (sz * 6) + 7;
if (sz % 8)
sz += 8;
sz /= 8;
return sz;
}
static u8 ieee80211_he_mcs_set_size(const u8 *phy_cap_info)
{
u8 sz = 4;
if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)
sz += 4;
if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
sz += 4;
return sz;
}
static int ieee80211_invalid_he_cap_size(const u8 *buf, size_t len)
{
struct ieee80211_he_capabilities *cap;
size_t cap_len;
cap = (struct ieee80211_he_capabilities *) buf;
cap_len = sizeof(*cap) - sizeof(cap->optional);
if (len < cap_len)
return 1;
cap_len += ieee80211_he_mcs_set_size(cap->he_phy_capab_info);
if (len < cap_len)
return 1;
cap_len += ieee80211_he_ppet_size(buf[cap_len], cap->he_phy_capab_info);
return len != cap_len;
}
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
enum ieee80211_op_mode opmode)
{
struct ieee80211_he_capabilities *cap;
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
u8 *pos = eid;
u8 ie_size = 0, mcs_nss_size = 4, ppet_size = 0;
if (!mode)
return eid;
ie_size = sizeof(*cap) - sizeof(cap->optional);
ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
mode->he_capab[opmode].phy_cap);
switch (hapd->iface->conf->he_oper_chwidth) {
case CHANWIDTH_80P80MHZ:
he_oper_chwidth |=
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
mcs_nss_size += 4;
/* fall through */
case CHANWIDTH_160MHZ:
he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
mcs_nss_size += 4;
/* fall through */
case CHANWIDTH_80MHZ:
case CHANWIDTH_USE_HT:
he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
break;
}
ie_size += mcs_nss_size + ppet_size;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + ie_size;
*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
cap = (struct ieee80211_he_capabilities *) pos;
os_memset(cap, 0, sizeof(*cap));
os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
HE_MAX_MAC_CAPAB_SIZE);
os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
HE_MAX_PHY_CAPAB_SIZE);
os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
if (ppet_size)
os_memcpy(&cap->optional[mcs_nss_size],
mode->he_capab[opmode].ppet, ppet_size);
if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
HE_PHYCAP_SU_BEAMFORMER_CAPAB;
else
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
else
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
HE_PHYCAP_MU_BEAMFORMER_CAPAB;
else
cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
he_oper_chwidth;
pos += ie_size;
return pos;
}
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
{
struct ieee80211_he_operation *oper;
u8 *pos = eid;
int oper_size = 6;
u32 params = 0;
if (!hapd->iface->current_mode)
return eid;
if (is_6ghz_op_class(hapd->iconf->op_class))
oper_size += 5;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + oper_size;
*pos++ = WLAN_EID_EXT_HE_OPERATION;
oper = (struct ieee80211_he_operation *) pos;
os_memset(oper, 0, sizeof(*oper));
if (hapd->iface->conf->he_op.he_default_pe_duration)
params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
HE_OPERATION_DFLT_PE_DURATION_OFFSET);
if (hapd->iface->conf->he_op.he_twt_required)
params |= HE_OPERATION_TWT_REQUIRED;
if (hapd->iface->conf->he_op.he_rts_threshold)
params |= (hapd->iface->conf->he_op.he_rts_threshold <<
HE_OPERATION_RTS_THRESHOLD_OFFSET);
+ if (hapd->iface->conf->he_op.he_er_su_disable)
+ params |= HE_OPERATION_ER_SU_DISABLE;
+
if (hapd->iface->conf->he_op.he_bss_color_disabled)
params |= HE_OPERATION_BSS_COLOR_DISABLED;
if (hapd->iface->conf->he_op.he_bss_color_partial)
params |= HE_OPERATION_BSS_COLOR_PARTIAL;
params |= hapd->iface->conf->he_op.he_bss_color <<
HE_OPERATION_BSS_COLOR_OFFSET;
/* HE minimum required basic MCS and NSS for STAs */
oper->he_mcs_nss_set =
host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set);
/* TODO: conditional MaxBSSID Indicator subfield */
pos += 6; /* skip the fixed part */
if (is_6ghz_op_class(hapd->iconf->op_class)) {
u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf);
u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
if (!seg0)
seg0 = hapd->iconf->channel;
params |= HE_OPERATION_6GHZ_OPER_INFO;
/* 6 GHz Operation Information field
* IEEE P802.11ax/D8.0, 9.4.2.249 HE Operation element,
* Figure 9-788k
*/
*pos++ = hapd->iconf->channel; /* Primary Channel */
/* Control: Channel Width */
if (seg1)
*pos++ = 3;
else
*pos++ = center_idx_to_bw_6ghz(seg0);
/* Channel Center Freq Seg0/Seg1 */
if (hapd->iconf->he_oper_chwidth == 2) {
/*
* Seg 0 indicates the channel center frequency index of
* the 160 MHz channel.
*/
seg1 = seg0;
if (hapd->iconf->channel < seg0)
seg0 -= 8;
else
seg0 += 8;
}
*pos++ = seg0;
*pos++ = seg1;
/* Minimum Rate */
*pos++ = 6; /* TODO: what should be set here? */
}
oper->he_oper_params = host_to_le32(params);
return pos;
}
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
{
struct ieee80211_he_mu_edca_parameter_set *edca;
u8 *pos;
size_t i;
pos = (u8 *) &hapd->iface->conf->he_mu_edca;
for (i = 0; i < sizeof(*edca); i++) {
if (pos[i])
break;
}
if (i == sizeof(*edca))
return eid; /* no MU EDCA Parameters configured */
pos = eid;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + sizeof(*edca);
*pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
pos, sizeof(*edca));
pos += sizeof(*edca);
return pos;
}
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
{
struct ieee80211_spatial_reuse *spr;
u8 *pos = eid, *spr_param;
u8 sz = 1;
if (!hapd->iface->conf->spr.sr_control)
return eid;
if (hapd->iface->conf->spr.sr_control &
SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
sz++;
if (hapd->iface->conf->spr.sr_control &
SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
sz += 18;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + sz;
*pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
spr = (struct ieee80211_spatial_reuse *) pos;
os_memset(spr, 0, sizeof(*spr));
spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
pos++;
spr_param = spr->params;
if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
*spr_param++ =
hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
pos++;
}
if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
os_memcpy(spr_param,
hapd->iface->conf->spr.srg_bss_color_bitmap, 8);
spr_param += 8;
os_memcpy(spr_param,
hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8);
pos += 18;
}
return pos;
}
u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid)
{
struct hostapd_config *conf = hapd->iface->conf;
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
struct he_capabilities *he_cap;
struct ieee80211_he_6ghz_band_cap *cap;
u16 capab;
u8 *pos;
if (!mode || !is_6ghz_op_class(hapd->iconf->op_class) ||
!is_6ghz_freq(hapd->iface->freq))
return eid;
he_cap = &mode->he_capab[IEEE80211_MODE_AP];
capab = he_cap->he_6ghz_capa & HE_6GHZ_BAND_CAP_MIN_MPDU_START;
capab |= (conf->he_6ghz_max_ampdu_len_exp <<
HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT) &
HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK;
capab |= (conf->he_6ghz_max_mpdu <<
HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT) &
HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK;
capab |= HE_6GHZ_BAND_CAP_SMPS_DISABLED;
if (conf->he_6ghz_rx_ant_pat)
capab |= HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS;
if (conf->he_6ghz_tx_ant_pat)
capab |= HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS;
pos = eid;
*pos++ = WLAN_EID_EXTENSION;
*pos++ = 1 + sizeof(*cap);
*pos++ = WLAN_EID_EXT_HE_6GHZ_BAND_CAP;
cap = (struct ieee80211_he_6ghz_band_cap *) pos;
cap->capab = host_to_le16(capab);
pos += sizeof(*cap);
return pos;
}
void hostapd_get_he_capab(struct hostapd_data *hapd,
const struct ieee80211_he_capabilities *he_cap,
struct ieee80211_he_capabilities *neg_he_cap,
size_t he_capab_len)
{
if (!he_cap)
return;
if (he_capab_len > sizeof(*neg_he_cap))
he_capab_len = sizeof(*neg_he_cap);
/* TODO: mask out unsupported features */
os_memcpy(neg_he_cap, he_cap, he_capab_len);
}
static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
enum ieee80211_op_mode opmode)
{
u16 sta_rx_mcs_set, ap_tx_mcs_set;
u8 mcs_count = 0;
const u16 *ap_mcs_set, *sta_mcs_set;
int i;
if (!hapd->iface->current_mode)
return 1;
ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
sta_he_capab)->optional;
/*
* Disable HE capabilities for STAs for which there is not even a single
* allowed MCS in any supported number of streams, i.e., STA is
* advertising 3 (not supported) as HE MCS rates for all supported
* band/stream cases.
*/
switch (hapd->iface->conf->he_oper_chwidth) {
case CHANWIDTH_80P80MHZ:
mcs_count = 3;
break;
case CHANWIDTH_160MHZ:
mcs_count = 2;
break;
default:
mcs_count = 1;
break;
}
for (i = 0; i < mcs_count; i++) {
int j;
/* AP Tx MCS map vs. STA Rx MCS map */
sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
&ap_mcs_set[(i * 2) + 1]);
for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
continue;
if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
continue;
return 1;
}
}
wpa_printf(MSG_DEBUG,
"No matching HE MCS found between AP TX and STA RX");
return 0;
}
u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
enum ieee80211_op_mode opmode, const u8 *he_capab,
size_t he_capab_len)
{
if (!he_capab || !(sta->flags & WLAN_STA_WMM) ||
!hapd->iconf->ieee80211ax || hapd->conf->disable_11ax ||
!check_valid_he_mcs(hapd, he_capab, opmode) ||
ieee80211_invalid_he_cap_size(he_capab, he_capab_len) ||
he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
sta->flags &= ~WLAN_STA_HE;
os_free(sta->he_capab);
sta->he_capab = NULL;
return WLAN_STATUS_SUCCESS;
}
if (!sta->he_capab) {
sta->he_capab =
os_zalloc(sizeof(struct ieee80211_he_capabilities));
if (!sta->he_capab)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sta->flags |= WLAN_STA_HE;
os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
os_memcpy(sta->he_capab, he_capab, he_capab_len);
sta->he_capab_len = he_capab_len;
return WLAN_STATUS_SUCCESS;
}
u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *he_6ghz_capab)
{
if (!he_6ghz_capab || !hapd->iconf->ieee80211ax ||
hapd->conf->disable_11ax ||
!is_6ghz_op_class(hapd->iconf->op_class)) {
sta->flags &= ~WLAN_STA_6GHZ;
os_free(sta->he_6ghz_capab);
sta->he_6ghz_capab = NULL;
return WLAN_STATUS_SUCCESS;
}
if (!sta->he_6ghz_capab) {
sta->he_6ghz_capab =
os_zalloc(sizeof(struct ieee80211_he_6ghz_band_cap));
if (!sta->he_6ghz_capab)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sta->flags |= WLAN_STA_6GHZ;
os_memcpy(sta->he_6ghz_capab, he_6ghz_capab,
sizeof(struct ieee80211_he_6ghz_band_cap));
return WLAN_STATUS_SUCCESS;
}
int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
enum ieee80211_op_mode mode)
{
u8 *mac_cap;
if (!hapd->iface->current_mode ||
!hapd->iface->current_mode->he_capab[mode].he_supported)
return 0;
mac_cap = hapd->iface->current_mode->he_capab[mode].mac_cap;
return !!(mac_cap[HE_MAC_CAPAB_0] & HE_MACCAP_TWT_RESPONDER) &&
hapd->iface->conf->he_op.he_twt_responder;
}
diff --git a/contrib/wpa/src/ap/ndisc_snoop.c b/contrib/wpa/src/ap/ndisc_snoop.c
index 4d6a92e086bd..788c12fdc1cf 100644
--- a/contrib/wpa/src/ap/ndisc_snoop.c
+++ b/contrib/wpa/src/ap/ndisc_snoop.c
@@ -1,186 +1,188 @@
/*
* Neighbor Discovery snooping for Proxy ARP
* Copyright (c) 2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include "utils/common.h"
#include "l2_packet/l2_packet.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ap_drv_ops.h"
#include "list.h"
#include "x_snoop.h"
#include "ndisc_snoop.h"
struct ip6addr {
struct in6_addr addr;
struct dl_list list;
};
struct icmpv6_ndmsg {
struct ip6_hdr ipv6h;
struct icmp6_hdr icmp6h;
struct in6_addr target_addr;
u8 opt_type;
u8 len;
u8 opt_lladdr[0];
} STRUCT_PACKED;
#define ROUTER_ADVERTISEMENT 134
#define NEIGHBOR_SOLICITATION 135
#define NEIGHBOR_ADVERTISEMENT 136
#define SOURCE_LL_ADDR 1
static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr)
{
struct ip6addr *ip6addr;
ip6addr = os_zalloc(sizeof(*ip6addr));
if (!ip6addr)
return -1;
os_memcpy(&ip6addr->addr, addr, sizeof(*addr));
dl_list_add_tail(&sta->ip6addr, &ip6addr->list);
return 0;
}
void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
{
struct ip6addr *ip6addr, *prev;
dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
list) {
hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
os_free(ip6addr);
}
}
static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr)
{
struct ip6addr *ip6addr;
dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) {
if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] &&
ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] &&
ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] &&
ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3])
return 1;
}
return 0;
}
static void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len)
{
struct sta_info *sta;
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (!(sta->flags & WLAN_STA_AUTHORIZED))
continue;
x_snoop_mcast_to_ucast_convert_send(hapd, sta, (u8 *) buf, len);
}
}
static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
struct hostapd_data *hapd = ctx;
struct icmpv6_ndmsg *msg;
struct in6_addr saddr;
struct sta_info *sta;
int res;
char addrtxt[INET6_ADDRSTRLEN + 1];
if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
return;
msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN];
switch (msg->icmp6h.icmp6_type) {
case NEIGHBOR_SOLICITATION:
if (len < ETH_HLEN + sizeof(*msg))
return;
if (msg->opt_type != SOURCE_LL_ADDR)
return;
/*
* IPv6 header may not be 32-bit aligned in the buffer, so use
* a local copy to avoid unaligned reads.
*/
os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr));
if (!(saddr.s6_addr32[0] == 0 && saddr.s6_addr32[1] == 0 &&
saddr.s6_addr32[2] == 0 && saddr.s6_addr32[3] == 0)) {
if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
return;
sta = ap_get_sta(hapd, msg->opt_lladdr);
if (!sta)
return;
if (sta_has_ip6addr(sta, &saddr))
return;
if (inet_ntop(AF_INET6, &saddr, addrtxt,
sizeof(addrtxt)) == NULL)
addrtxt[0] = '\0';
wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
MACSTR, addrtxt, MAC2STR(sta->addr));
hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &saddr);
res = hostapd_drv_br_add_ip_neigh(hapd, 6,
(u8 *) &saddr,
128, sta->addr);
if (res) {
wpa_printf(MSG_ERROR,
"ndisc_snoop: Adding ip neigh failed: %d",
res);
return;
}
if (sta_ip6addr_add(sta, &saddr))
return;
}
break;
+#ifdef CONFIG_HS20
case ROUTER_ADVERTISEMENT:
if (hapd->conf->disable_dgaf)
ucast_to_stas(hapd, buf, len);
break;
+#endif /* CONFIG_HS20 */
case NEIGHBOR_ADVERTISEMENT:
if (hapd->conf->na_mcast_to_ucast)
ucast_to_stas(hapd, buf, len);
break;
default:
break;
}
}
int ndisc_snoop_init(struct hostapd_data *hapd)
{
hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc,
L2_PACKET_FILTER_NDISC);
if (hapd->sock_ndisc == NULL) {
wpa_printf(MSG_DEBUG,
"ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s",
strerror(errno));
return -1;
}
return 0;
}
void ndisc_snoop_deinit(struct hostapd_data *hapd)
{
l2_packet_deinit(hapd->sock_ndisc);
hapd->sock_ndisc = NULL;
}
diff --git a/contrib/wpa/src/ap/neighbor_db.c b/contrib/wpa/src/ap/neighbor_db.c
index 2bbe31859214..229edd2a94c4 100644
--- a/contrib/wpa/src/ap/neighbor_db.c
+++ b/contrib/wpa/src/ap/neighbor_db.c
@@ -1,318 +1,322 @@
/*
* hostapd / Neighboring APs DB
* Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
* Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/crc32.h"
#include "hostapd.h"
#include "ieee802_11.h"
#include "neighbor_db.h"
struct hostapd_neighbor_entry *
hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid)
{
struct hostapd_neighbor_entry *nr;
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
list) {
if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
(!ssid ||
(ssid->ssid_len == nr->ssid.ssid_len &&
os_memcmp(ssid->ssid, nr->ssid.ssid,
ssid->ssid_len) == 0)))
return nr;
}
return NULL;
}
int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen)
{
struct hostapd_neighbor_entry *nr;
char *pos, *end;
pos = buf;
end = buf + buflen;
dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
list) {
int ret;
char nrie[2 * 255 + 1];
char lci[2 * 255 + 1];
char civic[2 * 255 + 1];
char ssid[SSID_MAX_LEN * 2 + 1];
ssid[0] = '\0';
wpa_snprintf_hex(ssid, sizeof(ssid), nr->ssid.ssid,
nr->ssid.ssid_len);
nrie[0] = '\0';
if (nr->nr)
wpa_snprintf_hex(nrie, sizeof(nrie),
wpabuf_head(nr->nr),
wpabuf_len(nr->nr));
lci[0] = '\0';
if (nr->lci)
wpa_snprintf_hex(lci, sizeof(lci),
wpabuf_head(nr->lci),
wpabuf_len(nr->lci));
civic[0] = '\0';
if (nr->civic)
wpa_snprintf_hex(civic, sizeof(civic),
wpabuf_head(nr->civic),
wpabuf_len(nr->civic));
ret = os_snprintf(pos, end - pos, MACSTR
" ssid=%s%s%s%s%s%s%s%s\n",
MAC2STR(nr->bssid), ssid,
nr->nr ? " nr=" : "", nrie,
nr->lci ? " lci=" : "", lci,
nr->civic ? " civic=" : "", civic,
nr->stationary ? " stat" : "");
if (os_snprintf_error(end - pos, ret))
break;
pos += ret;
}
return pos - buf;
}
static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
{
wpabuf_free(nr->nr);
nr->nr = NULL;
wpabuf_free(nr->lci);
nr->lci = NULL;
wpabuf_free(nr->civic);
nr->civic = NULL;
os_memset(nr->bssid, 0, sizeof(nr->bssid));
os_memset(&nr->ssid, 0, sizeof(nr->ssid));
nr->stationary = 0;
}
static struct hostapd_neighbor_entry *
hostapd_neighbor_add(struct hostapd_data *hapd)
{
struct hostapd_neighbor_entry *nr;
nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
if (!nr)
return NULL;
dl_list_add(&hapd->nr_db, &nr->list);
return nr;
}
int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid,
const struct wpabuf *nr, const struct wpabuf *lci,
- const struct wpabuf *civic, int stationary)
+ const struct wpabuf *civic, int stationary,
+ u8 bss_parameters)
{
struct hostapd_neighbor_entry *entry;
entry = hostapd_neighbor_get(hapd, bssid, ssid);
if (!entry)
entry = hostapd_neighbor_add(hapd);
if (!entry)
return -1;
hostapd_neighbor_clear_entry(entry);
os_memcpy(entry->bssid, bssid, ETH_ALEN);
os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
+ entry->short_ssid = crc32(ssid->ssid, ssid->ssid_len);
entry->nr = wpabuf_dup(nr);
if (!entry->nr)
goto fail;
if (lci && wpabuf_len(lci)) {
entry->lci = wpabuf_dup(lci);
if (!entry->lci || os_get_time(&entry->lci_date))
goto fail;
}
if (civic && wpabuf_len(civic)) {
entry->civic = wpabuf_dup(civic);
if (!entry->civic)
goto fail;
}
entry->stationary = stationary;
+ entry->bss_parameters = bss_parameters;
return 0;
fail:
hostapd_neighbor_remove(hapd, bssid, ssid);
return -1;
}
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid)
{
struct hostapd_neighbor_entry *nr;
nr = hostapd_neighbor_get(hapd, bssid, ssid);
if (!nr)
return -1;
hostapd_neighbor_clear_entry(nr);
dl_list_del(&nr->list);
os_free(nr);
return 0;
}
void hostapd_free_neighbor_db(struct hostapd_data *hapd)
{
struct hostapd_neighbor_entry *nr, *prev;
dl_list_for_each_safe(nr, prev, &hapd->nr_db,
struct hostapd_neighbor_entry, list) {
hostapd_neighbor_clear_entry(nr);
dl_list_del(&nr->list);
os_free(nr);
}
}
#ifdef NEED_AP_MLME
static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
int ht, int vht, int he)
{
u8 oper_chwidth = hostapd_get_oper_chwidth(hapd->iconf);
if (!ht && !vht && !he)
return NR_CHAN_WIDTH_20;
if (!hapd->iconf->secondary_channel)
return NR_CHAN_WIDTH_20;
if ((!vht && !he) || oper_chwidth == CHANWIDTH_USE_HT)
return NR_CHAN_WIDTH_40;
if (oper_chwidth == CHANWIDTH_80MHZ)
return NR_CHAN_WIDTH_80;
if (oper_chwidth == CHANWIDTH_160MHZ)
return NR_CHAN_WIDTH_160;
if (oper_chwidth == CHANWIDTH_80P80MHZ)
return NR_CHAN_WIDTH_80P80;
return NR_CHAN_WIDTH_20;
}
#endif /* NEED_AP_MLME */
void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
{
#ifdef NEED_AP_MLME
u16 capab = hostapd_own_capab_info(hapd);
int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
int he = hapd->iconf->ieee80211ax && !hapd->conf->disable_11ax;
struct wpa_ssid_value ssid;
u8 channel, op_class;
u8 center_freq1_idx = 0, center_freq2_idx = 0;
enum nr_chan_width width;
u32 bssid_info;
struct wpabuf *nr;
if (!(hapd->conf->radio_measurements[0] &
WLAN_RRM_CAPS_NEIGHBOR_REPORT))
return;
bssid_info = 3; /* AP is reachable */
bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
if (hapd->conf->wmm_enabled) {
bssid_info |= NEI_REP_BSSID_INFO_QOS;
if (hapd->conf->wmm_uapsd &&
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
bssid_info |= NEI_REP_BSSID_INFO_APSD;
}
if (ht) {
bssid_info |= NEI_REP_BSSID_INFO_HT |
NEI_REP_BSSID_INFO_DELAYED_BA;
/* VHT bit added in IEEE P802.11-REVmc/D4.3 */
if (vht)
bssid_info |= NEI_REP_BSSID_INFO_VHT;
if (he)
bssid_info |= NEI_REP_BSSID_INFO_HE;
}
/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
hapd->iconf->secondary_channel,
hostapd_get_oper_chwidth(hapd->iconf),
&op_class, &channel) ==
NUM_HOSTAPD_MODES)
return;
width = hostapd_get_nr_chan_width(hapd, ht, vht, he);
if (vht) {
center_freq1_idx = hostapd_get_oper_centr_freq_seg0_idx(
hapd->iconf);
if (width == NR_CHAN_WIDTH_80P80)
center_freq2_idx =
hostapd_get_oper_centr_freq_seg1_idx(
hapd->iconf);
} else if (ht) {
ieee80211_freq_to_chan(hapd->iface->freq +
10 * hapd->iconf->secondary_channel,
&center_freq1_idx);
}
ssid.ssid_len = hapd->conf->ssid.ssid_len;
os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
/*
* Neighbor Report element size = BSSID + BSSID info + op_class + chan +
* phy type + wide bandwidth channel subelement.
*/
nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
if (!nr)
return;
wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
wpabuf_put_le32(nr, bssid_info);
wpabuf_put_u8(nr, op_class);
wpabuf_put_u8(nr, channel);
wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
/*
* Wide Bandwidth Channel subelement may be needed to allow the
* receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
* Figure 9-301.
*/
wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
wpabuf_put_u8(nr, 3);
wpabuf_put_u8(nr, width);
wpabuf_put_u8(nr, center_freq1_idx);
wpabuf_put_u8(nr, center_freq2_idx);
hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
- hapd->iconf->civic, hapd->iconf->stationary_ap);
+ hapd->iconf->civic, hapd->iconf->stationary_ap, 0);
wpabuf_free(nr);
#endif /* NEED_AP_MLME */
}
diff --git a/contrib/wpa/src/ap/neighbor_db.h b/contrib/wpa/src/ap/neighbor_db.h
index bed0a2f5fb7f..992671b62608 100644
--- a/contrib/wpa/src/ap/neighbor_db.h
+++ b/contrib/wpa/src/ap/neighbor_db.h
@@ -1,26 +1,27 @@
/*
* hostapd / Neighboring APs DB
* Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
* Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef NEIGHBOR_DB_H
#define NEIGHBOR_DB_H
struct hostapd_neighbor_entry *
hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid);
int hostapd_neighbor_show(struct hostapd_data *hapd, char *buf, size_t buflen);
int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid,
const struct wpabuf *nr, const struct wpabuf *lci,
- const struct wpabuf *civic, int stationary);
+ const struct wpabuf *civic, int stationary,
+ u8 bss_parameters);
void hostapd_neighbor_set_own_report(struct hostapd_data *hapd);
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid);
void hostapd_free_neighbor_db(struct hostapd_data *hapd);
#endif /* NEIGHBOR_DB_H */
diff --git a/contrib/wpa/src/ap/wnm_ap.c b/contrib/wpa/src/ap/wnm_ap.c
index d32967e6cef6..0042ed6a115a 100644
--- a/contrib/wpa/src/ap/wnm_ap.c
+++ b/contrib/wpa/src/ap/wnm_ap.c
@@ -1,901 +1,903 @@
/*
* hostapd - WNM
* Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "common/ocv.h"
#include "ap/hostapd.h"
#include "ap/sta_info.h"
#include "ap/ap_config.h"
#include "ap/ap_drv_ops.h"
#include "ap/wpa_auth.h"
#include "mbo_ap.h"
#include "wnm_ap.h"
#define MAX_TFS_IE_LEN 1024
/* get the TFS IE from driver */
static int ieee80211_11_get_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
u8 *buf, u16 *buf_len, enum wnm_oper oper)
{
wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
}
/* set the TFS IE to driver */
static int ieee80211_11_set_tfs_ie(struct hostapd_data *hapd, const u8 *addr,
u8 *buf, u16 *buf_len, enum wnm_oper oper)
{
wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
return hostapd_drv_wnm_oper(hapd, oper, addr, buf, buf_len);
}
/* MLME-SLEEPMODE.response */
static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
const u8 *addr, u8 dialog_token,
u8 action_type, u16 intval)
{
struct ieee80211_mgmt *mgmt;
int res;
size_t len;
size_t gtk_elem_len = 0;
size_t igtk_elem_len = 0;
size_t bigtk_elem_len = 0;
struct wnm_sleep_element wnmsleep_ie;
u8 *wnmtfs_ie, *oci_ie;
u8 wnmsleep_ie_len, oci_ie_len;
u16 wnmtfs_ie_len;
u8 *pos;
struct sta_info *sta;
enum wnm_oper tfs_oper = action_type == WNM_SLEEP_MODE_ENTER ?
WNM_SLEEP_TFS_RESP_IE_ADD : WNM_SLEEP_TFS_RESP_IE_NONE;
sta = ap_get_sta(hapd, addr);
if (sta == NULL) {
wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
return -EINVAL;
}
/* WNM-Sleep Mode IE */
os_memset(&wnmsleep_ie, 0, sizeof(struct wnm_sleep_element));
wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
wnmsleep_ie.eid = WLAN_EID_WNMSLEEP;
wnmsleep_ie.len = wnmsleep_ie_len - 2;
wnmsleep_ie.action_type = action_type;
wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
wnmsleep_ie.intval = host_to_le16(intval);
/* TFS IE(s) */
wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
if (wnmtfs_ie == NULL)
return -1;
if (ieee80211_11_get_tfs_ie(hapd, addr, wnmtfs_ie, &wnmtfs_ie_len,
tfs_oper)) {
wnmtfs_ie_len = 0;
os_free(wnmtfs_ie);
wnmtfs_ie = NULL;
}
oci_ie = NULL;
oci_ie_len = 0;
#ifdef CONFIG_OCV
if (action_type == WNM_SLEEP_MODE_EXIT &&
wpa_auth_uses_ocv(sta->wpa_sm)) {
struct wpa_channel_info ci;
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info for OCI element in WNM-Sleep Mode frame");
os_free(wnmtfs_ie);
return -1;
}
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->oci_freq_override_wnm_sleep) {
wpa_printf(MSG_INFO,
"TEST: Override OCI frequency %d -> %u MHz",
ci.frequency,
hapd->conf->oci_freq_override_wnm_sleep);
ci.frequency = hapd->conf->oci_freq_override_wnm_sleep;
}
#endif /* CONFIG_TESTING_OPTIONS */
oci_ie_len = OCV_OCI_EXTENDED_LEN;
oci_ie = os_zalloc(oci_ie_len);
if (!oci_ie) {
wpa_printf(MSG_WARNING,
"Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
os_free(wnmtfs_ie);
return -1;
}
if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
os_free(wnmtfs_ie);
os_free(oci_ie);
return -1;
}
}
#endif /* CONFIG_OCV */
#define MAX_GTK_SUBELEM_LEN 45
#define MAX_IGTK_SUBELEM_LEN 26
#define MAX_BIGTK_SUBELEM_LEN 26
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
MAX_BIGTK_SUBELEM_LEN +
oci_ie_len);
if (mgmt == NULL) {
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
"WNM-Sleep Response action frame");
res = -1;
goto fail;
}
os_memcpy(mgmt->da, addr, ETH_ALEN);
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.wnm_sleep_resp.action = WNM_SLEEP_MODE_RESP;
mgmt->u.action.u.wnm_sleep_resp.dialogtoken = dialog_token;
pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable;
/* add key data if MFP is enabled */
if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
hapd->conf->wnm_sleep_mode_no_keys ||
action_type != WNM_SLEEP_MODE_EXIT) {
mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0;
} else {
gtk_elem_len = wpa_wnmsleep_gtk_subelem(sta->wpa_sm, pos);
pos += gtk_elem_len;
wpa_printf(MSG_DEBUG, "Pass 4, gtk_len = %d",
(int) gtk_elem_len);
res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
if (res < 0)
goto fail;
igtk_elem_len = res;
pos += igtk_elem_len;
wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
(int) igtk_elem_len);
if (hapd->conf->beacon_prot &&
(hapd->iface->drv_flags &
WPA_DRIVER_FLAGS_BEACON_PROTECTION)) {
res = wpa_wnmsleep_bigtk_subelem(sta->wpa_sm, pos);
if (res < 0)
goto fail;
bigtk_elem_len = res;
pos += bigtk_elem_len;
wpa_printf(MSG_DEBUG, "Pass 4 bigtk_len = %d",
(int) bigtk_elem_len);
}
WPA_PUT_LE16((u8 *)
&mgmt->u.action.u.wnm_sleep_resp.keydata_len,
gtk_elem_len + igtk_elem_len + bigtk_elem_len);
}
os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
/* copy TFS IE here */
pos += wnmsleep_ie_len;
if (wnmtfs_ie) {
os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
pos += wnmtfs_ie_len;
}
#ifdef CONFIG_OCV
/* copy OCV OCI here */
if (oci_ie_len > 0)
os_memcpy(pos, oci_ie, oci_ie_len);
#endif /* CONFIG_OCV */
len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
igtk_elem_len + bigtk_elem_len +
wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
/* In driver, response frame should be forced to sent when STA is in
* PS mode */
res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
mgmt->da, &mgmt->u.action.category, len);
if (!res) {
wpa_printf(MSG_DEBUG, "Successfully send WNM-Sleep Response "
"frame");
/* when entering wnmsleep
* 1. pause the node in driver
* 2. mark the node so that AP won't update GTK/IGTK/BIGTK
* during WNM Sleep
*/
if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
addr, NULL, NULL);
wpa_set_wnmsleep(sta->wpa_sm, 1);
}
/* when exiting wnmsleep
* 1. unmark the node
* 2. start GTK/IGTK/BIGTK update if MFP is not used
* 3. unpause the node in driver
*/
if ((wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT ||
wnmsleep_ie.status ==
WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
wpa_set_wnmsleep(sta->wpa_sm, 0);
hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
addr, NULL, NULL);
if (!wpa_auth_uses_mfp(sta->wpa_sm) ||
hapd->conf->wnm_sleep_mode_no_keys)
wpa_wnmsleep_rekey_gtk(sta->wpa_sm);
}
} else
wpa_printf(MSG_DEBUG, "Fail to send WNM-Sleep Response frame");
#undef MAX_GTK_SUBELEM_LEN
#undef MAX_IGTK_SUBELEM_LEN
#undef MAX_BIGTK_SUBELEM_LEN
fail:
os_free(wnmtfs_ie);
os_free(oci_ie);
os_free(mgmt);
return res;
}
static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *frm, int len)
{
/* Dialog Token [1] | WNM-Sleep Mode IE | TFS Response IE */
const u8 *pos = frm;
u8 dialog_token;
struct wnm_sleep_element *wnmsleep_ie = NULL;
/* multiple TFS Req IE (assuming consecutive) */
u8 *tfsreq_ie_start = NULL;
u8 *tfsreq_ie_end = NULL;
u16 tfsreq_ie_len = 0;
#ifdef CONFIG_OCV
struct sta_info *sta;
const u8 *oci_ie = NULL;
u8 oci_ie_len = 0;
#endif /* CONFIG_OCV */
if (!hapd->conf->wnm_sleep_mode) {
wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
MACSTR " since WNM-Sleep Mode is disabled",
MAC2STR(addr));
return;
}
if (len < 1) {
wpa_printf(MSG_DEBUG,
"WNM: Ignore too short WNM-Sleep Mode Request from "
MACSTR, MAC2STR(addr));
return;
}
dialog_token = *pos++;
while (pos + 1 < frm + len) {
u8 ie_len = pos[1];
if (pos + 2 + ie_len > frm + len)
break;
if (*pos == WLAN_EID_WNMSLEEP &&
ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
wnmsleep_ie = (struct wnm_sleep_element *) pos;
else if (*pos == WLAN_EID_TFS_REQ) {
if (!tfsreq_ie_start)
tfsreq_ie_start = (u8 *) pos;
tfsreq_ie_end = (u8 *) pos;
#ifdef CONFIG_OCV
} else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
pos[2] == WLAN_EID_EXT_OCV_OCI) {
oci_ie = pos + 3;
oci_ie_len = ie_len - 1;
#endif /* CONFIG_OCV */
} else
wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
*pos);
pos += ie_len + 2;
}
if (!wnmsleep_ie) {
wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
return;
}
#ifdef CONFIG_OCV
sta = ap_get_sta(hapd, addr);
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
struct wpa_channel_info ci;
if (hostapd_drv_channel_info(hapd, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
return;
}
if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx) != OCI_SUCCESS) {
wpa_msg(hapd, MSG_WARNING, "WNM: OCV failed: %s",
ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
tfsreq_ie_start && tfsreq_ie_end &&
tfsreq_ie_end - tfsreq_ie_start >= 0) {
tfsreq_ie_len = (tfsreq_ie_end + tfsreq_ie_end[1] + 2) -
tfsreq_ie_start;
wpa_printf(MSG_DEBUG, "TFS Req IE(s) found");
/* pass the TFS Req IE(s) to driver for processing */
if (ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
&tfsreq_ie_len,
WNM_SLEEP_TFS_REQ_IE_SET))
wpa_printf(MSG_DEBUG, "Fail to set TFS Req IE");
}
ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
wnmsleep_ie->action_type,
le_to_host16(wnmsleep_ie->intval));
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
/* clear the tfs after sending the resp frame */
ieee80211_11_set_tfs_ie(hapd, addr, tfsreq_ie_start,
&tfsreq_ie_len, WNM_SLEEP_TFS_IE_DEL);
}
}
static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
const u8 *addr,
u8 dialog_token)
{
struct ieee80211_mgmt *mgmt;
size_t len;
u8 *pos;
int res;
mgmt = os_zalloc(sizeof(*mgmt));
if (mgmt == NULL)
return -1;
os_memcpy(mgmt->da, addr, ETH_ALEN);
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
mgmt->u.action.u.bss_tm_req.req_mode = 0;
mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
mgmt->u.action.u.bss_tm_req.validity_interval = 1;
pos = mgmt->u.action.u.bss_tm_req.variable;
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
"validity_interval=%u",
MAC2STR(addr), dialog_token,
mgmt->u.action.u.bss_tm_req.req_mode,
le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
mgmt->u.action.u.bss_tm_req.validity_interval);
len = pos - &mgmt->u.action.category;
res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
mgmt->da, &mgmt->u.action.category, len);
os_free(mgmt);
return res;
}
static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
const u8 *addr, const u8 *frm,
size_t len)
{
u8 dialog_token, reason;
const u8 *pos, *end;
int enabled = hapd->conf->bss_transition;
#ifdef CONFIG_MBO
if (hapd->conf->mbo_enabled)
enabled = 1;
#endif /* CONFIG_MBO */
if (!enabled) {
wpa_printf(MSG_DEBUG,
"Ignore BSS Transition Management Query from "
MACSTR
" since BSS Transition Management is disabled",
MAC2STR(addr));
return;
}
if (len < 2) {
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
MACSTR, MAC2STR(addr));
return;
}
pos = frm;
end = pos + len;
dialog_token = *pos++;
reason = *pos++;
wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
MACSTR " dialog_token=%u reason=%u",
MAC2STR(addr), dialog_token, reason);
wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
pos, end - pos);
ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
}
void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
struct sta_info *sta = timeout_ctx;
if (sta->agreed_to_steer) {
wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR,
hapd->conf->iface, MAC2STR(sta->addr));
sta->agreed_to_steer = 0;
}
}
static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
const u8 *addr, const u8 *frm,
size_t len)
{
u8 dialog_token, status_code, bss_termination_delay;
const u8 *pos, *end;
int enabled = hapd->conf->bss_transition;
struct sta_info *sta;
#ifdef CONFIG_MBO
if (hapd->conf->mbo_enabled)
enabled = 1;
#endif /* CONFIG_MBO */
if (!enabled) {
wpa_printf(MSG_DEBUG,
"Ignore BSS Transition Management Response from "
MACSTR
" since BSS Transition Management is disabled",
MAC2STR(addr));
return;
}
if (len < 3) {
wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
MACSTR, MAC2STR(addr));
return;
}
pos = frm;
end = pos + len;
dialog_token = *pos++;
status_code = *pos++;
bss_termination_delay = *pos++;
wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
MACSTR " dialog_token=%u status_code=%u "
"bss_termination_delay=%u", MAC2STR(addr), dialog_token,
status_code, bss_termination_delay);
sta = ap_get_sta(hapd, addr);
if (!sta) {
wpa_printf(MSG_DEBUG, "Station " MACSTR
" not found for received BSS TM Response",
MAC2STR(addr));
return;
}
if (status_code == WNM_BSS_TM_ACCEPT) {
if (end - pos < ETH_ALEN) {
wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
return;
}
sta->agreed_to_steer = 1;
eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta);
eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer,
hapd, sta);
wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
MAC2STR(pos));
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
" status_code=%u bss_termination_delay=%u target_bssid="
MACSTR,
MAC2STR(addr), status_code, bss_termination_delay,
MAC2STR(pos));
pos += ETH_ALEN;
} else {
sta->agreed_to_steer = 0;
wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
" status_code=%u bss_termination_delay=%u",
MAC2STR(addr), status_code, bss_termination_delay);
}
wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
pos, end - pos);
}
static void wnm_beacon_protection_failure(struct hostapd_data *hapd,
const u8 *addr)
{
struct sta_info *sta;
if (!hapd->conf->beacon_prot ||
!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION))
return;
sta = ap_get_sta(hapd, addr);
if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
wpa_printf(MSG_DEBUG, "Station " MACSTR
" not found for received WNM-Notification Request",
MAC2STR(addr));
return;
}
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
"Beacon protection failure reported");
wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_UNPROT_BEACON "reporter="
MACSTR, MAC2STR(addr));
}
static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *buf,
size_t len)
{
u8 dialog_token, type;
if (len < 2)
return;
dialog_token = *buf++;
type = *buf++;
len -= 2;
wpa_printf(MSG_DEBUG,
"WNM: Received WNM Notification Request frame from "
MACSTR " (dialog_token=%u type=%u)",
MAC2STR(addr), dialog_token, type);
wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements",
buf, len);
switch (type) {
case WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE:
wnm_beacon_protection_failure(hapd, addr);
break;
case WNM_NOTIF_TYPE_VENDOR_SPECIFIC:
mbo_ap_wnm_notification_req(hapd, addr, buf, len);
break;
}
}
static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd,
const u8 *addr, const u8 *buf,
size_t len)
{
u8 dialog_token;
char *hex;
size_t hex_len;
if (!hapd->conf->coloc_intf_reporting) {
wpa_printf(MSG_DEBUG,
"WNM: Ignore unexpected Collocated Interference Report from "
MACSTR, MAC2STR(addr));
return;
}
if (len < 1) {
wpa_printf(MSG_DEBUG,
"WNM: Ignore too short Collocated Interference Report from "
MACSTR, MAC2STR(addr));
return;
}
dialog_token = *buf++;
len--;
wpa_printf(MSG_DEBUG,
"WNM: Received Collocated Interference Report frame from "
MACSTR " (dialog_token=%u)",
MAC2STR(addr), dialog_token);
wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements",
buf, len);
hex_len = 2 * len + 1;
hex = os_malloc(hex_len);
if (!hex)
return;
wpa_snprintf_hex(hex, hex_len, buf, len);
wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s",
MAC2STR(addr), dialog_token, hex);
os_free(hex);
}
int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len)
{
u8 action;
const u8 *payload;
size_t plen;
if (len < IEEE80211_HDRLEN + 2)
return -1;
payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
action = *payload++;
plen = len - IEEE80211_HDRLEN - 2;
switch (action) {
case WNM_BSS_TRANS_MGMT_QUERY:
ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
plen);
return 0;
case WNM_BSS_TRANS_MGMT_RESP:
ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
plen);
return 0;
case WNM_SLEEP_MODE_REQ:
ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
return 0;
case WNM_NOTIFICATION_REQ:
ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
plen);
return 0;
case WNM_COLLOCATED_INTERFERENCE_REPORT:
ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload,
plen);
return 0;
}
wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
action, MAC2STR(mgmt->sa));
return -1;
}
int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
struct sta_info *sta, int disassoc_timer)
{
u8 buf[1000], *pos;
struct ieee80211_mgmt *mgmt;
os_memset(buf, 0, sizeof(buf));
mgmt = (struct ieee80211_mgmt *) buf;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
mgmt->u.action.u.bss_tm_req.req_mode =
WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
mgmt->u.action.u.bss_tm_req.disassoc_timer =
host_to_le16(disassoc_timer);
mgmt->u.action.u.bss_tm_req.validity_interval = 0;
pos = mgmt->u.action.u.bss_tm_req.variable;
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
MACSTR, disassoc_timer, MAC2STR(sta->addr));
if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
"Management Request frame");
return -1;
}
return 0;
}
static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
int disassoc_timer)
{
int timeout, beacon_int;
/*
* Prevent STA from reconnecting using cached PMKSA to force
* full authentication with the authentication server (which may
* decide to reject the connection),
*/
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
beacon_int = hapd->iconf->beacon_int;
if (beacon_int < 1)
beacon_int = 100; /* best guess */
/* Calculate timeout in ms based on beacon_int in TU */
timeout = disassoc_timer * beacon_int * 128 / 125;
wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
" set to %d ms", MAC2STR(sta->addr), timeout);
sta->timeout_next = STA_DISASSOC_FROM_CLI;
eloop_cancel_timeout(ap_handle_timer, hapd, sta);
eloop_register_timeout(timeout / 1000,
timeout % 1000 * 1000,
ap_handle_timer, hapd, sta);
}
int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
struct sta_info *sta, const char *url,
int disassoc_timer)
{
u8 buf[1000], *pos;
struct ieee80211_mgmt *mgmt;
size_t url_len;
os_memset(buf, 0, sizeof(buf));
mgmt = (struct ieee80211_mgmt *) buf;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
mgmt->u.action.u.bss_tm_req.dialog_token = 1;
mgmt->u.action.u.bss_tm_req.req_mode =
WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
mgmt->u.action.u.bss_tm_req.disassoc_timer =
host_to_le16(disassoc_timer);
mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
pos = mgmt->u.action.u.bss_tm_req.variable;
/* Session Information URL */
url_len = os_strlen(url);
if (url_len > 255)
return -1;
*pos++ = url_len;
os_memcpy(pos, url, url_len);
pos += url_len;
if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
"Management Request frame");
return -1;
}
if (disassoc_timer) {
/* send disassociation frame after time-out */
set_disassoc_timer(hapd, sta, disassoc_timer);
}
return 0;
}
int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
u8 req_mode, int disassoc_timer, u8 valid_int,
- const u8 *bss_term_dur, const char *url,
- const u8 *nei_rep, size_t nei_rep_len,
+ const u8 *bss_term_dur, u8 dialog_token,
+ const char *url, const u8 *nei_rep, size_t nei_rep_len,
const u8 *mbo_attrs, size_t mbo_len)
{
u8 *buf, *pos;
struct ieee80211_mgmt *mgmt;
size_t url_len;
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
- MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
- MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
+ MACSTR
+ " req_mode=0x%x disassoc_timer=%d valid_int=0x%x dialog_token=%u",
+ MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int,
+ dialog_token);
buf = os_zalloc(1000 + nei_rep_len + mbo_len);
if (buf == NULL)
return -1;
mgmt = (struct ieee80211_mgmt *) buf;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
- mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+ mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
mgmt->u.action.u.bss_tm_req.disassoc_timer =
host_to_le16(disassoc_timer);
mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
pos = mgmt->u.action.u.bss_tm_req.variable;
if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
bss_term_dur) {
os_memcpy(pos, bss_term_dur, 12);
pos += 12;
}
if (url) {
/* Session Information URL */
url_len = os_strlen(url);
if (url_len > 255) {
os_free(buf);
return -1;
}
*pos++ = url_len;
os_memcpy(pos, url, url_len);
pos += url_len;
}
if (nei_rep) {
os_memcpy(pos, nei_rep, nei_rep_len);
pos += nei_rep_len;
}
if (mbo_len > 0) {
pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs,
mbo_len);
}
if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
wpa_printf(MSG_DEBUG,
"Failed to send BSS Transition Management Request frame");
os_free(buf);
return -1;
}
os_free(buf);
if (disassoc_timer) {
/* send disassociation frame after time-out */
set_disassoc_timer(hapd, sta, disassoc_timer);
}
return 0;
}
int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
unsigned int auto_report, unsigned int timeout)
{
u8 buf[100], *pos;
struct ieee80211_mgmt *mgmt;
u8 dialog_token = 1;
if (auto_report > 3 || timeout > 63)
return -1;
os_memset(buf, 0, sizeof(buf));
mgmt = (struct ieee80211_mgmt *) buf;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
mgmt->u.action.category = WLAN_ACTION_WNM;
mgmt->u.action.u.coloc_intf_req.action =
WNM_COLLOCATED_INTERFERENCE_REQ;
mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token;
mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2);
pos = &mgmt->u.action.u.coloc_intf_req.req_info;
pos++;
wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to "
MACSTR " (dialog_token=%u auto_report=%u timeout=%u)",
MAC2STR(sta->addr), dialog_token, auto_report, timeout);
if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0, NULL, 0, 0) < 0) {
wpa_printf(MSG_DEBUG,
"WNM: Failed to send Collocated Interference Request frame");
return -1;
}
return 0;
}
diff --git a/contrib/wpa/src/ap/wnm_ap.h b/contrib/wpa/src/ap/wnm_ap.h
index 1806ba0e0525..f86c6b2afabf 100644
--- a/contrib/wpa/src/ap/wnm_ap.h
+++ b/contrib/wpa/src/ap/wnm_ap.h
@@ -1,30 +1,30 @@
/*
* IEEE 802.11v WNM related functions and structures
* Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WNM_AP_H
#define WNM_AP_H
struct sta_info;
int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len);
int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
struct sta_info *sta, int disassoc_timer);
int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
struct sta_info *sta, const char *url,
int disassoc_timer);
int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
u8 req_mode, int disassoc_timer, u8 valid_int,
- const u8 *bss_term_dur, const char *url,
- const u8 *nei_rep, size_t nei_rep_len,
+ const u8 *bss_term_dur, u8 dialog_token,
+ const char *url, const u8 *nei_rep, size_t nei_rep_len,
const u8 *mbo_attrs, size_t mbo_len);
void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx);
int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta,
unsigned int auto_report, unsigned int timeout);
#endif /* WNM_AP_H */
diff --git a/contrib/wpa/src/ap/wpa_auth.c b/contrib/wpa/src/ap/wpa_auth.c
index 83805681ed97..6d60f262991b 100644
--- a/contrib/wpa/src/ap/wpa_auth.c
+++ b/contrib/wpa/src/ap/wpa_auth.c
@@ -1,5689 +1,5689 @@
/*
* IEEE 802.11 RSN / WPA Authenticator
* Copyright (c) 2004-2019, 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"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/state_machine.h"
#include "utils/bitfield.h"
#include "common/ieee802_11_defs.h"
#include "common/ocv.h"
#include "common/dpp.h"
#include "common/wpa_ctrl.h"
#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "crypto/aes_siv.h"
#include "crypto/crypto.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/sha384.h"
#include "crypto/random.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "drivers/driver.h"
#include "ap_config.h"
#include "ieee802_11.h"
#include "wpa_auth.h"
#include "pmksa_cache_auth.h"
#include "wpa_auth_i.h"
#include "wpa_auth_ie.h"
#define STATE_MACHINE_DATA struct wpa_state_machine
#define STATE_MACHINE_DEBUG_PREFIX "WPA"
#define STATE_MACHINE_ADDR sm->addr
static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
static int wpa_sm_step(struct wpa_state_machine *sm);
static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
u8 *data, size_t data_len);
#ifdef CONFIG_FILS
static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
u8 *buf, size_t buf_len, u16 *_key_data_len);
static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
const struct wpabuf *hlp);
#endif /* CONFIG_FILS */
static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static void wpa_request_new_ptk(struct wpa_state_machine *sm);
static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk, int force_sha256);
static void wpa_group_free(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static void wpa_group_get(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static void wpa_group_put(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static int ieee80211w_kde_len(struct wpa_state_machine *sm);
static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
static const u32 eapol_key_timeout_first = 100; /* ms */
static const u32 eapol_key_timeout_subseq = 1000; /* ms */
static const u32 eapol_key_timeout_first_group = 500; /* ms */
static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */
/* TODO: make these configurable */
static const int dot11RSNAConfigPMKLifetime = 43200;
static const int dot11RSNAConfigPMKReauthThreshold = 70;
static const int dot11RSNAConfigSATimeout = 60;
static inline int wpa_auth_mic_failure_report(
struct wpa_authenticator *wpa_auth, const u8 *addr)
{
if (wpa_auth->cb->mic_failure_report)
return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr);
return 0;
}
static inline void wpa_auth_psk_failure_report(
struct wpa_authenticator *wpa_auth, const u8 *addr)
{
if (wpa_auth->cb->psk_failure_report)
wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr);
}
static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
const u8 *addr, wpa_eapol_variable var,
int value)
{
if (wpa_auth->cb->set_eapol)
wpa_auth->cb->set_eapol(wpa_auth->cb_ctx, addr, var, value);
}
static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
const u8 *addr, wpa_eapol_variable var)
{
if (!wpa_auth->cb->get_eapol)
return -1;
return wpa_auth->cb->get_eapol(wpa_auth->cb_ctx, addr, var);
}
static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
const u8 *addr,
const u8 *p2p_dev_addr,
const u8 *prev_psk, size_t *psk_len,
int *vlan_id)
{
if (!wpa_auth->cb->get_psk)
return NULL;
return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
prev_psk, psk_len, vlan_id);
}
static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
const u8 *addr, u8 *msk, size_t *len)
{
if (!wpa_auth->cb->get_msk)
return -1;
return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len);
}
static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
int vlan_id,
enum wpa_alg alg, const u8 *addr, int idx,
u8 *key, size_t key_len,
enum key_flag key_flag)
{
if (!wpa_auth->cb->set_key)
return -1;
return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
key, key_len, key_flag);
}
static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
const u8 *addr, int idx, u8 *seq)
{
int res;
if (!wpa_auth->cb->get_seqnum)
return -1;
res = wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
#ifdef CONFIG_TESTING_OPTIONS
if (!addr && idx < 4 && wpa_auth->conf.gtk_rsc_override_set) {
wpa_printf(MSG_DEBUG,
"TESTING: Override GTK RSC %016llx --> %016llx",
(long long unsigned) WPA_GET_LE64(seq),
(long long unsigned)
WPA_GET_LE64(wpa_auth->conf.gtk_rsc_override));
os_memcpy(seq, wpa_auth->conf.gtk_rsc_override,
WPA_KEY_RSC_LEN);
}
if (!addr && idx >= 4 && idx <= 5 &&
wpa_auth->conf.igtk_rsc_override_set) {
wpa_printf(MSG_DEBUG,
"TESTING: Override IGTK RSC %016llx --> %016llx",
(long long unsigned) WPA_GET_LE64(seq),
(long long unsigned)
WPA_GET_LE64(wpa_auth->conf.igtk_rsc_override));
os_memcpy(seq, wpa_auth->conf.igtk_rsc_override,
WPA_KEY_RSC_LEN);
}
#endif /* CONFIG_TESTING_OPTIONS */
return res;
}
static inline int
wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *data, size_t data_len, int encrypt)
{
if (!wpa_auth->cb->send_eapol)
return -1;
return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len,
encrypt);
}
#ifdef CONFIG_MESH
static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
const u8 *addr)
{
if (!wpa_auth->cb->start_ampe)
return -1;
return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr);
}
#endif /* CONFIG_MESH */
int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_state_machine *sm, void *ctx),
void *cb_ctx)
{
if (!wpa_auth->cb->for_each_sta)
return 0;
return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx);
}
int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_authenticator *a, void *ctx),
void *cb_ctx)
{
if (!wpa_auth->cb->for_each_auth)
return 0;
return wpa_auth->cb->for_each_auth(wpa_auth->cb_ctx, cb, cb_ctx);
}
void wpa_auth_store_ptksa(struct wpa_authenticator *wpa_auth,
const u8 *addr, int cipher,
u32 life_time, const struct wpa_ptk *ptk)
{
if (wpa_auth->cb->store_ptksa)
wpa_auth->cb->store_ptksa(wpa_auth->cb_ctx, addr, cipher,
life_time, ptk);
}
void wpa_auth_remove_ptksa(struct wpa_authenticator *wpa_auth,
const u8 *addr, int cipher)
{
if (wpa_auth->cb->clear_ptksa)
wpa_auth->cb->clear_ptksa(wpa_auth->cb_ctx, addr, cipher);
}
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
logger_level level, const char *txt)
{
if (!wpa_auth->cb->logger)
return;
wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt);
}
void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr,
logger_level level, const char *fmt, ...)
{
char *format;
int maxlen;
va_list ap;
if (!wpa_auth->cb->logger)
return;
maxlen = os_strlen(fmt) + 100;
format = os_malloc(maxlen);
if (!format)
return;
va_start(ap, fmt);
vsnprintf(format, maxlen, fmt, ap);
va_end(ap);
wpa_auth_logger(wpa_auth, addr, level, format);
os_free(format);
}
static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
const u8 *addr, u16 reason)
{
if (!wpa_auth->cb->disconnect)
return;
wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)",
MAC2STR(addr), reason);
wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason);
}
#ifdef CONFIG_OCV
static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
struct wpa_channel_info *ci)
{
if (!wpa_auth->cb->channel_info)
return -1;
return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
}
#endif /* CONFIG_OCV */
static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth,
const u8 *addr, int vlan_id)
{
if (!wpa_auth->cb->update_vlan)
return -1;
return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id);
}
static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
if (random_get_bytes(wpa_auth->group->GMK, WPA_GMK_LEN)) {
wpa_printf(MSG_ERROR,
"Failed to get random data for WPA initialization.");
} else {
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd");
wpa_hexdump_key(MSG_DEBUG, "GMK",
wpa_auth->group->GMK, WPA_GMK_LEN);
}
if (wpa_auth->conf.wpa_gmk_rekey) {
eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
wpa_rekey_gmk, wpa_auth, NULL);
}
}
static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_group *group, *next;
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
group = wpa_auth->group;
while (group) {
wpa_group_get(wpa_auth, group);
group->GTKReKey = true;
do {
group->changed = false;
wpa_group_sm_step(wpa_auth, group);
} while (group->changed);
next = group->next;
wpa_group_put(wpa_auth, group);
group = next;
}
if (wpa_auth->conf.wpa_group_rekey) {
eloop_register_timeout(wpa_auth->conf.wpa_group_rekey,
0, wpa_rekey_gtk, wpa_auth, NULL);
}
}
static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_state_machine *sm = timeout_ctx;
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
wpa_request_new_ptk(sm);
wpa_sm_step(sm);
}
void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm)
{
if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) {
wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for "
MACSTR " (%d seconds)", MAC2STR(sm->addr),
sm->wpa_auth->conf.wpa_ptk_rekey);
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0,
wpa_rekey_ptk, sm->wpa_auth, sm);
}
}
static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
{
if (sm->pmksa == ctx)
sm->pmksa = NULL;
return 0;
}
static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
void *ctx)
{
struct wpa_authenticator *wpa_auth = ctx;
wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry);
}
static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
u8 buf[ETH_ALEN + 8 + sizeof(unsigned long)];
u8 rkey[32];
unsigned long ptr;
if (random_get_bytes(group->GMK, WPA_GMK_LEN) < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN);
/*
* Counter = PRF-256(Random number, "Init Counter",
* Local MAC Address || Time)
*/
os_memcpy(buf, wpa_auth->addr, ETH_ALEN);
wpa_get_ntp_timestamp(buf + ETH_ALEN);
ptr = (unsigned long) group;
os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
#ifdef TEST_FUZZ
os_memset(buf + ETH_ALEN, 0xab, 8);
os_memset(buf + ETH_ALEN + 8, 0xcd, sizeof(ptr));
#endif /* TEST_FUZZ */
if (random_get_bytes(rkey, sizeof(rkey)) < 0)
return -1;
if (sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
group->Counter, WPA_NONCE_LEN) < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "Key Counter",
group->Counter, WPA_NONCE_LEN);
return 0;
}
static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth,
int vlan_id, int delay_init)
{
struct wpa_group *group;
group = os_zalloc(sizeof(struct wpa_group));
if (!group)
return NULL;
group->GTKAuthenticator = true;
group->vlan_id = vlan_id;
group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
if (random_pool_ready() != 1) {
wpa_printf(MSG_INFO,
"WPA: Not enough entropy in random pool for secure operations - update keys later when the first station connects");
}
/*
* Set initial GMK/Counter value here. The actual values that will be
* used in negotiations will be set once the first station tries to
* connect. This allows more time for collecting additional randomness
* on embedded devices.
*/
if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0) {
wpa_printf(MSG_ERROR,
"Failed to get random data for WPA initialization.");
os_free(group);
return NULL;
}
group->GInit = true;
if (delay_init) {
wpa_printf(MSG_DEBUG,
"WPA: Delay group state machine start until Beacon frames have been configured");
/* Initialization is completed in wpa_init_keys(). */
} else {
wpa_group_sm_step(wpa_auth, group);
group->GInit = false;
wpa_group_sm_step(wpa_auth, group);
}
return group;
}
/**
* wpa_init - Initialize WPA authenticator
* @addr: Authenticator address
* @conf: Configuration for WPA authenticator
* @cb: Callback functions for WPA authenticator
* Returns: Pointer to WPA authenticator data or %NULL on failure
*/
struct wpa_authenticator * wpa_init(const u8 *addr,
struct wpa_auth_config *conf,
const struct wpa_auth_callbacks *cb,
void *cb_ctx)
{
struct wpa_authenticator *wpa_auth;
wpa_auth = os_zalloc(sizeof(struct wpa_authenticator));
if (!wpa_auth)
return NULL;
os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
wpa_auth->cb = cb;
wpa_auth->cb_ctx = cb_ctx;
if (wpa_auth_gen_wpa_ie(wpa_auth)) {
wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
os_free(wpa_auth);
return NULL;
}
wpa_auth->group = wpa_group_init(wpa_auth, 0, 1);
if (!wpa_auth->group) {
os_free(wpa_auth->wpa_ie);
os_free(wpa_auth);
return NULL;
}
wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
wpa_auth);
if (!wpa_auth->pmksa) {
wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
os_free(wpa_auth->group);
os_free(wpa_auth->wpa_ie);
os_free(wpa_auth);
return NULL;
}
#ifdef CONFIG_IEEE80211R_AP
wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
if (!wpa_auth->ft_pmk_cache) {
wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
os_free(wpa_auth->group);
os_free(wpa_auth->wpa_ie);
pmksa_cache_auth_deinit(wpa_auth->pmksa);
os_free(wpa_auth);
return NULL;
}
#endif /* CONFIG_IEEE80211R_AP */
if (wpa_auth->conf.wpa_gmk_rekey) {
eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
wpa_rekey_gmk, wpa_auth, NULL);
}
if (wpa_auth->conf.wpa_group_rekey) {
eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0,
wpa_rekey_gtk, wpa_auth, NULL);
}
#ifdef CONFIG_P2P
if (WPA_GET_BE32(conf->ip_addr_start)) {
int count = WPA_GET_BE32(conf->ip_addr_end) -
WPA_GET_BE32(conf->ip_addr_start) + 1;
if (count > 1000)
count = 1000;
if (count > 0)
wpa_auth->ip_pool = bitfield_alloc(count);
}
#endif /* CONFIG_P2P */
return wpa_auth;
}
int wpa_init_keys(struct wpa_authenticator *wpa_auth)
{
struct wpa_group *group = wpa_auth->group;
wpa_printf(MSG_DEBUG,
"WPA: Start group state machine to set initial keys");
wpa_group_sm_step(wpa_auth, group);
group->GInit = false;
wpa_group_sm_step(wpa_auth, group);
if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
return -1;
return 0;
}
/**
* wpa_deinit - Deinitialize WPA authenticator
* @wpa_auth: Pointer to WPA authenticator data from wpa_init()
*/
void wpa_deinit(struct wpa_authenticator *wpa_auth)
{
struct wpa_group *group, *prev;
eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
pmksa_cache_auth_deinit(wpa_auth->pmksa);
#ifdef CONFIG_IEEE80211R_AP
wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
wpa_auth->ft_pmk_cache = NULL;
wpa_ft_deinit(wpa_auth);
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
bitfield_free(wpa_auth->ip_pool);
#endif /* CONFIG_P2P */
os_free(wpa_auth->wpa_ie);
group = wpa_auth->group;
while (group) {
prev = group;
group = group->next;
os_free(prev);
}
os_free(wpa_auth);
}
/**
* wpa_reconfig - Update WPA authenticator configuration
* @wpa_auth: Pointer to WPA authenticator data from wpa_init()
* @conf: Configuration for WPA authenticator
*/
int wpa_reconfig(struct wpa_authenticator *wpa_auth,
struct wpa_auth_config *conf)
{
struct wpa_group *group;
if (!wpa_auth)
return 0;
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
if (wpa_auth_gen_wpa_ie(wpa_auth)) {
wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
return -1;
}
/*
* Reinitialize GTK to make sure it is suitable for the new
* configuration.
*/
group = wpa_auth->group;
group->GTK_len = wpa_cipher_key_len(wpa_auth->conf.wpa_group);
group->GInit = true;
wpa_group_sm_step(wpa_auth, group);
group->GInit = false;
wpa_group_sm_step(wpa_auth, group);
return 0;
}
struct wpa_state_machine *
wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *p2p_dev_addr)
{
struct wpa_state_machine *sm;
if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
return NULL;
sm = os_zalloc(sizeof(struct wpa_state_machine));
if (!sm)
return NULL;
os_memcpy(sm->addr, addr, ETH_ALEN);
if (p2p_dev_addr)
os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
sm->wpa_auth = wpa_auth;
sm->group = wpa_auth->group;
wpa_group_get(sm->wpa_auth, sm->group);
return sm;
}
int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm)
{
if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
return -1;
#ifdef CONFIG_IEEE80211R_AP
if (sm->ft_completed) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"FT authentication already completed - do not start 4-way handshake");
/* Go to PTKINITDONE state to allow GTK rekeying */
sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
sm->Pair = true;
return 0;
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_FILS
if (sm->fils_completed) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"FILS authentication already completed - do not start 4-way handshake");
/* Go to PTKINITDONE state to allow GTK rekeying */
sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
sm->Pair = true;
return 0;
}
#endif /* CONFIG_FILS */
if (sm->started) {
os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
sm->ReAuthenticationRequest = true;
return wpa_sm_step(sm);
}
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"start authentication");
sm->started = 1;
sm->Init = true;
if (wpa_sm_step(sm) == 1)
return 1; /* should not really happen */
sm->Init = false;
sm->AuthenticationRequest = true;
return wpa_sm_step(sm);
}
void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
{
/* WPA/RSN was not used - clear WPA state. This is needed if the STA
* reassociates back to the same AP while the previous entry for the
* STA has not yet been removed. */
if (!sm)
return;
sm->wpa_key_mgmt = 0;
}
static void wpa_free_sta_sm(struct wpa_state_machine *sm)
{
#ifdef CONFIG_P2P
if (WPA_GET_BE32(sm->ip_addr)) {
u32 start;
wpa_printf(MSG_DEBUG,
"P2P: Free assigned IP address %u.%u.%u.%u from "
MACSTR,
sm->ip_addr[0], sm->ip_addr[1],
sm->ip_addr[2], sm->ip_addr[3],
MAC2STR(sm->addr));
start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start);
bitfield_clear(sm->wpa_auth->ip_pool,
WPA_GET_BE32(sm->ip_addr) - start);
}
#endif /* CONFIG_P2P */
if (sm->GUpdateStationKeys) {
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = false;
}
#ifdef CONFIG_IEEE80211R_AP
os_free(sm->assoc_resp_ftie);
wpabuf_free(sm->ft_pending_req_ies);
#endif /* CONFIG_IEEE80211R_AP */
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
os_free(sm->rsnxe);
wpa_group_put(sm->wpa_auth, sm->group);
#ifdef CONFIG_DPP2
wpabuf_clear_free(sm->dpp_z);
#endif /* CONFIG_DPP2 */
bin_clear_free(sm, sizeof(*sm));
}
void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
{
struct wpa_authenticator *wpa_auth;
if (!sm)
return;
wpa_auth = sm->wpa_auth;
if (wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"strict rekeying - force GTK rekey since STA is leaving");
if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk,
wpa_auth, NULL) == -1)
eloop_register_timeout(0, 500000, wpa_rekey_gtk,
wpa_auth, NULL);
}
eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
eloop_cancel_timeout(wpa_rekey_ptk, wpa_auth, sm);
#ifdef CONFIG_IEEE80211R_AP
wpa_ft_sta_deinit(sm);
#endif /* CONFIG_IEEE80211R_AP */
if (sm->in_step_loop) {
/* Must not free state machine while wpa_sm_step() is running.
* Freeing will be completed in the end of wpa_sm_step(). */
wpa_printf(MSG_DEBUG,
"WPA: Registering pending STA state machine deinit for "
MACSTR, MAC2STR(sm->addr));
sm->pending_deinit = 1;
} else
wpa_free_sta_sm(sm);
}
static void wpa_request_new_ptk(struct wpa_state_machine *sm)
{
if (!sm)
return;
if (!sm->use_ext_key_id && sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
wpa_printf(MSG_INFO,
"WPA: PTK0 rekey not allowed, disconnect " MACSTR,
MAC2STR(sm->addr));
sm->Disconnect = true;
/* Try to encourage the STA to reconnect */
sm->disconnect_reason =
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
} else {
if (sm->use_ext_key_id)
sm->keyidx_active ^= 1; /* flip Key ID */
sm->PTKRequest = true;
sm->PTK_valid = 0;
}
}
static int wpa_replay_counter_valid(struct wpa_key_replay_counter *ctr,
const u8 *replay_counter)
{
int i;
for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
if (!ctr[i].valid)
break;
if (os_memcmp(replay_counter, ctr[i].counter,
WPA_REPLAY_COUNTER_LEN) == 0)
return 1;
}
return 0;
}
static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr,
const u8 *replay_counter)
{
int i;
for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
if (ctr[i].valid &&
(!replay_counter ||
os_memcmp(replay_counter, ctr[i].counter,
WPA_REPLAY_COUNTER_LEN) == 0))
ctr[i].valid = false;
}
}
#ifdef CONFIG_IEEE80211R_AP
static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
struct wpa_eapol_ie_parse *kde)
{
struct wpa_ie_data ie;
struct rsn_mdie *mdie;
if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
ie.num_pmkid != 1 || !ie.pmkid) {
wpa_printf(MSG_DEBUG,
"FT: No PMKR1Name in FT 4-way handshake message 2/4");
return -1;
}
os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
sm->sup_pmk_r1_name, PMKID_LEN);
if (!kde->mdie || !kde->ftie) {
wpa_printf(MSG_DEBUG,
"FT: No %s in FT 4-way handshake message 2/4",
kde->mdie ? "FTIE" : "MDIE");
return -1;
}
mdie = (struct rsn_mdie *) (kde->mdie + 2);
if (kde->mdie[1] < sizeof(struct rsn_mdie) ||
os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
return -1;
}
if (sm->assoc_resp_ftie &&
(kde->ftie[1] != sm->assoc_resp_ftie[1] ||
os_memcmp(kde->ftie, sm->assoc_resp_ftie,
2 + sm->assoc_resp_ftie[1]) != 0)) {
wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4",
kde->ftie, kde->ftie_len);
wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp",
sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]);
return -1;
}
return 0;
}
#endif /* CONFIG_IEEE80211R_AP */
static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int group)
{
/* Supplicant reported a Michael MIC error */
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key Error Request (STA detected Michael MIC failure (group=%d))",
group);
if (group && wpa_auth->conf.wpa_group != WPA_CIPHER_TKIP) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"ignore Michael MIC failure report since group cipher is not TKIP");
} else if (!group && sm->pairwise != WPA_CIPHER_TKIP) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"ignore Michael MIC failure report since pairwise cipher is not TKIP");
} else {
if (wpa_auth_mic_failure_report(wpa_auth, sm->addr) > 0)
return 1; /* STA entry was removed */
sm->dot11RSNAStatsTKIPRemoteMICFailures++;
wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++;
}
/*
* Error report is not a request for a new key handshake, but since
* Authenticator may do it, let's change the keys now anyway.
*/
wpa_request_new_ptk(sm);
return 0;
}
static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
size_t data_len)
{
struct wpa_ptk PTK;
int ok = 0;
const u8 *pmk = NULL;
size_t pmk_len;
int vlan_id = 0;
os_memset(&PTK, 0, sizeof(PTK));
for (;;) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
!wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
sm->p2p_dev_addr, pmk, &pmk_len,
&vlan_id);
if (!pmk)
break;
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
os_memcpy(sm->xxkey, pmk, pmk_len);
sm->xxkey_len = pmk_len;
}
#endif /* CONFIG_IEEE80211R_AP */
} else {
pmk = sm->PMK;
pmk_len = sm->pmk_len;
}
if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK, 0) <
0)
break;
if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
data, data_len) == 0) {
if (sm->PMK != pmk) {
os_memcpy(sm->PMK, pmk, pmk_len);
sm->pmk_len = pmk_len;
}
ok = 1;
break;
}
if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
wpa_key_mgmt_sae(sm->wpa_key_mgmt))
break;
}
if (!ok) {
wpa_printf(MSG_DEBUG,
"WPA: Earlier SNonce did not result in matching MIC");
return -1;
}
wpa_printf(MSG_DEBUG,
"WPA: Earlier SNonce resulted in matching MIC");
sm->alt_snonce_valid = 0;
if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0)
return -1;
os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
forced_memzero(&PTK, sizeof(PTK));
sm->PTK_valid = true;
return 0;
}
static bool wpa_auth_gtk_rekey_in_process(struct wpa_authenticator *wpa_auth)
{
struct wpa_group *group;
for (group = wpa_auth->group; group; group = group->next) {
if (group->GKeyDoneStations)
return true;
}
return false;
}
void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
u8 *data, size_t data_len)
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
u16 key_info, key_data_length;
enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg;
char *msgtxt;
struct wpa_eapol_ie_parse kde;
const u8 *key_data;
size_t keyhdrlen, mic_len;
u8 *mic;
if (!wpa_auth || !wpa_auth->conf.wpa || !sm)
return;
wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
keyhdrlen = sizeof(*key) + mic_len + 2;
if (data_len < sizeof(*hdr) + keyhdrlen) {
wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame");
return;
}
hdr = (struct ieee802_1x_hdr *) data;
key = (struct wpa_eapol_key *) (hdr + 1);
mic = (u8 *) (key + 1);
key_info = WPA_GET_BE16(key->key_info);
key_data = mic + mic_len + 2;
key_data_length = WPA_GET_BE16(mic + mic_len);
wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
" key_info=0x%x type=%u mic_len=%zu key_data_length=%u",
MAC2STR(sm->addr), key_info, key->type,
mic_len, key_data_length);
wpa_hexdump(MSG_MSGDUMP,
"WPA: EAPOL-Key header (ending before Key MIC)",
key, sizeof(*key));
wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC",
mic, mic_len);
if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
wpa_printf(MSG_INFO,
"WPA: Invalid EAPOL-Key frame - key_data overflow (%d > %zu)",
key_data_length,
data_len - sizeof(*hdr) - keyhdrlen);
return;
}
if (sm->wpa == WPA_VERSION_WPA2) {
if (key->type == EAPOL_KEY_TYPE_WPA) {
/*
* Some deployed station implementations seem to send
* msg 4/4 with incorrect type value in WPA2 mode.
*/
wpa_printf(MSG_DEBUG,
"Workaround: Allow EAPOL-Key with unexpected WPA type in RSN mode");
} else if (key->type != EAPOL_KEY_TYPE_RSN) {
wpa_printf(MSG_DEBUG,
"Ignore EAPOL-Key with unexpected type %d in RSN mode",
key->type);
return;
}
} else {
if (key->type != EAPOL_KEY_TYPE_WPA) {
wpa_printf(MSG_DEBUG,
"Ignore EAPOL-Key with unexpected type %d in WPA mode",
key->type);
return;
}
}
wpa_hexdump(MSG_DEBUG, "WPA: Received Key Nonce", key->key_nonce,
WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPA: Received Replay Counter",
key->replay_counter, WPA_REPLAY_COUNTER_LEN);
/* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys
* are set */
if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message");
return;
}
if (key_info & WPA_KEY_INFO_REQUEST) {
msg = REQUEST;
msgtxt = "Request";
} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
msg = GROUP_2;
msgtxt = "2/2 Group";
} else if (key_data_length == 0 ||
(mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
key_data_length == AES_BLOCK_SIZE)) {
msg = PAIRWISE_4;
msgtxt = "4/4 Pairwise";
} else {
msg = PAIRWISE_2;
msgtxt = "2/4 Pairwise";
}
if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 ||
msg == GROUP_2) {
u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK;
if (sm->pairwise == WPA_CIPHER_CCMP ||
sm->pairwise == WPA_CIPHER_GCMP) {
if (wpa_use_cmac(sm->wpa_key_mgmt) &&
!wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
"advertised support for AES-128-CMAC, but did not use it");
return;
}
if (!wpa_use_cmac(sm->wpa_key_mgmt) &&
!wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_auth_logger(wpa_auth, sm->addr,
LOGGER_WARNING,
"did not use HMAC-SHA1-AES with CCMP/GCMP");
return;
}
}
if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
return;
}
}
if (key_info & WPA_KEY_INFO_REQUEST) {
if (sm->req_replay_counter_used &&
os_memcmp(key->replay_counter, sm->req_replay_counter,
WPA_REPLAY_COUNTER_LEN) <= 0) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
"received EAPOL-Key request with replayed counter");
return;
}
}
if (!(key_info & WPA_KEY_INFO_REQUEST) &&
!wpa_replay_counter_valid(sm->key_replay, key->replay_counter)) {
int i;
if (msg == PAIRWISE_2 &&
wpa_replay_counter_valid(sm->prev_key_replay,
key->replay_counter) &&
sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
os_memcmp(sm->SNonce, key->key_nonce, WPA_NONCE_LEN) != 0)
{
/*
* Some supplicant implementations (e.g., Windows XP
* WZC) update SNonce for each EAPOL-Key 2/4. This
* breaks the workaround on accepting any of the
* pending requests, so allow the SNonce to be updated
* even if we have already sent out EAPOL-Key 3/4.
*/
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"Process SNonce update from STA based on retransmitted EAPOL-Key 1/4");
sm->update_snonce = 1;
os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
sm->alt_snonce_valid = true;
os_memcpy(sm->alt_replay_counter,
sm->key_replay[0].counter,
WPA_REPLAY_COUNTER_LEN);
goto continue_processing;
}
if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
os_memcmp(key->replay_counter, sm->alt_replay_counter,
WPA_REPLAY_COUNTER_LEN) == 0) {
/*
* Supplicant may still be using the old SNonce since
* there was two EAPOL-Key 2/4 messages and they had
* different SNonce values.
*/
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
goto continue_processing;
}
if (msg == PAIRWISE_2 &&
wpa_replay_counter_valid(sm->prev_key_replay,
key->replay_counter) &&
sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"ignore retransmitted EAPOL-Key %s - SNonce did not change",
msgtxt);
} else {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"received EAPOL-Key %s with unexpected replay counter",
msgtxt);
}
for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
if (!sm->key_replay[i].valid)
break;
wpa_hexdump(MSG_DEBUG, "pending replay counter",
sm->key_replay[i].counter,
WPA_REPLAY_COUNTER_LEN);
}
wpa_hexdump(MSG_DEBUG, "received replay counter",
key->replay_counter, WPA_REPLAY_COUNTER_LEN);
return;
}
continue_processing:
#ifdef CONFIG_FILS
if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
return;
}
#endif /* CONFIG_FILS */
switch (msg) {
case PAIRWISE_2:
if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING &&
(!sm->update_snonce ||
sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING)) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key msg 2/4 in invalid state (%d) - dropped",
sm->wpa_ptk_state);
return;
}
random_add_randomness(key->key_nonce, WPA_NONCE_LEN);
if (sm->group->reject_4way_hs_for_entropy) {
/*
* The system did not have enough entropy to generate
* strong random numbers. Reject the first 4-way
* handshake(s) and collect some entropy based on the
* information from it. Once enough entropy is
* available, the next atempt will trigger GMK/Key
* Counter update and the station will be allowed to
* continue.
*/
wpa_printf(MSG_DEBUG,
"WPA: Reject 4-way handshake to collect more entropy for random number generation");
random_mark_pool_ready();
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
break;
case PAIRWISE_4:
if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
!sm->PTK_valid) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key msg 4/4 in invalid state (%d) - dropped",
sm->wpa_ptk_state);
return;
}
break;
case GROUP_2:
if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING
|| !sm->PTK_valid) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key msg 2/2 in invalid state (%d) - dropped",
sm->wpa_ptk_group_state);
return;
}
break;
case REQUEST:
break;
}
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"received EAPOL-Key frame (%s)", msgtxt);
if (key_info & WPA_KEY_INFO_ACK) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received invalid EAPOL-Key: Key Ack set");
return;
}
if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
!(key_info & WPA_KEY_INFO_MIC)) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received invalid EAPOL-Key: Key MIC not set");
return;
}
#ifdef CONFIG_FILS
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
(key_info & WPA_KEY_INFO_MIC)) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received invalid EAPOL-Key: Key MIC set");
return;
}
#endif /* CONFIG_FILS */
sm->MICVerified = false;
if (sm->PTK_valid && !sm->update_snonce) {
if (mic_len &&
wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK,
data, data_len) &&
(msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
wpa_try_alt_snonce(sm, data, data_len))) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key with invalid MIC");
#ifdef TEST_FUZZ
wpa_printf(MSG_INFO,
"TEST: Ignore Key MIC failure for fuzz testing");
goto continue_fuzz;
#endif /* TEST_FUZZ */
return;
}
#ifdef CONFIG_FILS
if (!mic_len &&
wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
&key_data_length) < 0) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key with invalid MIC");
#ifdef TEST_FUZZ
wpa_printf(MSG_INFO,
"TEST: Ignore Key MIC failure for fuzz testing");
goto continue_fuzz;
#endif /* TEST_FUZZ */
return;
}
#endif /* CONFIG_FILS */
#ifdef TEST_FUZZ
continue_fuzz:
#endif /* TEST_FUZZ */
sm->MICVerified = true;
eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
sm->pending_1_of_4_timeout = 0;
}
if (key_info & WPA_KEY_INFO_REQUEST) {
if (sm->MICVerified) {
sm->req_replay_counter_used = 1;
os_memcpy(sm->req_replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
} else {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key request with invalid MIC");
return;
}
/*
* TODO: should decrypt key data field if encryption was used;
* even though MAC address KDE is not normally encrypted,
* supplicant is allowed to encrypt it.
*/
if (key_info & WPA_KEY_INFO_ERROR) {
if (wpa_receive_error_report(
wpa_auth, sm,
!(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0)
return; /* STA entry was removed */
} else if (key_info & WPA_KEY_INFO_KEY_TYPE) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key Request for new 4-Way Handshake");
wpa_request_new_ptk(sm);
} else if (key_data_length > 0 &&
wpa_parse_kde_ies(key_data, key_data_length,
&kde) == 0 &&
kde.mac_addr) {
} else {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key Request for GTK rekeying");
eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
if (wpa_auth_gtk_rekey_in_process(wpa_auth))
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG,
"skip new GTK rekey - already in process");
else
wpa_rekey_gtk(wpa_auth, NULL);
}
} else {
/* Do not allow the same key replay counter to be reused. */
wpa_replay_counter_mark_invalid(sm->key_replay,
key->replay_counter);
if (msg == PAIRWISE_2) {
/*
* Maintain a copy of the pending EAPOL-Key frames in
* case the EAPOL-Key frame was retransmitted. This is
* needed to allow EAPOL-Key msg 2/4 reply to another
* pending msg 1/4 to update the SNonce to work around
* unexpected supplicant behavior.
*/
os_memcpy(sm->prev_key_replay, sm->key_replay,
sizeof(sm->key_replay));
} else {
os_memset(sm->prev_key_replay, 0,
sizeof(sm->prev_key_replay));
}
/*
* Make sure old valid counters are not accepted anymore and
* do not get copied again.
*/
wpa_replay_counter_mark_invalid(sm->key_replay, NULL);
}
os_free(sm->last_rx_eapol_key);
sm->last_rx_eapol_key = os_memdup(data, data_len);
if (!sm->last_rx_eapol_key)
return;
sm->last_rx_eapol_key_len = data_len;
sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE);
sm->EAPOLKeyReceived = true;
sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST);
os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN);
wpa_sm_step(sm);
}
static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
const u8 *gnonce, u8 *gtk, size_t gtk_len)
{
u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN];
u8 *pos;
int ret = 0;
/* GTK = PRF-X(GMK, "Group key expansion",
* AA || GNonce || Time || random data)
* The example described in the IEEE 802.11 standard uses only AA and
* GNonce as inputs here. Add some more entropy since this derivation
* is done only at the Authenticator and as such, does not need to be
* exactly same.
*/
os_memset(data, 0, sizeof(data));
os_memcpy(data, addr, ETH_ALEN);
os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
pos = data + ETH_ALEN + WPA_NONCE_LEN;
wpa_get_ntp_timestamp(pos);
#ifdef TEST_FUZZ
os_memset(pos, 0xef, 8);
#endif /* TEST_FUZZ */
pos += 8;
if (random_get_bytes(pos, gtk_len) < 0)
ret = -1;
#ifdef CONFIG_SHA384
if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
gtk, gtk_len) < 0)
ret = -1;
#else /* CONFIG_SHA384 */
#ifdef CONFIG_SHA256
if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
gtk, gtk_len) < 0)
ret = -1;
#else /* CONFIG_SHA256 */
if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data),
gtk, gtk_len) < 0)
ret = -1;
#endif /* CONFIG_SHA256 */
#endif /* CONFIG_SHA384 */
forced_memzero(data, sizeof(data));
return ret;
}
static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
struct wpa_state_machine *sm = timeout_ctx;
sm->pending_1_of_4_timeout = 0;
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout");
sm->TimeoutEvt = true;
wpa_sm_step(sm);
}
void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int key_info,
const u8 *key_rsc, const u8 *nonce,
const u8 *kde, size_t kde_len,
int keyidx, int encr, int force_version)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
size_t len, mic_len, keyhdrlen;
int alg;
int key_data_len, pad_len = 0;
u8 *buf, *pos;
int version, pairwise;
int i;
u8 *key_mic, *key_data;
mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
keyhdrlen = sizeof(*key) + mic_len + 2;
len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
if (force_version)
version = force_version;
else if (wpa_use_akm_defined(sm->wpa_key_mgmt))
version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
else if (wpa_use_cmac(sm->wpa_key_mgmt))
version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
else if (sm->pairwise != WPA_CIPHER_TKIP)
version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
else
version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
wpa_printf(MSG_DEBUG,
"WPA: Send EAPOL(version=%d secure=%d mic=%d ack=%d install=%d pairwise=%d kde_len=%zu keyidx=%d encr=%d)",
version,
(key_info & WPA_KEY_INFO_SECURE) ? 1 : 0,
(key_info & WPA_KEY_INFO_MIC) ? 1 : 0,
(key_info & WPA_KEY_INFO_ACK) ? 1 : 0,
(key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0,
pairwise, kde_len, keyidx, encr);
key_data_len = kde_len;
if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
pad_len = key_data_len % 8;
if (pad_len)
pad_len = 8 - pad_len;
key_data_len += pad_len + 8;
}
len += key_data_len;
if (!mic_len && encr)
len += AES_BLOCK_SIZE;
hdr = os_zalloc(len);
if (!hdr)
return;
hdr->version = conf->eapol_version;
hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
hdr->length = host_to_be16(len - sizeof(*hdr));
key = (struct wpa_eapol_key *) (hdr + 1);
key_mic = (u8 *) (key + 1);
key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
key->type = sm->wpa == WPA_VERSION_WPA2 ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info |= version;
if (encr && sm->wpa == WPA_VERSION_WPA2)
key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
if (sm->wpa != WPA_VERSION_WPA2)
key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT;
WPA_PUT_BE16(key->key_info, key_info);
alg = pairwise ? sm->pairwise : conf->wpa_group;
if (sm->wpa == WPA_VERSION_WPA2 && !pairwise)
WPA_PUT_BE16(key->key_length, 0);
else
WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
os_memcpy(sm->key_replay[i].counter,
sm->key_replay[i - 1].counter,
WPA_REPLAY_COUNTER_LEN);
}
inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
os_memcpy(key->replay_counter, sm->key_replay[0].counter,
WPA_REPLAY_COUNTER_LEN);
wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter",
key->replay_counter, WPA_REPLAY_COUNTER_LEN);
sm->key_replay[0].valid = true;
if (nonce)
os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);
if (key_rsc)
os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
if (kde && !encr) {
os_memcpy(key_data, kde, kde_len);
WPA_PUT_BE16(key_mic + mic_len, kde_len);
#ifdef CONFIG_FILS
} else if (!mic_len && kde) {
const u8 *aad[1];
size_t aad_len[1];
WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len);
wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
kde, kde_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: KEK",
sm->PTK.kek, sm->PTK.kek_len);
/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
* to Key Data (exclusive). */
aad[0] = (u8 *) hdr;
aad_len[0] = key_mic + 2 - (u8 *) hdr;
if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len,
1, aad, aad_len, key_mic + 2) < 0) {
wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed");
return;
}
wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
key_mic + 2, AES_BLOCK_SIZE + kde_len);
#endif /* CONFIG_FILS */
} else if (encr && kde) {
buf = os_zalloc(key_data_len);
if (!buf) {
os_free(hdr);
return;
}
pos = buf;
os_memcpy(pos, kde, kde_len);
pos += kde_len;
if (pad_len)
*pos++ = 0xdd;
wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
buf, key_data_len);
if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_printf(MSG_DEBUG,
"WPA: Encrypt Key Data using AES-WRAP (KEK length %zu)",
sm->PTK.kek_len);
if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
(key_data_len - 8) / 8, buf, key_data)) {
os_free(hdr);
os_free(buf);
return;
}
WPA_PUT_BE16(key_mic + mic_len, key_data_len);
#ifndef CONFIG_NO_RC4
} else if (sm->PTK.kek_len == 16) {
u8 ek[32];
wpa_printf(MSG_DEBUG,
"WPA: Encrypt Key Data using RC4");
os_memcpy(key->key_iv,
sm->group->Counter + WPA_NONCE_LEN - 16, 16);
inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
os_memcpy(ek, key->key_iv, 16);
os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
os_memcpy(key_data, buf, key_data_len);
rc4_skip(ek, 32, 256, key_data, key_data_len);
WPA_PUT_BE16(key_mic + mic_len, key_data_len);
#endif /* CONFIG_NO_RC4 */
} else {
os_free(hdr);
os_free(buf);
return;
}
os_free(buf);
}
if (key_info & WPA_KEY_INFO_MIC) {
if (!sm->PTK_valid || !mic_len) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PTK not valid when sending EAPOL-Key frame");
os_free(hdr);
return;
}
if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
sm->wpa_key_mgmt, version,
(u8 *) hdr, len, key_mic) < 0) {
os_free(hdr);
return;
}
#ifdef CONFIG_TESTING_OPTIONS
if (!pairwise &&
conf->corrupt_gtk_rekey_mic_probability > 0.0 &&
drand48() < conf->corrupt_gtk_rekey_mic_probability) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"Corrupting group EAPOL-Key Key MIC");
key_mic[0]++;
}
#endif /* CONFIG_TESTING_OPTIONS */
}
wpa_auth_set_eapol(wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, 1);
wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len,
sm->pairwise_set);
os_free(hdr);
}
static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int key_info,
const u8 *key_rsc, const u8 *nonce,
const u8 *kde, size_t kde_len,
int keyidx, int encr)
{
int timeout_ms;
int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
u32 ctr;
if (!sm)
return;
__wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
keyidx, encr, 0);
ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
if (ctr == 1 && wpa_auth->conf.tx_status)
timeout_ms = pairwise ? eapol_key_timeout_first :
eapol_key_timeout_first_group;
else
timeout_ms = eapol_key_timeout_subseq;
if (wpa_auth->conf.wpa_disable_eapol_key_retries &&
(!pairwise || (key_info & WPA_KEY_INFO_MIC)))
timeout_ms = eapol_key_timeout_no_retrans;
if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
sm->pending_1_of_4_timeout = 1;
#ifdef TEST_FUZZ
timeout_ms = 1;
#endif /* TEST_FUZZ */
wpa_printf(MSG_DEBUG,
"WPA: Use EAPOL-Key timeout of %u ms (retry counter %u)",
timeout_ms, ctr);
eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
}
static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK,
u8 *data, size_t data_len)
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
u16 key_info;
int ret = 0;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
size_t mic_len = wpa_mic_len(akmp, pmk_len);
if (data_len < sizeof(*hdr) + sizeof(*key))
return -1;
hdr = (struct ieee802_1x_hdr *) data;
key = (struct wpa_eapol_key *) (hdr + 1);
mic_pos = (u8 *) (key + 1);
key_info = WPA_GET_BE16(key->key_info);
os_memcpy(mic, mic_pos, mic_len);
os_memset(mic_pos, 0, mic_len);
if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
key_info & WPA_KEY_INFO_TYPE_MASK,
data, data_len, mic_pos) ||
os_memcmp_const(mic, mic_pos, mic_len) != 0)
ret = -1;
os_memcpy(mic_pos, mic, mic_len);
return ret;
}
void wpa_remove_ptk(struct wpa_state_machine *sm)
{
sm->PTK_valid = false;
os_memset(&sm->PTK, 0, sizeof(sm->PTK));
wpa_auth_remove_ptksa(sm->wpa_auth, sm->addr, sm->pairwise);
if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
0, KEY_FLAG_PAIRWISE))
wpa_printf(MSG_DEBUG,
"RSN: PTK removal from the driver failed");
if (sm->use_ext_key_id &&
wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 1, NULL,
0, KEY_FLAG_PAIRWISE))
wpa_printf(MSG_DEBUG,
"RSN: PTK Key ID 1 removal from the driver failed");
sm->pairwise_set = false;
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
}
int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
{
int remove_ptk = 1;
if (!sm)
return -1;
wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"event %d notification", event);
switch (event) {
case WPA_AUTH:
#ifdef CONFIG_MESH
/* PTKs are derived through AMPE */
if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) {
/* not mesh */
break;
}
return 0;
#endif /* CONFIG_MESH */
case WPA_ASSOC:
break;
case WPA_DEAUTH:
case WPA_DISASSOC:
sm->DeauthenticationRequest = true;
#ifdef CONFIG_IEEE80211R_AP
os_memset(sm->PMK, 0, sizeof(sm->PMK));
sm->pmk_len = 0;
os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
sm->xxkey_len = 0;
os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
sm->pmk_r1_len = 0;
#endif /* CONFIG_IEEE80211R_AP */
break;
case WPA_REAUTH:
case WPA_REAUTH_EAPOL:
if (!sm->started) {
/*
* When using WPS, we may end up here if the STA
* manages to re-associate without the previous STA
* entry getting removed. Consequently, we need to make
* sure that the WPA state machines gets initialized
* properly at this point.
*/
wpa_printf(MSG_DEBUG,
"WPA state machine had not been started - initialize now");
sm->started = 1;
sm->Init = true;
if (wpa_sm_step(sm) == 1)
return 1; /* should not really happen */
sm->Init = false;
sm->AuthenticationRequest = true;
break;
}
if (!sm->use_ext_key_id &&
sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
wpa_printf(MSG_INFO,
"WPA: PTK0 rekey not allowed, disconnect "
MACSTR, MAC2STR(sm->addr));
sm->Disconnect = true;
/* Try to encourage the STA to reconnect */
sm->disconnect_reason =
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
break;
}
if (sm->use_ext_key_id)
sm->keyidx_active ^= 1; /* flip Key ID */
if (sm->GUpdateStationKeys) {
/*
* Reauthentication cancels the pending group key
* update for this STA.
*/
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = false;
sm->PtkGroupInit = true;
}
sm->ReAuthenticationRequest = true;
break;
case WPA_ASSOC_FT:
#ifdef CONFIG_IEEE80211R_AP
wpa_printf(MSG_DEBUG,
"FT: Retry PTK configuration after association");
wpa_ft_install_ptk(sm, 1);
/* Using FT protocol, not WPA auth state machine */
sm->ft_completed = 1;
wpa_auth_set_ptk_rekey_timer(sm);
return 0;
#else /* CONFIG_IEEE80211R_AP */
break;
#endif /* CONFIG_IEEE80211R_AP */
case WPA_ASSOC_FILS:
#ifdef CONFIG_FILS
wpa_printf(MSG_DEBUG,
"FILS: TK configuration after association");
fils_set_tk(sm);
sm->fils_completed = 1;
return 0;
#else /* CONFIG_FILS */
break;
#endif /* CONFIG_FILS */
case WPA_DRV_STA_REMOVED:
sm->tk_already_set = false;
return 0;
}
#ifdef CONFIG_IEEE80211R_AP
sm->ft_completed = 0;
#endif /* CONFIG_IEEE80211R_AP */
if (sm->mgmt_frame_prot && event == WPA_AUTH)
remove_ptk = 0;
#ifdef CONFIG_FILS
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
(event == WPA_AUTH || event == WPA_ASSOC))
remove_ptk = 0;
#endif /* CONFIG_FILS */
if (remove_ptk) {
sm->PTK_valid = false;
os_memset(&sm->PTK, 0, sizeof(sm->PTK));
if (event != WPA_REAUTH_EAPOL)
wpa_remove_ptk(sm);
}
if (sm->in_step_loop) {
/*
* wpa_sm_step() is already running - avoid recursive call to
* it by making the existing loop process the new update.
*/
sm->changed = true;
return 0;
}
return wpa_sm_step(sm);
}
SM_STATE(WPA_PTK, INITIALIZE)
{
SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk);
if (sm->Init) {
/* Init flag is not cleared here, so avoid busy
* loop by claiming nothing changed. */
sm->changed = false;
}
sm->keycount = 0;
if (sm->GUpdateStationKeys)
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = false;
if (sm->wpa == WPA_VERSION_WPA)
sm->PInitAKeys = false;
if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and
* Local AA > Remote AA)) */) {
sm->Pair = true;
}
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0);
wpa_remove_ptk(sm);
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
sm->TimeoutCtr = 0;
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_authorized, 0);
}
}
SM_STATE(WPA_PTK, DISCONNECT)
{
u16 reason = sm->disconnect_reason;
SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk);
sm->Disconnect = false;
sm->disconnect_reason = 0;
if (!reason)
reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason);
}
SM_STATE(WPA_PTK, DISCONNECTED)
{
SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk);
sm->DeauthenticationRequest = false;
}
SM_STATE(WPA_PTK, AUTHENTICATION)
{
SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk);
os_memset(&sm->PTK, 0, sizeof(sm->PTK));
sm->PTK_valid = false;
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto,
1);
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1);
sm->AuthenticationRequest = false;
}
static void wpa_group_ensure_init(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
if (group->first_sta_seen)
return;
/*
* System has run bit further than at the time hostapd was started
* potentially very early during boot up. This provides better chances
* of collecting more randomness on embedded systems. Re-initialize the
* GMK and Counter here to improve their strength if there was not
* enough entropy available immediately after system startup.
*/
wpa_printf(MSG_DEBUG,
"WPA: Re-initialize GMK/Counter on first station");
if (random_pool_ready() != 1) {
wpa_printf(MSG_INFO,
"WPA: Not enough entropy in random pool to proceed - reject first 4-way handshake");
group->reject_4way_hs_for_entropy = true;
} else {
group->first_sta_seen = true;
group->reject_4way_hs_for_entropy = false;
}
if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 ||
wpa_gtk_update(wpa_auth, group) < 0 ||
wpa_group_config_group_keys(wpa_auth, group) < 0) {
wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed");
group->first_sta_seen = false;
group->reject_4way_hs_for_entropy = true;
}
}
SM_STATE(WPA_PTK, AUTHENTICATION2)
{
SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk);
wpa_group_ensure_init(sm->wpa_auth, sm->group);
sm->ReAuthenticationRequest = false;
/*
* Definition of ANonce selection in IEEE Std 802.11i-2004 is somewhat
* ambiguous. The Authenticator state machine uses a counter that is
* incremented by one for each 4-way handshake. However, the security
* analysis of 4-way handshake points out that unpredictable nonces
* help in preventing precomputation attacks. Instead of the state
* machine definition, use an unpredictable nonce value here to provide
* stronger protection against potential precomputation attacks.
*/
if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_ERROR,
"WPA: Failed to get random data for ANonce.");
sm->Disconnect = true;
return;
}
wpa_hexdump(MSG_DEBUG, "WPA: Assign ANonce", sm->ANonce,
WPA_NONCE_LEN);
/* IEEE 802.11i does not clear TimeoutCtr here, but this is more
* logical place than INITIALIZE since AUTHENTICATION2 can be
* re-entered on ReAuthenticationRequest without going through
* INITIALIZE. */
sm->TimeoutCtr = 0;
}
static int wpa_auth_sm_ptk_update(struct wpa_state_machine *sm)
{
if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_ERROR,
"WPA: Failed to get random data for ANonce");
sm->Disconnect = true;
return -1;
}
wpa_hexdump(MSG_DEBUG, "WPA: Assign new ANonce", sm->ANonce,
WPA_NONCE_LEN);
sm->TimeoutCtr = 0;
return 0;
}
SM_STATE(WPA_PTK, INITPMK)
{
u8 msk[2 * PMK_LEN];
size_t len = 2 * PMK_LEN;
SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
#ifdef CONFIG_IEEE80211R_AP
sm->xxkey_len = 0;
#endif /* CONFIG_IEEE80211R_AP */
if (sm->pmksa) {
wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
sm->pmk_len = sm->pmksa->pmk_len;
#ifdef CONFIG_DPP
} else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
wpa_printf(MSG_DEBUG,
"DPP: No PMKSA cache entry for STA - reject connection");
sm->Disconnect = true;
sm->disconnect_reason = WLAN_REASON_INVALID_PMKID;
return;
#endif /* CONFIG_DPP */
} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
unsigned int pmk_len;
if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
pmk_len = PMK_LEN_SUITE_B_192;
else
pmk_len = PMK_LEN;
wpa_printf(MSG_DEBUG,
"WPA: PMK from EAPOL state machine (MSK len=%zu PMK len=%u)",
len, pmk_len);
if (len < pmk_len) {
wpa_printf(MSG_DEBUG,
"WPA: MSK not long enough (%zu) to create PMK (%u)",
len, pmk_len);
sm->Disconnect = true;
return;
}
os_memcpy(sm->PMK, msk, pmk_len);
sm->pmk_len = pmk_len;
#ifdef CONFIG_IEEE80211R_AP
if (len >= 2 * PMK_LEN) {
if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN);
sm->xxkey_len = SHA384_MAC_LEN;
} else {
os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
sm->xxkey_len = PMK_LEN;
}
}
#endif /* CONFIG_IEEE80211R_AP */
} else {
wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
sm->wpa_auth->cb->get_msk);
sm->Disconnect = true;
return;
}
forced_memzero(msk, sizeof(msk));
sm->req_replay_counter_used = 0;
/* IEEE 802.11i does not set keyRun to false, but not doing this
* will break reauthentication since EAPOL state machines may not be
* get into AUTHENTICATING state that clears keyRun before WPA state
* machine enters AUTHENTICATION2 state and goes immediately to INITPMK
* state and takes PMK from the previously used AAA Key. This will
* eventually fail in 4-Way Handshake because Supplicant uses PMK
* derived from the new AAA Key. Setting keyRun = false here seems to
* be good workaround for this issue. */
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, false);
}
SM_STATE(WPA_PTK, INITPSK)
{
const u8 *psk;
size_t psk_len;
SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
&psk_len, NULL);
if (psk) {
os_memcpy(sm->PMK, psk, psk_len);
sm->pmk_len = psk_len;
#ifdef CONFIG_IEEE80211R_AP
os_memcpy(sm->xxkey, psk, PMK_LEN);
sm->xxkey_len = PMK_LEN;
#endif /* CONFIG_IEEE80211R_AP */
}
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sm) && sm->pmksa) {
wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
sm->pmk_len = sm->pmksa->pmk_len;
#ifdef CONFIG_IEEE80211R_AP
os_memcpy(sm->xxkey, sm->pmksa->pmk, sm->pmksa->pmk_len);
sm->xxkey_len = sm->pmksa->pmk_len;
#endif /* CONFIG_IEEE80211R_AP */
}
#endif /* CONFIG_SAE */
sm->req_replay_counter_used = 0;
}
SM_STATE(WPA_PTK, PTKSTART)
{
u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL;
size_t pmkid_len = 0;
SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
sm->PTKRequest = false;
sm->TimeoutEvt = false;
sm->alt_snonce_valid = false;
sm->TimeoutCtr++;
if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
}
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 1/4 msg of 4-Way Handshake");
/*
* For infrastructure BSS cases, it is better for the AP not to include
* the PMKID KDE in EAPOL-Key msg 1/4 since it could be used to initiate
* offline search for the passphrase/PSK without having to be able to
* capture a 4-way handshake from a STA that has access to the network.
*
* For IBSS cases, addition of PMKID KDE could be considered even with
* WPA2-PSK cases that use multiple PSKs, but only if there is a single
* possible PSK for this STA. However, this should not be done unless
* there is support for using that information on the supplicant side.
* The concern about exposing PMKID unnecessarily in infrastructure BSS
* cases would also apply here, but at least in the IBSS case, this
* would cover a potential real use case.
*/
if (sm->wpa == WPA_VERSION_WPA2 &&
(wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
(sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) ||
wpa_key_mgmt_sae(sm->wpa_key_mgmt)) &&
sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
pmkid = buf;
pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
if (sm->pmksa) {
wpa_hexdump(MSG_DEBUG,
"RSN: Message 1/4 PMKID from PMKSA entry",
sm->pmksa->pmkid, PMKID_LEN);
os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
sm->pmksa->pmkid, PMKID_LEN);
} else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
/* No KCK available to derive PMKID */
wpa_printf(MSG_DEBUG,
"RSN: No KCK available to derive PMKID for message 1/4");
pmkid = NULL;
#ifdef CONFIG_FILS
} else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
if (sm->pmkid_set) {
wpa_hexdump(MSG_DEBUG,
"RSN: Message 1/4 PMKID from FILS/ERP",
sm->pmkid, PMKID_LEN);
os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
sm->pmkid, PMKID_LEN);
} else {
/* No PMKID available */
wpa_printf(MSG_DEBUG,
"RSN: No FILS/ERP PMKID available for message 1/4");
pmkid = NULL;
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R_AP
} else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
sm->ft_completed) {
wpa_printf(MSG_DEBUG,
"FT: No PMKID in message 1/4 when using FT protocol");
pmkid = NULL;
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
} else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
if (sm->pmkid_set) {
wpa_hexdump(MSG_DEBUG,
"RSN: Message 1/4 PMKID from SAE",
sm->pmkid, PMKID_LEN);
os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
sm->pmkid, PMKID_LEN);
} else {
/* No PMKID available */
wpa_printf(MSG_DEBUG,
"RSN: No SAE PMKID available for message 1/4");
pmkid = NULL;
}
#endif /* CONFIG_SAE */
} else {
/*
* Calculate PMKID since no PMKSA cache entry was
* available with pre-calculated PMKID.
*/
rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
sm->wpa_key_mgmt);
wpa_hexdump(MSG_DEBUG,
"RSN: Message 1/4 PMKID derived from PMK",
&pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN);
}
}
if (!pmkid)
pmkid_len = 0;
wpa_send_eapol(sm->wpa_auth, sm,
WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
sm->ANonce, pmkid, pmkid_len, 0, 0);
}
static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk, int force_sha256)
{
const u8 *z = NULL;
size_t z_len = 0, kdk_len;
int akmp;
if (sm->wpa_auth->conf.force_kdk_derivation ||
(sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
kdk_len = WPA_KDK_MAX_LEN;
else
kdk_len = 0;
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
if (sm->ft_completed) {
u8 ptk_name[WPA_PMK_NAME_LEN];
return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
sm->SNonce, sm->ANonce,
sm->addr, sm->wpa_auth->addr,
sm->pmk_r1_name,
ptk, ptk_name,
sm->wpa_key_mgmt,
sm->pairwise,
kdk_len);
}
return wpa_auth_derive_ptk_ft(sm, ptk);
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_DPP2
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
z = wpabuf_head(sm->dpp_z);
z_len = wpabuf_len(sm->dpp_z);
}
#endif /* CONFIG_DPP2 */
akmp = sm->wpa_key_mgmt;
if (force_sha256)
akmp |= WPA_KEY_MGMT_PSK_SHA256;
return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
ptk, akmp, sm->pairwise, z, z_len, kdk_len);
}
#ifdef CONFIG_FILS
int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
size_t pmk_len, const u8 *snonce, const u8 *anonce,
const u8 *dhss, size_t dhss_len,
struct wpabuf *g_sta, struct wpabuf *g_ap)
{
u8 ick[FILS_ICK_MAX_LEN];
size_t ick_len;
int res;
u8 fils_ft[FILS_FT_MAX_LEN];
size_t fils_ft_len = 0, kdk_len;
if (sm->wpa_auth->conf.force_kdk_derivation ||
(sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
kdk_len = WPA_KDK_MAX_LEN;
else
kdk_len = 0;
res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
snonce, anonce, dhss, dhss_len,
&sm->PTK, ick, &ick_len,
sm->wpa_key_mgmt, sm->pairwise,
fils_ft, &fils_ft_len, kdk_len);
if (res < 0)
return res;
sm->PTK_valid = true;
sm->tk_already_set = false;
#ifdef CONFIG_IEEE80211R_AP
if (fils_ft_len) {
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
struct wpa_auth_config *conf = &wpa_auth->conf;
u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
if (wpa_derive_pmk_r0(fils_ft, fils_ft_len,
conf->ssid, conf->ssid_len,
conf->mobility_domain,
conf->r0_key_holder,
conf->r0_key_holder_len,
sm->addr, pmk_r0, pmk_r0_name,
use_sha384) < 0)
return -1;
wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
forced_memzero(fils_ft, sizeof(fils_ft));
res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
sm->addr, sm->pmk_r1_name,
use_sha384);
forced_memzero(pmk_r0, PMK_LEN_MAX);
if (res < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
WPA_PMK_NAME_LEN);
sm->pmk_r1_name_valid = 1;
}
#endif /* CONFIG_IEEE80211R_AP */
res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
sm->addr, sm->wpa_auth->addr,
g_sta ? wpabuf_head(g_sta) : NULL,
g_sta ? wpabuf_len(g_sta) : 0,
g_ap ? wpabuf_head(g_ap) : NULL,
g_ap ? wpabuf_len(g_ap) : 0,
sm->wpa_key_mgmt, sm->fils_key_auth_sta,
sm->fils_key_auth_ap,
&sm->fils_key_auth_len);
forced_memzero(ick, sizeof(ick));
/* Store nonces for (Re)Association Request/Response frame processing */
os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
return res;
}
static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
u8 *buf, size_t buf_len, u16 *_key_data_len)
{
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
u8 *pos;
u16 key_data_len;
u8 *tmp;
const u8 *aad[1];
size_t aad_len[1];
hdr = (struct ieee802_1x_hdr *) buf;
key = (struct wpa_eapol_key *) (hdr + 1);
pos = (u8 *) (key + 1);
key_data_len = WPA_GET_BE16(pos);
if (key_data_len < AES_BLOCK_SIZE ||
key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
"No room for AES-SIV data in the frame");
return -1;
}
pos += 2; /* Pointing at the Encrypted Key Data field */
tmp = os_malloc(key_data_len);
if (!tmp)
return -1;
/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
* to Key Data (exclusive). */
aad[0] = buf;
aad_len[0] = pos - buf;
if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len,
1, aad, aad_len, tmp) < 0) {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
"Invalid AES-SIV data in the frame");
bin_clear_free(tmp, key_data_len);
return -1;
}
/* AEAD decryption and validation completed successfully */
key_data_len -= AES_BLOCK_SIZE;
wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
tmp, key_data_len);
/* Replace Key Data field with the decrypted version */
os_memcpy(pos, tmp, key_data_len);
pos -= 2; /* Key Data Length field */
WPA_PUT_BE16(pos, key_data_len);
bin_clear_free(tmp, key_data_len);
if (_key_data_len)
*_key_data_len = key_data_len;
return 0;
}
const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
const u8 *ies, size_t ies_len,
const u8 *fils_session)
{
const u8 *ie, *end;
const u8 *session = NULL;
if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
wpa_printf(MSG_DEBUG,
"FILS: Not a FILS AKM - reject association");
return NULL;
}
/* Verify Session element */
ie = ies;
end = ((const u8 *) ie) + ies_len;
while (ie + 1 < end) {
if (ie + 2 + ie[1] > end)
break;
if (ie[0] == WLAN_EID_EXTENSION &&
ie[1] >= 1 + FILS_SESSION_LEN &&
ie[2] == WLAN_EID_EXT_FILS_SESSION) {
session = ie;
break;
}
ie += 2 + ie[1];
}
if (!session) {
wpa_printf(MSG_DEBUG,
"FILS: %s: Could not find FILS Session element in Assoc Req - reject",
__func__);
return NULL;
}
if (!fils_session) {
wpa_printf(MSG_DEBUG,
"FILS: %s: Could not find FILS Session element in STA entry - reject",
__func__);
return NULL;
}
if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
fils_session, FILS_SESSION_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
session + 3, FILS_SESSION_LEN);
return NULL;
}
return session;
}
int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
size_t ies_len)
{
struct ieee802_11_elems elems;
if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
wpa_printf(MSG_DEBUG,
"FILS: Failed to parse decrypted elements");
return -1;
}
if (!elems.fils_session) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
return -1;
}
if (!elems.fils_key_confirm) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
return -1;
}
if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
wpa_printf(MSG_DEBUG,
"FILS: Unexpected Key-Auth length %d (expected %zu)",
elems.fils_key_confirm_len,
sm->fils_key_auth_len);
return -1;
}
if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
sm->fils_key_auth_len) != 0) {
wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
elems.fils_key_confirm, elems.fils_key_confirm_len);
wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
sm->fils_key_auth_sta, sm->fils_key_auth_len);
return -1;
}
return 0;
}
int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
const struct ieee80211_mgmt *mgmt, size_t frame_len,
u8 *pos, size_t left)
{
u16 fc, stype;
const u8 *end, *ie_start, *ie, *session, *crypt;
const u8 *aad[5];
size_t aad_len[5];
if (!sm || !sm->PTK_valid) {
wpa_printf(MSG_DEBUG,
"FILS: No KEK to decrypt Assocication Request frame");
return -1;
}
if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
wpa_printf(MSG_DEBUG,
"FILS: Not a FILS AKM - reject association");
return -1;
}
end = ((const u8 *) mgmt) + frame_len;
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
if (stype == WLAN_FC_STYPE_REASSOC_REQ)
ie_start = mgmt->u.reassoc_req.variable;
else
ie_start = mgmt->u.assoc_req.variable;
ie = ie_start;
/*
* Find FILS Session element which is the last unencrypted element in
* the frame.
*/
session = wpa_fils_validate_fils_session(sm, ie, end - ie,
fils_session);
if (!session) {
wpa_printf(MSG_DEBUG, "FILS: Session validation failed");
return -1;
}
crypt = session + 2 + session[1];
if (end - crypt < AES_BLOCK_SIZE) {
wpa_printf(MSG_DEBUG,
"FILS: Too short frame to include AES-SIV data");
return -1;
}
/* AES-SIV AAD vectors */
/* The STA's MAC address */
aad[0] = mgmt->sa;
aad_len[0] = ETH_ALEN;
/* The AP's BSSID */
aad[1] = mgmt->da;
aad_len[1] = ETH_ALEN;
/* The STA's nonce */
aad[2] = sm->SNonce;
aad_len[2] = FILS_NONCE_LEN;
/* The AP's nonce */
aad[3] = sm->ANonce;
aad_len[3] = FILS_NONCE_LEN;
/*
* The (Re)Association Request frame from the Capability Information
* field to the FILS Session element (both inclusive).
*/
aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info;
aad_len[4] = crypt - aad[4];
if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt,
5, aad, aad_len, pos + (crypt - ie_start)) < 0) {
wpa_printf(MSG_DEBUG,
"FILS: Invalid AES-SIV data in the frame");
return -1;
}
wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
pos, left - AES_BLOCK_SIZE);
if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) {
wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed");
return -1;
}
return left - AES_BLOCK_SIZE;
}
int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
size_t current_len, size_t max_len,
const struct wpabuf *hlp)
{
u8 *end = buf + max_len;
u8 *pos = buf + current_len;
struct ieee80211_mgmt *mgmt;
struct wpabuf *plain;
const u8 *aad[5];
size_t aad_len[5];
if (!sm || !sm->PTK_valid)
return -1;
wpa_hexdump(MSG_DEBUG,
"FILS: Association Response frame before FILS processing",
buf, current_len);
mgmt = (struct ieee80211_mgmt *) buf;
/* AES-SIV AAD vectors */
/* The AP's BSSID */
aad[0] = mgmt->sa;
aad_len[0] = ETH_ALEN;
/* The STA's MAC address */
aad[1] = mgmt->da;
aad_len[1] = ETH_ALEN;
/* The AP's nonce */
aad[2] = sm->ANonce;
aad_len[2] = FILS_NONCE_LEN;
/* The STA's nonce */
aad[3] = sm->SNonce;
aad_len[3] = FILS_NONCE_LEN;
/*
* The (Re)Association Response frame from the Capability Information
* field (the same offset in both Association and Reassociation
* Response frames) to the FILS Session element (both inclusive).
*/
aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info;
aad_len[4] = pos - aad[4];
/* The following elements will be encrypted with AES-SIV */
plain = fils_prepare_plainbuf(sm, hlp);
if (!plain) {
wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
return -1;
}
if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
wpa_printf(MSG_DEBUG,
"FILS: Not enough room for FILS elements");
wpabuf_clear_free(plain);
return -1;
}
wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
plain);
if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
wpabuf_head(plain), wpabuf_len(plain),
5, aad, aad_len, pos) < 0) {
wpabuf_clear_free(plain);
return -1;
}
wpa_hexdump(MSG_DEBUG,
"FILS: Encrypted Association Response elements",
pos, AES_BLOCK_SIZE + wpabuf_len(plain));
current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
wpabuf_clear_free(plain);
sm->fils_completed = 1;
return current_len;
}
static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
const struct wpabuf *hlp)
{
struct wpabuf *plain;
u8 *len, *tmp, *tmp2;
u8 hdr[2];
- u8 *gtk, dummy_gtk[32];
+ u8 *gtk, stub_gtk[32];
size_t gtk_len;
struct wpa_group *gsm;
size_t plain_len;
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
plain_len = 1000 + ieee80211w_kde_len(sm);
if (conf->transition_disable)
plain_len += 2 + RSN_SELECTOR_LEN + 1;
plain = wpabuf_alloc(plain_len);
if (!plain)
return NULL;
/* TODO: FILS Public Key */
/* FILS Key Confirmation */
wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */
/* Element ID Extension */
wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM);
wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len);
/* FILS HLP Container */
if (hlp)
wpabuf_put_buf(plain, hlp);
/* TODO: FILS IP Address Assignment */
/* Key Delivery */
gsm = sm->group;
wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
len = wpabuf_put(plain, 1);
wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY);
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN,
wpabuf_put(plain, WPA_KEY_RSC_LEN));
/* GTK KDE */
gtk = gsm->GTK[gsm->GN - 1];
gtk_len = gsm->GTK_len;
if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
*/
- if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
+ if (random_get_bytes(stub_gtk, gtk_len) < 0) {
wpabuf_clear_free(plain);
return NULL;
}
- gtk = dummy_gtk;
+ gtk = stub_gtk;
}
hdr[0] = gsm->GN & 0x03;
hdr[1] = 0;
tmp = wpabuf_put(plain, 0);
tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gtk_len);
wpabuf_put(plain, tmp2 - tmp);
/* IGTK KDE and BIGTK KDE */
tmp = wpabuf_put(plain, 0);
tmp2 = ieee80211w_kde_add(sm, tmp);
wpabuf_put(plain, tmp2 - tmp);
if (conf->transition_disable) {
tmp = wpabuf_put(plain, 0);
tmp2 = wpa_add_kde(tmp, WFA_KEY_DATA_TRANSITION_DISABLE,
&conf->transition_disable, 1, NULL, 0);
wpabuf_put(plain, tmp2 - tmp);
}
*len = (u8 *) wpabuf_put(plain, 0) - len - 1;
#ifdef CONFIG_OCV
if (wpa_auth_uses_ocv(sm)) {
struct wpa_channel_info ci;
u8 *pos;
if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
wpa_printf(MSG_WARNING,
"FILS: Failed to get channel info for OCI element");
wpabuf_clear_free(plain);
return NULL;
}
#ifdef CONFIG_TESTING_OPTIONS
if (conf->oci_freq_override_fils_assoc) {
wpa_printf(MSG_INFO,
"TEST: Override OCI frequency %d -> %u MHz",
ci.frequency,
conf->oci_freq_override_fils_assoc);
ci.frequency = conf->oci_freq_override_fils_assoc;
}
#endif /* CONFIG_TESTING_OPTIONS */
pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
if (ocv_insert_extended_oci(&ci, pos) < 0) {
wpabuf_clear_free(plain);
return NULL;
}
}
#endif /* CONFIG_OCV */
return plain;
}
int fils_set_tk(struct wpa_state_machine *sm)
{
enum wpa_alg alg;
int klen;
if (!sm || !sm->PTK_valid) {
wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK");
return -1;
}
if (sm->tk_already_set) {
wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver");
return -1;
}
alg = wpa_cipher_to_alg(sm->pairwise);
klen = wpa_cipher_key_len(sm->pairwise);
wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX)) {
wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
return -1;
}
sm->tk_already_set = true;
wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
dot11RSNAConfigPMKLifetime, &sm->PTK);
return 0;
}
u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf,
const u8 *fils_session, struct wpabuf *hlp)
{
struct wpabuf *plain;
u8 *pos = buf;
/* FILS Session */
*pos++ = WLAN_EID_EXTENSION; /* Element ID */
*pos++ = 1 + FILS_SESSION_LEN; /* Length */
*pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
os_memcpy(pos, fils_session, FILS_SESSION_LEN);
pos += FILS_SESSION_LEN;
plain = fils_prepare_plainbuf(sm, hlp);
if (!plain) {
wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed");
return NULL;
}
os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain));
pos += wpabuf_len(plain);
wpa_printf(MSG_DEBUG, "%s: plain buf_len: %zu", __func__,
wpabuf_len(plain));
wpabuf_clear_free(plain);
sm->fils_completed = 1;
return pos;
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_OCV
int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
int ap_seg1_idx, int *bandwidth, int *seg1_idx)
{
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
if (!wpa_auth->cb->get_sta_tx_params)
return -1;
return wpa_auth->cb->get_sta_tx_params(wpa_auth->cb_ctx, sm->addr,
ap_max_chanwidth, ap_seg1_idx,
bandwidth, seg1_idx);
}
#endif /* CONFIG_OCV */
SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
{
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
struct wpa_ptk PTK;
int ok = 0, psk_found = 0;
const u8 *pmk = NULL;
size_t pmk_len;
int ft;
const u8 *eapol_key_ie, *key_data, *mic;
u16 key_data_length;
size_t mic_len, eapol_key_ie_len;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
struct wpa_eapol_ie_parse kde;
int vlan_id = 0;
int owe_ptk_workaround = !!wpa_auth->conf.owe_ptk_workaround;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = false;
sm->update_snonce = false;
os_memset(&PTK, 0, sizeof(PTK));
mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
/* WPA with IEEE 802.1X: use the derived PMK from EAP
* WPA-PSK: iterate through possible PSKs and select the one matching
* the packet */
for (;;) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
!wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
sm->p2p_dev_addr, pmk, &pmk_len,
&vlan_id);
if (!pmk)
break;
psk_found = 1;
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
os_memcpy(sm->xxkey, pmk, pmk_len);
sm->xxkey_len = pmk_len;
}
#endif /* CONFIG_IEEE80211R_AP */
} else {
pmk = sm->PMK;
pmk_len = sm->pmk_len;
}
if ((!pmk || !pmk_len) && sm->pmksa) {
wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache");
pmk = sm->pmksa->pmk;
pmk_len = sm->pmksa->pmk_len;
}
if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK,
owe_ptk_workaround == 2) < 0)
break;
if (mic_len &&
wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
sm->last_rx_eapol_key,
sm->last_rx_eapol_key_len) == 0) {
if (sm->PMK != pmk) {
os_memcpy(sm->PMK, pmk, pmk_len);
sm->pmk_len = pmk_len;
}
ok = 1;
break;
}
#ifdef CONFIG_FILS
if (!mic_len &&
wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key,
sm->last_rx_eapol_key_len, NULL) == 0) {
ok = 1;
break;
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && pmk_len > 32 &&
owe_ptk_workaround == 1) {
wpa_printf(MSG_DEBUG,
"OWE: Try PTK derivation workaround with SHA256");
owe_ptk_workaround = 2;
continue;
}
#endif /* CONFIG_OWE */
if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
wpa_key_mgmt_sae(sm->wpa_key_mgmt))
break;
}
if (!ok) {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"invalid MIC in msg 2/4 of 4-Way Handshake");
if (psk_found)
wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr);
return;
}
/*
* Note: last_rx_eapol_key length fields have already been validated in
* wpa_receive().
*/
hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
key = (struct wpa_eapol_key *) (hdr + 1);
mic = (u8 *) (key + 1);
key_data = mic + mic_len + 2;
key_data_length = WPA_GET_BE16(mic + mic_len);
if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
sizeof(*key) - mic_len - 2)
return;
if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key msg 2/4 with invalid Key Data contents");
return;
}
if (kde.rsn_ie) {
eapol_key_ie = kde.rsn_ie;
eapol_key_ie_len = kde.rsn_ie_len;
} else if (kde.osen) {
eapol_key_ie = kde.osen;
eapol_key_ie_len = kde.osen_len;
} else {
eapol_key_ie = kde.wpa_ie;
eapol_key_ie_len = kde.wpa_ie_len;
}
ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt);
if (!sm->wpa_ie ||
wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len,
eapol_key_ie, eapol_key_ie_len)) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"WPA IE from (Re)AssocReq did not match with msg 2/4");
if (sm->wpa_ie) {
wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
sm->wpa_ie, sm->wpa_ie_len);
}
wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
eapol_key_ie, eapol_key_ie_len);
/* MLME-DEAUTHENTICATE.request */
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
if ((!sm->rsnxe && kde.rsnxe) ||
(sm->rsnxe && !kde.rsnxe) ||
(sm->rsnxe && kde.rsnxe &&
(sm->rsnxe_len != kde.rsnxe_len ||
os_memcmp(sm->rsnxe, kde.rsnxe, sm->rsnxe_len) != 0))) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"RSNXE from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4");
wpa_hexdump(MSG_DEBUG, "RSNXE in AssocReq",
sm->rsnxe, sm->rsnxe_len);
wpa_hexdump(MSG_DEBUG, "RSNXE in EAPOL-Key msg 2/4",
kde.rsnxe, kde.rsnxe_len);
/* MLME-DEAUTHENTICATE.request */
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
#ifdef CONFIG_OCV
if (wpa_auth_uses_ocv(sm)) {
struct wpa_channel_info ci;
int tx_chanwidth;
int tx_seg1_idx;
enum oci_verify_result res;
if (wpa_channel_info(wpa_auth, &ci) != 0) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"Failed to get channel info to validate received OCI in EAPOL-Key 2/4");
return;
}
if (get_sta_tx_parameters(sm,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx, &tx_chanwidth,
&tx_seg1_idx) < 0)
return;
res = ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
tx_chanwidth, tx_seg1_idx);
if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
/* Work around misbehaving STAs */
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"Disable OCV with a STA that does not send OCI");
wpa_auth_set_ocv(sm, 0);
} else if (res != OCI_SUCCESS) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"OCV failed: %s", ocv_errorstr);
if (wpa_auth->conf.msg_ctx)
wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
OCV_FAILURE "addr=" MACSTR
" frame=eapol-key-m2 error=%s",
MAC2STR(sm->addr), ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
#ifdef CONFIG_IEEE80211R_AP
if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
if (kde.ip_addr_req && kde.ip_addr_req[0] &&
wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
int idx;
wpa_printf(MSG_DEBUG,
"P2P: IP address requested in EAPOL-Key exchange");
idx = bitfield_get_first_zero(wpa_auth->ip_pool);
if (idx >= 0) {
u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start);
bitfield_set(wpa_auth->ip_pool, idx);
WPA_PUT_BE32(sm->ip_addr, start + idx);
wpa_printf(MSG_DEBUG,
"P2P: Assigned IP address %u.%u.%u.%u to "
MACSTR, sm->ip_addr[0], sm->ip_addr[1],
sm->ip_addr[2], sm->ip_addr[3],
MAC2STR(sm->addr));
}
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_DPP2
if (DPP_VERSION > 1 && kde.dpp_kde) {
wpa_printf(MSG_DEBUG,
"DPP: peer Protocol Version %u Flags 0x%x",
kde.dpp_kde[0], kde.dpp_kde[1]);
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP &&
wpa_auth->conf.dpp_pfs != 2 &&
(kde.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) &&
!sm->dpp_z) {
wpa_printf(MSG_INFO,
"DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
}
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_IEEE80211R_AP
if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
/*
* Verify that PMKR1Name from EAPOL-Key message 2/4 matches
* with the value we derived.
*/
if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
WPA_PMK_NAME_LEN) != 0) {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"PMKR1Name mismatch in FT 4-way handshake");
wpa_hexdump(MSG_DEBUG,
"FT: PMKR1Name from Supplicant",
sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
sm->pmk_r1_name, WPA_PMK_NAME_LEN);
return;
}
}
#endif /* CONFIG_IEEE80211R_AP */
if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
wpa_sta_disconnect(wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && sm->PMK != pmk) {
/* PSK may have changed from the previous choice, so update
* state machine data based on whatever PSK was selected here.
*/
os_memcpy(sm->PMK, pmk, PMK_LEN);
sm->pmk_len = PMK_LEN;
}
sm->MICVerified = true;
os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
forced_memzero(&PTK, sizeof(PTK));
sm->PTK_valid = true;
}
SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
{
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk);
sm->TimeoutCtr = 0;
}
static int ieee80211w_kde_len(struct wpa_state_machine *sm)
{
size_t len = 0;
if (sm->mgmt_frame_prot) {
len += 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN;
len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
}
if (sm->mgmt_frame_prot && sm->wpa_auth->conf.beacon_prot) {
len += 2 + RSN_SELECTOR_LEN + WPA_BIGTK_KDE_PREFIX_LEN;
len += wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
}
return len;
}
static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
{
struct wpa_igtk_kde igtk;
struct wpa_bigtk_kde bigtk;
struct wpa_group *gsm = sm->group;
u8 rsc[WPA_KEY_RSC_LEN];
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
size_t len = wpa_cipher_key_len(conf->group_mgmt_cipher);
if (!sm->mgmt_frame_prot)
return pos;
igtk.keyid[0] = gsm->GN_igtk;
igtk.keyid[1] = 0;
if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0)
os_memset(igtk.pn, 0, sizeof(igtk.pn));
else
os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random IGTK to each STA to prevent use of
* IGTK in the BSS.
*/
if (random_get_bytes(igtk.igtk, len) < 0)
return pos;
}
pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
(const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
NULL, 0);
if (!conf->beacon_prot)
return pos;
bigtk.keyid[0] = gsm->GN_bigtk;
bigtk.keyid[1] = 0;
if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, rsc) < 0)
os_memset(bigtk.pn, 0, sizeof(bigtk.pn));
else
os_memcpy(bigtk.pn, rsc, sizeof(bigtk.pn));
os_memcpy(bigtk.bigtk, gsm->BIGTK[gsm->GN_bigtk - 6], len);
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random BIGTK to each OSEN STA to prevent use
* of BIGTK in the BSS.
*/
if (random_get_bytes(bigtk.bigtk, len) < 0)
return pos;
}
pos = wpa_add_kde(pos, RSN_KEY_DATA_BIGTK,
(const u8 *) &bigtk, WPA_BIGTK_KDE_PREFIX_LEN + len,
NULL, 0);
return pos;
}
static int ocv_oci_len(struct wpa_state_machine *sm)
{
#ifdef CONFIG_OCV
if (wpa_auth_uses_ocv(sm))
return OCV_OCI_KDE_LEN;
#endif /* CONFIG_OCV */
return 0;
}
static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos,
unsigned int freq)
{
#ifdef CONFIG_OCV
struct wpa_channel_info ci;
if (!wpa_auth_uses_ocv(sm))
return 0;
if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info for OCI element");
return -1;
}
#ifdef CONFIG_TESTING_OPTIONS
if (freq) {
wpa_printf(MSG_INFO,
"TEST: Override OCI KDE frequency %d -> %u MHz",
ci.frequency, freq);
ci.frequency = freq;
}
#endif /* CONFIG_TESTING_OPTIONS */
return ocv_insert_oci_kde(&ci, argpos);
#else /* CONFIG_OCV */
return 0;
#endif /* CONFIG_OCV */
}
#ifdef CONFIG_TESTING_OPTIONS
static u8 * replace_ie(const char *name, const u8 *old_buf, size_t *len, u8 eid,
const u8 *ie, size_t ie_len)
{
const u8 *elem;
u8 *buf;
wpa_printf(MSG_DEBUG, "TESTING: %s EAPOL override", name);
wpa_hexdump(MSG_DEBUG, "TESTING: wpa_ie before override",
old_buf, *len);
buf = os_malloc(*len + ie_len);
if (!buf)
return NULL;
os_memcpy(buf, old_buf, *len);
elem = get_ie(buf, *len, eid);
if (elem) {
u8 elem_len = 2 + elem[1];
os_memmove((void *) elem, elem + elem_len,
*len - (elem - buf) - elem_len);
*len -= elem_len;
}
os_memcpy(buf + *len, ie, ie_len);
*len += ie_len;
wpa_hexdump(MSG_DEBUG, "TESTING: wpa_ie after EAPOL override",
buf, *len);
return buf;
}
#endif /* CONFIG_TESTING_OPTIONS */
SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
{
- u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, dummy_gtk[32];
+ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, stub_gtk[32];
size_t gtk_len, kde_len, wpa_ie_len;
struct wpa_group *gsm = sm->group;
u8 *wpa_ie;
int secure, gtkidx, encr = 0;
u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
u8 hdr[2];
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
sm->TimeoutEvt = false;
sm->TimeoutCtr++;
if (conf->wpa_disable_eapol_key_retries && sm->TimeoutCtr > 1) {
/* Do not allow retransmission of EAPOL-Key msg 3/4 */
return;
}
if (sm->TimeoutCtr > conf->wpa_pairwise_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
}
/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
*/
os_memset(rsc, 0, WPA_KEY_RSC_LEN);
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
/* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
wpa_ie = sm->wpa_auth->wpa_ie;
wpa_ie_len = sm->wpa_auth->wpa_ie_len;
if (sm->wpa == WPA_VERSION_WPA && (conf->wpa & WPA_PROTO_RSN) &&
wpa_ie_len > wpa_ie[1] + 2U && wpa_ie[0] == WLAN_EID_RSN) {
/* WPA-only STA, remove RSN IE and possible MDIE */
wpa_ie = wpa_ie + wpa_ie[1] + 2;
if (wpa_ie[0] == WLAN_EID_RSNX)
wpa_ie = wpa_ie + wpa_ie[1] + 2;
if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
wpa_ie = wpa_ie + wpa_ie[1] + 2;
wpa_ie_len = wpa_ie[1] + 2;
}
#ifdef CONFIG_TESTING_OPTIONS
if (conf->rsne_override_eapol_set) {
wpa_ie_buf2 = replace_ie(
"RSNE", wpa_ie, &wpa_ie_len, WLAN_EID_RSN,
conf->rsne_override_eapol,
conf->rsne_override_eapol_len);
if (!wpa_ie_buf2)
goto done;
wpa_ie = wpa_ie_buf2;
}
if (conf->rsnxe_override_eapol_set) {
wpa_ie_buf = replace_ie(
"RSNXE", wpa_ie, &wpa_ie_len, WLAN_EID_RSNX,
conf->rsnxe_override_eapol,
conf->rsnxe_override_eapol_len);
if (!wpa_ie_buf)
goto done;
wpa_ie = wpa_ie_buf;
}
#endif /* CONFIG_TESTING_OPTIONS */
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 3/4 msg of 4-Way Handshake");
if (sm->wpa == WPA_VERSION_WPA2) {
if (sm->use_ext_key_id && sm->TimeoutCtr == 1 &&
wpa_auth_set_key(sm->wpa_auth, 0,
wpa_cipher_to_alg(sm->pairwise),
sm->addr,
sm->keyidx_active, sm->PTK.tk,
wpa_cipher_key_len(sm->pairwise),
KEY_FLAG_PAIRWISE_RX)) {
wpa_sta_disconnect(sm->wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
/* WPA2 send GTK in the 4-way handshake */
secure = 1;
gtk = gsm->GTK[gsm->GN - 1];
gtk_len = gsm->GTK_len;
if (conf->disable_gtk ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
*/
- if (random_get_bytes(dummy_gtk, gtk_len) < 0)
+ if (random_get_bytes(stub_gtk, gtk_len) < 0)
goto done;
- gtk = dummy_gtk;
+ gtk = stub_gtk;
}
gtkidx = gsm->GN;
_rsc = rsc;
encr = 1;
} else {
/* WPA does not include GTK in msg 3/4 */
secure = 0;
gtk = NULL;
gtk_len = 0;
_rsc = NULL;
if (sm->rx_eapol_key_secure) {
/*
* It looks like Windows 7 supplicant tries to use
* Secure bit in msg 2/4 after having reported Michael
* MIC failure and it then rejects the 4-way handshake
* if msg 3/4 does not set Secure bit. Work around this
* by setting the Secure bit here even in the case of
* WPA if the supplicant used it first.
*/
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"STA used Secure bit in WPA msg 2/4 - set Secure for 3/4 as workaround");
secure = 1;
}
}
kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
if (sm->use_ext_key_id)
kde_len += 2 + RSN_SELECTOR_LEN + 2;
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
kde_len += 300; /* FTIE + 2 * TIE */
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
if (WPA_GET_BE32(sm->ip_addr) > 0)
kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
#endif /* CONFIG_P2P */
if (conf->transition_disable)
kde_len += 2 + RSN_SELECTOR_LEN + 1;
#ifdef CONFIG_DPP2
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
kde_len += 2 + RSN_SELECTOR_LEN + 2;
#endif /* CONFIG_DPP2 */
kde = os_malloc(kde_len);
if (!kde)
goto done;
pos = kde;
os_memcpy(pos, wpa_ie, wpa_ie_len);
pos += wpa_ie_len;
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res;
size_t elen;
elen = pos - kde;
res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
if (res < 0) {
wpa_printf(MSG_ERROR,
"FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
goto done;
}
pos -= wpa_ie_len;
pos += elen;
}
#endif /* CONFIG_IEEE80211R_AP */
hdr[1] = 0;
if (sm->use_ext_key_id) {
hdr[0] = sm->keyidx_active & 0x01;
pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
}
if (gtk) {
hdr[0] = gtkidx & 0x03;
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gtk_len);
}
pos = ieee80211w_kde_add(sm, pos);
if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0)
goto done;
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res;
if (sm->assoc_resp_ftie &&
kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
os_memcpy(pos, sm->assoc_resp_ftie,
2 + sm->assoc_resp_ftie[1]);
res = 2 + sm->assoc_resp_ftie[1];
} else {
int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
res = wpa_write_ftie(conf, use_sha384,
conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, pos,
kde + kde_len - pos,
NULL, 0, 0);
}
if (res < 0) {
wpa_printf(MSG_ERROR,
"FT: Failed to insert FTIE into EAPOL-Key Key Data");
goto done;
}
pos += res;
/* TIE[ReassociationDeadline] (TU) */
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
WPA_PUT_LE32(pos, conf->reassociation_deadline);
pos += 4;
/* TIE[KeyLifetime] (seconds) */
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
WPA_PUT_LE32(pos, conf->r0_key_lifetime);
pos += 4;
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_P2P
if (WPA_GET_BE32(sm->ip_addr) > 0) {
u8 addr[3 * 4];
os_memcpy(addr, sm->ip_addr, 4);
os_memcpy(addr + 4, conf->ip_addr_mask, 4);
os_memcpy(addr + 8, conf->ip_addr_go, 4);
pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
addr, sizeof(addr), NULL, 0);
}
#endif /* CONFIG_P2P */
if (conf->transition_disable)
pos = wpa_add_kde(pos, WFA_KEY_DATA_TRANSITION_DISABLE,
&conf->transition_disable, 1, NULL, 0);
#ifdef CONFIG_DPP2
if (DPP_VERSION > 1 && sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) {
u8 payload[2];
payload[0] = DPP_VERSION; /* Protocol Version */
payload[1] = 0; /* Flags */
if (conf->dpp_pfs == 0)
payload[1] |= DPP_KDE_PFS_ALLOWED;
else if (conf->dpp_pfs == 1)
payload[1] |= DPP_KDE_PFS_ALLOWED |
DPP_KDE_PFS_REQUIRED;
pos = wpa_add_kde(pos, WFA_KEY_DATA_DPP,
payload, sizeof(payload), NULL, 0);
}
#endif /* CONFIG_DPP2 */
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) |
(wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
WPA_KEY_INFO_KEY_TYPE,
_rsc, sm->ANonce, kde, pos - kde, 0, encr);
done:
os_free(kde);
os_free(wpa_ie_buf);
os_free(wpa_ie_buf2);
}
SM_STATE(WPA_PTK, PTKINITDONE)
{
SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk);
sm->EAPOLKeyReceived = false;
if (sm->Pair) {
enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
int klen = wpa_cipher_key_len(sm->pairwise);
int res;
if (sm->use_ext_key_id)
res = wpa_auth_set_key(sm->wpa_auth, 0, 0, sm->addr,
sm->keyidx_active, NULL, 0,
KEY_FLAG_PAIRWISE_RX_TX_MODIFY);
else
res = wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr,
0, sm->PTK.tk, klen,
KEY_FLAG_PAIRWISE_RX_TX);
if (res) {
wpa_sta_disconnect(sm->wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
sm->pairwise_set = true;
wpa_auth_set_ptk_rekey_timer(sm);
wpa_auth_store_ptksa(sm->wpa_auth, sm->addr, sm->pairwise,
dot11RSNAConfigPMKLifetime, &sm->PTK);
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) {
wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_authorized, 1);
}
}
if (0 /* IBSS == TRUE */) {
sm->keycount++;
if (sm->keycount == 2) {
wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_portValid, 1);
}
} else {
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid,
1);
}
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable,
false);
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, true);
if (sm->wpa == WPA_VERSION_WPA)
sm->PInitAKeys = true;
else
sm->has_GTK = true;
wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
"pairwise key handshake completed (%s)",
sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO, "EAPOL-4WAY-HS-COMPLETED "
MACSTR, MAC2STR(sm->addr));
#ifdef CONFIG_IEEE80211R_AP
wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
#endif /* CONFIG_IEEE80211R_AP */
}
SM_STEP(WPA_PTK)
{
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
struct wpa_auth_config *conf = &wpa_auth->conf;
if (sm->Init)
SM_ENTER(WPA_PTK, INITIALIZE);
else if (sm->Disconnect
/* || FIX: dot11RSNAConfigSALifetime timeout */) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
"WPA_PTK: sm->Disconnect");
SM_ENTER(WPA_PTK, DISCONNECT);
}
else if (sm->DeauthenticationRequest)
SM_ENTER(WPA_PTK, DISCONNECTED);
else if (sm->AuthenticationRequest)
SM_ENTER(WPA_PTK, AUTHENTICATION);
else if (sm->ReAuthenticationRequest)
SM_ENTER(WPA_PTK, AUTHENTICATION2);
else if (sm->PTKRequest) {
if (wpa_auth_sm_ptk_update(sm) < 0)
SM_ENTER(WPA_PTK, DISCONNECTED);
else
SM_ENTER(WPA_PTK, PTKSTART);
} else switch (sm->wpa_ptk_state) {
case WPA_PTK_INITIALIZE:
break;
case WPA_PTK_DISCONNECT:
SM_ENTER(WPA_PTK, DISCONNECTED);
break;
case WPA_PTK_DISCONNECTED:
SM_ENTER(WPA_PTK, INITIALIZE);
break;
case WPA_PTK_AUTHENTICATION:
SM_ENTER(WPA_PTK, AUTHENTICATION2);
break;
case WPA_PTK_AUTHENTICATION2:
if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
wpa_auth_get_eapol(wpa_auth, sm->addr,
WPA_EAPOL_keyRun))
SM_ENTER(WPA_PTK, INITPMK);
else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE
/* FIX: && 802.1X::keyRun */)
SM_ENTER(WPA_PTK, INITPSK);
else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP)
SM_ENTER(WPA_PTK, INITPMK);
break;
case WPA_PTK_INITPMK:
if (wpa_auth_get_eapol(wpa_auth, sm->addr,
WPA_EAPOL_keyAvailable)) {
SM_ENTER(WPA_PTK, PTKSTART);
#ifdef CONFIG_DPP
} else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) {
SM_ENTER(WPA_PTK, PTKSTART);
#endif /* CONFIG_DPP */
} else {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"INITPMK - keyAvailable = false");
SM_ENTER(WPA_PTK, DISCONNECT);
}
break;
case WPA_PTK_INITPSK:
if (wpa_auth_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
NULL, NULL, NULL)) {
SM_ENTER(WPA_PTK, PTKSTART);
#ifdef CONFIG_SAE
} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
SM_ENTER(WPA_PTK, PTKSTART);
#endif /* CONFIG_SAE */
} else {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"no PSK configured for the STA");
wpa_auth->dot11RSNA4WayHandshakeFailures++;
SM_ENTER(WPA_PTK, DISCONNECT);
}
break;
case WPA_PTK_PTKSTART:
if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
sm->EAPOLKeyPairwise)
SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
else if (sm->TimeoutCtr > conf->wpa_pairwise_update_count) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PTKSTART: Retry limit %u reached",
conf->wpa_pairwise_update_count);
sm->disconnect_reason =
WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT;
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKSTART);
break;
case WPA_PTK_PTKCALCNEGOTIATING:
if (sm->MICVerified)
SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2);
else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
sm->EAPOLKeyPairwise)
SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKSTART);
break;
case WPA_PTK_PTKCALCNEGOTIATING2:
SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
break;
case WPA_PTK_PTKINITNEGOTIATING:
if (sm->update_snonce)
SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
sm->EAPOLKeyPairwise && sm->MICVerified)
SM_ENTER(WPA_PTK, PTKINITDONE);
else if (sm->TimeoutCtr >
conf->wpa_pairwise_update_count ||
(conf->wpa_disable_eapol_key_retries &&
sm->TimeoutCtr > 1)) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PTKINITNEGOTIATING: Retry limit %u reached",
conf->wpa_pairwise_update_count);
sm->disconnect_reason =
WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT;
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
break;
case WPA_PTK_PTKINITDONE:
break;
}
}
SM_STATE(WPA_PTK_GROUP, IDLE)
{
SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group);
if (sm->Init) {
/* Init flag is not cleared here, so avoid busy
* loop by claiming nothing changed. */
sm->changed = false;
}
sm->GTimeoutCtr = 0;
}
SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
{
u8 rsc[WPA_KEY_RSC_LEN];
struct wpa_group *gsm = sm->group;
const u8 *kde;
u8 *kde_buf = NULL, *pos, hdr[2];
size_t kde_len;
- u8 *gtk, dummy_gtk[32];
+ u8 *gtk, stub_gtk[32];
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
sm->GTimeoutCtr++;
if (conf->wpa_disable_eapol_key_retries && sm->GTimeoutCtr > 1) {
/* Do not allow retransmission of EAPOL-Key group msg 1/2 */
return;
}
if (sm->GTimeoutCtr > conf->wpa_group_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
}
if (sm->wpa == WPA_VERSION_WPA)
sm->PInitAKeys = false;
sm->TimeoutEvt = false;
/* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
os_memset(rsc, 0, WPA_KEY_RSC_LEN);
if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE)
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 1/2 msg of Group Key Handshake");
gtk = gsm->GTK[gsm->GN - 1];
if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
*/
- if (random_get_bytes(dummy_gtk, gsm->GTK_len) < 0)
+ if (random_get_bytes(stub_gtk, gsm->GTK_len) < 0)
return;
- gtk = dummy_gtk;
+ gtk = stub_gtk;
}
if (sm->wpa == WPA_VERSION_WPA2) {
kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
ieee80211w_kde_len(sm) + ocv_oci_len(sm);
kde_buf = os_malloc(kde_len);
if (!kde_buf)
return;
kde = pos = kde_buf;
hdr[0] = gsm->GN & 0x03;
hdr[1] = 0;
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gsm->GTK_len);
pos = ieee80211w_kde_add(sm, pos);
if (ocv_oci_add(sm, &pos,
conf->oci_freq_override_eapol_g1) < 0) {
os_free(kde_buf);
return;
}
kde_len = pos - kde;
} else {
kde = gtk;
kde_len = gsm->GTK_len;
}
wpa_send_eapol(sm->wpa_auth, sm,
WPA_KEY_INFO_SECURE |
(wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK |
(!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
rsc, NULL, kde, kde_len, gsm->GN, 1);
os_free(kde_buf);
}
SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
{
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
#ifdef CONFIG_OCV
const u8 *key_data, *mic;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
struct wpa_eapol_ie_parse kde;
size_t mic_len;
u16 key_data_length;
#endif /* CONFIG_OCV */
SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
sm->EAPOLKeyReceived = false;
#ifdef CONFIG_OCV
mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
/*
* Note: last_rx_eapol_key length fields have already been validated in
* wpa_receive().
*/
hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
key = (struct wpa_eapol_key *) (hdr + 1);
mic = (u8 *) (key + 1);
key_data = mic + mic_len + 2;
key_data_length = WPA_GET_BE16(mic + mic_len);
if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
sizeof(*key) - mic_len - 2)
return;
if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key group msg 2/2 with invalid Key Data contents");
return;
}
if (wpa_auth_uses_ocv(sm)) {
struct wpa_channel_info ci;
int tx_chanwidth;
int tx_seg1_idx;
if (wpa_channel_info(wpa_auth, &ci) != 0) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"Failed to get channel info to validate received OCI in EAPOL-Key group 2/2");
return;
}
if (get_sta_tx_parameters(sm,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx, &tx_chanwidth,
&tx_seg1_idx) < 0)
return;
if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
tx_chanwidth, tx_seg1_idx) !=
OCI_SUCCESS) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"OCV failed: %s", ocv_errorstr);
if (wpa_auth->conf.msg_ctx)
wpa_msg(wpa_auth->conf.msg_ctx, MSG_INFO,
OCV_FAILURE "addr=" MACSTR
" frame=eapol-key-g2 error=%s",
MAC2STR(sm->addr), ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
if (sm->GUpdateStationKeys)
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = false;
sm->GTimeoutCtr = 0;
/* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"group key handshake completed (%s)",
sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
sm->has_GTK = true;
}
SM_STATE(WPA_PTK_GROUP, KEYERROR)
{
SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group);
if (sm->GUpdateStationKeys)
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = false;
sm->Disconnect = true;
sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT;
wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
"group key handshake failed (%s) after %u tries",
sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN",
sm->wpa_auth->conf.wpa_group_update_count);
}
SM_STEP(WPA_PTK_GROUP)
{
if (sm->Init || sm->PtkGroupInit) {
SM_ENTER(WPA_PTK_GROUP, IDLE);
sm->PtkGroupInit = false;
} else switch (sm->wpa_ptk_group_state) {
case WPA_PTK_GROUP_IDLE:
if (sm->GUpdateStationKeys ||
(sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys))
SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
break;
case WPA_PTK_GROUP_REKEYNEGOTIATING:
if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest &&
!sm->EAPOLKeyPairwise && sm->MICVerified)
SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
else if (sm->GTimeoutCtr >
sm->wpa_auth->conf.wpa_group_update_count ||
(sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
sm->GTimeoutCtr > 1))
SM_ENTER(WPA_PTK_GROUP, KEYERROR);
else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
break;
case WPA_PTK_GROUP_KEYERROR:
SM_ENTER(WPA_PTK_GROUP, IDLE);
break;
case WPA_PTK_GROUP_REKEYESTABLISHED:
SM_ENTER(WPA_PTK_GROUP, IDLE);
break;
}
}
static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
int ret = 0;
size_t len;
os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
inc_byte_array(group->Counter, WPA_NONCE_LEN);
if (wpa_gmk_to_gtk(group->GMK, "Group key expansion",
wpa_auth->addr, group->GNonce,
group->GTK[group->GN - 1], group->GTK_len) < 0)
ret = -1;
wpa_hexdump_key(MSG_DEBUG, "GTK",
group->GTK[group->GN - 1], group->GTK_len);
if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
len = wpa_cipher_key_len(conf->group_mgmt_cipher);
os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
inc_byte_array(group->Counter, WPA_NONCE_LEN);
if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
wpa_auth->addr, group->GNonce,
group->IGTK[group->GN_igtk - 4], len) < 0)
ret = -1;
wpa_hexdump_key(MSG_DEBUG, "IGTK",
group->IGTK[group->GN_igtk - 4], len);
}
if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
conf->beacon_prot) {
len = wpa_cipher_key_len(conf->group_mgmt_cipher);
os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
inc_byte_array(group->Counter, WPA_NONCE_LEN);
if (wpa_gmk_to_gtk(group->GMK, "BIGTK key expansion",
wpa_auth->addr, group->GNonce,
group->BIGTK[group->GN_bigtk - 6], len) < 0)
ret = -1;
wpa_hexdump_key(MSG_DEBUG, "BIGTK",
group->BIGTK[group->GN_bigtk - 6], len);
}
return ret;
}
static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
wpa_printf(MSG_DEBUG,
"WPA: group state machine entering state GTK_INIT (VLAN-ID %d)",
group->vlan_id);
group->changed = false; /* GInit is not cleared here; avoid loop */
group->wpa_group_state = WPA_GROUP_GTK_INIT;
/* GTK[0..N] = 0 */
os_memset(group->GTK, 0, sizeof(group->GTK));
group->GN = 1;
group->GM = 2;
group->GN_igtk = 4;
group->GM_igtk = 5;
group->GN_bigtk = 6;
group->GM_bigtk = 7;
/* GTK[GN] = CalcGTK() */
wpa_gtk_update(wpa_auth, group);
}
static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
{
if (ctx != NULL && ctx != sm->group)
return 0;
if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) {
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"Not in PTKINITDONE; skip Group Key update");
sm->GUpdateStationKeys = false;
return 0;
}
if (sm->GUpdateStationKeys) {
/*
* This should not really happen, so add a debug log entry.
* Since we clear the GKeyDoneStations before the loop, the
* station needs to be counted here anyway.
*/
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"GUpdateStationKeys was already set when marking station for GTK rekeying");
}
/* Do not rekey GTK/IGTK when STA is in WNM-Sleep Mode */
if (sm->is_wnmsleep)
return 0;
sm->group->GKeyDoneStations++;
sm->GUpdateStationKeys = true;
wpa_sm_step(sm);
return 0;
}
#ifdef CONFIG_WNM_AP
/* update GTK when exiting WNM-Sleep Mode */
void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
{
if (!sm || sm->is_wnmsleep)
return;
wpa_group_update_sta(sm, NULL);
}
void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
{
if (sm)
sm->is_wnmsleep = !!flag;
}
int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos)
{
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
u8 *start = pos;
/*
* GTK subelement:
* Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
* Key[5..32]
*/
*pos++ = WNM_SLEEP_SUBELEM_GTK;
*pos++ = 11 + gsm->GTK_len;
/* Key ID in B0-B1 of Key Info */
WPA_PUT_LE16(pos, gsm->GN & 0x03);
pos += 2;
*pos++ = gsm->GTK_len;
if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, pos) != 0)
return 0;
pos += 8;
os_memcpy(pos, gsm->GTK[gsm->GN - 1], gsm->GTK_len);
if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
*/
if (random_get_bytes(pos, gsm->GTK_len) < 0)
return 0;
}
pos += gsm->GTK_len;
wpa_printf(MSG_DEBUG, "WNM: GTK Key ID %u in WNM-Sleep Mode exit",
gsm->GN);
wpa_hexdump_key(MSG_DEBUG, "WNM: GTK in WNM-Sleep Mode exit",
gsm->GTK[gsm->GN - 1], gsm->GTK_len);
return pos - start;
}
int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
{
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
u8 *start = pos;
size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/*
* IGTK subelement:
* Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
*/
*pos++ = WNM_SLEEP_SUBELEM_IGTK;
*pos++ = 2 + 6 + len;
WPA_PUT_LE16(pos, gsm->GN_igtk);
pos += 2;
if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0)
return 0;
pos += 6;
os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random IGTK to each STA to prevent use
* of IGTK in the BSS.
*/
if (random_get_bytes(pos, len) < 0)
return 0;
}
pos += len;
wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
gsm->GN_igtk);
wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit",
gsm->IGTK[gsm->GN_igtk - 4], len);
return pos - start;
}
int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos)
{
struct wpa_group *gsm = sm->group;
u8 *start = pos;
size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/*
* BIGTK subelement:
* Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
*/
*pos++ = WNM_SLEEP_SUBELEM_BIGTK;
*pos++ = 2 + 6 + len;
WPA_PUT_LE16(pos, gsm->GN_bigtk);
pos += 2;
if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos) != 0)
return 0;
pos += 6;
os_memcpy(pos, gsm->BIGTK[gsm->GN_bigtk - 6], len);
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random BIGTK to each STA to prevent use
* of BIGTK in the BSS.
*/
if (random_get_bytes(pos, len) < 0)
return 0;
}
pos += len;
wpa_printf(MSG_DEBUG, "WNM: BIGTK Key ID %u in WNM-Sleep Mode exit",
gsm->GN_bigtk);
wpa_hexdump_key(MSG_DEBUG, "WNM: BIGTK in WNM-Sleep Mode exit",
gsm->BIGTK[gsm->GN_bigtk - 6], len);
return pos - start;
}
#endif /* CONFIG_WNM_AP */
static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
int tmp;
wpa_printf(MSG_DEBUG,
"WPA: group state machine entering state SETKEYS (VLAN-ID %d)",
group->vlan_id);
group->changed = true;
group->wpa_group_state = WPA_GROUP_SETKEYS;
group->GTKReKey = false;
tmp = group->GM;
group->GM = group->GN;
group->GN = tmp;
tmp = group->GM_igtk;
group->GM_igtk = group->GN_igtk;
group->GN_igtk = tmp;
tmp = group->GM_bigtk;
group->GM_bigtk = group->GN_bigtk;
group->GN_bigtk = tmp;
/* "GKeyDoneStations = GNoStations" is done in more robust way by
* counting the STAs that are marked with GUpdateStationKeys instead of
* including all STAs that could be in not-yet-completed state. */
wpa_gtk_update(wpa_auth, group);
if (group->GKeyDoneStations) {
wpa_printf(MSG_DEBUG,
"wpa_group_setkeys: Unexpected GKeyDoneStations=%d when starting new GTK rekey",
group->GKeyDoneStations);
group->GKeyDoneStations = 0;
}
wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group);
wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d",
group->GKeyDoneStations);
}
static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
int ret = 0;
if (wpa_auth_set_key(wpa_auth, group->vlan_id,
wpa_cipher_to_alg(conf->wpa_group),
broadcast_ether_addr, group->GN,
group->GTK[group->GN - 1], group->GTK_len,
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
ret = -1;
if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
enum wpa_alg alg;
size_t len;
alg = wpa_cipher_to_alg(conf->group_mgmt_cipher);
len = wpa_cipher_key_len(conf->group_mgmt_cipher);
if (ret == 0 &&
wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
broadcast_ether_addr, group->GN_igtk,
group->IGTK[group->GN_igtk - 4], len,
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
ret = -1;
if (ret == 0 && conf->beacon_prot &&
wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
broadcast_ether_addr, group->GN_bigtk,
group->BIGTK[group->GN_bigtk - 6], len,
KEY_FLAG_GROUP_TX_DEFAULT) < 0)
ret = -1;
}
return ret;
}
static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx)
{
if (sm->group == ctx) {
wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR
" for disconnection due to fatal failure",
MAC2STR(sm->addr));
sm->Disconnect = true;
}
return 0;
}
static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
wpa_printf(MSG_DEBUG,
"WPA: group state machine entering state FATAL_FAILURE");
group->changed = true;
group->wpa_group_state = WPA_GROUP_FATAL_FAILURE;
wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group);
}
static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
wpa_printf(MSG_DEBUG,
"WPA: group state machine entering state SETKEYSDONE (VLAN-ID %d)",
group->vlan_id);
group->changed = true;
group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
wpa_group_fatal_failure(wpa_auth, group);
return -1;
}
return 0;
}
static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
if (group->GInit) {
wpa_group_gtk_init(wpa_auth, group);
} else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) {
/* Do not allow group operations */
} else if (group->wpa_group_state == WPA_GROUP_GTK_INIT &&
group->GTKAuthenticator) {
wpa_group_setkeysdone(wpa_auth, group);
} else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE &&
group->GTKReKey) {
wpa_group_setkeys(wpa_auth, group);
} else if (group->wpa_group_state == WPA_GROUP_SETKEYS) {
if (group->GKeyDoneStations == 0)
wpa_group_setkeysdone(wpa_auth, group);
else if (group->GTKReKey)
wpa_group_setkeys(wpa_auth, group);
}
}
static int wpa_sm_step(struct wpa_state_machine *sm)
{
if (!sm)
return 0;
if (sm->in_step_loop) {
/* This should not happen, but if it does, make sure we do not
* end up freeing the state machine too early by exiting the
* recursive call. */
wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively");
return 0;
}
sm->in_step_loop = 1;
do {
if (sm->pending_deinit)
break;
sm->changed = false;
sm->wpa_auth->group->changed = false;
SM_STEP_RUN(WPA_PTK);
if (sm->pending_deinit)
break;
SM_STEP_RUN(WPA_PTK_GROUP);
if (sm->pending_deinit)
break;
wpa_group_sm_step(sm->wpa_auth, sm->group);
} while (sm->changed || sm->wpa_auth->group->changed);
sm->in_step_loop = 0;
if (sm->pending_deinit) {
wpa_printf(MSG_DEBUG,
"WPA: Completing pending STA state machine deinit for "
MACSTR, MAC2STR(sm->addr));
wpa_free_sta_sm(sm);
return 1;
}
return 0;
}
static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_state_machine *sm = eloop_ctx;
wpa_sm_step(sm);
}
void wpa_auth_sm_notify(struct wpa_state_machine *sm)
{
if (!sm)
return;
eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL);
}
void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth)
{
int tmp, i;
struct wpa_group *group;
if (!wpa_auth)
return;
group = wpa_auth->group;
for (i = 0; i < 2; i++) {
tmp = group->GM;
group->GM = group->GN;
group->GN = tmp;
tmp = group->GM_igtk;
group->GM_igtk = group->GN_igtk;
group->GN_igtk = tmp;
tmp = group->GM_bigtk;
group->GM_bigtk = group->GN_bigtk;
group->GN_bigtk = tmp;
wpa_gtk_update(wpa_auth, group);
wpa_group_config_group_keys(wpa_auth, group);
}
}
static const char * wpa_bool_txt(int val)
{
return val ? "TRUE" : "FALSE";
}
#define RSN_SUITE "%02x-%02x-%02x-%d"
#define RSN_SUITE_ARG(s) \
((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
{
struct wpa_auth_config *conf;
int len = 0, ret;
char pmkid_txt[PMKID_LEN * 2 + 1];
#ifdef CONFIG_RSN_PREAUTH
const int preauth = 1;
#else /* CONFIG_RSN_PREAUTH */
const int preauth = 0;
#endif /* CONFIG_RSN_PREAUTH */
if (!wpa_auth)
return len;
conf = &wpa_auth->conf;
ret = os_snprintf(buf + len, buflen - len,
"dot11RSNAOptionImplemented=TRUE\n"
"dot11RSNAPreauthenticationImplemented=%s\n"
"dot11RSNAEnabled=%s\n"
"dot11RSNAPreauthenticationEnabled=%s\n",
wpa_bool_txt(preauth),
wpa_bool_txt(conf->wpa & WPA_PROTO_RSN),
wpa_bool_txt(conf->rsn_preauth));
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN);
ret = os_snprintf(
buf + len, buflen - len,
"dot11RSNAConfigVersion=%u\n"
"dot11RSNAConfigPairwiseKeysSupported=9999\n"
/* FIX: dot11RSNAConfigGroupCipher */
/* FIX: dot11RSNAConfigGroupRekeyMethod */
/* FIX: dot11RSNAConfigGroupRekeyTime */
/* FIX: dot11RSNAConfigGroupRekeyPackets */
"dot11RSNAConfigGroupRekeyStrict=%u\n"
"dot11RSNAConfigGroupUpdateCount=%u\n"
"dot11RSNAConfigPairwiseUpdateCount=%u\n"
"dot11RSNAConfigGroupCipherSize=%u\n"
"dot11RSNAConfigPMKLifetime=%u\n"
"dot11RSNAConfigPMKReauthThreshold=%u\n"
"dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n"
"dot11RSNAConfigSATimeout=%u\n"
"dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
"dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
"dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
"dot11RSNAPMKIDUsed=%s\n"
"dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
"dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
"dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
"dot11RSNATKIPCounterMeasuresInvoked=%u\n"
"dot11RSNA4WayHandshakeFailures=%u\n"
"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
RSN_VERSION,
!!conf->wpa_strict_rekey,
conf->wpa_group_update_count,
conf->wpa_pairwise_update_count,
wpa_cipher_key_len(conf->wpa_group) * 8,
dot11RSNAConfigPMKLifetime,
dot11RSNAConfigPMKReauthThreshold,
dot11RSNAConfigSATimeout,
RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected),
RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected),
RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected),
pmkid_txt,
RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested),
RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested),
RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
wpa_auth->dot11RSNA4WayHandshakeFailures);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
/* TODO: dot11RSNAConfigPairwiseCiphersTable */
/* TODO: dot11RSNAConfigAuthenticationSuitesTable */
/* Private MIB */
ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
wpa_auth->group->wpa_group_state);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
return len;
}
int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
{
int len = 0, ret;
u32 pairwise = 0;
if (!sm)
return 0;
/* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */
/* dot11RSNAStatsEntry */
pairwise = wpa_cipher_to_suite(sm->wpa == WPA_VERSION_WPA2 ?
WPA_PROTO_RSN : WPA_PROTO_WPA,
sm->pairwise);
if (pairwise == 0)
return 0;
ret = os_snprintf(
buf + len, buflen - len,
/* TODO: dot11RSNAStatsIndex */
"dot11RSNAStatsSTAAddress=" MACSTR "\n"
"dot11RSNAStatsVersion=1\n"
"dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n"
/* TODO: dot11RSNAStatsTKIPICVErrors */
"dot11RSNAStatsTKIPLocalMICFailures=%u\n"
"dot11RSNAStatsTKIPRemoteMICFailures=%u\n"
/* TODO: dot11RSNAStatsCCMPReplays */
/* TODO: dot11RSNAStatsCCMPDecryptErrors */
/* TODO: dot11RSNAStatsTKIPReplays */,
MAC2STR(sm->addr),
RSN_SUITE_ARG(pairwise),
sm->dot11RSNAStatsTKIPLocalMICFailures,
sm->dot11RSNAStatsTKIPRemoteMICFailures);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
/* Private MIB */
ret = os_snprintf(buf + len, buflen - len,
"wpa=%d\n"
"AKMSuiteSelector=" RSN_SUITE "\n"
"hostapdWPAPTKState=%d\n"
"hostapdWPAPTKGroupState=%d\n",
sm->wpa,
RSN_SUITE_ARG(wpa_akm_to_suite(sm->wpa_key_mgmt)),
sm->wpa_ptk_state,
sm->wpa_ptk_group_state);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
return len;
}
void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth)
{
if (wpa_auth)
wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++;
}
int wpa_auth_pairwise_set(struct wpa_state_machine *sm)
{
return sm && sm->pairwise_set;
}
int wpa_auth_get_pairwise(struct wpa_state_machine *sm)
{
return sm->pairwise;
}
const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len)
{
if (!sm)
return NULL;
*len = sm->pmk_len;
return sm->PMK;
}
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
{
if (!sm)
return -1;
return sm->wpa_key_mgmt;
}
int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm)
{
if (!sm)
return 0;
return sm->wpa;
}
int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm)
{
if (!sm || !wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return 0;
return sm->tk_already_set;
}
int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm)
{
if (!sm || !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
return 0;
return sm->tk_already_set;
}
int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm,
struct rsn_pmksa_cache_entry *entry)
{
if (!sm || sm->pmksa != entry)
return -1;
sm->pmksa = NULL;
return 0;
}
struct rsn_pmksa_cache_entry *
wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm)
{
return sm ? sm->pmksa : NULL;
}
void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm)
{
if (sm)
sm->dot11RSNAStatsTKIPLocalMICFailures++;
}
const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len)
{
if (!wpa_auth)
return NULL;
*len = wpa_auth->wpa_ie_len;
return wpa_auth->wpa_ie;
}
int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
unsigned int pmk_len,
int session_timeout, struct eapol_state_machine *eapol)
{
if (!sm || sm->wpa != WPA_VERSION_WPA2 ||
sm->wpa_auth->conf.disable_pmksa_caching)
return -1;
#ifdef CONFIG_IEEE80211R_AP
if (pmk_len >= 2 * PMK_LEN && wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
!wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
/* Cache MPMK/XXKey instead of initial part from MSK */
pmk = pmk + PMK_LEN;
pmk_len = PMK_LEN;
} else
#endif /* CONFIG_IEEE80211R_AP */
if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
if (pmk_len > PMK_LEN_SUITE_B_192)
pmk_len = PMK_LEN_SUITE_B_192;
} else if (pmk_len > PMK_LEN) {
pmk_len = PMK_LEN;
}
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK", pmk, pmk_len);
if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL,
sm->PTK.kck, sm->PTK.kck_len,
sm->wpa_auth->addr, sm->addr, session_timeout,
eapol, sm->wpa_key_mgmt))
return 0;
return -1;
}
int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
const u8 *pmk, size_t len, const u8 *sta_addr,
int session_timeout,
struct eapol_state_machine *eapol)
{
if (!wpa_auth)
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from preauth", pmk, len);
if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL,
NULL, 0,
wpa_auth->addr,
sta_addr, session_timeout, eapol,
WPA_KEY_MGMT_IEEE8021X))
return 0;
return -1;
}
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, const u8 *pmkid)
{
if (wpa_auth->conf.disable_pmksa_caching)
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, PMK_LEN);
if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
NULL, 0,
wpa_auth->addr, addr, 0, NULL,
WPA_KEY_MGMT_SAE))
return 0;
return -1;
}
void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid)
{
os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
sm->pmkid_set = 1;
}
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
int session_timeout, int akmp)
{
if (!wpa_auth || wpa_auth->conf.disable_pmksa_caching)
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK (2)", pmk, PMK_LEN);
if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
NULL, 0, wpa_auth->addr, addr, session_timeout,
NULL, akmp))
return 0;
return -1;
}
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr)
{
struct rsn_pmksa_cache_entry *pmksa;
if (!wpa_auth || !wpa_auth->pmksa)
return;
pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
if (pmksa) {
wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
MACSTR " based on request", MAC2STR(sta_addr));
pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
}
}
int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
size_t len)
{
if (!wpa_auth || !wpa_auth->pmksa)
return 0;
return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len);
}
void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
{
if (wpa_auth && wpa_auth->pmksa)
pmksa_cache_auth_flush(wpa_auth->pmksa);
}
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
#ifdef CONFIG_MESH
int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
char *buf, size_t len)
{
if (!wpa_auth || !wpa_auth->pmksa)
return 0;
return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len);
}
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
const u8 *pmkid, int expiration)
{
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa,
spa, 0, NULL, WPA_KEY_MGMT_SAE);
if (!entry)
return NULL;
os_get_reltime(&now);
entry->expiration = now.sec + expiration;
return entry;
}
int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
struct rsn_pmksa_cache_entry *entry)
{
int ret;
if (!wpa_auth || !wpa_auth->pmksa)
return -1;
ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry);
if (ret < 0)
wpa_printf(MSG_DEBUG,
"RSN: Failed to store external PMKSA cache for "
MACSTR, MAC2STR(entry->spa));
return ret;
}
#endif /* CONFIG_MESH */
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
const u8 *pmkid)
{
if (!wpa_auth || !wpa_auth->pmksa)
return NULL;
return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid);
}
void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
struct wpa_state_machine *sm,
struct wpa_authenticator *wpa_auth,
u8 *pmkid, u8 *pmk)
{
if (!sm)
return;
sm->pmksa = pmksa;
os_memcpy(pmk, pmksa->pmk, PMK_LEN);
os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN);
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN);
}
/*
* Remove and free the group from wpa_authenticator. This is triggered by a
* callback to make sure nobody is currently iterating the group list while it
* gets modified.
*/
static void wpa_group_free(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
struct wpa_group *prev = wpa_auth->group;
wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d",
group->vlan_id);
while (prev) {
if (prev->next == group) {
/* This never frees the special first group as needed */
prev->next = group->next;
os_free(group);
break;
}
prev = prev->next;
}
}
/* Increase the reference counter for group */
static void wpa_group_get(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
/* Skip the special first group */
if (wpa_auth->group == group)
return;
group->references++;
}
/* Decrease the reference counter and maybe free the group */
static void wpa_group_put(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
/* Skip the special first group */
if (wpa_auth->group == group)
return;
group->references--;
if (group->references)
return;
wpa_group_free(wpa_auth, group);
}
/*
* Add a group that has its references counter set to zero. Caller needs to
* call wpa_group_get() on the return value to mark the entry in use.
*/
static struct wpa_group *
wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
{
struct wpa_group *group;
if (!wpa_auth || !wpa_auth->group)
return NULL;
wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d",
vlan_id);
group = wpa_group_init(wpa_auth, vlan_id, 0);
if (!group)
return NULL;
group->next = wpa_auth->group->next;
wpa_auth->group->next = group;
return group;
}
/*
* Enforce that the group state machine for the VLAN is running, increase
* reference counter as interface is up. References might have been increased
* even if a negative value is returned.
* Returns: -1 on error (group missing, group already failed); otherwise, 0
*/
int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id)
{
struct wpa_group *group;
if (!wpa_auth)
return 0;
group = wpa_auth->group;
while (group) {
if (group->vlan_id == vlan_id)
break;
group = group->next;
}
if (!group) {
group = wpa_auth_add_group(wpa_auth, vlan_id);
if (!group)
return -1;
}
wpa_printf(MSG_DEBUG,
"WPA: Ensure group state machine running for VLAN ID %d",
vlan_id);
wpa_group_get(wpa_auth, group);
group->num_setup_iface++;
if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
return -1;
return 0;
}
/*
* Decrease reference counter, expected to be zero afterwards.
* returns: -1 on error (group not found, group in fail state)
* -2 if wpa_group is still referenced
* 0 else
*/
int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id)
{
struct wpa_group *group;
int ret = 0;
if (!wpa_auth)
return 0;
group = wpa_auth->group;
while (group) {
if (group->vlan_id == vlan_id)
break;
group = group->next;
}
if (!group)
return -1;
wpa_printf(MSG_DEBUG,
"WPA: Try stopping group state machine for VLAN ID %d",
vlan_id);
if (group->num_setup_iface <= 0) {
wpa_printf(MSG_ERROR,
"WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.",
vlan_id);
return -1;
}
group->num_setup_iface--;
if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
ret = -1;
if (group->references > 1) {
wpa_printf(MSG_DEBUG,
"WPA: Cannot stop group state machine for VLAN ID %d as references are still hold",
vlan_id);
ret = -2;
}
wpa_group_put(wpa_auth, group);
return ret;
}
int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
{
struct wpa_group *group;
if (!sm || !sm->wpa_auth)
return 0;
group = sm->wpa_auth->group;
while (group) {
if (group->vlan_id == vlan_id)
break;
group = group->next;
}
if (!group) {
group = wpa_auth_add_group(sm->wpa_auth, vlan_id);
if (!group)
return -1;
}
if (sm->group == group)
return 0;
if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
return -1;
wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR
" to use group state machine for VLAN ID %d",
MAC2STR(sm->addr), vlan_id);
wpa_group_get(sm->wpa_auth, group);
wpa_group_put(sm->wpa_auth, sm->group);
sm->group = group;
return 0;
}
void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int ack)
{
if (!wpa_auth || !sm)
return;
wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key TX status for STA " MACSTR
" ack=%d", MAC2STR(sm->addr), ack);
if (sm->pending_1_of_4_timeout && ack) {
/*
* Some deployed supplicant implementations update their SNonce
* for each EAPOL-Key 2/4 message even within the same 4-way
* handshake and then fail to use the first SNonce when
* deriving the PTK. This results in unsuccessful 4-way
* handshake whenever the relatively short initial timeout is
* reached and EAPOL-Key 1/4 is retransmitted. Try to work
* around this by increasing the timeout now that we know that
* the station has received the frame.
*/
int timeout_ms = eapol_key_timeout_subseq;
wpa_printf(MSG_DEBUG,
"WPA: Increase initial EAPOL-Key 1/4 timeout by %u ms because of acknowledged frame",
timeout_ms);
eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
eloop_register_timeout(timeout_ms / 1000,
(timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
}
#ifdef CONFIG_TESTING_OPTIONS
if (sm->eapol_status_cb) {
sm->eapol_status_cb(sm->eapol_status_cb_ctx1,
sm->eapol_status_cb_ctx2);
sm->eapol_status_cb = NULL;
}
#endif /* CONFIG_TESTING_OPTIONS */
}
int wpa_auth_uses_sae(struct wpa_state_machine *sm)
{
if (!sm)
return 0;
return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
}
int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm)
{
if (!sm)
return 0;
return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
}
#ifdef CONFIG_P2P
int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
{
if (!sm || WPA_GET_BE32(sm->ip_addr) == 0)
return -1;
os_memcpy(addr, sm->ip_addr, 4);
return 0;
}
#endif /* CONFIG_P2P */
int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
struct radius_das_attrs *attr)
{
return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr);
}
void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
{
struct wpa_group *group;
if (!wpa_auth)
return;
for (group = wpa_auth->group; group; group = group->next)
wpa_group_config_group_keys(wpa_auth, group);
}
#ifdef CONFIG_FILS
struct wpa_auth_fils_iter_data {
struct wpa_authenticator *auth;
const u8 *cache_id;
struct rsn_pmksa_cache_entry *pmksa;
const u8 *spa;
const u8 *pmkid;
};
static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx)
{
struct wpa_auth_fils_iter_data *data = ctx;
if (a == data->auth || !a->conf.fils_cache_id_set ||
os_memcmp(a->conf.fils_cache_id, data->cache_id,
FILS_CACHE_ID_LEN) != 0)
return 0;
data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid);
return data->pmksa != NULL;
}
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr, const u8 *pmkid)
{
struct wpa_auth_fils_iter_data idata;
if (!wpa_auth->conf.fils_cache_id_set)
return NULL;
idata.auth = wpa_auth;
idata.cache_id = wpa_auth->conf.fils_cache_id;
idata.pmksa = NULL;
idata.spa = sta_addr;
idata.pmkid = pmkid;
wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata);
return idata.pmksa;
}
#ifdef CONFIG_IEEE80211R_AP
int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
u8 *buf, size_t len)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, buf, len, NULL, 0, 0);
}
#endif /* CONFIG_IEEE80211R_AP */
void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
u8 *fils_anonce, u8 *fils_snonce,
u8 *fils_kek, size_t *fils_kek_len)
{
os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN);
os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN);
os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN);
*fils_kek_len = sm->PTK.kek_len;
}
void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
size_t pmk_len, const u8 *pmkid)
{
os_memcpy(sm->PMK, pmk, pmk_len);
sm->pmk_len = pmk_len;
os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
sm->pmkid_set = 1;
}
#endif /* CONFIG_FILS */
void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg)
{
if (sm)
sm->auth_alg = auth_alg;
}
#ifdef CONFIG_DPP2
void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z)
{
if (sm) {
wpabuf_clear_free(sm->dpp_z);
sm->dpp_z = z ? wpabuf_dup(z) : NULL;
}
}
#endif /* CONFIG_DPP2 */
void wpa_auth_set_transition_disable(struct wpa_authenticator *wpa_auth,
u8 val)
{
if (wpa_auth)
wpa_auth->conf.transition_disable = val;
}
#ifdef CONFIG_TESTING_OPTIONS
int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
void (*cb)(void *ctx1, void *ctx2),
void *ctx1, void *ctx2)
{
const u8 *anonce = sm->ANonce;
u8 anonce_buf[WPA_NONCE_LEN];
if (change_anonce) {
if (random_get_bytes(anonce_buf, WPA_NONCE_LEN))
return -1;
anonce = anonce_buf;
}
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 1/4 msg of 4-Way Handshake (TESTING)");
wpa_send_eapol(sm->wpa_auth, sm,
WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
anonce, NULL, 0, 0, 0);
return 0;
}
int wpa_auth_resend_m3(struct wpa_state_machine *sm,
void (*cb)(void *ctx1, void *ctx2),
void *ctx1, void *ctx2)
{
u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
u8 *opos;
size_t gtk_len, kde_len;
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
u8 *wpa_ie;
int wpa_ie_len, secure, gtkidx, encr = 0;
u8 hdr[2];
/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
*/
/* Use 0 RSC */
os_memset(rsc, 0, WPA_KEY_RSC_LEN);
/* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
wpa_ie = sm->wpa_auth->wpa_ie;
wpa_ie_len = sm->wpa_auth->wpa_ie_len;
if (sm->wpa == WPA_VERSION_WPA &&
(sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
/* WPA-only STA, remove RSN IE and possible MDIE */
wpa_ie = wpa_ie + wpa_ie[1] + 2;
if (wpa_ie[0] == WLAN_EID_RSNX)
wpa_ie = wpa_ie + wpa_ie[1] + 2;
if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
wpa_ie = wpa_ie + wpa_ie[1] + 2;
wpa_ie_len = wpa_ie[1] + 2;
}
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 3/4 msg of 4-Way Handshake (TESTING)");
if (sm->wpa == WPA_VERSION_WPA2) {
/* WPA2 send GTK in the 4-way handshake */
secure = 1;
gtk = gsm->GTK[gsm->GN - 1];
gtk_len = gsm->GTK_len;
gtkidx = gsm->GN;
_rsc = rsc;
encr = 1;
} else {
/* WPA does not include GTK in msg 3/4 */
secure = 0;
gtk = NULL;
gtk_len = 0;
_rsc = NULL;
if (sm->rx_eapol_key_secure) {
/*
* It looks like Windows 7 supplicant tries to use
* Secure bit in msg 2/4 after having reported Michael
* MIC failure and it then rejects the 4-way handshake
* if msg 3/4 does not set Secure bit. Work around this
* by setting the Secure bit here even in the case of
* WPA if the supplicant used it first.
*/
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"STA used Secure bit in WPA msg 2/4 - set Secure for 3/4 as workaround");
secure = 1;
}
}
kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
if (sm->use_ext_key_id)
kde_len += 2 + RSN_SELECTOR_LEN + 2;
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
kde_len += 300; /* FTIE + 2 * TIE */
}
#endif /* CONFIG_IEEE80211R_AP */
kde = os_malloc(kde_len);
if (!kde)
return -1;
pos = kde;
os_memcpy(pos, wpa_ie, wpa_ie_len);
pos += wpa_ie_len;
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res;
size_t elen;
elen = pos - kde;
res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name);
if (res < 0) {
wpa_printf(MSG_ERROR,
"FT: Failed to insert PMKR1Name into RSN IE in EAPOL-Key data");
os_free(kde);
return -1;
}
pos -= wpa_ie_len;
pos += elen;
}
#endif /* CONFIG_IEEE80211R_AP */
hdr[1] = 0;
if (sm->use_ext_key_id) {
hdr[0] = sm->keyidx_active & 0x01;
pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
}
if (gtk) {
hdr[0] = gtkidx & 0x03;
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gtk_len);
}
opos = pos;
pos = ieee80211w_kde_add(sm, pos);
if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
/* skip KDE header and keyid */
opos += 2 + RSN_SELECTOR_LEN + 2;
os_memset(opos, 0, 6); /* clear PN */
}
if (ocv_oci_add(sm, &pos, conf->oci_freq_override_eapol_m3) < 0) {
os_free(kde);
return -1;
}
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
int res;
if (sm->assoc_resp_ftie &&
kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
os_memcpy(pos, sm->assoc_resp_ftie,
2 + sm->assoc_resp_ftie[1]);
res = 2 + sm->assoc_resp_ftie[1];
} else {
int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
res = wpa_write_ftie(conf, use_sha384,
conf->r0_key_holder,
conf->r0_key_holder_len,
NULL, NULL, pos,
kde + kde_len - pos,
NULL, 0, 0);
}
if (res < 0) {
wpa_printf(MSG_ERROR,
"FT: Failed to insert FTIE into EAPOL-Key Key Data");
os_free(kde);
return -1;
}
pos += res;
/* TIE[ReassociationDeadline] (TU) */
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
WPA_PUT_LE32(pos, conf->reassociation_deadline);
pos += 4;
/* TIE[KeyLifetime] (seconds) */
*pos++ = WLAN_EID_TIMEOUT_INTERVAL;
*pos++ = 5;
*pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
WPA_PUT_LE32(pos, conf->r0_key_lifetime);
pos += 4;
}
#endif /* CONFIG_IEEE80211R_AP */
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) |
(wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
WPA_KEY_INFO_KEY_TYPE,
_rsc, sm->ANonce, kde, pos - kde, 0, encr);
os_free(kde);
return 0;
}
int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
void (*cb)(void *ctx1, void *ctx2),
void *ctx1, void *ctx2)
{
u8 rsc[WPA_KEY_RSC_LEN];
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
const u8 *kde;
u8 *kde_buf = NULL, *pos, hdr[2];
u8 *opos;
size_t kde_len;
u8 *gtk;
/* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */
os_memset(rsc, 0, WPA_KEY_RSC_LEN);
/* Use 0 RSC */
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 1/2 msg of Group Key Handshake (TESTING)");
gtk = gsm->GTK[gsm->GN - 1];
if (sm->wpa == WPA_VERSION_WPA2) {
kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
ieee80211w_kde_len(sm) + ocv_oci_len(sm);
kde_buf = os_malloc(kde_len);
if (!kde_buf)
return -1;
kde = pos = kde_buf;
hdr[0] = gsm->GN & 0x03;
hdr[1] = 0;
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gsm->GTK_len);
opos = pos;
pos = ieee80211w_kde_add(sm, pos);
if (pos - opos >=
2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) {
/* skip KDE header and keyid */
opos += 2 + RSN_SELECTOR_LEN + 2;
os_memset(opos, 0, 6); /* clear PN */
}
if (ocv_oci_add(sm, &pos,
conf->oci_freq_override_eapol_g1) < 0) {
os_free(kde_buf);
return -1;
}
kde_len = pos - kde;
} else {
kde = gtk;
kde_len = gsm->GTK_len;
}
sm->eapol_status_cb = cb;
sm->eapol_status_cb_ctx1 = ctx1;
sm->eapol_status_cb_ctx2 = ctx2;
wpa_send_eapol(sm->wpa_auth, sm,
WPA_KEY_INFO_SECURE |
(wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ?
WPA_KEY_INFO_MIC : 0) |
WPA_KEY_INFO_ACK |
(!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
rsc, NULL, kde, kde_len, gsm->GN, 1);
os_free(kde_buf);
return 0;
}
int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth)
{
if (!wpa_auth)
return -1;
eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL);
}
int wpa_auth_rekey_ptk(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm)
{
if (!wpa_auth || !sm)
return -1;
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
wpa_request_new_ptk(sm);
wpa_sm_step(sm);
return 0;
}
void wpa_auth_set_ft_rsnxe_used(struct wpa_authenticator *wpa_auth, int val)
{
if (wpa_auth)
wpa_auth->conf.ft_rsnxe_used = val;
}
void wpa_auth_set_ocv_override_freq(struct wpa_authenticator *wpa_auth,
enum wpa_auth_ocv_override_frame frame,
unsigned int freq)
{
if (!wpa_auth)
return;
switch (frame) {
case WPA_AUTH_OCV_OVERRIDE_EAPOL_M3:
wpa_auth->conf.oci_freq_override_eapol_m3 = freq;
break;
case WPA_AUTH_OCV_OVERRIDE_EAPOL_G1:
wpa_auth->conf.oci_freq_override_eapol_g1 = freq;
break;
case WPA_AUTH_OCV_OVERRIDE_FT_ASSOC:
wpa_auth->conf.oci_freq_override_ft_assoc = freq;
break;
case WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC:
wpa_auth->conf.oci_freq_override_fils_assoc = freq;
break;
}
}
#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/contrib/wpa/src/ap/wpa_auth_ft.c b/contrib/wpa/src/ap/wpa_auth_ft.c
index e80086b93d8d..fef1104d2aa0 100644
--- a/contrib/wpa/src/ap/wpa_auth_ft.c
+++ b/contrib/wpa/src/ap/wpa_auth_ft.c
@@ -1,4812 +1,4812 @@
/*
* hostapd - IEEE 802.11r - Fast BSS Transition
* Copyright (c) 2004-2018, 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"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/list.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/ocv.h"
#include "common/wpa_ctrl.h"
#include "drivers/driver.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
#include "crypto/aes_wrap.h"
#include "crypto/sha384.h"
#include "crypto/random.h"
#include "ap_config.h"
#include "ieee802_11.h"
#include "wmm.h"
#include "wpa_auth.h"
#include "wpa_auth_i.h"
#include "pmksa_cache_auth.h"
#ifdef CONFIG_IEEE80211R_AP
const unsigned int ftRRBseqTimeout = 10;
const unsigned int ftRRBmaxQueueLen = 100;
static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
const u8 *current_ap, const u8 *sta_addr,
u16 status, const u8 *resp_ies,
size_t resp_ies_len);
static void ft_finish_pull(struct wpa_state_machine *sm);
static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx);
static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx);
struct tlv_list {
u16 type;
size_t len;
const u8 *data;
};
/**
* wpa_ft_rrb_decrypt - Decrypt FT RRB message
* @key: AES-SIV key for AEAD
* @key_len: Length of key in octets
* @enc: Pointer to encrypted TLVs
* @enc_len: Length of encrypted TLVs in octets
* @auth: Pointer to authenticated TLVs
* @auth_len: Length of authenticated TLVs in octets
* @src_addr: MAC address of the frame sender
* @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
* @plain: Pointer to return the pointer to the allocated plaintext buffer;
* needs to be freed by the caller if not NULL;
* will only be returned on success
* @plain_len: Pointer to return the length of the allocated plaintext buffer
* in octets
* Returns: 0 on success, -1 on error
*/
static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
const u8 *enc, size_t enc_len,
const u8 *auth, const size_t auth_len,
const u8 *src_addr, u8 type,
u8 **plain, size_t *plain_size)
{
const u8 *ad[3] = { src_addr, auth, &type };
size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
MAC2STR(src_addr), type);
wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len);
wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
if (!key) { /* skip decryption */
*plain = os_memdup(enc, enc_len);
if (enc_len > 0 && !*plain)
goto err;
*plain_size = enc_len;
return 0;
}
*plain = NULL;
/* SIV overhead */
if (enc_len < AES_BLOCK_SIZE)
goto err;
*plain = os_zalloc(enc_len - AES_BLOCK_SIZE);
if (!*plain)
goto err;
if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
*plain) < 0) {
if (enc_len < AES_BLOCK_SIZE + 2)
goto err;
/* Try to work around Ethernet devices that add extra
* two octet padding even if the frame is longer than
* the minimum Ethernet frame. */
enc_len -= 2;
if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
*plain) < 0)
goto err;
}
*plain_size = enc_len - AES_BLOCK_SIZE;
wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs",
*plain, *plain_size);
return 0;
err:
os_free(*plain);
*plain = NULL;
*plain_size = 0;
wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt");
return -1;
}
/* get first tlv record in packet matching type
* @data (decrypted) packet
* @return 0 on success else -1
*/
static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len,
u16 type, size_t *tlv_len, const u8 **tlv_data)
{
const struct ft_rrb_tlv *f;
size_t left;
le16 type16;
size_t len;
left = plain_len;
type16 = host_to_le16(type);
while (left >= sizeof(*f)) {
f = (const struct ft_rrb_tlv *) plain;
left -= sizeof(*f);
plain += sizeof(*f);
len = le_to_host16(f->len);
if (left < len) {
wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
break;
}
if (f->type == type16) {
*tlv_len = len;
*tlv_data = plain;
return 0;
}
left -= len;
plain += len;
}
return -1;
}
static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len)
{
const struct ft_rrb_tlv *f;
size_t left;
size_t len;
left = plain_len;
wpa_printf(MSG_DEBUG, "FT: RRB dump message");
while (left >= sizeof(*f)) {
f = (const struct ft_rrb_tlv *) plain;
left -= sizeof(*f);
plain += sizeof(*f);
len = le_to_host16(f->len);
wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu",
le_to_host16(f->type), len);
if (left < len) {
wpa_printf(MSG_DEBUG,
"FT: RRB message truncated: left %zu bytes, need %zu",
left, len);
break;
}
wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len);
left -= len;
plain += len;
}
if (left > 0)
wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left);
wpa_printf(MSG_DEBUG, "FT: RRB dump message end");
}
static int cmp_int(const void *a, const void *b)
{
int x, y;
x = *((int *) a);
y = *((int *) b);
return x - y;
}
static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len,
struct vlan_description *vlan)
{
struct ft_rrb_tlv *f;
size_t left;
size_t len;
int taggedidx;
int vlan_id;
int type;
left = plain_len;
taggedidx = 0;
os_memset(vlan, 0, sizeof(*vlan));
while (left >= sizeof(*f)) {
f = (struct ft_rrb_tlv *) plain;
left -= sizeof(*f);
plain += sizeof(*f);
len = le_to_host16(f->len);
type = le_to_host16(f->type);
if (left < len) {
wpa_printf(MSG_DEBUG, "FT: RRB message truncated");
return -1;
}
if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED)
goto skip;
if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) {
wpa_printf(MSG_DEBUG,
"FT: RRB VLAN_UNTAGGED invalid length");
return -1;
}
if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) {
wpa_printf(MSG_DEBUG,
"FT: RRB VLAN_TAGGED invalid length");
return -1;
}
while (len >= sizeof(le16)) {
vlan_id = WPA_GET_LE16(plain);
plain += sizeof(le16);
left -= sizeof(le16);
len -= sizeof(le16);
if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) {
wpa_printf(MSG_DEBUG,
"FT: RRB VLAN ID invalid %d",
vlan_id);
continue;
}
if (type == FT_RRB_VLAN_UNTAGGED)
vlan->untagged = vlan_id;
if (type == FT_RRB_VLAN_TAGGED &&
taggedidx < MAX_NUM_TAGGED_VLAN) {
vlan->tagged[taggedidx] = vlan_id;
taggedidx++;
} else if (type == FT_RRB_VLAN_TAGGED) {
wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs");
}
}
skip:
left -= len;
plain += len;
}
if (taggedidx)
qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int);
vlan->notempty = vlan->untagged || vlan->tagged[0];
return 0;
}
static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs)
{
size_t tlv_len = 0;
int i;
if (!tlvs)
return 0;
for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
tlv_len += sizeof(struct ft_rrb_tlv);
tlv_len += tlvs[i].len;
}
return tlv_len;
}
static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start,
u8 *endpos)
{
int i;
size_t tlv_len;
struct ft_rrb_tlv *hdr;
u8 *pos;
if (!tlvs)
return 0;
tlv_len = 0;
pos = start;
for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) {
if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start))
return tlv_len;
tlv_len += sizeof(*hdr);
hdr = (struct ft_rrb_tlv *) pos;
hdr->type = host_to_le16(tlvs[i].type);
hdr->len = host_to_le16(tlvs[i].len);
pos = start + tlv_len;
if (tlv_len + tlvs[i].len > (size_t) (endpos - start))
return tlv_len;
if (tlvs[i].len == 0)
continue;
tlv_len += tlvs[i].len;
os_memcpy(pos, tlvs[i].data, tlvs[i].len);
pos = start + tlv_len;
}
return tlv_len;
}
static size_t wpa_ft_vlan_len(const struct vlan_description *vlan)
{
size_t tlv_len = 0;
int i;
if (!vlan || !vlan->notempty)
return 0;
if (vlan->untagged) {
tlv_len += sizeof(struct ft_rrb_tlv);
tlv_len += sizeof(le16);
}
if (vlan->tagged[0])
tlv_len += sizeof(struct ft_rrb_tlv);
for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++)
tlv_len += sizeof(le16);
return tlv_len;
}
static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan,
u8 *start, u8 *endpos)
{
size_t tlv_len;
int i, len;
struct ft_rrb_tlv *hdr;
u8 *pos = start;
if (!vlan || !vlan->notempty)
return 0;
tlv_len = 0;
if (vlan->untagged) {
tlv_len += sizeof(*hdr);
if (start + tlv_len > endpos)
return tlv_len;
hdr = (struct ft_rrb_tlv *) pos;
hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED);
hdr->len = host_to_le16(sizeof(le16));
pos = start + tlv_len;
tlv_len += sizeof(le16);
if (start + tlv_len > endpos)
return tlv_len;
WPA_PUT_LE16(pos, vlan->untagged);
pos = start + tlv_len;
}
if (!vlan->tagged[0])
return tlv_len;
tlv_len += sizeof(*hdr);
if (start + tlv_len > endpos)
return tlv_len;
hdr = (struct ft_rrb_tlv *) pos;
hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED);
len = 0; /* len is computed below */
pos = start + tlv_len;
for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) {
tlv_len += sizeof(le16);
if (start + tlv_len > endpos)
break;
len += sizeof(le16);
WPA_PUT_LE16(pos, vlan->tagged[i]);
pos = start + tlv_len;
}
hdr->len = host_to_le16(len);
return tlv_len;
}
static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1,
const struct tlv_list *tlvs2,
const struct vlan_description *vlan,
u8 **plain, size_t *plain_len)
{
u8 *pos, *endpos;
size_t tlv_len;
tlv_len = wpa_ft_tlv_len(tlvs1);
tlv_len += wpa_ft_tlv_len(tlvs2);
tlv_len += wpa_ft_vlan_len(vlan);
*plain_len = tlv_len;
*plain = os_zalloc(tlv_len);
if (!*plain) {
wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext");
goto err;
}
pos = *plain;
endpos = *plain + tlv_len;
pos += wpa_ft_tlv_lin(tlvs1, pos, endpos);
pos += wpa_ft_tlv_lin(tlvs2, pos, endpos);
pos += wpa_ft_vlan_lin(vlan, pos, endpos);
- /* sanity check */
+ /* validity check */
if (pos != endpos) {
wpa_printf(MSG_ERROR, "FT: Length error building RRB");
goto err;
}
return 0;
err:
os_free(*plain);
*plain = NULL;
*plain_len = 0;
return -1;
}
static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
const u8 *plain, const size_t plain_len,
const u8 *auth, const size_t auth_len,
const u8 *src_addr, u8 type, u8 *enc)
{
const u8 *ad[3] = { src_addr, auth, &type };
size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
MAC2STR(src_addr), type);
wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message",
plain, plain_len);
wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len);
wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
if (!key) {
/* encryption not needed, return plaintext as packet */
os_memcpy(enc, plain, plain_len);
} else if (aes_siv_encrypt(key, key_len, plain, plain_len,
3, ad, ad_len, enc) < 0) {
wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
return -1;
}
wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs",
enc, plain_len + AES_BLOCK_SIZE);
return 0;
}
/**
* wpa_ft_rrb_build - Build and encrypt an FT RRB message
* @key: AES-SIV key for AEAD
* @key_len: Length of key in octets
* @tlvs_enc0: First set of to-be-encrypted TLVs
* @tlvs_enc1: Second set of to-be-encrypted TLVs
* @tlvs_auth: Set of to-be-authenticated TLVs
* @src_addr: MAC address of the frame sender
* @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*)
* @packet Pointer to return the pointer to the allocated packet buffer;
* needs to be freed by the caller if not null;
* will only be returned on success
* @packet_len: Pointer to return the length of the allocated buffer in octets
* Returns: 0 on success, -1 on error
*/
static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
const struct tlv_list *tlvs_enc0,
const struct tlv_list *tlvs_enc1,
const struct tlv_list *tlvs_auth,
const struct vlan_description *vlan,
const u8 *src_addr, u8 type,
u8 **packet, size_t *packet_len)
{
u8 *plain = NULL, *auth = NULL, *pos, *tmp;
size_t plain_len = 0, auth_len = 0;
int ret = -1;
size_t pad_len = 0;
*packet = NULL;
if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
goto out;
if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0)
goto out;
*packet_len = sizeof(u16) + auth_len + plain_len;
if (key)
*packet_len += AES_BLOCK_SIZE;
#define RRB_MIN_MSG_LEN 64
if (*packet_len < RRB_MIN_MSG_LEN) {
pad_len = RRB_MIN_MSG_LEN - *packet_len;
if (pad_len < sizeof(struct ft_rrb_tlv))
pad_len = sizeof(struct ft_rrb_tlv);
wpa_printf(MSG_DEBUG,
"FT: Pad message to minimum Ethernet frame length (%d --> %d)",
(int) *packet_len, (int) (*packet_len + pad_len));
*packet_len += pad_len;
tmp = os_realloc(auth, auth_len + pad_len);
if (!tmp)
goto out;
auth = tmp;
pos = auth + auth_len;
WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY);
pos += 2;
WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv));
pos += 2;
os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv));
auth_len += pad_len;
}
*packet = os_zalloc(*packet_len);
if (!*packet)
goto out;
pos = *packet;
WPA_PUT_LE16(pos, auth_len);
pos += 2;
os_memcpy(pos, auth, auth_len);
pos += auth_len;
if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
auth_len, src_addr, type, pos) < 0)
goto out;
wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len);
ret = 0;
out:
bin_clear_free(plain, plain_len);
os_free(auth);
if (ret) {
wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message");
os_free(*packet);
*packet = NULL;
*packet_len = 0;
}
return ret;
}
#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \
if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
&f_##field##_len, &f_##field) < 0 || \
(checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
wpa_printf(MSG_INFO, "FT: Missing required " #field \
" in %s from " MACSTR, txt, MAC2STR(src_addr)); \
wpa_ft_rrb_dump(srcfield, srcfield##_len); \
goto out; \
} \
} while (0)
#define RRB_GET(type, field, txt, checklength) \
RRB_GET_SRC(plain, type, field, txt, checklength)
#define RRB_GET_AUTH(type, field, txt, checklength) \
RRB_GET_SRC(auth, type, field, txt, checklength)
#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \
if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \
&f_##field##_len, &f_##field) < 0 || \
(checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \
wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \
" in %s from " MACSTR, txt, MAC2STR(src_addr)); \
f_##field##_len = 0; \
f_##field = NULL; \
} \
} while (0)
#define RRB_GET_OPTIONAL(type, field, txt, checklength) \
RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength)
#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \
RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength)
static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
const u8 *data, size_t data_len)
{
if (wpa_auth->cb->send_ether == NULL)
return -1;
wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB,
data, data_len);
}
static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth,
const u8 *dst, u8 oui_suffix,
const u8 *data, size_t data_len)
{
if (!wpa_auth->cb->send_oui)
return -1;
wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)",
oui_suffix, MAC2STR(dst), (unsigned int) data_len);
return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
data_len);
}
static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
const u8 *dst, const u8 *data, size_t data_len)
{
if (wpa_auth->cb->send_ft_action == NULL)
return -1;
return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst,
data, data_len);
}
static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
const u8 *addr, const u8 *p2p_dev_addr,
const u8 *prev_psk)
{
if (wpa_auth->cb->get_psk == NULL)
return NULL;
return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
prev_psk, NULL, NULL);
}
static struct wpa_state_machine *
wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
{
if (wpa_auth->cb->add_sta == NULL)
return NULL;
return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr);
}
static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr, struct vlan_description *vlan)
{
if (!wpa_auth->cb->set_vlan)
return -1;
return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
}
static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr, struct vlan_description *vlan)
{
if (!wpa_auth->cb->get_vlan)
return -1;
return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan);
}
static int
wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
const u8 *identity, size_t identity_len)
{
if (!wpa_auth->cb->set_identity)
return -1;
return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity,
identity_len);
}
static size_t
wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
const u8 **buf)
{
*buf = NULL;
if (!wpa_auth->cb->get_identity)
return 0;
return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf);
}
static int
wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
const u8 *radius_cui, size_t radius_cui_len)
{
if (!wpa_auth->cb->set_radius_cui)
return -1;
return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr,
radius_cui, radius_cui_len);
}
static size_t
wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
const u8 **buf)
{
*buf = NULL;
if (!wpa_auth->cb->get_radius_cui)
return 0;
return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf);
}
static void
wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr, int session_timeout)
{
if (!wpa_auth->cb->set_session_timeout)
return;
wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr,
session_timeout);
}
static int
wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr)
{
if (!wpa_auth->cb->get_session_timeout)
return 0;
return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr);
}
static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr,
u8 *tspec_ie, size_t tspec_ielen)
{
if (wpa_auth->cb->add_tspec == NULL) {
wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
return -1;
}
return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie,
tspec_ielen);
}
#ifdef CONFIG_OCV
static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
struct wpa_channel_info *ci)
{
if (!wpa_auth->cb->channel_info)
return -1;
return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
}
#endif /* CONFIG_OCV */
int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
{
u8 *pos = buf;
u8 capab;
if (len < 2 + sizeof(struct rsn_mdie))
return -1;
*pos++ = WLAN_EID_MOBILITY_DOMAIN;
*pos++ = MOBILITY_DOMAIN_ID_LEN + 1;
os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
pos += MOBILITY_DOMAIN_ID_LEN;
capab = 0;
if (conf->ft_over_ds)
capab |= RSN_FT_CAPAB_FT_OVER_DS;
*pos++ = capab;
return pos - buf;
}
int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
size_t subelem_len, int rsnxe_used)
{
u8 *pos = buf, *ielen;
size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) :
sizeof(struct rsn_ftie);
if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len +
subelem_len)
return -1;
*pos++ = WLAN_EID_FAST_BSS_TRANSITION;
ielen = pos++;
if (use_sha384) {
struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos;
os_memset(hdr, 0, sizeof(*hdr));
pos += sizeof(*hdr);
WPA_PUT_LE16(hdr->mic_control, !!rsnxe_used);
if (anonce)
os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
if (snonce)
os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
} else {
struct rsn_ftie *hdr = (struct rsn_ftie *) pos;
os_memset(hdr, 0, sizeof(*hdr));
pos += sizeof(*hdr);
WPA_PUT_LE16(hdr->mic_control, !!rsnxe_used);
if (anonce)
os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN);
if (snonce)
os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN);
}
/* Optional Parameters */
*pos++ = FTIE_SUBELEM_R1KH_ID;
*pos++ = FT_R1KH_ID_LEN;
os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN);
pos += FT_R1KH_ID_LEN;
if (r0kh_id) {
*pos++ = FTIE_SUBELEM_R0KH_ID;
*pos++ = r0kh_id_len;
os_memcpy(pos, r0kh_id, r0kh_id_len);
pos += r0kh_id_len;
}
if (subelem) {
os_memcpy(pos, subelem, subelem_len);
pos += subelem_len;
}
*ielen = pos - buf - 2;
return pos - buf;
}
/* A packet to be handled after seq response */
struct ft_remote_item {
struct dl_list list;
u8 nonce[FT_RRB_NONCE_LEN];
struct os_reltime nonce_ts;
u8 src_addr[ETH_ALEN];
u8 *enc;
size_t enc_len;
u8 *auth;
size_t auth_len;
int (*cb)(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
int no_defer);
};
static void wpa_ft_rrb_seq_free(struct ft_remote_item *item)
{
eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item);
dl_list_del(&item->list);
bin_clear_free(item->enc, item->enc_len);
os_free(item->auth);
os_free(item);
}
static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth,
struct ft_remote_seq *rkh_seq, int cb)
{
struct ft_remote_item *item, *n;
dl_list_for_each_safe(item, n, &rkh_seq->rx.queue,
struct ft_remote_item, list) {
if (cb && item->cb)
item->cb(wpa_auth, item->src_addr, item->enc,
item->enc_len, item->auth, item->auth_len, 1);
wpa_ft_rrb_seq_free(item);
}
}
static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct ft_remote_item *item = timeout_ctx;
wpa_ft_rrb_seq_free(item);
}
static int
wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
struct ft_remote_seq *rkh_seq, const u8 *src_addr,
const u8 *f_r0kh_id, size_t f_r0kh_id_len,
const u8 *f_r1kh_id, const u8 *key, size_t key_len,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
int (*cb)(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
int no_defer))
{
struct ft_remote_item *item = NULL;
u8 *packet = NULL;
size_t packet_len;
struct tlv_list seq_req_auth[] = {
{ .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
.data = NULL /* to be filled: item->nonce */ },
{ .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len,
.data = f_r0kh_id },
{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
.data = f_r1kh_id },
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) {
wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long");
goto err;
}
wpa_printf(MSG_DEBUG, "FT: Send sequence number request from " MACSTR
" to " MACSTR,
MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
item = os_zalloc(sizeof(*item));
if (!item)
goto err;
os_memcpy(item->src_addr, src_addr, ETH_ALEN);
item->cb = cb;
if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) {
wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes");
goto err;
}
if (os_get_reltime(&item->nonce_ts) < 0)
goto err;
if (enc && enc_len > 0) {
item->enc = os_memdup(enc, enc_len);
item->enc_len = enc_len;
if (!item->enc)
goto err;
}
if (auth && auth_len > 0) {
item->auth = os_memdup(auth, auth_len);
item->auth_len = auth_len;
if (!item->auth)
goto err;
}
eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout,
wpa_auth, item);
seq_req_auth[0].data = item->nonce;
if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL,
wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
&packet, &packet_len) < 0) {
item = NULL; /* some other seq resp might still accept this */
goto err;
}
dl_list_add(&rkh_seq->rx.queue, &item->list);
wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
packet, packet_len);
os_free(packet);
return 0;
err:
wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request");
if (item) {
os_free(item->auth);
bin_clear_free(item->enc, item->enc_len);
os_free(item);
}
return -1;
}
#define FT_RRB_SEQ_OK 0
#define FT_RRB_SEQ_DROP 1
#define FT_RRB_SEQ_DEFER 2
static int
wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
const char *msgtype, int no_defer)
{
const u8 *f_seq;
size_t f_seq_len;
const struct ft_rrb_seq *msg_both;
u32 msg_seq, msg_off, rkh_off;
struct os_reltime now;
unsigned int i;
RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len);
msg_both = (const struct ft_rrb_seq *) f_seq;
if (rkh_seq->rx.num_last == 0) {
/* first packet from remote */
goto defer;
}
if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) {
/* remote might have rebooted */
goto defer;
}
if (os_get_reltime(&now) == 0) {
u32 msg_ts_now_remote, msg_ts_off;
struct os_reltime now_remote;
os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote);
msg_ts_now_remote = now_remote.sec;
msg_ts_off = le_to_host32(msg_both->ts) -
(msg_ts_now_remote - ftRRBseqTimeout);
if (msg_ts_off > 2 * ftRRBseqTimeout)
goto defer;
}
msg_seq = le_to_host32(msg_both->seq);
rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
msg_off = msg_seq - rkh_off;
if (msg_off > 0xC0000000)
goto out; /* too old message, drop it */
if (msg_off <= 0x40000000) {
for (i = 0; i < rkh_seq->rx.num_last; i++) {
if (rkh_seq->rx.last[i] == msg_seq)
goto out; /* duplicate message, drop it */
}
return FT_RRB_SEQ_OK;
}
defer:
if (no_defer)
goto out;
wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from "
MACSTR, msgtype, MAC2STR(src_addr));
return FT_RRB_SEQ_DEFER;
out:
wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR,
msgtype, MAC2STR(src_addr));
return FT_RRB_SEQ_DROP;
}
static void
wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth,
struct ft_remote_seq *rkh_seq, const u8 *src_addr,
const u8 *auth, size_t auth_len,
const char *msgtype)
{
const u8 *f_seq;
size_t f_seq_len;
const struct ft_rrb_seq *msg_both;
u32 msg_seq, msg_off, min_off, rkh_off;
int minidx = 0;
unsigned int i;
RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both));
msg_both = (const struct ft_rrb_seq *) f_seq;
msg_seq = le_to_host32(msg_both->seq);
if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) {
rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq;
rkh_seq->rx.num_last++;
return;
}
rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx];
for (i = 0; i < rkh_seq->rx.num_last; i++) {
msg_off = rkh_seq->rx.last[i] - rkh_off;
min_off = rkh_seq->rx.last[minidx] - rkh_off;
if (msg_off < min_off && i != rkh_seq->rx.offsetidx)
minidx = i;
}
rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq;
rkh_seq->rx.offsetidx = minidx;
return;
out:
/* RRB_GET_AUTH should never fail here as
* wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */
wpa_printf(MSG_ERROR, "FT: %s() failed", __func__);
}
static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq,
struct ft_rrb_seq *f_seq)
{
struct os_reltime now;
if (os_get_reltime(&now) < 0)
return -1;
if (!rkh_seq->tx.dom) {
if (random_get_bytes((u8 *) &rkh_seq->tx.seq,
sizeof(rkh_seq->tx.seq))) {
wpa_printf(MSG_ERROR,
"FT: Failed to get random data for sequence number initialization");
rkh_seq->tx.seq = now.usec;
}
if (random_get_bytes((u8 *) &rkh_seq->tx.dom,
sizeof(rkh_seq->tx.dom))) {
wpa_printf(MSG_ERROR,
"FT: Failed to get random data for sequence number initialization");
rkh_seq->tx.dom = now.usec;
}
rkh_seq->tx.dom |= 1;
}
f_seq->dom = host_to_le32(rkh_seq->tx.dom);
f_seq->seq = host_to_le32(rkh_seq->tx.seq);
f_seq->ts = host_to_le32(now.sec);
rkh_seq->tx.seq++;
return 0;
}
struct wpa_ft_pmk_r0_sa {
struct dl_list list;
u8 pmk_r0[PMK_LEN_MAX];
size_t pmk_r0_len;
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
u8 spa[ETH_ALEN];
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
struct vlan_description *vlan;
os_time_t expiration; /* 0 for no expiration */
u8 *identity;
size_t identity_len;
u8 *radius_cui;
size_t radius_cui_len;
os_time_t session_timeout; /* 0 for no expiration */
/* TODO: radius_class, EAP type */
int pmk_r1_pushed;
};
struct wpa_ft_pmk_r1_sa {
struct dl_list list;
u8 pmk_r1[PMK_LEN_MAX];
size_t pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 spa[ETH_ALEN];
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
struct vlan_description *vlan;
u8 *identity;
size_t identity_len;
u8 *radius_cui;
size_t radius_cui_len;
os_time_t session_timeout; /* 0 for no expiration */
/* TODO: radius_class, EAP type */
};
struct wpa_ft_pmk_cache {
struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */
struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */
};
static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx);
static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx);
static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0)
{
if (!r0)
return;
dl_list_del(&r0->list);
eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
os_memset(r0->pmk_r0, 0, PMK_LEN_MAX);
os_free(r0->vlan);
os_free(r0->identity);
os_free(r0->radius_cui);
os_free(r0);
}
static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx;
struct os_reltime now;
int expires_in;
int session_timeout;
os_get_reltime(&now);
if (!r0)
return;
expires_in = r0->expiration - now.sec;
session_timeout = r0->session_timeout - now.sec;
/* conditions to remove from cache:
* a) r0->expiration is set and hit
* -or-
* b) r0->session_timeout is set and hit
*/
if ((!r0->expiration || expires_in > 0) &&
(!r0->session_timeout || session_timeout > 0)) {
wpa_printf(MSG_ERROR,
"FT: %s() called for non-expired entry %p",
__func__, r0);
eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL);
if (r0->expiration && expires_in > 0)
eloop_register_timeout(expires_in + 1, 0,
wpa_ft_expire_pmk_r0, r0, NULL);
if (r0->session_timeout && session_timeout > 0)
eloop_register_timeout(session_timeout + 1, 0,
wpa_ft_expire_pmk_r0, r0, NULL);
return;
}
wpa_ft_free_pmk_r0(r0);
}
static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1)
{
if (!r1)
return;
dl_list_del(&r1->list);
eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL);
os_memset(r1->pmk_r1, 0, PMK_LEN_MAX);
os_free(r1->vlan);
os_free(r1->identity);
os_free(r1->radius_cui);
os_free(r1);
}
static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx;
wpa_ft_free_pmk_r1(r1);
}
struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void)
{
struct wpa_ft_pmk_cache *cache;
cache = os_zalloc(sizeof(*cache));
if (cache) {
dl_list_init(&cache->pmk_r0);
dl_list_init(&cache->pmk_r1);
}
return cache;
}
void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache)
{
struct wpa_ft_pmk_r0_sa *r0, *r0prev;
struct wpa_ft_pmk_r1_sa *r1, *r1prev;
dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0,
struct wpa_ft_pmk_r0_sa, list)
wpa_ft_free_pmk_r0(r0);
dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1,
struct wpa_ft_pmk_r1_sa, list)
wpa_ft_free_pmk_r1(r1);
os_free(cache);
}
static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r0,
size_t pmk_r0_len,
const u8 *pmk_r0_name, int pairwise,
const struct vlan_description *vlan,
int expires_in, int session_timeout,
const u8 *identity, size_t identity_len,
const u8 *radius_cui, size_t radius_cui_len)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r0_sa *r0;
struct os_reltime now;
/* TODO: add limit on number of entries in cache */
os_get_reltime(&now);
r0 = os_zalloc(sizeof(*r0));
if (r0 == NULL)
return -1;
os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len);
r0->pmk_r0_len = pmk_r0_len;
os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
os_memcpy(r0->spa, spa, ETH_ALEN);
r0->pairwise = pairwise;
if (expires_in > 0)
r0->expiration = now.sec + expires_in;
if (vlan && vlan->notempty) {
r0->vlan = os_zalloc(sizeof(*vlan));
if (!r0->vlan) {
bin_clear_free(r0, sizeof(*r0));
return -1;
}
*r0->vlan = *vlan;
}
if (identity) {
r0->identity = os_malloc(identity_len);
if (r0->identity) {
os_memcpy(r0->identity, identity, identity_len);
r0->identity_len = identity_len;
}
}
if (radius_cui) {
r0->radius_cui = os_malloc(radius_cui_len);
if (r0->radius_cui) {
os_memcpy(r0->radius_cui, radius_cui, radius_cui_len);
r0->radius_cui_len = radius_cui_len;
}
}
if (session_timeout > 0)
r0->session_timeout = now.sec + session_timeout;
dl_list_add(&cache->pmk_r0, &r0->list);
if (expires_in > 0)
eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0,
r0, NULL);
if (session_timeout > 0)
eloop_register_timeout(session_timeout + 1, 0,
wpa_ft_expire_pmk_r0, r0, NULL);
return 0;
}
static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r0_name,
const struct wpa_ft_pmk_r0_sa **r0_out)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r0_sa *r0;
struct os_reltime now;
os_get_reltime(&now);
dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
WPA_PMK_NAME_LEN) == 0) {
*r0_out = r0;
return 0;
}
}
*r0_out = NULL;
return -1;
}
static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r1,
size_t pmk_r1_len,
const u8 *pmk_r1_name, int pairwise,
const struct vlan_description *vlan,
int expires_in, int session_timeout,
const u8 *identity, size_t identity_len,
const u8 *radius_cui, size_t radius_cui_len)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
int max_expires_in = wpa_auth->conf.r1_max_key_lifetime;
struct wpa_ft_pmk_r1_sa *r1;
struct os_reltime now;
/* TODO: limit on number of entries in cache */
os_get_reltime(&now);
if (max_expires_in && (max_expires_in < expires_in || expires_in == 0))
expires_in = max_expires_in;
r1 = os_zalloc(sizeof(*r1));
if (r1 == NULL)
return -1;
os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len);
r1->pmk_r1_len = pmk_r1_len;
os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
os_memcpy(r1->spa, spa, ETH_ALEN);
r1->pairwise = pairwise;
if (vlan && vlan->notempty) {
r1->vlan = os_zalloc(sizeof(*vlan));
if (!r1->vlan) {
bin_clear_free(r1, sizeof(*r1));
return -1;
}
*r1->vlan = *vlan;
}
if (identity) {
r1->identity = os_malloc(identity_len);
if (r1->identity) {
os_memcpy(r1->identity, identity, identity_len);
r1->identity_len = identity_len;
}
}
if (radius_cui) {
r1->radius_cui = os_malloc(radius_cui_len);
if (r1->radius_cui) {
os_memcpy(r1->radius_cui, radius_cui, radius_cui_len);
r1->radius_cui_len = radius_cui_len;
}
}
if (session_timeout > 0)
r1->session_timeout = now.sec + session_timeout;
dl_list_add(&cache->pmk_r1, &r1->list);
if (expires_in > 0)
eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1,
r1, NULL);
if (session_timeout > 0)
eloop_register_timeout(session_timeout + 1, 0,
wpa_ft_expire_pmk_r1, r1, NULL);
return 0;
}
int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
const u8 *spa, const u8 *pmk_r1_name,
u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise,
struct vlan_description *vlan,
const u8 **identity, size_t *identity_len,
const u8 **radius_cui, size_t *radius_cui_len,
int *session_timeout)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r1_sa *r1;
struct os_reltime now;
os_get_reltime(&now);
dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) {
if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
WPA_PMK_NAME_LEN) == 0) {
os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len);
*pmk_r1_len = r1->pmk_r1_len;
if (pairwise)
*pairwise = r1->pairwise;
if (vlan && r1->vlan)
*vlan = *r1->vlan;
if (vlan && !r1->vlan)
os_memset(vlan, 0, sizeof(*vlan));
if (identity && identity_len) {
*identity = r1->identity;
*identity_len = r1->identity_len;
}
if (radius_cui && radius_cui_len) {
*radius_cui = r1->radius_cui;
*radius_cui_len = r1->radius_cui_len;
}
if (session_timeout && r1->session_timeout > now.sec)
*session_timeout = r1->session_timeout -
now.sec;
else if (session_timeout && r1->session_timeout)
*session_timeout = 1;
else if (session_timeout)
*session_timeout = 0;
return 0;
}
}
return -1;
}
static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh)
{
if (r0kh->seq)
return 0;
r0kh->seq = os_zalloc(sizeof(*r0kh->seq));
if (!r0kh->seq) {
wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq");
return -1;
}
dl_list_init(&r0kh->seq->rx.queue);
return 0;
}
static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth,
const u8 *f_r0kh_id, size_t f_r0kh_id_len,
struct ft_remote_r0kh **r0kh_out,
struct ft_remote_r0kh **r0kh_wildcard)
{
struct ft_remote_r0kh *r0kh;
*r0kh_wildcard = NULL;
*r0kh_out = NULL;
if (wpa_auth->conf.r0kh_list)
r0kh = *wpa_auth->conf.r0kh_list;
else
r0kh = NULL;
for (; r0kh; r0kh = r0kh->next) {
if (r0kh->id_len == 1 && r0kh->id[0] == '*')
*r0kh_wildcard = r0kh;
if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len &&
os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0)
*r0kh_out = r0kh;
}
if (!*r0kh_out && !*r0kh_wildcard)
wpa_printf(MSG_DEBUG, "FT: No matching R0KH found");
if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0)
*r0kh_out = NULL;
}
static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh)
{
if (r1kh->seq)
return 0;
r1kh->seq = os_zalloc(sizeof(*r1kh->seq));
if (!r1kh->seq) {
wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq");
return -1;
}
dl_list_init(&r1kh->seq->rx.queue);
return 0;
}
static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth,
const u8 *f_r1kh_id,
struct ft_remote_r1kh **r1kh_out,
struct ft_remote_r1kh **r1kh_wildcard)
{
struct ft_remote_r1kh *r1kh;
*r1kh_wildcard = NULL;
*r1kh_out = NULL;
if (wpa_auth->conf.r1kh_list)
r1kh = *wpa_auth->conf.r1kh_list;
else
r1kh = NULL;
for (; r1kh; r1kh = r1kh->next) {
if (is_zero_ether_addr(r1kh->addr) &&
is_zero_ether_addr(r1kh->id))
*r1kh_wildcard = r1kh;
if (f_r1kh_id &&
os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0)
*r1kh_out = r1kh;
}
if (!*r1kh_out && !*r1kh_wildcard)
wpa_printf(MSG_DEBUG, "FT: No matching R1KH found");
if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0)
*r1kh_out = NULL;
}
static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth,
const u8 *f_r0kh_id, size_t f_r0kh_id_len)
{
if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len ||
os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder,
f_r0kh_id_len) != 0)
return -1;
return 0;
}
static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth,
const u8 *f_r1kh_id)
{
if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder,
FT_R1KH_ID_LEN) != 0)
return -1;
return 0;
}
static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
struct ft_remote_r0kh *r0kh, *prev = NULL;
if (!wpa_auth->conf.r0kh_list)
return;
for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) {
if (r0kh == timeout_ctx)
break;
prev = r0kh;
}
if (!r0kh)
return;
if (prev)
prev->next = r0kh->next;
else
*wpa_auth->conf.r0kh_list = r0kh->next;
if (r0kh->seq)
wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
os_free(r0kh->seq);
os_free(r0kh);
}
static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth,
struct ft_remote_r0kh *r0kh, int timeout)
{
if (timeout > 0)
eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
wpa_auth, r0kh);
}
static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth,
struct ft_remote_r0kh *r0kh, int timeout)
{
eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh);
if (timeout > 0)
eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
wpa_auth, r0kh);
}
static struct ft_remote_r0kh *
wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth,
struct ft_remote_r0kh *r0kh_wildcard,
const u8 *src_addr, const u8 *r0kh_id, size_t id_len,
int timeout)
{
struct ft_remote_r0kh *r0kh;
if (!wpa_auth->conf.r0kh_list)
return NULL;
r0kh = os_zalloc(sizeof(*r0kh));
if (!r0kh)
return NULL;
if (src_addr)
os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr));
if (id_len > FT_R0KH_ID_MAX_LEN)
id_len = FT_R0KH_ID_MAX_LEN;
os_memcpy(r0kh->id, r0kh_id, id_len);
r0kh->id_len = id_len;
os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key));
r0kh->next = *wpa_auth->conf.r0kh_list;
*wpa_auth->conf.r0kh_list = r0kh;
if (timeout > 0)
eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh,
wpa_auth, r0kh);
if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0)
return NULL;
return r0kh;
}
static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
struct ft_remote_r1kh *r1kh, *prev = NULL;
if (!wpa_auth->conf.r1kh_list)
return;
for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
if (r1kh == timeout_ctx)
break;
prev = r1kh;
}
if (!r1kh)
return;
if (prev)
prev->next = r1kh->next;
else
*wpa_auth->conf.r1kh_list = r1kh->next;
if (r1kh->seq)
wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
os_free(r1kh->seq);
os_free(r1kh);
}
static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth,
struct ft_remote_r1kh *r1kh, int timeout)
{
if (timeout > 0)
eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
wpa_auth, r1kh);
}
static struct ft_remote_r1kh *
wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth,
struct ft_remote_r1kh *r1kh_wildcard,
const u8 *src_addr, const u8 *r1kh_id, int timeout)
{
struct ft_remote_r1kh *r1kh;
if (!wpa_auth->conf.r1kh_list)
return NULL;
r1kh = os_zalloc(sizeof(*r1kh));
if (!r1kh)
return NULL;
os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr));
os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id));
os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key));
r1kh->next = *wpa_auth->conf.r1kh_list;
*wpa_auth->conf.r1kh_list = r1kh;
if (timeout > 0)
eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh,
wpa_auth, r1kh);
if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
return NULL;
return r1kh;
}
void wpa_ft_sta_deinit(struct wpa_state_machine *sm)
{
eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
}
static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth)
{
struct ft_remote_r0kh *r0kh;
struct ft_remote_r1kh *r1kh;
eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX);
if (wpa_auth->conf.r0kh_list)
r0kh = *wpa_auth->conf.r0kh_list;
else
r0kh = NULL;
for (; r0kh; r0kh = r0kh->next) {
if (!r0kh->seq)
continue;
wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0);
os_free(r0kh->seq);
r0kh->seq = NULL;
}
if (wpa_auth->conf.r1kh_list)
r1kh = *wpa_auth->conf.r1kh_list;
else
r1kh = NULL;
for (; r1kh; r1kh = r1kh->next) {
if (!r1kh->seq)
continue;
wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0);
os_free(r1kh->seq);
r1kh->seq = NULL;
}
}
static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth)
{
struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL;
struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL;
if (wpa_auth->conf.r0kh_list)
r0kh = *wpa_auth->conf.r0kh_list;
else
r0kh = NULL;
while (r0kh) {
r0kh_next = r0kh->next;
if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth,
r0kh) > 0) {
if (r0kh_prev)
r0kh_prev->next = r0kh_next;
else
*wpa_auth->conf.r0kh_list = r0kh_next;
os_free(r0kh);
} else {
r0kh_prev = r0kh;
}
r0kh = r0kh_next;
}
if (wpa_auth->conf.r1kh_list)
r1kh = *wpa_auth->conf.r1kh_list;
else
r1kh = NULL;
while (r1kh) {
r1kh_next = r1kh->next;
if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth,
r1kh) > 0) {
if (r1kh_prev)
r1kh_prev->next = r1kh_next;
else
*wpa_auth->conf.r1kh_list = r1kh_next;
os_free(r1kh);
} else {
r1kh_prev = r1kh;
}
r1kh = r1kh_next;
}
}
void wpa_ft_deinit(struct wpa_authenticator *wpa_auth)
{
wpa_ft_deinit_seq(wpa_auth);
wpa_ft_deinit_rkh_tmp(wpa_auth);
}
static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth,
const u8 *f_r0kh_id, size_t f_r0kh_id_len)
{
struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
if (!wpa_auth->conf.rkh_neg_timeout)
return;
wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
&r0kh, &r0kh_wildcard);
if (!r0kh_wildcard) {
/* r0kh removed after neg_timeout and might need re-adding */
return;
}
wpa_hexdump(MSG_DEBUG, "FT: Temporarily block R0KH-ID",
f_r0kh_id, f_r0kh_id_len);
if (r0kh) {
wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh,
wpa_auth->conf.rkh_neg_timeout);
os_memset(r0kh->addr, 0, ETH_ALEN);
} else
wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id,
f_r0kh_id_len,
wpa_auth->conf.rkh_neg_timeout);
}
static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_state_machine *sm = eloop_ctx;
wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR,
MAC2STR(sm->addr));
if (sm->ft_pending_pull_left_retries <= 0)
wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len);
/* cancel multiple timeouts */
eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL);
ft_finish_pull(sm);
}
static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
const u8 *ies, size_t ies_len,
const u8 *pmk_r0_name)
{
struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
u8 *packet = NULL;
const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder;
size_t packet_len, key_len;
struct ft_rrb_seq f_seq;
int tsecs, tusecs, first;
struct wpabuf *ft_pending_req_ies;
int r0kh_timeout;
struct tlv_list req_enc[] = {
{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
.data = pmk_r0_name },
{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
.data = sm->addr },
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
struct tlv_list req_auth[] = {
{ .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN,
.data = sm->ft_pending_pull_nonce },
{ .type = FT_RRB_SEQ, .len = sizeof(f_seq),
.data = (u8 *) &f_seq },
{ .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len,
.data = sm->r0kh_id },
{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
.data = f_r1kh_id },
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
if (sm->ft_pending_pull_left_retries <= 0)
return -1;
first = sm->ft_pending_pull_left_retries ==
sm->wpa_auth->conf.rkh_pull_retries;
sm->ft_pending_pull_left_retries--;
wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len,
&r0kh, &r0kh_wildcard);
/* Keep r0kh sufficiently long in the list for seq num check */
r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 +
1 + ftRRBseqTimeout;
if (r0kh) {
wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout);
} else if (r0kh_wildcard) {
wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
/* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */
r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard,
r0kh_wildcard->addr,
sm->r0kh_id, sm->r0kh_id_len,
r0kh_timeout);
}
if (r0kh == NULL) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
return -1;
}
if (is_zero_ether_addr(r0kh->addr)) {
wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is temporarily blocked",
sm->r0kh_id, sm->r0kh_id_len);
return -1;
}
if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"FT: R0KH-ID points to self - no matching key available");
return -1;
}
key = r0kh->key;
key_len = sizeof(r0kh->key);
if (r0kh->seq->rx.num_last == 0) {
/* A sequence request will be sent out anyway when pull
* response is received. Send it out now to avoid one RTT. */
wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr,
r0kh->id, r0kh->id_len, f_r1kh_id, key,
key_len, NULL, 0, NULL, 0, NULL);
}
wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request from " MACSTR
" to remote R0KH address " MACSTR,
MAC2STR(sm->wpa_auth->addr), MAC2STR(r0kh->addr));
if (first &&
random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"nonce");
return -1;
}
if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
return -1;
}
if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL,
sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL,
&packet, &packet_len) < 0)
return -1;
ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
wpabuf_free(sm->ft_pending_req_ies);
sm->ft_pending_req_ies = ft_pending_req_ies;
if (!sm->ft_pending_req_ies) {
os_free(packet);
return -1;
}
tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000;
tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000;
eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL);
wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL,
packet, packet_len);
os_free(packet);
return 0;
}
int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm,
const u8 *pmk_r0, const u8 *pmk_r0_name)
{
int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
struct vlan_description vlan;
const u8 *identity, *radius_cui;
size_t identity_len, radius_cui_len;
int session_timeout;
size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
SHA384_MAC_LEN : PMK_LEN;
if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
MAC2STR(sm->addr));
return -1;
}
identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
&radius_cui);
session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
pmk_r0_name, sm->pairwise, &vlan, expires_in,
session_timeout, identity, identity_len,
radius_cui, radius_cui_len);
}
int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
{
u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
SHA384_MAC_LEN : PMK_LEN;
size_t pmk_r1_len = pmk_r0_len;
u8 pmk_r1[PMK_LEN_MAX];
u8 ptk_name[WPA_PMK_NAME_LEN];
const u8 *mdid = sm->wpa_auth->conf.mobility_domain;
const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder;
size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len;
const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
const u8 *ssid = sm->wpa_auth->conf.ssid;
size_t ssid_len = sm->wpa_auth->conf.ssid_len;
int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
int expires_in = sm->wpa_auth->conf.r0_key_lifetime;
struct vlan_description vlan;
const u8 *identity, *radius_cui;
size_t identity_len, radius_cui_len;
int session_timeout;
const u8 *mpmk;
size_t mpmk_len;
if (sm->xxkey_len > 0) {
mpmk = sm->xxkey;
mpmk_len = sm->xxkey_len;
} else if (sm->pmksa) {
mpmk = sm->pmksa->pmk;
mpmk_len = sm->pmksa->pmk_len;
} else {
wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
"derivation");
return -1;
}
if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR,
MAC2STR(sm->addr));
return -1;
}
identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity);
radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr,
&radius_cui);
session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr);
if (wpa_derive_pmk_r0(mpmk, mpmk_len, ssid, ssid_len, mdid,
r0kh, r0kh_len, sm->addr,
pmk_r0, pmk_r0_name,
wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0)
return -1;
if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len,
pmk_r0_name,
sm->pairwise, &vlan, expires_in,
session_timeout, identity, identity_len,
radius_cui, radius_cui_len);
if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr,
pmk_r1, sm->pmk_r1_name) < 0)
return -1;
if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len,
sm->pmk_r1_name, sm->pairwise, &vlan,
expires_in, session_timeout, identity,
identity_len, radius_cui, radius_cui_len);
return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name,
ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise,
0);
}
static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
const u8 *addr, int idx, u8 *seq)
{
if (wpa_auth->cb->get_seqnum == NULL)
return -1;
return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
}
static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
{
u8 *subelem;
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
size_t subelem_len, pad_len;
const u8 *key;
size_t key_len;
u8 keybuf[WPA_GTK_MAX_LEN];
const u8 *kek;
size_t kek_len;
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
kek = sm->PTK.kek2;
kek_len = sm->PTK.kek2_len;
} else {
kek = sm->PTK.kek;
kek_len = sm->PTK.kek_len;
}
key_len = gsm->GTK_len;
if (key_len > sizeof(keybuf))
return NULL;
/*
* Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less
* than 16 bytes.
*/
pad_len = key_len % 8;
if (pad_len)
pad_len = 8 - pad_len;
if (key_len + pad_len < 16)
pad_len += 8;
if (pad_len && key_len < sizeof(keybuf)) {
os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len);
if (conf->disable_gtk ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use
* of GTK in the BSS.
*/
if (random_get_bytes(keybuf, key_len) < 0)
return NULL;
}
os_memset(keybuf + key_len, 0, pad_len);
keybuf[key_len] = 0xdd;
key_len += pad_len;
key = keybuf;
} else if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random GTK to each STA to prevent use of GTK
* in the BSS.
*/
if (random_get_bytes(keybuf, key_len) < 0)
return NULL;
key = keybuf;
} else {
key = gsm->GTK[gsm->GN - 1];
}
/*
* Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] |
* Key[5..32].
*/
subelem_len = 13 + key_len + 8;
subelem = os_zalloc(subelem_len);
if (subelem == NULL)
return NULL;
subelem[0] = FTIE_SUBELEM_GTK;
subelem[1] = 11 + key_len + 8;
/* Key ID in B0-B1 of Key Info */
WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
subelem[4] = gsm->GTK_len;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) {
wpa_printf(MSG_DEBUG,
"FT: GTK subelem encryption failed: kek_len=%d",
(int) kek_len);
os_free(subelem);
return NULL;
}
forced_memzero(keybuf, sizeof(keybuf));
*len = subelem_len;
return subelem;
}
static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
{
u8 *subelem, *pos;
struct wpa_auth_config *conf = &sm->wpa_auth->conf;
struct wpa_group *gsm = sm->group;
size_t subelem_len;
const u8 *kek, *igtk;
size_t kek_len;
size_t igtk_len;
- u8 dummy_igtk[WPA_IGTK_MAX_LEN];
+ u8 stub_igtk[WPA_IGTK_MAX_LEN];
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
kek = sm->PTK.kek2;
kek_len = sm->PTK.kek2_len;
} else {
kek = sm->PTK.kek;
kek_len = sm->PTK.kek_len;
}
igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] |
* Key[16+8] */
subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8;
subelem = os_zalloc(subelem_len);
if (subelem == NULL)
return NULL;
pos = subelem;
*pos++ = FTIE_SUBELEM_IGTK;
*pos++ = subelem_len - 2;
WPA_PUT_LE16(pos, gsm->GN_igtk);
pos += 2;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
pos += 6;
*pos++ = igtk_len;
igtk = gsm->IGTK[gsm->GN_igtk - 4];
if (conf->disable_gtk || sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random IGTK to each STA to prevent use of
* IGTK in the BSS.
*/
- if (random_get_bytes(dummy_igtk, igtk_len / 8) < 0) {
+ if (random_get_bytes(stub_igtk, igtk_len / 8) < 0) {
os_free(subelem);
return NULL;
}
- igtk = dummy_igtk;
+ igtk = stub_igtk;
}
if (aes_wrap(kek, kek_len, igtk_len / 8, igtk, pos)) {
wpa_printf(MSG_DEBUG,
"FT: IGTK subelem encryption failed: kek_len=%d",
(int) kek_len);
os_free(subelem);
return NULL;
}
*len = subelem_len;
return subelem;
}
static u8 * wpa_ft_bigtk_subelem(struct wpa_state_machine *sm, size_t *len)
{
u8 *subelem, *pos;
struct wpa_group *gsm = sm->group;
size_t subelem_len;
const u8 *kek, *bigtk;
size_t kek_len;
size_t bigtk_len;
- u8 dummy_bigtk[WPA_IGTK_MAX_LEN];
+ u8 stub_bigtk[WPA_IGTK_MAX_LEN];
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
kek = sm->PTK.kek2;
kek_len = sm->PTK.kek2_len;
} else {
kek = sm->PTK.kek;
kek_len = sm->PTK.kek_len;
}
bigtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
/* Sub-elem ID[1] | Length[1] | KeyID[2] | BIPN[6] | Key Length[1] |
* Key[16+8] */
subelem_len = 1 + 1 + 2 + 6 + 1 + bigtk_len + 8;
subelem = os_zalloc(subelem_len);
if (subelem == NULL)
return NULL;
pos = subelem;
*pos++ = FTIE_SUBELEM_BIGTK;
*pos++ = subelem_len - 2;
WPA_PUT_LE16(pos, gsm->GN_bigtk);
pos += 2;
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_bigtk, pos);
pos += 6;
*pos++ = bigtk_len;
bigtk = gsm->IGTK[gsm->GN_bigtk - 6];
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) {
/*
* Provide unique random BIGTK to each OSEN STA to prevent use
* of BIGTK in the BSS.
*/
- if (random_get_bytes(dummy_bigtk, bigtk_len / 8) < 0) {
+ if (random_get_bytes(stub_bigtk, bigtk_len / 8) < 0) {
os_free(subelem);
return NULL;
}
- bigtk = dummy_bigtk;
+ bigtk = stub_bigtk;
}
if (aes_wrap(kek, kek_len, bigtk_len / 8, bigtk, pos)) {
wpa_printf(MSG_DEBUG,
"FT: BIGTK subelem encryption failed: kek_len=%d",
(int) kek_len);
os_free(subelem);
return NULL;
}
*len = subelem_len;
return subelem;
}
static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
u8 *pos, u8 *end, u8 id, u8 descr_count,
const u8 *ies, size_t ies_len)
{
struct ieee802_11_elems parse;
struct rsn_rdie *rdie;
wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d",
id, descr_count);
wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)",
ies, ies_len);
if (end - pos < (int) sizeof(*rdie)) {
wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE");
return pos;
}
*pos++ = WLAN_EID_RIC_DATA;
*pos++ = sizeof(*rdie);
rdie = (struct rsn_rdie *) pos;
rdie->id = id;
rdie->descr_count = 0;
rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS);
pos += sizeof(*rdie);
if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) ==
ParseFailed) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs");
rdie->status_code =
host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
return pos;
}
if (parse.wmm_tspec) {
struct wmm_tspec_element *tspec;
if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) {
wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE "
"(%d)", (int) parse.wmm_tspec_len);
rdie->status_code =
host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
return pos;
}
if (end - pos < (int) sizeof(*tspec)) {
wpa_printf(MSG_ERROR, "FT: Not enough room for "
"response TSPEC");
rdie->status_code =
host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
return pos;
}
tspec = (struct wmm_tspec_element *) pos;
os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
}
#ifdef NEED_AP_MLME
if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
int res;
res = wmm_process_tspec((struct wmm_tspec_element *) pos);
wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res);
if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS)
rdie->status_code =
host_to_le16(WLAN_STATUS_INVALID_PARAMETERS);
else if (res == WMM_ADDTS_STATUS_REFUSED)
rdie->status_code =
host_to_le16(WLAN_STATUS_REQUEST_DECLINED);
else {
/* TSPEC accepted; include updated TSPEC in response */
rdie->descr_count = 1;
pos += sizeof(struct wmm_tspec_element);
}
return pos;
}
#endif /* NEED_AP_MLME */
if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
int res;
res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
sizeof(struct wmm_tspec_element));
if (res >= 0) {
if (res)
rdie->status_code = host_to_le16(res);
else {
/* TSPEC accepted; include updated TSPEC in
* response */
rdie->descr_count = 1;
pos += sizeof(struct wmm_tspec_element);
}
return pos;
}
}
wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
return pos;
}
static u8 * wpa_ft_process_ric(struct wpa_state_machine *sm, u8 *pos, u8 *end,
const u8 *ric, size_t ric_len)
{
const u8 *rpos, *start;
const struct rsn_rdie *rdie;
wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len);
rpos = ric;
while (rpos + sizeof(*rdie) < ric + ric_len) {
if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) ||
rpos + 2 + rpos[1] > ric + ric_len)
break;
rdie = (const struct rsn_rdie *) (rpos + 2);
rpos += 2 + rpos[1];
start = rpos;
while (rpos + 2 <= ric + ric_len &&
rpos + 2 + rpos[1] <= ric + ric_len) {
if (rpos[0] == WLAN_EID_RIC_DATA)
break;
rpos += 2 + rpos[1];
}
pos = wpa_ft_process_rdie(sm, pos, end, rdie->id,
rdie->descr_count,
start, rpos - start);
}
return pos;
}
u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
size_t max_len, int auth_alg,
const u8 *req_ies, size_t req_ies_len,
int omit_rsnxe)
{
u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
u8 *fte_mic, *elem_count;
size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
u8 rsnxe_buf[10], *rsnxe = rsnxe_buf;
size_t rsnxe_len;
int rsnxe_used;
int res;
struct wpa_auth_config *conf;
struct wpa_ft_ies parse;
u8 *ric_start;
u8 *anonce, *snonce;
const u8 *kck;
size_t kck_len;
int use_sha384;
if (sm == NULL)
return pos;
use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
conf = &sm->wpa_auth->conf;
if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return pos;
end = pos + max_len;
#ifdef CONFIG_TESTING_OPTIONS
if (auth_alg == WLAN_AUTH_FT &&
sm->wpa_auth->conf.rsne_override_ft_set) {
wpa_printf(MSG_DEBUG,
"TESTING: RSNE FT override for MIC calculation");
rsnie = sm->wpa_auth->conf.rsne_override_ft;
rsnie_len = sm->wpa_auth->conf.rsne_override_ft_len;
if (end - pos < (long int) rsnie_len)
return pos;
os_memcpy(pos, rsnie, rsnie_len);
rsnie = pos;
pos += rsnie_len;
if (rsnie_len > PMKID_LEN && sm->pmk_r1_name_valid) {
int idx;
/* Replace all 0xff PMKID with the valid PMKR1Name */
for (idx = 0; idx < PMKID_LEN; idx++) {
if (rsnie[rsnie_len - 1 - idx] != 0xff)
break;
}
if (idx == PMKID_LEN)
os_memcpy(&rsnie[rsnie_len - PMKID_LEN],
sm->pmk_r1_name, WPA_PMK_NAME_LEN);
}
} else
#endif /* CONFIG_TESTING_OPTIONS */
if (auth_alg == WLAN_AUTH_FT ||
((auth_alg == WLAN_AUTH_FILS_SK ||
auth_alg == WLAN_AUTH_FILS_SK_PFS ||
auth_alg == WLAN_AUTH_FILS_PK) &&
(sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
WPA_KEY_MGMT_FT_FILS_SHA384)))) {
if (!sm->pmk_r1_name_valid) {
wpa_printf(MSG_ERROR,
"FT: PMKR1Name is not valid for Assoc Resp RSNE");
return NULL;
}
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE",
sm->pmk_r1_name, WPA_PMK_NAME_LEN);
/*
* RSN (only present if this is a Reassociation Response and
* part of a fast BSS transition; or if this is a
* (Re)Association Response frame during an FT initial mobility
* domain association using FILS)
*/
res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
if (res < 0)
return NULL;
rsnie = pos;
rsnie_len = res;
pos += res;
}
/* Mobility Domain Information */
res = wpa_write_mdie(conf, pos, end - pos);
if (res < 0)
return NULL;
mdie = pos;
mdie_len = res;
pos += res;
/* Fast BSS Transition Information */
if (auth_alg == WLAN_AUTH_FT) {
subelem = wpa_ft_gtk_subelem(sm, &subelem_len);
if (!subelem) {
wpa_printf(MSG_DEBUG,
"FT: Failed to add GTK subelement");
return NULL;
}
r0kh_id = sm->r0kh_id;
r0kh_id_len = sm->r0kh_id_len;
anonce = sm->ANonce;
snonce = sm->SNonce;
if (sm->mgmt_frame_prot) {
u8 *igtk;
size_t igtk_len;
u8 *nbuf;
igtk = wpa_ft_igtk_subelem(sm, &igtk_len);
if (igtk == NULL) {
wpa_printf(MSG_DEBUG,
"FT: Failed to add IGTK subelement");
os_free(subelem);
return NULL;
}
nbuf = os_realloc(subelem, subelem_len + igtk_len);
if (nbuf == NULL) {
os_free(subelem);
os_free(igtk);
return NULL;
}
subelem = nbuf;
os_memcpy(subelem + subelem_len, igtk, igtk_len);
subelem_len += igtk_len;
os_free(igtk);
}
if (sm->mgmt_frame_prot && conf->beacon_prot) {
u8 *bigtk;
size_t bigtk_len;
u8 *nbuf;
bigtk = wpa_ft_bigtk_subelem(sm, &bigtk_len);
if (!bigtk) {
wpa_printf(MSG_DEBUG,
"FT: Failed to add BIGTK subelement");
os_free(subelem);
return NULL;
}
nbuf = os_realloc(subelem, subelem_len + bigtk_len);
if (!nbuf) {
os_free(subelem);
os_free(bigtk);
return NULL;
}
subelem = nbuf;
os_memcpy(subelem + subelem_len, bigtk, bigtk_len);
subelem_len += bigtk_len;
os_free(bigtk);
}
#ifdef CONFIG_OCV
if (wpa_auth_uses_ocv(sm)) {
struct wpa_channel_info ci;
u8 *nbuf, *ocipos;
if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info for OCI element");
os_free(subelem);
return NULL;
}
#ifdef CONFIG_TESTING_OPTIONS
if (conf->oci_freq_override_ft_assoc) {
wpa_printf(MSG_INFO,
"TEST: Override OCI frequency %d -> %u MHz",
ci.frequency,
conf->oci_freq_override_ft_assoc);
ci.frequency = conf->oci_freq_override_ft_assoc;
}
#endif /* CONFIG_TESTING_OPTIONS */
subelem_len += 2 + OCV_OCI_LEN;
nbuf = os_realloc(subelem, subelem_len);
if (!nbuf) {
os_free(subelem);
return NULL;
}
subelem = nbuf;
ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN;
*ocipos++ = FTIE_SUBELEM_OCI;
*ocipos++ = OCV_OCI_LEN;
if (ocv_insert_oci(&ci, &ocipos) < 0) {
os_free(subelem);
return NULL;
}
}
#endif /* CONFIG_OCV */
} else {
r0kh_id = conf->r0_key_holder;
r0kh_id_len = conf->r0_key_holder_len;
anonce = NULL;
snonce = NULL;
}
rsnxe_used = (auth_alg == WLAN_AUTH_FT) &&
(conf->sae_pwe == 1 || conf->sae_pwe == 2);
#ifdef CONFIG_TESTING_OPTIONS
if (sm->wpa_auth->conf.ft_rsnxe_used) {
rsnxe_used = sm->wpa_auth->conf.ft_rsnxe_used == 1;
wpa_printf(MSG_DEBUG, "TESTING: FT: Force RSNXE Used %d",
rsnxe_used);
}
#endif /* CONFIG_TESTING_OPTIONS */
res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len,
anonce, snonce, pos, end - pos,
subelem, subelem_len, rsnxe_used);
os_free(subelem);
if (res < 0)
return NULL;
ftie = pos;
ftie_len = res;
pos += res;
if (use_sha384) {
struct rsn_ftie_sha384 *_ftie =
(struct rsn_ftie_sha384 *) (ftie + 2);
fte_mic = _ftie->mic;
elem_count = &_ftie->mic_control[1];
} else {
struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2);
fte_mic = _ftie->mic;
elem_count = &_ftie->mic_control[1];
}
if (auth_alg == WLAN_AUTH_FT)
*elem_count = 3; /* Information element count */
ric_start = pos;
if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0
&& parse.ric) {
pos = wpa_ft_process_ric(sm, pos, end, parse.ric,
parse.ric_len);
if (auth_alg == WLAN_AUTH_FT)
*elem_count +=
ieee802_11_ie_count(ric_start,
pos - ric_start);
}
if (ric_start == pos)
ric_start = NULL;
if (omit_rsnxe) {
rsnxe_len = 0;
} else {
res = wpa_write_rsnxe(&sm->wpa_auth->conf, rsnxe,
sizeof(rsnxe_buf));
if (res < 0)
return NULL;
rsnxe_len = res;
}
#ifdef CONFIG_TESTING_OPTIONS
if (auth_alg == WLAN_AUTH_FT &&
sm->wpa_auth->conf.rsnxe_override_ft_set) {
wpa_printf(MSG_DEBUG,
"TESTING: RSNXE FT override for MIC calculation");
rsnxe = sm->wpa_auth->conf.rsnxe_override_ft;
rsnxe_len = sm->wpa_auth->conf.rsnxe_override_ft_len;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (auth_alg == WLAN_AUTH_FT && rsnxe_len)
*elem_count += 1;
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
kck = sm->PTK.kck2;
kck_len = sm->PTK.kck2_len;
} else {
kck = sm->PTK.kck;
kck_len = sm->PTK.kck_len;
}
if (auth_alg == WLAN_AUTH_FT &&
wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6,
mdie, mdie_len, ftie, ftie_len,
rsnie, rsnie_len,
ric_start, ric_start ? pos - ric_start : 0,
rsnxe_len ? rsnxe : NULL, rsnxe_len,
fte_mic) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
return NULL;
}
os_free(sm->assoc_resp_ftie);
sm->assoc_resp_ftie = os_malloc(ftie_len);
if (!sm->assoc_resp_ftie)
return NULL;
os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
return pos;
}
static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
int vlan_id,
enum wpa_alg alg, const u8 *addr, int idx,
u8 *key, size_t key_len,
enum key_flag key_flag)
{
if (wpa_auth->cb->set_key == NULL)
return -1;
return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
key, key_len, key_flag);
}
static inline int wpa_auth_add_sta_ft(struct wpa_authenticator *wpa_auth,
const u8 *addr)
{
if (!wpa_auth->cb->add_sta_ft)
return -1;
return wpa_auth->cb->add_sta_ft(wpa_auth->cb_ctx, addr);
}
void wpa_ft_install_ptk(struct wpa_state_machine *sm, int retry)
{
enum wpa_alg alg;
int klen;
/* MLME-SETKEYS.request(PTK) */
alg = wpa_cipher_to_alg(sm->pairwise);
klen = wpa_cipher_key_len(sm->pairwise);
if (!wpa_cipher_valid_pairwise(sm->pairwise)) {
wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
"PTK configuration", sm->pairwise);
return;
}
if (sm->tk_already_set) {
/* Must avoid TK reconfiguration to prevent clearing of TX/RX
* PN in the driver */
wpa_printf(MSG_DEBUG,
"FT: Do not re-install same PTK to the driver");
return;
}
if (!retry)
wpa_auth_add_sta_ft(sm->wpa_auth, sm->addr);
/* FIX: add STA entry to kernel/driver here? The set_key will fail
* most likely without this.. At the moment, STA entry is added only
* after association has been completed. This function will be called
* again after association to get the PTK configured, but that could be
* optimized by adding the STA entry earlier.
*/
if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, sm->keyidx_active,
sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
return;
/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
sm->pairwise_set = true;
sm->tk_already_set = true;
}
/* Derive PMK-R1 from PSK, check all available PSK */
static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
const u8 *req_pmk_r1_name,
u8 *out_pmk_r1, int *out_pairwise,
struct vlan_description *out_vlan,
const u8 **out_identity, size_t *out_identity_len,
const u8 **out_radius_cui,
size_t *out_radius_cui_len,
int *out_session_timeout)
{
const u8 *pmk = NULL;
u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
const u8 *mdid = wpa_auth->conf.mobility_domain;
const u8 *r0kh = sm->r0kh_id;
size_t r0kh_len = sm->r0kh_id_len;
const u8 *r1kh = wpa_auth->conf.r1_key_holder;
const u8 *ssid = wpa_auth->conf.ssid;
size_t ssid_len = wpa_auth->conf.ssid_len;
int pairwise;
pairwise = sm->pairwise;
for (;;) {
pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
pmk);
if (pmk == NULL)
break;
if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
r0kh_len, sm->addr,
pmk_r0, pmk_r0_name, 0) < 0 ||
wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh,
sm->addr, pmk_r1, pmk_r1_name) < 0 ||
os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
WPA_PMK_NAME_LEN) != 0)
continue;
/* We found a PSK that matches the requested pmk_r1_name */
wpa_printf(MSG_DEBUG,
"FT: Found PSK to generate PMK-R1 locally");
os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
if (out_pairwise)
*out_pairwise = pairwise;
os_memcpy(sm->PMK, pmk, PMK_LEN);
sm->pmk_len = PMK_LEN;
if (out_vlan &&
wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
MACSTR, MAC2STR(sm->addr));
return -1;
}
if (out_identity && out_identity_len) {
*out_identity_len = wpa_ft_get_identity(
sm->wpa_auth, sm->addr, out_identity);
}
if (out_radius_cui && out_radius_cui_len) {
*out_radius_cui_len = wpa_ft_get_radius_cui(
sm->wpa_auth, sm->addr, out_radius_cui);
}
if (out_session_timeout) {
*out_session_timeout = wpa_ft_get_session_timeout(
sm->wpa_auth, sm->addr);
}
return 0;
}
wpa_printf(MSG_DEBUG,
"FT: Did not find PSK to generate PMK-R1 locally");
return -1;
}
/* Detect the configuration the station asked for.
* Required to detect FT-PSK and pairwise cipher.
*/
static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm,
struct wpa_ft_ies *parse)
{
int key_mgmt, ciphers;
if (sm->wpa_key_mgmt)
return 0;
key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt;
if (!key_mgmt) {
wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from "
MACSTR, parse->key_mgmt, MAC2STR(sm->addr));
return -1;
}
if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
#ifdef CONFIG_SHA384
else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
#endif /* CONFIG_SHA384 */
else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
#ifdef CONFIG_FILS
else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
#endif /* CONFIG_FILS */
ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
if (!ciphers) {
wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
MACSTR,
parse->pairwise_cipher, MAC2STR(sm->addr));
return -1;
}
sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
return 0;
}
static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *req_pmk_r0_name,
const u8 *req_pmk_r1_name,
u8 *out_pmk_r1, int *out_pairwise,
struct vlan_description *vlan,
const u8 **identity, size_t *identity_len,
const u8 **radius_cui,
size_t *radius_cui_len,
int *out_session_timeout)
{
struct wpa_auth_config *conf = &wpa_auth->conf;
const struct wpa_ft_pmk_r0_sa *r0;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
int expires_in = 0;
int session_timeout = 0;
struct os_reltime now;
if (conf->r0_key_holder_len != r0kh_id_len ||
os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) !=
0)
return -1; /* not our R0KH-ID */
wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration");
if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) <
0)
return -1; /* no matching PMKR0Name in local cache */
wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache");
if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name,
conf->r1_key_holder,
sm->addr, out_pmk_r1, pmk_r1_name) < 0)
return -1;
os_get_reltime(&now);
if (r0->expiration)
expires_in = r0->expiration - now.sec;
if (r0->session_timeout)
session_timeout = r0->session_timeout - now.sec;
wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len,
pmk_r1_name,
sm->pairwise, r0->vlan, expires_in, session_timeout,
r0->identity, r0->identity_len,
r0->radius_cui, r0->radius_cui_len);
*out_pairwise = sm->pairwise;
if (vlan) {
if (r0->vlan)
*vlan = *r0->vlan;
else
os_memset(vlan, 0, sizeof(*vlan));
}
if (identity && identity_len) {
*identity = r0->identity;
*identity_len = r0->identity_len;
}
if (radius_cui && radius_cui_len) {
*radius_cui = r0->radius_cui;
*radius_cui_len = r0->radius_cui_len;
}
*out_session_timeout = session_timeout;
return 0;
}
static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
const u8 *ies, size_t ies_len,
u8 **resp_ies, size_t *resp_ies_len)
{
struct rsn_mdie *mdie;
u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN];
u8 ptk_name[WPA_PMK_NAME_LEN];
struct wpa_auth_config *conf;
struct wpa_ft_ies parse;
size_t buflen;
int ret;
u8 *pos, *end;
int pairwise, session_timeout = 0;
struct vlan_description vlan;
const u8 *identity, *radius_cui;
size_t identity_len = 0, radius_cui_len = 0;
int use_sha384;
size_t pmk_r1_len, kdk_len;
*resp_ies = NULL;
*resp_ies_len = 0;
sm->pmk_r1_name_valid = 0;
conf = &sm->wpa_auth->conf;
wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs",
ies, ies_len);
if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt);
pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
mdie = (struct rsn_mdie *) parse.mdie;
if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
os_memcmp(mdie->mobility_domain,
sm->wpa_auth->conf.mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
return WLAN_STATUS_INVALID_MDIE;
}
if (use_sha384) {
struct rsn_ftie_sha384 *ftie;
ftie = (struct rsn_ftie_sha384 *) parse.ftie;
if (!ftie || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
return WLAN_STATUS_INVALID_FTIE;
}
os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
} else {
struct rsn_ftie *ftie;
ftie = (struct rsn_ftie *) parse.ftie;
if (!ftie || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
return WLAN_STATUS_INVALID_FTIE;
}
os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN);
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID");
return WLAN_STATUS_INVALID_FTIE;
}
wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID",
parse.r0kh_id, parse.r0kh_id_len);
os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
sm->r0kh_id_len = parse.r0kh_id_len;
if (parse.rsn_pmkid == NULL) {
wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
return WLAN_STATUS_INVALID_PMKID;
}
if (wpa_ft_set_key_mgmt(sm, &parse) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
parse.rsn_pmkid, WPA_PMK_NAME_LEN);
if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
sm->wpa_auth->conf.r1_key_holder, sm->addr,
pmk_r1_name, use_sha384) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
if (conf->ft_psk_generate_local &&
wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise,
&vlan, &identity, &identity_len,
&radius_cui, &radius_cui_len,
&session_timeout) < 0)
return WLAN_STATUS_INVALID_PMKID;
wpa_printf(MSG_DEBUG,
"FT: Generated PMK-R1 for FT-PSK locally");
} else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
pmk_r1, &pmk_r1_len, &pairwise, &vlan,
&identity, &identity_len, &radius_cui,
&radius_cui_len, &session_timeout) < 0) {
wpa_printf(MSG_DEBUG,
"FT: No PMK-R1 available in local cache for the requested PMKR1Name");
if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm,
parse.r0kh_id, parse.r0kh_id_len,
parse.rsn_pmkid,
pmk_r1_name, pmk_r1, &pairwise,
&vlan, &identity, &identity_len,
&radius_cui, &radius_cui_len,
&session_timeout) == 0) {
wpa_printf(MSG_DEBUG,
"FT: Generated PMK-R1 based on local PMK-R0");
goto pmk_r1_derived;
}
if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
wpa_printf(MSG_DEBUG,
"FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH");
return WLAN_STATUS_INVALID_PMKID;
}
return -1; /* Status pending */
} else {
wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache");
}
pmk_r1_derived:
wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
sm->pmk_r1_name_valid = 1;
os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len);
sm->pmk_r1_len = pmk_r1_len;
if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
"ANonce");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
sm->SNonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
sm->ANonce, WPA_NONCE_LEN);
if (sm->wpa_auth->conf.force_kdk_derivation ||
(sm->wpa_auth->conf.secure_ltf &&
ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
kdk_len = WPA_KDK_MAX_LEN;
else
kdk_len = 0;
if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce,
sm->addr, sm->wpa_auth->addr, pmk_r1_name,
&sm->PTK, ptk_name, sm->wpa_key_mgmt,
pairwise, kdk_len) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
sm->pairwise = pairwise;
sm->PTK_valid = true;
sm->tk_already_set = false;
wpa_ft_install_ptk(sm, 0);
if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (wpa_ft_set_identity(sm->wpa_auth, sm->addr,
identity, identity_len) < 0 ||
wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr,
radius_cui, radius_cui_len) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout);
buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
2 + FT_R1KH_ID_LEN + 200;
*resp_ies = os_zalloc(buflen);
if (*resp_ies == NULL)
goto fail;
pos = *resp_ies;
end = *resp_ies + buflen;
ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
if (ret < 0)
goto fail;
pos += ret;
ret = wpa_write_mdie(conf, pos, end - pos);
if (ret < 0)
goto fail;
pos += ret;
ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len,
sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0,
0);
if (ret < 0)
goto fail;
pos += ret;
*resp_ies_len = pos - *resp_ies;
return WLAN_STATUS_SUCCESS;
fail:
os_free(*resp_ies);
*resp_ies = NULL;
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
u16 auth_transaction, const u8 *ies, size_t ies_len,
void (*cb)(void *ctx, const u8 *dst, const u8 *bssid,
u16 auth_transaction, u16 status,
const u8 *ies, size_t ies_len),
void *ctx)
{
u16 status;
u8 *resp_ies;
size_t resp_ies_len;
int res;
if (sm == NULL) {
wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
"WPA SM not available");
return;
}
wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
" BSSID=" MACSTR " transaction=%d",
MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
sm->ft_pending_cb = cb;
sm->ft_pending_cb_ctx = ctx;
sm->ft_pending_auth_transaction = auth_transaction;
sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
&resp_ies_len);
if (res < 0) {
wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available");
return;
}
status = res;
wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
" auth_transaction=%d status=%u (%s)",
MAC2STR(sm->addr), auth_transaction + 1, status,
status2str(status));
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
cb(ctx, sm->addr, bssid, auth_transaction + 1, status,
resp_ies, resp_ies_len);
os_free(resp_ies);
}
int wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
size_t ies_len)
{
struct wpa_ft_ies parse;
struct rsn_mdie *mdie;
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
size_t mic_len = 16;
unsigned int count;
const u8 *kck;
size_t kck_len;
int use_sha384;
const u8 *anonce, *snonce, *fte_mic;
u8 fte_elem_count;
int rsnxe_used;
struct wpa_auth_config *conf;
if (sm == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
conf = &sm->wpa_auth->conf;
use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt);
wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len);
if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (parse.rsn == NULL) {
wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (parse.rsn_pmkid == NULL) {
wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE");
return WLAN_STATUS_INVALID_PMKID;
}
if (os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)
!= 0) {
wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match "
"with the PMKR1Name derived from auth request");
return WLAN_STATUS_INVALID_PMKID;
}
mdie = (struct rsn_mdie *) parse.mdie;
if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
os_memcmp(mdie->mobility_domain, conf->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
return WLAN_STATUS_INVALID_MDIE;
}
if (use_sha384) {
struct rsn_ftie_sha384 *ftie;
ftie = (struct rsn_ftie_sha384 *) parse.ftie;
if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
return WLAN_STATUS_INVALID_FTIE;
}
anonce = ftie->anonce;
snonce = ftie->snonce;
rsnxe_used = ftie->mic_control[0] & 0x01;
fte_elem_count = ftie->mic_control[1];
fte_mic = ftie->mic;
} else {
struct rsn_ftie *ftie;
ftie = (struct rsn_ftie *) parse.ftie;
if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
return WLAN_STATUS_INVALID_FTIE;
}
anonce = ftie->anonce;
snonce = ftie->snonce;
rsnxe_used = ftie->mic_control[0] & 0x01;
fte_elem_count = ftie->mic_control[1];
fte_mic = ftie->mic;
}
if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
sm->SNonce, WPA_NONCE_LEN);
return WLAN_STATUS_INVALID_FTIE;
}
if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
anonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
sm->ANonce, WPA_NONCE_LEN);
return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r0kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r0kh_id_len != sm->r0kh_id_len ||
os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
{
wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
"the current R0KH-ID");
wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
parse.r0kh_id, parse.r0kh_id_len);
wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
return WLAN_STATUS_INVALID_FTIE;
}
if (parse.r1kh_id == NULL) {
wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
return WLAN_STATUS_INVALID_FTIE;
}
if (os_memcmp_const(parse.r1kh_id, conf->r1_key_holder,
FT_R1KH_ID_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
"ReassocReq");
wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE",
parse.r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
conf->r1_key_holder, FT_R1KH_ID_LEN);
return WLAN_STATUS_INVALID_FTIE;
}
if (parse.rsn_pmkid == NULL ||
os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN))
{
wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
"RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
return WLAN_STATUS_INVALID_PMKID;
}
count = 3;
if (parse.ric)
count += ieee802_11_ie_count(parse.ric, parse.ric_len);
if (parse.rsnxe)
count++;
if (fte_elem_count != count) {
wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
"Control: received %u expected %u",
fte_elem_count, count);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
kck = sm->PTK.kck2;
kck_len = sm->PTK.kck2_len;
} else {
kck = sm->PTK.kck;
kck_len = sm->PTK.kck_len;
}
if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5,
parse.mdie - 2, parse.mdie_len + 2,
parse.ftie - 2, parse.ftie_len + 2,
parse.rsn - 2, parse.rsn_len + 2,
parse.ric, parse.ric_len,
parse.rsnxe ? parse.rsnxe - 2 : NULL,
parse.rsnxe ? parse.rsnxe_len + 2 : 0,
mic) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
fte_mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
parse.mdie - 2, parse.mdie_len + 2);
wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
parse.ftie - 2, parse.ftie_len + 2);
wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
parse.rsn - 2, parse.rsn_len + 2);
wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE",
parse.rsnxe ? parse.rsnxe - 2 : NULL,
parse.rsnxe ? parse.rsnxe_len + 2 : 0);
return WLAN_STATUS_INVALID_FTIE;
}
if (rsnxe_used && (conf->sae_pwe == 1 || conf->sae_pwe == 2) &&
!parse.rsnxe) {
wpa_printf(MSG_INFO,
"FT: FTE indicated that STA uses RSNXE, but RSNXE was not included");
return -1; /* discard request */
}
#ifdef CONFIG_OCV
if (wpa_auth_uses_ocv(sm)) {
struct wpa_channel_info ci;
int tx_chanwidth;
int tx_seg1_idx;
enum oci_verify_result res;
if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in (Re)Assoc Request");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (get_sta_tx_parameters(sm,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx, &tx_chanwidth,
&tx_seg1_idx) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
res = ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
tx_chanwidth, tx_seg1_idx);
if (wpa_auth_uses_ocv(sm) == 2 && res == OCI_NOT_FOUND) {
/* Work around misbehaving STAs */
wpa_printf(MSG_INFO,
"Disable OCV with a STA that does not send OCI");
wpa_auth_set_ocv(sm, 0);
} else if (res != OCI_SUCCESS) {
wpa_printf(MSG_WARNING, "OCV failed: %s", ocv_errorstr);
if (sm->wpa_auth->conf.msg_ctx)
wpa_msg(sm->wpa_auth->conf.msg_ctx, MSG_INFO,
OCV_FAILURE "addr=" MACSTR
" frame=ft-reassoc-req error=%s",
MAC2STR(sm->addr), ocv_errorstr);
return WLAN_STATUS_INVALID_FTIE;
}
}
#endif /* CONFIG_OCV */
return WLAN_STATUS_SUCCESS;
}
int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
{
const u8 *sta_addr, *target_ap;
const u8 *ies;
size_t ies_len;
u8 action;
struct ft_rrb_frame *frame;
if (sm == NULL)
return -1;
/*
* data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
* FT Request action frame body[variable]
*/
if (len < 14) {
wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame "
"(len=%lu)", (unsigned long) len);
return -1;
}
action = data[1];
sta_addr = data + 2;
target_ap = data + 8;
ies = data + 14;
ies_len = len - 14;
wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR
" Target AP=" MACSTR " Action=%d)",
MAC2STR(sta_addr), MAC2STR(target_ap), action);
if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: "
"STA=" MACSTR " STA-Address=" MACSTR,
MAC2STR(sm->addr), MAC2STR(sta_addr));
return -1;
}
/*
- * Do some sanity checking on the target AP address (not own and not
+ * Do some validity checking on the target AP address (not own and not
* broadcast. This could be extended to filter based on a list of known
* APs in the MD (if such a list were configured).
*/
if ((target_ap[0] & 0x01) ||
os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action "
"frame");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
if (!sm->wpa_auth->conf.ft_over_ds) {
wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject");
return -1;
}
/* RRB - Forward action frame to the target AP */
frame = os_malloc(sizeof(*frame) + len);
if (frame == NULL)
return -1;
frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
frame->packet_type = FT_PACKET_REQUEST;
frame->action_length = host_to_le16(len);
os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN);
os_memcpy(frame + 1, data, len);
wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame,
sizeof(*frame) + len);
os_free(frame);
return 0;
}
static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
u16 auth_transaction, u16 resp,
const u8 *ies, size_t ies_len)
{
struct wpa_state_machine *sm = ctx;
wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
MAC2STR(sm->addr));
wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
WLAN_STATUS_SUCCESS, ies, ies_len);
}
static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
const u8 *current_ap, const u8 *sta_addr,
const u8 *body, size_t len)
{
struct wpa_state_machine *sm;
u16 status;
u8 *resp_ies;
size_t resp_ies_len;
int res;
sm = wpa_ft_add_sta(wpa_auth, sta_addr);
if (sm == NULL) {
wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on "
"RRB Request");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len);
sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
sm->ft_pending_cb_ctx = sm;
os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries;
res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
&resp_ies_len);
if (res < 0) {
wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response");
return 0;
}
status = res;
res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status,
resp_ies, resp_ies_len);
os_free(resp_ies);
return res;
}
static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
const u8 *current_ap, const u8 *sta_addr,
u16 status, const u8 *resp_ies,
size_t resp_ies_len)
{
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
size_t rlen;
struct ft_rrb_frame *frame;
u8 *pos;
wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
" CurrentAP=" MACSTR " status=%u (%s)",
MAC2STR(sm->addr), MAC2STR(current_ap), status,
status2str(status));
wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len);
/* RRB - Forward action frame response to the Current AP */
/*
* data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6]
* Status_Code[2] FT Request action frame body[variable]
*/
rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
frame = os_malloc(sizeof(*frame) + rlen);
if (frame == NULL)
return -1;
frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
frame->packet_type = FT_PACKET_RESPONSE;
frame->action_length = host_to_le16(rlen);
os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN);
pos = (u8 *) (frame + 1);
*pos++ = WLAN_ACTION_FT;
*pos++ = 2; /* Action: Response */
os_memcpy(pos, sta_addr, ETH_ALEN);
pos += ETH_ALEN;
os_memcpy(pos, wpa_auth->addr, ETH_ALEN);
pos += ETH_ALEN;
WPA_PUT_LE16(pos, status);
pos += 2;
if (resp_ies)
os_memcpy(pos, resp_ies, resp_ies_len);
wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
sizeof(*frame) + rlen);
os_free(frame);
return 0;
}
static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len,
const struct tlv_list *tlvs,
const struct wpa_ft_pmk_r0_sa *pmk_r0,
const u8 *r1kh_id, const u8 *s1kh_id,
const struct tlv_list *tlv_auth,
const u8 *src_addr, u8 type,
u8 **packet, size_t *packet_len)
{
u8 pmk_r1[PMK_LEN_MAX];
size_t pmk_r1_len = pmk_r0->pmk_r0_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 f_pairwise[sizeof(le16)];
u8 f_expires_in[sizeof(le16)];
u8 f_session_timeout[sizeof(le32)];
int expires_in;
int session_timeout;
struct os_reltime now;
int ret;
struct tlv_list sess_tlv[] = {
{ .type = FT_RRB_PMK_R1, .len = pmk_r1_len,
.data = pmk_r1 },
{ .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name),
.data = pmk_r1_name },
{ .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise),
.data = f_pairwise },
{ .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in),
.data = f_expires_in },
{ .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len,
.data = pmk_r0->identity },
{ .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len,
.data = pmk_r0->radius_cui },
{ .type = FT_RRB_SESSION_TIMEOUT,
.len = sizeof(f_session_timeout),
.data = f_session_timeout },
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 for peer AP");
if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len,
pmk_r0->pmk_r0_name, r1kh_id,
s1kh_id, pmk_r1, pmk_r1_name) < 0)
return -1;
WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise);
os_get_reltime(&now);
if (pmk_r0->expiration > now.sec)
expires_in = pmk_r0->expiration - now.sec;
else if (pmk_r0->expiration)
expires_in = 1;
else
expires_in = 0;
WPA_PUT_LE16(f_expires_in, expires_in);
if (pmk_r0->session_timeout > now.sec)
session_timeout = pmk_r0->session_timeout - now.sec;
else if (pmk_r0->session_timeout)
session_timeout = 1;
else
session_timeout = 0;
WPA_PUT_LE32(f_session_timeout, session_timeout);
ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth,
pmk_r0->vlan, src_addr, type,
packet, packet_len);
forced_memzero(pmk_r1, sizeof(pmk_r1));
return ret;
}
static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
int no_defer)
{
const char *msgtype = "pull request";
u8 *plain = NULL, *packet = NULL;
size_t plain_len = 0, packet_len = 0;
struct ft_remote_r1kh *r1kh, *r1kh_wildcard;
const u8 *key;
size_t key_len;
int seq_ret;
const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name;
size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len;
size_t f_pmk_r0_name_len;
const struct wpa_ft_pmk_r0_sa *r0;
int ret;
struct tlv_list resp[2];
struct tlv_list resp_auth[5];
struct ft_rrb_seq f_seq;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) {
wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch");
goto out;
}
RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard);
if (r1kh) {
key = r1kh->key;
key_len = sizeof(r1kh->key);
} else if (r1kh_wildcard) {
wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID");
key = r1kh_wildcard->key;
key_len = sizeof(r1kh_wildcard->key);
} else {
goto out;
}
RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
seq_ret = FT_RRB_SEQ_DROP;
if (r1kh)
seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len,
auth, auth_len, msgtype, no_defer);
if (!no_defer && r1kh_wildcard &&
(!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
/* wildcard: r1kh-id unknown or changed addr -> do a seq req */
seq_ret = FT_RRB_SEQ_DEFER;
}
if (seq_ret == FT_RRB_SEQ_DROP)
goto out;
if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
src_addr, FT_PACKET_R0KH_R1KH_PULL,
&plain, &plain_len) < 0)
goto out;
if (!r1kh)
r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr,
f_r1kh_id,
wpa_auth->conf.rkh_pos_timeout);
if (!r1kh)
goto out;
if (seq_ret == FT_RRB_SEQ_DEFER) {
wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id,
f_r0kh_id_len, f_r1kh_id, key, key_len,
enc, enc_len, auth, auth_len,
&wpa_ft_rrb_rx_pull);
goto out;
}
wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len,
msgtype);
wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
wpa_auth->conf.rkh_pos_timeout);
RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name,
f_pmk_r0_name_len);
RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
goto out;
}
wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull response from " MACSTR
" to " MACSTR,
MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
resp[0].type = FT_RRB_S1KH_ID;
resp[0].len = f_s1kh_id_len;
resp[0].data = f_s1kh_id;
resp[1].type = FT_RRB_LAST_EMPTY;
resp[1].len = 0;
resp[1].data = NULL;
resp_auth[0].type = FT_RRB_NONCE;
resp_auth[0].len = f_nonce_len;
resp_auth[0].data = f_nonce;
resp_auth[1].type = FT_RRB_SEQ;
resp_auth[1].len = sizeof(f_seq);
resp_auth[1].data = (u8 *) &f_seq;
resp_auth[2].type = FT_RRB_R0KH_ID;
resp_auth[2].len = f_r0kh_id_len;
resp_auth[2].data = f_r0kh_id;
resp_auth[3].type = FT_RRB_R1KH_ID;
resp_auth[3].len = f_r1kh_id_len;
resp_auth[3].data = f_r1kh_id;
resp_auth[4].type = FT_RRB_LAST_EMPTY;
resp_auth[4].len = 0;
resp_auth[4].data = NULL;
if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) {
wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found");
ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth,
NULL, wpa_auth->addr,
FT_PACKET_R0KH_R1KH_RESP,
&packet, &packet_len);
} else {
ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id,
f_s1kh_id, resp_auth, wpa_auth->addr,
FT_PACKET_R0KH_R1KH_RESP,
&packet, &packet_len);
}
if (!ret)
wpa_ft_rrb_oui_send(wpa_auth, src_addr,
FT_PACKET_R0KH_R1KH_RESP, packet,
packet_len);
out:
os_free(plain);
os_free(packet);
return 0;
}
/* @returns 0 on success
* -1 on error
* -2 if FR_RRB_PAIRWISE is missing
*/
static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth,
const u8 *src_addr, u8 type,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
const char *msgtype, u8 *s1kh_id_out,
int (*cb)(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
int no_defer))
{
u8 *plain = NULL;
size_t plain_len = 0;
struct ft_remote_r0kh *r0kh, *r0kh_wildcard;
const u8 *key;
size_t key_len;
int seq_ret;
const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id;
const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1;
const u8 *f_expires_in;
size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len;
const u8 *f_identity, *f_radius_cui;
const u8 *f_session_timeout;
size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len;
size_t f_expires_in_len;
size_t f_identity_len, f_radius_cui_len;
size_t f_session_timeout_len;
int pairwise;
int ret = -1;
int expires_in;
int session_timeout;
struct vlan_description vlan;
size_t pmk_r1_len;
RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1);
wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len);
RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN);
wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id));
if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) {
wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch");
goto out;
}
wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh,
&r0kh_wildcard);
if (r0kh) {
key = r0kh->key;
key_len = sizeof(r0kh->key);
} else if (r0kh_wildcard) {
wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID");
key = r0kh_wildcard->key;
key_len = sizeof(r0kh_wildcard->key);
} else {
goto out;
}
seq_ret = FT_RRB_SEQ_DROP;
if (r0kh) {
seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len,
auth, auth_len, msgtype,
cb ? 0 : 1);
}
if (cb && r0kh_wildcard &&
(!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
/* wildcard: r0kh-id unknown or changed addr -> do a seq req */
seq_ret = FT_RRB_SEQ_DEFER;
}
if (seq_ret == FT_RRB_SEQ_DROP)
goto out;
if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len,
src_addr, type, &plain, &plain_len) < 0)
goto out;
if (!r0kh)
r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr,
f_r0kh_id, f_r0kh_id_len,
wpa_auth->conf.rkh_pos_timeout);
if (!r0kh)
goto out;
if (seq_ret == FT_RRB_SEQ_DEFER) {
wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id,
f_r0kh_id_len, f_r1kh_id, key, key_len,
enc, enc_len, auth, auth_len, cb);
goto out;
}
wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len,
msgtype);
wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
wpa_auth->conf.rkh_pos_timeout);
RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN);
wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id));
if (s1kh_id_out)
os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN);
ret = -2;
RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16));
wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len);
ret = -1;
RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
f_pmk_r1_name, WPA_PMK_NAME_LEN);
pmk_r1_len = PMK_LEN;
if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len,
&f_pmk_r1) == 0 &&
(f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN))
pmk_r1_len = f_pmk_r1_len;
RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len);
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len);
pairwise = WPA_GET_LE16(f_pairwise);
RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype,
sizeof(le16));
if (f_expires_in)
expires_in = WPA_GET_LE16(f_expires_in);
else
expires_in = 0;
wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype,
expires_in);
if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) {
wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan");
wpa_ft_rrb_dump(plain, plain_len);
goto out;
}
wpa_printf(MSG_DEBUG, "FT: vlan %d%s",
le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : "");
RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1);
if (f_identity)
wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity,
f_identity_len);
RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1);
if (f_radius_cui)
wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui,
f_radius_cui_len);
RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype,
sizeof(le32));
if (f_session_timeout)
session_timeout = WPA_GET_LE32(f_session_timeout);
else
session_timeout = 0;
wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout);
if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len,
f_pmk_r1_name,
pairwise, &vlan, expires_in, session_timeout,
f_identity, f_identity_len, f_radius_cui,
f_radius_cui_len) < 0)
goto out;
ret = 0;
out:
bin_clear_free(plain, plain_len);
return ret;
}
static void ft_finish_pull(struct wpa_state_machine *sm)
{
int res;
u8 *resp_ies;
size_t resp_ies_len;
u16 status;
if (!sm->ft_pending_cb || !sm->ft_pending_req_ies)
return;
res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
wpabuf_len(sm->ft_pending_req_ies),
&resp_ies, &resp_ies_len);
if (res < 0) {
/* this loop is broken by ft_pending_pull_left_retries */
wpa_printf(MSG_DEBUG,
"FT: Callback postponed until response is available");
return;
}
wpabuf_free(sm->ft_pending_req_ies);
sm->ft_pending_req_ies = NULL;
status = res;
wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
" - status %u", MAC2STR(sm->addr), status);
sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
sm->ft_pending_auth_transaction + 1, status,
resp_ies, resp_ies_len);
os_free(resp_ies);
}
struct ft_get_sta_ctx {
const u8 *nonce;
const u8 *s1kh_id;
struct wpa_state_machine *sm;
};
static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx)
{
struct ft_get_sta_ctx *info = ctx;
if ((info->s1kh_id &&
os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) ||
os_memcmp(info->nonce, sm->ft_pending_pull_nonce,
FT_RRB_NONCE_LEN) != 0 ||
sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
return 0;
info->sm = sm;
return 1;
}
static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
int no_defer)
{
const char *msgtype = "pull response";
int nak, ret = -1;
struct ft_get_sta_ctx ctx;
u8 s1kh_id[ETH_ALEN];
const u8 *f_nonce;
size_t f_nonce_len;
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len);
os_memset(&ctx, 0, sizeof(ctx));
ctx.nonce = f_nonce;
if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
/* nonce not found */
wpa_printf(MSG_DEBUG, "FT: Invalid nonce");
return -1;
}
ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP,
enc, enc_len, auth, auth_len, msgtype, s1kh_id,
no_defer ? NULL : &wpa_ft_rrb_rx_resp);
if (ret == -2) {
ret = 0;
nak = 1;
} else {
nak = 0;
}
if (ret < 0)
return -1;
ctx.s1kh_id = s1kh_id;
if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) {
wpa_printf(MSG_DEBUG,
"FT: Response to a pending pull request for " MACSTR,
MAC2STR(ctx.sm->addr));
eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL);
if (nak)
ctx.sm->ft_pending_pull_left_retries = 0;
ft_finish_pull(ctx.sm);
}
out:
return ret;
}
static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len, int no_defer)
{
const char *msgtype = "push";
wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH,
enc, enc_len, auth, auth_len, msgtype, NULL,
no_defer ? NULL : wpa_ft_rrb_rx_push) < 0)
return -1;
return 0;
}
static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth,
const u8 *src_addr, int type,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
struct ft_remote_seq **rkh_seq,
u8 **key, size_t *key_len,
struct ft_remote_r0kh **r0kh_out,
struct ft_remote_r1kh **r1kh_out,
struct ft_remote_r0kh **r0kh_wildcard_out,
struct ft_remote_r1kh **r1kh_wildcard_out)
{
struct ft_remote_r0kh *r0kh = NULL;
struct ft_remote_r1kh *r1kh = NULL;
const u8 *f_r0kh_id, *f_r1kh_id;
size_t f_r0kh_id_len, f_r1kh_id_len;
int to_r0kh, to_r1kh;
u8 *plain = NULL;
size_t plain_len = 0;
struct ft_remote_r0kh *r0kh_wildcard;
struct ft_remote_r1kh *r1kh_wildcard;
RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len);
to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id);
if (to_r0kh && to_r1kh) {
wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID");
goto out;
}
if (!to_r0kh && !to_r1kh) {
wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID");
goto out;
}
if (!to_r0kh) {
wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len,
&r0kh, &r0kh_wildcard);
if (!r0kh_wildcard &&
(!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
f_r0kh_id, f_r0kh_id_len);
goto out;
}
if (r0kh) {
*key = r0kh->key;
*key_len = sizeof(r0kh->key);
} else {
*key = r0kh_wildcard->key;
*key_len = sizeof(r0kh_wildcard->key);
}
}
if (!to_r1kh) {
wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh,
&r1kh_wildcard);
if (!r1kh_wildcard &&
(!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) {
wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID",
f_r1kh_id, FT_R1KH_ID_LEN);
goto out;
}
if (r1kh) {
*key = r1kh->key;
*key_len = sizeof(r1kh->key);
} else {
*key = r1kh_wildcard->key;
*key_len = sizeof(r1kh_wildcard->key);
}
}
if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len,
src_addr, type, &plain, &plain_len) < 0)
goto out;
os_free(plain);
if (!to_r0kh) {
if (!r0kh)
r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard,
src_addr, f_r0kh_id,
f_r0kh_id_len,
ftRRBseqTimeout);
if (!r0kh)
goto out;
wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout);
*rkh_seq = r0kh->seq;
if (r0kh_out)
*r0kh_out = r0kh;
if (r0kh_wildcard_out)
*r0kh_wildcard_out = r0kh_wildcard;
}
if (!to_r1kh) {
if (!r1kh)
r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard,
src_addr, f_r1kh_id,
ftRRBseqTimeout);
if (!r1kh)
goto out;
wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout);
*rkh_seq = r1kh->seq;
if (r1kh_out)
*r1kh_out = r1kh;
if (r1kh_wildcard_out)
*r1kh_wildcard_out = r1kh_wildcard;
}
return 0;
out:
return -1;
}
static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
int no_defer)
{
int ret = -1;
struct ft_rrb_seq f_seq;
const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id;
size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len;
struct ft_remote_seq *rkh_seq = NULL;
u8 *packet = NULL, *key = NULL;
size_t packet_len = 0, key_len = 0;
struct tlv_list seq_resp_auth[5];
wpa_printf(MSG_DEBUG, "FT: Received sequence number request");
if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ,
enc, enc_len, auth, auth_len, &rkh_seq, &key,
&key_len, NULL, NULL, NULL, NULL) < 0)
goto out;
RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len);
RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1);
RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN);
if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
goto out;
}
wpa_printf(MSG_DEBUG, "FT: Send sequence number response from " MACSTR
" to " MACSTR,
MAC2STR(wpa_auth->addr), MAC2STR(src_addr));
seq_resp_auth[0].type = FT_RRB_NONCE;
seq_resp_auth[0].len = f_nonce_len;
seq_resp_auth[0].data = f_nonce;
seq_resp_auth[1].type = FT_RRB_SEQ;
seq_resp_auth[1].len = sizeof(f_seq);
seq_resp_auth[1].data = (u8 *) &f_seq;
seq_resp_auth[2].type = FT_RRB_R0KH_ID;
seq_resp_auth[2].len = f_r0kh_id_len;
seq_resp_auth[2].data = f_r0kh_id;
seq_resp_auth[3].type = FT_RRB_R1KH_ID;
seq_resp_auth[3].len = FT_R1KH_ID_LEN;
seq_resp_auth[3].data = f_r1kh_id;
seq_resp_auth[4].type = FT_RRB_LAST_EMPTY;
seq_resp_auth[4].len = 0;
seq_resp_auth[4].data = NULL;
if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL,
wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
&packet, &packet_len) < 0)
goto out;
wpa_ft_rrb_oui_send(wpa_auth, src_addr,
FT_PACKET_R0KH_R1KH_SEQ_RESP, packet,
packet_len);
out:
os_free(packet);
return ret;
}
static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth,
const u8 *src_addr,
const u8 *enc, size_t enc_len,
const u8 *auth, size_t auth_len,
int no_defer)
{
u8 *key = NULL;
size_t key_len = 0;
struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL;
struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL;
const u8 *f_nonce, *f_seq;
size_t f_nonce_len, f_seq_len;
struct ft_remote_seq *rkh_seq = NULL;
struct ft_remote_item *item;
struct os_reltime now, now_remote;
int seq_ret, found;
const struct ft_rrb_seq *msg_both;
u32 msg_dom, msg_seq;
wpa_printf(MSG_DEBUG, "FT: Received sequence number response");
if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP,
enc, enc_len, auth, auth_len, &rkh_seq, &key,
&key_len, &r0kh, &r1kh, &r0kh_wildcard,
&r1kh_wildcard) < 0)
goto out;
RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce,
f_nonce_len);
found = 0;
dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item,
list) {
if (os_memcmp_const(f_nonce, item->nonce,
FT_RRB_NONCE_LEN) != 0 ||
os_get_reltime(&now) < 0 ||
os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout))
continue;
found = 1;
break;
}
if (!found) {
wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce");
goto out;
}
if (r0kh) {
wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh,
wpa_auth->conf.rkh_pos_timeout);
if (r0kh_wildcard)
os_memcpy(r0kh->addr, src_addr, ETH_ALEN);
}
if (r1kh) {
wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh,
wpa_auth->conf.rkh_pos_timeout);
if (r1kh_wildcard)
os_memcpy(r1kh->addr, src_addr, ETH_ALEN);
}
seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth,
auth_len, "seq response", 1);
if (seq_ret == FT_RRB_SEQ_OK) {
wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number");
wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth,
auth_len, "seq response");
} else {
wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number");
RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response",
sizeof(*msg_both));
msg_both = (const struct ft_rrb_seq *) f_seq;
msg_dom = le_to_host32(msg_both->dom);
msg_seq = le_to_host32(msg_both->seq);
now_remote.sec = le_to_host32(msg_both->ts);
now_remote.usec = 0;
rkh_seq->rx.num_last = 2;
rkh_seq->rx.dom = msg_dom;
rkh_seq->rx.offsetidx = 0;
/* Accept some older, possibly cached packets as well */
rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG -
dl_list_len(&rkh_seq->rx.queue);
rkh_seq->rx.last[1] = msg_seq;
/* local time - offset = remote time
* <=> local time - remote time = offset */
os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset);
}
wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1);
return 0;
out:
return -1;
}
int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
const u8 *data, size_t data_len)
{
struct ft_rrb_frame *frame;
u16 alen;
const u8 *pos, *end, *start;
u8 action;
const u8 *sta_addr, *target_ap_addr;
wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR,
MAC2STR(src_addr));
if (data_len < sizeof(*frame)) {
wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)",
(unsigned long) data_len);
return -1;
}
pos = data;
frame = (struct ft_rrb_frame *) pos;
pos += sizeof(*frame);
alen = le_to_host16(frame->action_length);
wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d "
"action_length=%d ap_address=" MACSTR,
frame->frame_type, frame->packet_type, alen,
MAC2STR(frame->ap_address));
if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) {
/* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */
wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with "
"unrecognized type %d", frame->frame_type);
return -1;
}
if (alen > data_len - sizeof(*frame)) {
wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action "
"frame");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen);
if (alen < 1 + 1 + 2 * ETH_ALEN) {
wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough "
"room for Action Frame body); alen=%lu",
(unsigned long) alen);
return -1;
}
start = pos;
end = pos + alen;
if (*pos != WLAN_ACTION_FT) {
wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category "
"%d", *pos);
return -1;
}
pos++;
action = *pos++;
sta_addr = pos;
pos += ETH_ALEN;
target_ap_addr = pos;
pos += ETH_ALEN;
wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr="
MACSTR " target_ap_addr=" MACSTR,
action, MAC2STR(sta_addr), MAC2STR(target_ap_addr));
if (frame->packet_type == FT_PACKET_REQUEST) {
wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request");
if (action != 1) {
wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in "
"RRB Request", action);
return -1;
}
if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "FT: Target AP address in the "
"RRB Request does not match with own "
"address");
return -1;
}
if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address,
sta_addr, pos, end - pos) < 0)
return -1;
} else if (frame->packet_type == FT_PACKET_RESPONSE) {
u16 status_code;
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "FT: Not enough room for status "
"code in RRB Response");
return -1;
}
status_code = WPA_GET_LE16(pos);
wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response "
"(status_code=%d)", status_code);
if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0)
return -1;
} else {
wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown "
"packet_type %d", frame->packet_type);
return -1;
}
return 0;
}
void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
const u8 *dst_addr, u8 oui_suffix, const u8 *data,
size_t data_len)
{
const u8 *auth, *enc;
size_t alen, elen;
int no_defer = 0;
wpa_printf(MSG_DEBUG, "FT: RRB-OUI(" MACSTR
") received frame from remote AP "
MACSTR " oui_suffix=%u dst=" MACSTR,
MAC2STR(wpa_auth->addr), MAC2STR(src_addr), oui_suffix,
MAC2STR(dst_addr));
wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len);
if (is_multicast_ether_addr(src_addr)) {
wpa_printf(MSG_DEBUG,
"FT: RRB-OUI received frame from multicast address "
MACSTR, MAC2STR(src_addr));
return;
}
if (is_multicast_ether_addr(dst_addr))
no_defer = 1;
if (data_len < sizeof(u16)) {
wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
return;
}
alen = WPA_GET_LE16(data);
if (data_len < sizeof(u16) + alen) {
wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short");
return;
}
auth = data + sizeof(u16);
wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen);
enc = data + sizeof(u16) + alen;
elen = data_len - sizeof(u16) - alen;
wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen);
switch (oui_suffix) {
case FT_PACKET_R0KH_R1KH_PULL:
wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen,
no_defer);
break;
case FT_PACKET_R0KH_R1KH_RESP:
wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen,
no_defer);
break;
case FT_PACKET_R0KH_R1KH_PUSH:
wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen,
no_defer);
break;
case FT_PACKET_R0KH_R1KH_SEQ_REQ:
wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen,
no_defer);
break;
case FT_PACKET_R0KH_R1KH_SEQ_RESP:
wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth,
alen, no_defer);
break;
}
}
static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
struct wpa_ft_pmk_r0_sa *pmk_r0,
struct ft_remote_r1kh *r1kh,
const u8 *s1kh_id)
{
u8 *packet;
size_t packet_len;
struct ft_rrb_seq f_seq;
struct tlv_list push[] = {
{ .type = FT_RRB_S1KH_ID, .len = ETH_ALEN,
.data = s1kh_id },
{ .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN,
.data = pmk_r0->pmk_r0_name },
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
struct tlv_list push_auth[] = {
{ .type = FT_RRB_SEQ, .len = sizeof(f_seq),
.data = (u8 *) &f_seq },
{ .type = FT_RRB_R0KH_ID,
.len = wpa_auth->conf.r0_key_holder_len,
.data = wpa_auth->conf.r0_key_holder },
{ .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN,
.data = r1kh->id },
{ .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL },
};
if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to get seq num");
return -1;
}
wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 push from " MACSTR
" to remote R0KH address " MACSTR,
MAC2STR(wpa_auth->addr), MAC2STR(r1kh->addr));
if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0,
r1kh->id, s1kh_id, push_auth, wpa_auth->addr,
FT_PACKET_R0KH_R1KH_PUSH,
&packet, &packet_len) < 0)
return -1;
wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH,
packet, packet_len);
os_free(packet);
return 0;
}
void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr)
{
struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache;
struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL;
struct ft_remote_r1kh *r1kh;
if (!wpa_auth->conf.pmk_r1_push)
return;
if (!wpa_auth->conf.r1kh_list)
return;
dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) {
if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) {
r0found = r0;
break;
}
}
r0 = r0found;
if (r0 == NULL || r0->pmk_r1_pushed)
return;
r0->pmk_r1_pushed = 1;
wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs "
"for STA " MACSTR, MAC2STR(addr));
for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) {
if (is_zero_ether_addr(r1kh->addr) ||
is_zero_ether_addr(r1kh->id))
continue;
if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0)
continue;
wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr);
}
}
#endif /* CONFIG_IEEE80211R_AP */
diff --git a/contrib/wpa/src/ap/wps_hostapd.c b/contrib/wpa/src/ap/wps_hostapd.c
index 9f22e39a2e6a..4f1c76b7b65c 100644
--- a/contrib/wpa/src/ap/wps_hostapd.c
+++ b/contrib/wpa/src/ap/wps_hostapd.c
@@ -1,2279 +1,2284 @@
/*
* hostapd / WPS integration
* Copyright (c) 2008-2016, 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"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/uuid.h"
#include "common/wpa_ctrl.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
#include "wps/wps.h"
#include "wps/wps_defs.h"
#include "wps/wps_dev_attr.h"
#include "wps/wps_attr_parse.h"
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
#include "beacon.h"
#include "sta_info.h"
#include "wps_hostapd.h"
#ifdef CONFIG_WPS_UPNP
#include "wps/wps_upnp.h"
static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
struct wps_context *wps);
static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
#endif /* CONFIG_WPS_UPNP */
static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
const u8 *bssid,
const u8 *ie, size_t ie_len,
int ssi_signal);
static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
static void hostapd_wps_nfc_clear(struct wps_context *wps);
struct wps_for_each_data {
int (*func)(struct hostapd_data *h, void *ctx);
void *ctx;
struct hostapd_data *calling_hapd;
};
static int wps_for_each(struct hostapd_iface *iface, void *ctx)
{
struct wps_for_each_data *data = ctx;
size_t j;
if (iface == NULL)
return 0;
for (j = 0; j < iface->num_bss; j++) {
struct hostapd_data *hapd = iface->bss[j];
int ret;
if (hapd != data->calling_hapd &&
(hapd->conf->wps_independent ||
data->calling_hapd->conf->wps_independent))
continue;
ret = data->func(hapd, data->ctx);
if (ret)
return ret;
}
return 0;
}
static int hostapd_wps_for_each(struct hostapd_data *hapd,
int (*func)(struct hostapd_data *h, void *ctx),
void *ctx)
{
struct hostapd_iface *iface = hapd->iface;
struct wps_for_each_data data;
data.func = func;
data.ctx = ctx;
data.calling_hapd = hapd;
if (iface->interfaces == NULL ||
iface->interfaces->for_each_interface == NULL)
return wps_for_each(iface, &data);
return iface->interfaces->for_each_interface(iface->interfaces,
wps_for_each, &data);
}
static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
const u8 *p2p_dev_addr, const u8 *psk,
size_t psk_len)
{
struct hostapd_data *hapd = ctx;
struct hostapd_wpa_psk *p;
struct hostapd_ssid *ssid = &hapd->conf->ssid;
if (is_zero_ether_addr(p2p_dev_addr)) {
wpa_printf(MSG_DEBUG,
"Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
MAC2STR(mac_addr));
} else {
wpa_printf(MSG_DEBUG,
"Received new WPA/WPA2-PSK from WPS for STA " MACSTR
" P2P Device Addr " MACSTR,
MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
}
wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
if (psk_len != PMK_LEN) {
wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu",
(unsigned long) psk_len);
return -1;
}
/* Add the new PSK to runtime PSK list */
p = os_zalloc(sizeof(*p));
if (p == NULL)
return -1;
os_memcpy(p->addr, mac_addr, ETH_ALEN);
os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
os_memcpy(p->psk, psk, PMK_LEN);
p->wps = 1;
if (hapd->new_psk_cb) {
hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr,
psk, psk_len);
}
p->next = ssid->wpa_psk;
ssid->wpa_psk = p;
if (ssid->wpa_psk_file) {
FILE *f;
char hex[PMK_LEN * 2 + 1];
/* Add the new PSK to PSK list file */
f = fopen(ssid->wpa_psk_file, "a");
if (!f) {
wpa_printf(MSG_DEBUG, "Failed to add the PSK to '%s'",
ssid->wpa_psk_file);
return -1;
}
wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len);
fprintf(f, "wps=1 " MACSTR " %s\n", MAC2STR(mac_addr), hex);
fclose(f);
}
return 0;
}
static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
struct wpabuf *probe_resp_ie)
{
struct hostapd_data *hapd = ctx;
wpabuf_free(hapd->wps_beacon_ie);
hapd->wps_beacon_ie = beacon_ie;
wpabuf_free(hapd->wps_probe_resp_ie);
hapd->wps_probe_resp_ie = probe_resp_ie;
if (hapd->beacon_set_done)
ieee802_11_set_beacon(hapd);
return hostapd_set_ap_wps_ie(hapd);
}
static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
const struct wps_device_data *dev)
{
struct hostapd_data *hapd = ctx;
char uuid[40], txt[400];
int len;
char devtype[WPS_DEV_TYPE_BUFSIZE];
if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
return;
wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid);
len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED
"%s " MACSTR " [%s|%s|%s|%s|%s|%s]",
uuid, MAC2STR(dev->mac_addr), dev->device_name,
dev->manufacturer, dev->model_name,
dev->model_number, dev->serial_number,
wps_dev_type_bin2str(dev->pri_dev_type, devtype,
sizeof(devtype)));
if (!os_snprintf_error(sizeof(txt), len))
wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt);
if (hapd->conf->wps_pin_requests) {
FILE *f;
struct os_time t;
f = fopen(hapd->conf->wps_pin_requests, "a");
if (f == NULL)
return;
os_get_time(&t);
fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s"
"\t%s\n",
t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name,
dev->manufacturer, dev->model_name, dev->model_number,
dev->serial_number,
wps_dev_type_bin2str(dev->pri_dev_type, devtype,
sizeof(devtype)));
fclose(f);
}
}
struct wps_stop_reg_data {
struct hostapd_data *current_hapd;
const u8 *uuid_e;
const u8 *dev_pw;
size_t dev_pw_len;
};
static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
{
struct wps_stop_reg_data *data = ctx;
if (hapd != data->current_hapd && hapd->wps != NULL)
wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
data->dev_pw, data->dev_pw_len);
return 0;
}
static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
const u8 *uuid_e, const u8 *dev_pw,
size_t dev_pw_len)
{
struct hostapd_data *hapd = ctx;
char uuid[40];
struct wps_stop_reg_data data;
if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
return;
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
MAC2STR(mac_addr), uuid);
if (hapd->wps_reg_success_cb)
hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
mac_addr, uuid_e);
data.current_hapd = hapd;
data.uuid_e = uuid_e;
data.dev_pw = dev_pw;
data.dev_pw_len = dev_pw_len;
hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
}
static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr,
const u8 *uuid_e,
const u8 *pri_dev_type,
u16 config_methods,
u16 dev_password_id, u8 request_type,
const char *dev_name)
{
struct hostapd_data *hapd = ctx;
char uuid[40];
char devtype[WPS_DEV_TYPE_BUFSIZE];
if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
return;
if (dev_name == NULL)
dev_name = "";
wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR
" %s %s 0x%x %u %u [%s]",
MAC2STR(addr), uuid,
wps_dev_type_bin2str(pri_dev_type, devtype,
sizeof(devtype)),
config_methods, dev_password_id, request_type, dev_name);
}
static int hostapd_wps_lookup_pskfile_cb(void *ctx, const u8 *mac_addr,
const u8 **psk)
{
const struct hostapd_data *hapd = ctx;
const struct hostapd_wpa_psk *wpa_psk;
const u8 *any_psk = NULL;
const u8 *dev_psk = NULL;
for (wpa_psk = hapd->conf->ssid.wpa_psk; wpa_psk;
wpa_psk = wpa_psk->next) {
if (!wpa_psk->wps)
continue;
if (!any_psk && is_zero_ether_addr(wpa_psk->addr))
any_psk = wpa_psk->psk;
if (mac_addr && !dev_psk &&
os_memcmp(mac_addr, wpa_psk->addr, ETH_ALEN) == 0) {
dev_psk = wpa_psk->psk;
break;
}
}
if (dev_psk) {
*psk = dev_psk;
} else if (any_psk) {
*psk = any_psk;
} else {
*psk = NULL;
wpa_printf(MSG_DEBUG,
"WPS: No appropriate PSK in wpa_psk_file");
return 0;
}
return 1;
}
static void wps_reload_config(void *eloop_data, void *user_ctx)
{
struct hostapd_iface *iface = eloop_data;
wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
if (iface->interfaces == NULL ||
iface->interfaces->reload_config(iface) < 0) {
wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
"configuration");
}
}
void hostapd_wps_eap_completed(struct hostapd_data *hapd)
{
/*
* Reduce race condition of the station trying to reconnect immediately
* after AP reconfiguration through WPS by rescheduling the reload
* timeout to happen after EAP completion rather than the originally
* scheduled 100 ms after new configuration became known.
*/
if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) ==
1)
wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload");
}
static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
size_t attr_len)
{
size_t blen = attr_len * 2 + 1;
char *buf = os_malloc(blen);
if (buf) {
wpa_snprintf_hex(buf, blen, attr, attr_len);
wpa_msg(hapd->msg_ctx, MSG_INFO,
WPS_EVENT_NEW_AP_SETTINGS "%s", buf);
os_free(buf);
}
}
static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
const struct wps_credential *cred)
{
struct hostapd_bss_config *bss = hapd->conf;
wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration");
bss->wps_state = 2;
if (cred->ssid_len <= SSID_MAX_LEN) {
os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len);
bss->ssid.ssid_len = cred->ssid_len;
bss->ssid.ssid_set = 1;
}
#ifdef CONFIG_NO_TKIP
if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK |
WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
bss->wpa = 2;
else
bss->wpa = 0;
#else /* CONFIG_NO_TKIP */
if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
(cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
bss->wpa = 3;
else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
bss->wpa = 2;
else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
bss->wpa = 1;
else
bss->wpa = 0;
#endif /* CONFIG_NO_TKIP */
if (bss->wpa) {
if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA))
bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
bss->wpa_pairwise = 0;
if (cred->encr_type & WPS_ENCR_AES) {
if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
bss->wpa_pairwise |= WPA_CIPHER_GCMP;
else
bss->wpa_pairwise |= WPA_CIPHER_CCMP;
}
#ifndef CONFIG_NO_TKIP
if (cred->encr_type & WPS_ENCR_TKIP)
bss->wpa_pairwise |= WPA_CIPHER_TKIP;
#endif /* CONFIG_NO_TKIP */
bss->rsn_pairwise = bss->wpa_pairwise;
bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
bss->wpa_pairwise,
bss->rsn_pairwise);
if (hapd->conf->wps_cred_add_sae &&
(cred->auth_type & WPS_AUTH_WPA2PSK) &&
cred->key_len != 2 * PMK_LEN) {
bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE;
if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION)
bss->ieee80211w =
MGMT_FRAME_PROTECTION_OPTIONAL;
bss->sae_require_mfp = 1;
}
if (cred->key_len >= 8 && cred->key_len < 64) {
os_free(bss->ssid.wpa_passphrase);
bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1);
if (bss->ssid.wpa_passphrase)
os_memcpy(bss->ssid.wpa_passphrase, cred->key,
cred->key_len);
hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
} else if (cred->key_len == 64) {
hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
bss->ssid.wpa_psk =
os_zalloc(sizeof(struct hostapd_wpa_psk));
if (bss->ssid.wpa_psk &&
hexstr2bin((const char *) cred->key,
bss->ssid.wpa_psk->psk, PMK_LEN) == 0) {
bss->ssid.wpa_psk->group = 1;
os_free(bss->ssid.wpa_passphrase);
bss->ssid.wpa_passphrase = NULL;
}
}
bss->auth_algs = 1;
} else {
/*
* WPS 2.0 does not allow WEP to be configured, so no need to
* process that option here either.
*/
bss->auth_algs = 1;
}
/* Schedule configuration reload after short period of time to allow
* EAP-WSC to be finished.
*/
eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
NULL);
return 0;
}
static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
{
const struct wps_credential *cred = ctx;
FILE *oconf, *nconf;
size_t len, i;
char *tmp_fname;
char buf[1024];
int multi_bss;
int wpa;
int pmf_changed = 0;
if (hapd->wps == NULL)
return 0;
wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
cred->cred_attr, cred->cred_attr_len);
wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings");
wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
cred->auth_type);
wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
cred->key, cred->key_len);
wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
MAC2STR(cred->mac_addr));
if ((hapd->conf->wps_cred_processing == 1 ||
hapd->conf->wps_cred_processing == 2) && cred->cred_attr) {
hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len);
} else if (hapd->conf->wps_cred_processing == 1 ||
hapd->conf->wps_cred_processing == 2) {
struct wpabuf *attr;
attr = wpabuf_alloc(200);
if (attr && wps_build_credential_wrap(attr, cred) == 0)
hapd_new_ap_event(hapd, wpabuf_head_u8(attr),
wpabuf_len(attr));
wpabuf_free(attr);
} else
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
if (hapd->conf->wps_cred_processing == 1)
return 0;
os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len);
hapd->wps->ssid_len = cred->ssid_len;
hapd->wps->encr_types = cred->encr_type;
hapd->wps->encr_types_rsn = cred->encr_type;
hapd->wps->encr_types_wpa = cred->encr_type;
hapd->wps->auth_types = cred->auth_type;
hapd->wps->ap_encr_type = cred->encr_type;
hapd->wps->ap_auth_type = cred->auth_type;
if (cred->key_len == 0) {
os_free(hapd->wps->network_key);
hapd->wps->network_key = NULL;
hapd->wps->network_key_len = 0;
} else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) &&
(cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) {
wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2",
(unsigned long) cred->key_len);
return -1;
} else {
if (hapd->wps->network_key == NULL ||
hapd->wps->network_key_len < cred->key_len) {
hapd->wps->network_key_len = 0;
os_free(hapd->wps->network_key);
hapd->wps->network_key = os_malloc(cred->key_len);
if (hapd->wps->network_key == NULL)
return -1;
}
hapd->wps->network_key_len = cred->key_len;
os_memcpy(hapd->wps->network_key, cred->key, cred->key_len);
}
hapd->wps->wps_state = WPS_STATE_CONFIGURED;
if (hapd->iface->config_fname == NULL)
return hapd_wps_reconfig_in_memory(hapd, cred);
len = os_strlen(hapd->iface->config_fname) + 5;
tmp_fname = os_malloc(len);
if (tmp_fname == NULL)
return -1;
os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname);
oconf = fopen(hapd->iface->config_fname, "r");
if (oconf == NULL) {
wpa_printf(MSG_WARNING, "WPS: Could not open current "
"configuration file");
os_free(tmp_fname);
return -1;
}
nconf = fopen(tmp_fname, "w");
if (nconf == NULL) {
wpa_printf(MSG_WARNING, "WPS: Could not write updated "
"configuration file");
os_free(tmp_fname);
fclose(oconf);
return -1;
}
fprintf(nconf, "# WPS configuration - START\n");
fprintf(nconf, "wps_state=2\n");
if (is_hex(cred->ssid, cred->ssid_len)) {
fprintf(nconf, "ssid2=");
for (i = 0; i < cred->ssid_len; i++)
fprintf(nconf, "%02x", cred->ssid[i]);
fprintf(nconf, "\n");
} else {
fprintf(nconf, "ssid=");
for (i = 0; i < cred->ssid_len; i++)
fputc(cred->ssid[i], nconf);
fprintf(nconf, "\n");
}
#ifdef CONFIG_NO_TKIP
if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK |
WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
wpa = 2;
else
wpa = 0;
#else /* CONFIG_NO_TKIP */
if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
(cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
wpa = 3;
else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
wpa = 2;
else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
wpa = 1;
else
wpa = 0;
#endif /* CONFIG_NO_TKIP */
if (wpa) {
char *prefix;
int sae = 0;
fprintf(nconf, "wpa=%d\n", wpa);
fprintf(nconf, "wpa_key_mgmt=");
prefix = "";
if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) {
fprintf(nconf, "WPA-EAP");
prefix = " ";
}
if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
fprintf(nconf, "%sWPA-PSK", prefix);
prefix = " ";
}
if (hapd->conf->wps_cred_add_sae &&
(cred->auth_type & WPS_AUTH_WPA2PSK) &&
cred->key_len != 2 * PMK_LEN) {
fprintf(nconf, "%sSAE", prefix);
sae = 1;
}
fprintf(nconf, "\n");
if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
fprintf(nconf, "ieee80211w=%d\n",
MGMT_FRAME_PROTECTION_OPTIONAL);
pmf_changed = 1;
}
if (sae)
fprintf(nconf, "sae_require_mfp=1\n");
fprintf(nconf, "wpa_pairwise=");
prefix = "";
if (cred->encr_type & WPS_ENCR_AES) {
if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
fprintf(nconf, "GCMP");
else
fprintf(nconf, "CCMP");
prefix = " ";
}
#ifndef CONFIG_NO_TKIP
if (cred->encr_type & WPS_ENCR_TKIP) {
fprintf(nconf, "%sTKIP", prefix);
}
#endif /* CONFIG_NO_TKIP */
fprintf(nconf, "\n");
if (cred->key_len >= 8 && cred->key_len < 64) {
fprintf(nconf, "wpa_passphrase=");
for (i = 0; i < cred->key_len; i++)
fputc(cred->key[i], nconf);
fprintf(nconf, "\n");
} else if (cred->key_len == 64) {
fprintf(nconf, "wpa_psk=");
for (i = 0; i < cred->key_len; i++)
fputc(cred->key[i], nconf);
fprintf(nconf, "\n");
} else {
wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu "
"for WPA/WPA2",
(unsigned long) cred->key_len);
}
fprintf(nconf, "auth_algs=1\n");
} else {
/*
* WPS 2.0 does not allow WEP to be configured, so no need to
* process that option here either.
*/
fprintf(nconf, "auth_algs=1\n");
}
fprintf(nconf, "# WPS configuration - END\n");
multi_bss = 0;
while (fgets(buf, sizeof(buf), oconf)) {
if (os_strncmp(buf, "bss=", 4) == 0)
multi_bss = 1;
if (!multi_bss &&
(str_starts(buf, "ssid=") ||
str_starts(buf, "ssid2=") ||
str_starts(buf, "auth_algs=") ||
#ifdef CONFIG_WEP
str_starts(buf, "wep_default_key=") ||
str_starts(buf, "wep_key") ||
#endif /* CONFIG_WEP */
str_starts(buf, "wps_state=") ||
(pmf_changed && str_starts(buf, "ieee80211w=")) ||
str_starts(buf, "wpa=") ||
str_starts(buf, "wpa_psk=") ||
str_starts(buf, "wpa_pairwise=") ||
str_starts(buf, "rsn_pairwise=") ||
str_starts(buf, "wpa_key_mgmt=") ||
str_starts(buf, "wpa_passphrase="))) {
fprintf(nconf, "#WPS# %s", buf);
} else
fprintf(nconf, "%s", buf);
}
fclose(nconf);
fclose(oconf);
if (rename(tmp_fname, hapd->iface->config_fname) < 0) {
wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated "
"configuration file: %s", strerror(errno));
os_free(tmp_fname);
return -1;
}
os_free(tmp_fname);
/* Schedule configuration reload after short period of time to allow
* EAP-WSC to be finished.
*/
eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
NULL);
wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
return 0;
}
static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
{
struct hostapd_data *hapd = ctx;
return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred);
}
static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
{
struct hostapd_data *hapd = eloop_data;
if (hapd->conf->ap_setup_locked)
return;
if (hapd->ap_pin_failures_consecutive >= 10)
return;
wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
hapd->wps->ap_setup_locked = 0;
wps_registrar_update_ie(hapd->wps->registrar);
}
static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
{
struct wps_event_pwd_auth_fail *data = ctx;
if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL)
return 0;
/*
* Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
* for some time if this happens multiple times to slow down brute
* force attacks.
*/
hapd->ap_pin_failures++;
hapd->ap_pin_failures_consecutive++;
wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u "
"(%u consecutive)",
hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
if (hapd->ap_pin_failures < 3)
return 0;
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
hapd->wps->ap_setup_locked = 1;
wps_registrar_update_ie(hapd->wps->registrar);
if (!hapd->conf->ap_setup_locked &&
hapd->ap_pin_failures_consecutive >= 10) {
/*
* In indefinite lockdown - disable automatic AP PIN
* reenablement.
*/
eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
} else if (!hapd->conf->ap_setup_locked) {
if (hapd->ap_pin_lockout_time == 0)
hapd->ap_pin_lockout_time = 60;
else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
(hapd->ap_pin_failures % 3) == 0)
hapd->ap_pin_lockout_time *= 2;
wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds",
hapd->ap_pin_lockout_time);
eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
eloop_register_timeout(hapd->ap_pin_lockout_time, 0,
hostapd_wps_reenable_ap_pin, hapd,
NULL);
}
return 0;
}
static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
struct wps_event_pwd_auth_fail *data)
{
/* Update WPS Status - Authentication Failure */
wpa_printf(MSG_DEBUG, "WPS: Authentication failure update");
hapd->wps_stats.status = WPS_STATUS_FAILURE;
hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE;
os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN);
hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
}
static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx)
{
if (hapd->conf->ap_pin == NULL || hapd->wps == NULL)
return 0;
if (hapd->ap_pin_failures_consecutive == 0)
return 0;
wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter "
"- total validation failures %u (%u consecutive)",
hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
hapd->ap_pin_failures_consecutive = 0;
return 0;
}
static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
{
hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL);
}
static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd)
{
/* Update WPS Status - PBC Overlap */
hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP;
}
static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd)
{
/* Update WPS PBC Status:PBC Timeout */
hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT;
}
static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd)
{
/* Update WPS PBC status - Active */
hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE;
}
static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd)
{
/* Update WPS PBC status - Active */
hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
}
static void hostapd_wps_event_success(struct hostapd_data *hapd,
struct wps_event_success *success)
{
/* Update WPS status - Success */
hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
hapd->wps_stats.status = WPS_STATUS_SUCCESS;
os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN);
}
static void hostapd_wps_event_fail(struct hostapd_data *hapd,
struct wps_event_fail *fail)
{
/* Update WPS status - Failure */
hapd->wps_stats.status = WPS_STATUS_FAILURE;
os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN);
hapd->wps_stats.failure_reason = fail->error_indication;
if (fail->error_indication > 0 &&
fail->error_indication < NUM_WPS_EI_VALUES) {
wpa_msg(hapd->msg_ctx, MSG_INFO,
WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
fail->msg, fail->config_error, fail->error_indication,
wps_ei_str(fail->error_indication));
} else {
wpa_msg(hapd->msg_ctx, MSG_INFO,
WPS_EVENT_FAIL "msg=%d config_error=%d",
fail->msg, fail->config_error);
}
}
static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
union wps_event_data *data)
{
struct hostapd_data *hapd = ctx;
switch (event) {
case WPS_EV_M2D:
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D);
break;
case WPS_EV_FAIL:
hostapd_wps_event_fail(hapd, &data->fail);
break;
case WPS_EV_SUCCESS:
hostapd_wps_event_success(hapd, &data->success);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
break;
case WPS_EV_PWD_AUTH_FAIL:
hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
break;
case WPS_EV_PBC_OVERLAP:
hostapd_wps_event_pbc_overlap(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
break;
case WPS_EV_PBC_TIMEOUT:
hostapd_wps_event_pbc_timeout(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
break;
case WPS_EV_PBC_ACTIVE:
hostapd_wps_event_pbc_active(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE);
break;
case WPS_EV_PBC_DISABLE:
hostapd_wps_event_pbc_disable(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE);
break;
case WPS_EV_ER_AP_ADD:
break;
case WPS_EV_ER_AP_REMOVE:
break;
case WPS_EV_ER_ENROLLEE_ADD:
break;
case WPS_EV_ER_ENROLLEE_REMOVE:
break;
case WPS_EV_ER_AP_SETTINGS:
break;
case WPS_EV_ER_SET_SELECTED_REGISTRAR:
break;
case WPS_EV_AP_PIN_SUCCESS:
hostapd_wps_ap_pin_success(hapd);
break;
}
if (hapd->wps_event_cb)
hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data);
}
static int hostapd_wps_rf_band_cb(void *ctx)
{
struct hostapd_data *hapd = ctx;
return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
WPS_RF_50GHZ :
hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
}
static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only)
{
wpabuf_free(hapd->wps_beacon_ie);
hapd->wps_beacon_ie = NULL;
wpabuf_free(hapd->wps_probe_resp_ie);
hapd->wps_probe_resp_ie = NULL;
if (deinit_only) {
if (hapd->drv_priv)
hostapd_reset_ap_wps_ie(hapd);
return;
}
hostapd_set_ap_wps_ie(hapd);
}
static int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
{
const u8 **uuid = ctx;
size_t j;
if (iface == NULL)
return 0;
for (j = 0; j < iface->num_bss; j++) {
struct hostapd_data *hapd = iface->bss[j];
if (hapd->wps && !hapd->conf->wps_independent &&
!is_nil_uuid(hapd->wps->uuid)) {
*uuid = hapd->wps->uuid;
return 1;
}
}
return 0;
}
static const u8 * get_own_uuid(struct hostapd_iface *iface)
{
const u8 *uuid;
if (iface->interfaces == NULL ||
iface->interfaces->for_each_interface == NULL)
return NULL;
uuid = NULL;
iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb,
&uuid);
return uuid;
}
static int count_interface_cb(struct hostapd_iface *iface, void *ctx)
{
int *count= ctx;
(*count)++;
return 0;
}
static int interface_count(struct hostapd_iface *iface)
{
int count = 0;
if (iface->interfaces == NULL ||
iface->interfaces->for_each_interface == NULL)
return 0;
iface->interfaces->for_each_interface(iface->interfaces,
count_interface_cb, &count);
return count;
}
static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
struct wps_context *wps)
{
int i;
for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
wpabuf_free(wps->dev.vendor_ext[i]);
wps->dev.vendor_ext[i] = NULL;
if (hapd->conf->wps_vendor_ext[i] == NULL)
continue;
wps->dev.vendor_ext[i] =
wpabuf_dup(hapd->conf->wps_vendor_ext[i]);
if (wps->dev.vendor_ext[i] == NULL) {
while (--i >= 0)
wpabuf_free(wps->dev.vendor_ext[i]);
return -1;
}
}
return 0;
}
static int hostapd_wps_set_application_ext(struct hostapd_data *hapd,
struct wps_context *wps)
{
wpabuf_free(wps->dev.application_ext);
if (!hapd->conf->wps_application_ext) {
wps->dev.application_ext = NULL;
return 0;
}
wps->dev.application_ext = wpabuf_dup(hapd->conf->wps_application_ext);
return wps->dev.application_ext ? 0 : -1;
}
static void hostapd_free_wps(struct wps_context *wps)
{
int i;
for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
wpabuf_free(wps->dev.vendor_ext[i]);
wps_device_data_free(&wps->dev);
os_free(wps->network_key);
hostapd_wps_nfc_clear(wps);
wpabuf_free(wps->dh_pubkey);
wpabuf_free(wps->dh_privkey);
os_free(wps);
}
int hostapd_init_wps(struct hostapd_data *hapd,
struct hostapd_bss_config *conf)
{
struct wps_context *wps;
struct wps_registrar_config cfg;
u8 *multi_ap_netw_key = NULL;
if (conf->wps_state == 0) {
hostapd_wps_clear_ies(hapd, 0);
return 0;
}
wps = os_zalloc(sizeof(*wps));
if (wps == NULL)
return -1;
wps->cred_cb = hostapd_wps_cred_cb;
wps->event_cb = hostapd_wps_event_cb;
wps->rf_band_cb = hostapd_wps_rf_band_cb;
wps->cb_ctx = hapd;
os_memset(&cfg, 0, sizeof(cfg));
wps->wps_state = hapd->conf->wps_state;
wps->ap_setup_locked = hapd->conf->ap_setup_locked;
if (is_nil_uuid(hapd->conf->uuid)) {
const u8 *uuid;
uuid = get_own_uuid(hapd->iface);
if (uuid && !conf->wps_independent) {
os_memcpy(wps->uuid, uuid, UUID_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
"interface", wps->uuid, UUID_LEN);
} else {
uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
"address", wps->uuid, UUID_LEN);
}
} else {
os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID",
wps->uuid, UUID_LEN);
}
wps->ssid_len = hapd->conf->ssid.ssid_len;
os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
wps->ap = 1;
os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN);
wps->dev.device_name = hapd->conf->device_name ?
os_strdup(hapd->conf->device_name) : NULL;
wps->dev.manufacturer = hapd->conf->manufacturer ?
os_strdup(hapd->conf->manufacturer) : NULL;
wps->dev.model_name = hapd->conf->model_name ?
os_strdup(hapd->conf->model_name) : NULL;
wps->dev.model_number = hapd->conf->model_number ?
os_strdup(hapd->conf->model_number) : NULL;
wps->dev.serial_number = hapd->conf->serial_number ?
os_strdup(hapd->conf->serial_number) : NULL;
wps->config_methods =
wps_config_methods_str2bin(hapd->conf->config_methods);
if ((wps->config_methods &
(WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
wpa_printf(MSG_INFO, "WPS: Converting display to "
"virtual_display for WPS 2.0 compliance");
wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY;
}
if ((wps->config_methods &
(WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
wpa_printf(MSG_INFO, "WPS: Converting push_button to "
"virtual_push_button for WPS 2.0 compliance");
wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
}
os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
WPS_DEV_TYPE_LEN);
if (hostapd_wps_set_vendor_ext(hapd, wps) < 0 ||
hostapd_wps_set_application_ext(hapd, wps) < 0)
goto fail;
wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
if (conf->wps_rf_bands) {
wps->dev.rf_bands = conf->wps_rf_bands;
} else {
wps->dev.rf_bands =
hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
WPS_RF_50GHZ :
hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
}
if (conf->wpa & WPA_PROTO_RSN) {
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
wps->auth_types |= WPS_AUTH_WPA2PSK;
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
wps->auth_types |= WPS_AUTH_WPA2;
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE)
wps->auth_types |= WPS_AUTH_WPA2PSK;
if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
WPA_CIPHER_CCMP_256 |
WPA_CIPHER_GCMP_256)) {
wps->encr_types |= WPS_ENCR_AES;
wps->encr_types_rsn |= WPS_ENCR_AES;
}
if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
#ifdef CONFIG_NO_TKIP
wpa_printf(MSG_INFO, "WPS: TKIP not supported");
goto fail;
#else /* CONFIG_NO_TKIP */
wps->encr_types |= WPS_ENCR_TKIP;
wps->encr_types_rsn |= WPS_ENCR_TKIP;
#endif /* CONFIG_NO_TKIP */
}
}
if (conf->wpa & WPA_PROTO_WPA) {
#ifdef CONFIG_NO_TKIP
if (!(conf->wpa & WPA_PROTO_RSN)) {
wpa_printf(MSG_INFO, "WPS: WPA(v1) not supported");
goto fail;
}
conf->wpa &= ~WPA_PROTO_WPA;
#else /* CONFIG_NO_TKIP */
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
wps->auth_types |= WPS_AUTH_WPAPSK;
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
wps->auth_types |= WPS_AUTH_WPA;
if (conf->wpa_pairwise & WPA_CIPHER_CCMP) {
wps->encr_types |= WPS_ENCR_AES;
wps->encr_types_wpa |= WPS_ENCR_AES;
}
if (conf->wpa_pairwise & WPA_CIPHER_TKIP) {
wps->encr_types |= WPS_ENCR_TKIP;
wps->encr_types_wpa |= WPS_ENCR_TKIP;
}
#endif /* CONFIG_NO_TKIP */
}
if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
wps->encr_types |= WPS_ENCR_NONE;
wps->auth_types |= WPS_AUTH_OPEN;
}
if (conf->ssid.wpa_psk_file) {
/* Use per-device PSKs */
} else if (conf->ssid.wpa_passphrase) {
wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
} else if (conf->ssid.wpa_psk) {
wps->network_key = os_malloc(2 * PMK_LEN + 1);
if (wps->network_key == NULL)
goto fail;
wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
conf->ssid.wpa_psk->psk, PMK_LEN);
wps->network_key_len = 2 * PMK_LEN;
#ifdef CONFIG_WEP
} else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
wps->network_key = os_malloc(conf->ssid.wep.len[0]);
if (wps->network_key == NULL)
goto fail;
os_memcpy(wps->network_key, conf->ssid.wep.key[0],
conf->ssid.wep.len[0]);
wps->network_key_len = conf->ssid.wep.len[0];
#endif /* CONFIG_WEP */
}
if (conf->ssid.wpa_psk) {
os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
wps->psk_set = 1;
}
wps->ap_auth_type = wps->auth_types;
wps->ap_encr_type = wps->encr_types;
if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) {
/* Override parameters to enable security by default */
#ifdef CONFIG_NO_TKIP
wps->auth_types = WPS_AUTH_WPA2PSK;
wps->encr_types = WPS_ENCR_AES;
wps->encr_types_rsn = WPS_ENCR_AES;
wps->encr_types_wpa = WPS_ENCR_AES;
#else /* CONFIG_NO_TKIP */
wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
wps->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP;
wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP;
#endif /* CONFIG_NO_TKIP */
}
if ((hapd->conf->multi_ap & FRONTHAUL_BSS) &&
hapd->conf->multi_ap_backhaul_ssid.ssid_len) {
cfg.multi_ap_backhaul_ssid_len =
hapd->conf->multi_ap_backhaul_ssid.ssid_len;
cfg.multi_ap_backhaul_ssid =
hapd->conf->multi_ap_backhaul_ssid.ssid;
if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
cfg.multi_ap_backhaul_network_key = (const u8 *)
conf->multi_ap_backhaul_ssid.wpa_passphrase;
cfg.multi_ap_backhaul_network_key_len =
os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
} else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1);
if (!multi_ap_netw_key)
goto fail;
wpa_snprintf_hex((char *) multi_ap_netw_key,
2 * PMK_LEN + 1,
conf->multi_ap_backhaul_ssid.wpa_psk->psk,
PMK_LEN);
cfg.multi_ap_backhaul_network_key = multi_ap_netw_key;
cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
}
}
wps->ap_settings = conf->ap_settings;
wps->ap_settings_len = conf->ap_settings_len;
cfg.new_psk_cb = hostapd_wps_new_psk_cb;
cfg.set_ie_cb = hostapd_wps_set_ie_cb;
cfg.pin_needed_cb = hostapd_wps_pin_needed_cb;
cfg.reg_success_cb = hostapd_wps_reg_success_cb;
cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb;
cfg.lookup_pskfile_cb = hostapd_wps_lookup_pskfile_cb;
cfg.cb_ctx = hapd;
cfg.skip_cred_build = conf->skip_cred_build;
cfg.extra_cred = conf->extra_cred;
cfg.extra_cred_len = conf->extra_cred_len;
cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) &&
conf->skip_cred_build;
cfg.dualband = interface_count(hapd->iface) > 1;
if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) ==
(WPS_RF_50GHZ | WPS_RF_24GHZ))
cfg.dualband = 1;
if (cfg.dualband)
wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk;
wps->registrar = wps_registrar_init(wps, &cfg);
if (wps->registrar == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
goto fail;
}
#ifdef CONFIG_WPS_UPNP
wps->friendly_name = hapd->conf->friendly_name;
wps->manufacturer_url = hapd->conf->manufacturer_url;
wps->model_description = hapd->conf->model_description;
wps->model_url = hapd->conf->model_url;
wps->upc = hapd->conf->upc;
#endif /* CONFIG_WPS_UPNP */
hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
#ifdef CONFIG_P2P
if ((hapd->conf->p2p & P2P_ENABLED) &&
is_6ghz_op_class(hapd->iconf->op_class))
wps->use_passphrase = true;
#endif /* CONFIG_P2P */
hapd->wps = wps;
bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
return 0;
fail:
bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
hostapd_free_wps(wps);
return -1;
}
int hostapd_init_wps_complete(struct hostapd_data *hapd)
{
struct wps_context *wps = hapd->wps;
if (wps == NULL)
return 0;
#ifdef CONFIG_WPS_UPNP
if (hostapd_wps_upnp_init(hapd, wps) < 0) {
wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
wps_registrar_deinit(wps->registrar);
hostapd_free_wps(wps);
hapd->wps = NULL;
return -1;
}
#endif /* CONFIG_WPS_UPNP */
return 0;
}
static void hostapd_wps_nfc_clear(struct wps_context *wps)
{
#ifdef CONFIG_WPS_NFC
wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps);
wps->ap_nfc_dev_pw_id = 0;
wpabuf_free(wps->ap_nfc_dh_pubkey);
wps->ap_nfc_dh_pubkey = NULL;
wpabuf_free(wps->ap_nfc_dh_privkey);
wps->ap_nfc_dh_privkey = NULL;
wpabuf_free(wps->ap_nfc_dev_pw);
wps->ap_nfc_dev_pw = NULL;
#endif /* CONFIG_WPS_NFC */
}
static int hostapd_wps_update_multi_ap(struct hostapd_data *hapd,
struct wps_registrar *reg)
{
struct hostapd_bss_config *conf = hapd->conf;
u8 *multi_ap_backhaul_network_key = NULL;
size_t multi_ap_backhaul_network_key_len = 0;
int ret;
if (!(conf->multi_ap & FRONTHAUL_BSS) ||
!conf->multi_ap_backhaul_ssid.ssid_len)
return 0;
if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
multi_ap_backhaul_network_key =
(u8 *) os_strdup(
conf->multi_ap_backhaul_ssid.wpa_passphrase);
if (!multi_ap_backhaul_network_key)
return -1;
multi_ap_backhaul_network_key_len =
os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
} else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
multi_ap_backhaul_network_key = os_malloc(2 * PMK_LEN + 1);
if (!multi_ap_backhaul_network_key)
return -1;
wpa_snprintf_hex((char *) multi_ap_backhaul_network_key,
2 * PMK_LEN + 1,
conf->multi_ap_backhaul_ssid.wpa_psk->psk,
PMK_LEN);
multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
}
ret = wps_registrar_update_multi_ap(
reg, conf->multi_ap_backhaul_ssid.ssid,
conf->multi_ap_backhaul_ssid.ssid_len,
multi_ap_backhaul_network_key,
multi_ap_backhaul_network_key_len);
os_free(multi_ap_backhaul_network_key);
return ret;
}
void hostapd_deinit_wps(struct hostapd_data *hapd)
{
eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL);
if (hapd->wps == NULL) {
hostapd_wps_clear_ies(hapd, 1);
return;
}
#ifdef CONFIG_WPS_UPNP
hostapd_wps_upnp_deinit(hapd);
#endif /* CONFIG_WPS_UPNP */
wps_registrar_deinit(hapd->wps->registrar);
wps_free_pending_msgs(hapd->wps->upnp_msgs);
hostapd_free_wps(hapd->wps);
hapd->wps = NULL;
hostapd_wps_clear_ies(hapd, 1);
}
void hostapd_update_wps(struct hostapd_data *hapd)
{
struct wps_context *wps = hapd->wps;
struct hostapd_bss_config *conf = hapd->conf;
if (!wps)
return;
#ifdef CONFIG_WPS_UPNP
wps->friendly_name = conf->friendly_name;
wps->manufacturer_url = conf->manufacturer_url;
wps->model_description = conf->model_description;
wps->model_url = conf->model_url;
wps->upc = conf->upc;
#endif /* CONFIG_WPS_UPNP */
os_memcpy(wps->ssid, conf->ssid.ssid, conf->ssid.ssid_len);
wps->ssid_len = conf->ssid.ssid_len;
/* Clear WPS settings, then fill them again */
os_free(wps->network_key);
wps->network_key = NULL;
wps->network_key_len = 0;
wps->psk_set = 0;
if (conf->ssid.wpa_psk_file) {
/* Use per-device PSKs */
} else if (conf->ssid.wpa_passphrase) {
wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
if (!wps->network_key)
return;
wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
} else if (conf->ssid.wpa_psk) {
wps->network_key = os_malloc(2 * PMK_LEN + 1);
if (!wps->network_key)
return;
wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
conf->ssid.wpa_psk->psk, PMK_LEN);
wps->network_key_len = 2 * PMK_LEN;
#ifdef CONFIG_WEP
} else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
wps->network_key = os_malloc(conf->ssid.wep.len[0]);
if (!wps->network_key)
return;
os_memcpy(wps->network_key, conf->ssid.wep.key[0],
conf->ssid.wep.len[0]);
wps->network_key_len = conf->ssid.wep.len[0];
#endif /* CONFIG_WEP */
}
if (conf->ssid.wpa_psk) {
os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
wps->psk_set = 1;
}
hostapd_wps_update_multi_ap(hapd, wps->registrar);
hostapd_wps_set_vendor_ext(hapd, wps);
hostapd_wps_set_application_ext(hapd, wps);
if (conf->wps_state)
wps_registrar_update_ie(wps->registrar);
else
hostapd_deinit_wps(hapd);
}
struct wps_add_pin_data {
const u8 *addr;
const u8 *uuid;
const u8 *pin;
size_t pin_len;
int timeout;
int added;
};
static int wps_add_pin(struct hostapd_data *hapd, void *ctx)
{
struct wps_add_pin_data *data = ctx;
int ret;
if (hapd->wps == NULL)
return 0;
ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr,
data->uuid, data->pin, data->pin_len,
data->timeout);
if (ret == 0)
data->added++;
return ret;
}
int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
const char *uuid, const char *pin, int timeout)
{
u8 u[UUID_LEN];
struct wps_add_pin_data data;
data.addr = addr;
data.uuid = u;
data.pin = (const u8 *) pin;
data.pin_len = os_strlen(pin);
data.timeout = timeout;
data.added = 0;
if (os_strcmp(uuid, "any") == 0)
data.uuid = NULL;
else {
if (uuid_str2bin(uuid, u))
return -1;
data.uuid = u;
}
if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0)
return -1;
return data.added ? 0 : -1;
}
struct wps_button_pushed_ctx {
const u8 *p2p_dev_addr;
unsigned int count;
};
static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
{
struct wps_button_pushed_ctx *data = ctx;
if (hapd->wps) {
data->count++;
return wps_registrar_button_pushed(hapd->wps->registrar,
data->p2p_dev_addr);
}
return 0;
}
int hostapd_wps_button_pushed(struct hostapd_data *hapd,
const u8 *p2p_dev_addr)
{
struct wps_button_pushed_ctx ctx;
int ret;
os_memset(&ctx, 0, sizeof(ctx));
ctx.p2p_dev_addr = p2p_dev_addr;
ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx);
if (ret == 0 && !ctx.count)
ret = -1;
return ret;
}
struct wps_cancel_ctx {
unsigned int count;
};
static int wps_cancel(struct hostapd_data *hapd, void *ctx)
{
struct wps_cancel_ctx *data = ctx;
if (hapd->wps) {
data->count++;
wps_registrar_wps_cancel(hapd->wps->registrar);
ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_CANCEL);
}
return 0;
}
int hostapd_wps_cancel(struct hostapd_data *hapd)
{
struct wps_cancel_ctx ctx;
int ret;
os_memset(&ctx, 0, sizeof(ctx));
ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx);
if (ret == 0 && !ctx.count)
ret = -1;
return ret;
}
static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
const u8 *bssid,
const u8 *ie, size_t ie_len,
int ssi_signal)
{
struct hostapd_data *hapd = ctx;
struct wpabuf *wps_ie;
struct ieee802_11_elems elems;
if (hapd->wps == NULL)
return 0;
if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from "
MACSTR, MAC2STR(addr));
return 0;
}
if (elems.ssid && elems.ssid_len > 0 &&
(elems.ssid_len != hapd->conf->ssid.ssid_len ||
os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) !=
0))
return 0; /* Not for us */
wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
if (wps_ie == NULL)
return 0;
if (wps_validate_probe_req(wps_ie, addr) < 0) {
wpabuf_free(wps_ie);
return 0;
}
if (wpabuf_len(wps_ie) > 0) {
int p2p_wildcard = 0;
#ifdef CONFIG_P2P
if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
P2P_WILDCARD_SSID_LEN) == 0)
p2p_wildcard = 1;
#endif /* CONFIG_P2P */
wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
p2p_wildcard);
#ifdef CONFIG_WPS_UPNP
/* FIX: what exactly should be included in the WLANEvent?
* WPS attributes? Full ProbeReq frame? */
if (!p2p_wildcard)
upnp_wps_device_send_wlan_event(
hapd->wps_upnp, addr,
UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
#endif /* CONFIG_WPS_UPNP */
}
wpabuf_free(wps_ie);
return 0;
}
#ifdef CONFIG_WPS_UPNP
static int hostapd_rx_req_put_wlan_response(
void *priv, enum upnp_wps_wlanevent_type ev_type,
const u8 *mac_addr, const struct wpabuf *msg,
enum wps_msg_type msg_type)
{
struct hostapd_data *hapd = priv;
struct sta_info *sta;
struct upnp_pending_message *p;
wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr="
MACSTR, ev_type, MAC2STR(mac_addr));
wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage",
wpabuf_head(msg), wpabuf_len(msg));
if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected "
"PutWLANResponse WLANEventType %d", ev_type);
return -1;
}
/*
* EAP response to ongoing to WPS Registration. Send it to EAP-WSC
* server implementation for delivery to the peer.
*/
sta = ap_get_sta(hapd, mac_addr);
#ifndef CONFIG_WPS_STRICT
if (!sta) {
/*
* Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
* Pick STA that is in an ongoing WPS registration without
* checking the MAC address.
*/
wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based "
"on NewWLANEventMAC; try wildcard match");
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS))
break;
}
}
#endif /* CONFIG_WPS_STRICT */
if (!sta || !(sta->flags & WLAN_STA_WPS)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
return 0;
}
if (!sta->eapol_sm) {
/*
* This can happen, e.g., if an ER sends an extra message after
* the station has disassociated (but not fully
* deauthenticated).
*/
wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized");
return 0;
}
p = os_zalloc(sizeof(*p));
if (p == NULL)
return -1;
os_memcpy(p->addr, sta->addr, ETH_ALEN);
p->msg = wpabuf_dup(msg);
p->type = msg_type;
p->next = hapd->wps->upnp_msgs;
hapd->wps->upnp_msgs = p;
return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap);
}
static int hostapd_wps_upnp_init(struct hostapd_data *hapd,
struct wps_context *wps)
{
struct upnp_wps_device_ctx *ctx;
if (!hapd->conf->upnp_iface)
return 0;
ctx = os_zalloc(sizeof(*ctx));
if (ctx == NULL)
return -1;
ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response;
if (hapd->conf->ap_pin)
ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd,
hapd->conf->upnp_iface);
if (hapd->wps_upnp == NULL)
return -1;
wps->wps_upnp = hapd->wps_upnp;
return 0;
}
static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd)
{
upnp_wps_device_deinit(hapd->wps_upnp, hapd);
}
#endif /* CONFIG_WPS_UPNP */
int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
char *buf, size_t buflen)
{
if (hapd->wps == NULL)
return 0;
return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen);
}
static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
{
struct hostapd_data *hapd = eloop_data;
wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
hostapd_wps_ap_pin_disable(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED);
}
static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
{
wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
hapd->ap_pin_failures = 0;
hapd->ap_pin_failures_consecutive = 0;
hapd->conf->ap_setup_locked = 0;
if (hapd->wps->ap_setup_locked) {
wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
hapd->wps->ap_setup_locked = 0;
wps_registrar_update_ie(hapd->wps->registrar);
}
eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
if (timeout > 0)
eloop_register_timeout(timeout, 0,
hostapd_wps_ap_pin_timeout, hapd, NULL);
}
static int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx)
{
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = NULL;
#ifdef CONFIG_WPS_UPNP
upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
#endif /* CONFIG_WPS_UPNP */
eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
return 0;
}
void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
{
wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL);
}
struct wps_ap_pin_data {
char pin_txt[9];
int timeout;
};
static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
{
struct wps_ap_pin_data *data = ctx;
if (!hapd->wps)
return 0;
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = os_strdup(data->pin_txt);
#ifdef CONFIG_WPS_UPNP
upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt);
#endif /* CONFIG_WPS_UPNP */
hostapd_wps_ap_pin_enable(hapd, data->timeout);
return 0;
}
const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
{
unsigned int pin;
struct wps_ap_pin_data data;
if (wps_generate_pin(&pin) < 0)
return NULL;
os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
data.timeout = timeout;
hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
return hapd->conf->ap_pin;
}
const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
{
return hapd->conf->ap_pin;
}
int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
int timeout)
{
struct wps_ap_pin_data data;
int ret;
ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
if (os_snprintf_error(sizeof(data.pin_txt), ret))
return -1;
data.timeout = timeout;
return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
}
static int wps_update_ie(struct hostapd_data *hapd, void *ctx)
{
if (hapd->wps)
wps_registrar_update_ie(hapd->wps->registrar);
return 0;
}
void hostapd_wps_update_ie(struct hostapd_data *hapd)
{
hostapd_wps_for_each(hapd, wps_update_ie, NULL);
}
int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
const char *auth, const char *encr, const char *key)
{
struct wps_credential cred;
size_t len;
os_memset(&cred, 0, sizeof(cred));
len = os_strlen(ssid);
if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
hexstr2bin(ssid, cred.ssid, len / 2))
return -1;
cred.ssid_len = len / 2;
if (os_strncmp(auth, "OPEN", 4) == 0)
cred.auth_type = WPS_AUTH_OPEN;
#ifndef CONFIG_NO_TKIP
else if (os_strncmp(auth, "WPAPSK", 6) == 0)
cred.auth_type = WPS_AUTH_WPAPSK;
#endif /* CONFIG_NO_TKIP */
else if (os_strncmp(auth, "WPA2PSK", 7) == 0)
cred.auth_type = WPS_AUTH_WPA2PSK;
else
return -1;
if (encr) {
if (os_strncmp(encr, "NONE", 4) == 0)
cred.encr_type = WPS_ENCR_NONE;
#ifndef CONFIG_NO_TKIP
else if (os_strncmp(encr, "TKIP", 4) == 0)
cred.encr_type = WPS_ENCR_TKIP;
#endif /* CONFIG_NO_TKIP */
else if (os_strncmp(encr, "CCMP", 4) == 0)
cred.encr_type = WPS_ENCR_AES;
else
return -1;
} else
cred.encr_type = WPS_ENCR_NONE;
if (key) {
len = os_strlen(key);
if ((len & 1) || len > 2 * sizeof(cred.key) ||
hexstr2bin(key, cred.key, len / 2))
return -1;
cred.key_len = len / 2;
}
+ if (!hapd->wps) {
+ wpa_printf(MSG_ERROR, "WPS: WPS config does not exist");
+ return -1;
+ }
+
return wps_registrar_config_ap(hapd->wps->registrar, &cred);
}
#ifdef CONFIG_WPS_NFC
struct wps_nfc_password_token_data {
const u8 *oob_dev_pw;
size_t oob_dev_pw_len;
int added;
};
static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
{
struct wps_nfc_password_token_data *data = ctx;
int ret;
if (hapd->wps == NULL)
return 0;
ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
data->oob_dev_pw,
data->oob_dev_pw_len);
if (ret == 0)
data->added++;
return ret;
}
static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
struct wps_parse_attr *attr)
{
struct wps_nfc_password_token_data data;
data.oob_dev_pw = attr->oob_dev_password;
data.oob_dev_pw_len = attr->oob_dev_password_len;
data.added = 0;
if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
return -1;
return data.added ? 0 : -1;
}
static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
const struct wpabuf *wps)
{
struct wps_parse_attr attr;
wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
if (wps_parse_msg(wps, &attr)) {
wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
return -1;
}
if (attr.oob_dev_password)
return hostapd_wps_add_nfc_password_token(hapd, &attr);
wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
return -1;
}
int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
const struct wpabuf *data)
{
const struct wpabuf *wps = data;
struct wpabuf *tmp = NULL;
int ret;
if (wpabuf_len(data) < 4)
return -1;
if (*wpabuf_head_u8(data) != 0x10) {
/* Assume this contains full NDEF record */
tmp = ndef_parse_wifi(data);
if (tmp == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
return -1;
}
wps = tmp;
}
ret = hostapd_wps_nfc_tag_process(hapd, wps);
wpabuf_free(tmp);
return ret;
}
struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
int ndef)
{
struct wpabuf *ret;
if (hapd->wps == NULL)
return NULL;
ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd),
hapd->iconf->channel);
if (ndef && ret) {
struct wpabuf *tmp;
tmp = ndef_build_wifi(ret);
wpabuf_free(ret);
if (tmp == NULL)
return NULL;
ret = tmp;
}
return ret;
}
struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
{
struct wpabuf *ret;
if (hapd->wps == NULL)
return NULL;
if (hapd->conf->wps_nfc_dh_pubkey == NULL) {
struct wps_context *wps = hapd->wps;
if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey,
&hapd->conf->wps_nfc_dh_privkey) < 0)
return NULL;
hostapd_wps_nfc_clear(wps);
wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
wps->ap_nfc_dh_pubkey =
wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
wps->ap_nfc_dh_privkey =
wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
hostapd_wps_nfc_clear(wps);
return NULL;
}
}
ret = wps_build_nfc_handover_sel(hapd->wps,
hapd->conf->wps_nfc_dh_pubkey,
hapd->own_addr, hapd->iface->freq);
if (ndef && ret) {
struct wpabuf *tmp;
tmp = ndef_build_wifi(ret);
wpabuf_free(ret);
if (tmp == NULL)
return NULL;
ret = tmp;
}
return ret;
}
int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
const struct wpabuf *req,
const struct wpabuf *sel)
{
struct wpabuf *wps;
int ret = -1;
u16 wsc_len;
const u8 *pos;
struct wpabuf msg;
struct wps_parse_attr attr;
u16 dev_pw_id;
/*
* Enrollee/station is always initiator of the NFC connection handover,
* so use the request message here to find Enrollee public key hash.
*/
wps = ndef_parse_wifi(req);
if (wps == NULL)
return -1;
wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
"payload from NFC connection handover");
wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
if (wpabuf_len(wps) < 2) {
wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
"Message");
goto out;
}
pos = wpabuf_head(wps);
wsc_len = WPA_GET_BE16(pos);
if (wsc_len > wpabuf_len(wps) - 2) {
wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
"in rt Wi-Fi Handover Request Message", wsc_len);
goto out;
}
pos += 2;
wpa_hexdump(MSG_DEBUG,
"WPS: WSC attributes in Wi-Fi Handover Request Message",
pos, wsc_len);
if (wsc_len < wpabuf_len(wps) - 2) {
wpa_hexdump(MSG_DEBUG,
"WPS: Ignore extra data after WSC attributes",
pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
}
wpabuf_set(&msg, pos, wsc_len);
ret = wps_parse_msg(&msg, &attr);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
"Wi-Fi Handover Request Message");
goto out;
}
if (attr.oob_dev_password == NULL ||
attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
"included in Wi-Fi Handover Request Message");
ret = -1;
goto out;
}
if (attr.uuid_e == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
"Handover Request Message");
ret = -1;
goto out;
}
wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
attr.oob_dev_password, attr.oob_dev_password_len);
dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
WPS_OOB_PUBKEY_HASH_LEN);
if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
"%u in Wi-Fi Handover Request Message", dev_pw_id);
ret = -1;
goto out;
}
wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar,
attr.oob_dev_password,
DEV_PW_NFC_CONNECTION_HANDOVER,
NULL, 0, 1);
out:
wpabuf_free(wps);
return ret;
}
struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
{
if (hapd->conf->wps_nfc_pw_from_config) {
return wps_nfc_token_build(ndef,
hapd->conf->wps_nfc_dev_pw_id,
hapd->conf->wps_nfc_dh_pubkey,
hapd->conf->wps_nfc_dev_pw);
}
return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
&hapd->conf->wps_nfc_dh_pubkey,
&hapd->conf->wps_nfc_dh_privkey,
&hapd->conf->wps_nfc_dev_pw);
}
int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
{
struct wps_context *wps = hapd->wps;
struct wpabuf *pw;
if (wps == NULL)
return -1;
if (!hapd->conf->wps_nfc_dh_pubkey ||
!hapd->conf->wps_nfc_dh_privkey ||
!hapd->conf->wps_nfc_dev_pw ||
!hapd->conf->wps_nfc_dev_pw_id)
return -1;
hostapd_wps_nfc_clear(wps);
wpa_printf(MSG_DEBUG,
"WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)",
hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps);
wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
pw = hapd->conf->wps_nfc_dev_pw;
wps->ap_nfc_dev_pw = wpabuf_alloc(
wpabuf_len(pw) * 2 + 1);
if (wps->ap_nfc_dev_pw) {
wpa_snprintf_hex_uppercase(
(char *) wpabuf_put(wps->ap_nfc_dev_pw,
wpabuf_len(pw) * 2),
wpabuf_len(pw) * 2 + 1,
wpabuf_head(pw), wpabuf_len(pw));
}
if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
!wps->ap_nfc_dev_pw) {
hostapd_wps_nfc_clear(wps);
return -1;
}
return 0;
}
void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
{
wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s",
hapd->conf->iface);
hostapd_wps_nfc_clear(hapd->wps);
}
#endif /* CONFIG_WPS_NFC */
diff --git a/contrib/wpa/src/common/dpp.c b/contrib/wpa/src/common/dpp.c
index 3c8c7682d514..1fd074f05627 100644
--- a/contrib/wpa/src/common/dpp.c
+++ b/contrib/wpa/src/common/dpp.c
@@ -1,4425 +1,4382 @@
/*
* DPP functionality shared between hostapd and wpa_supplicant
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
-#include <openssl/opensslv.h>
-#include <openssl/err.h>
#include "utils/common.h"
#include "utils/base64.h"
#include "utils/json.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "common/gas.h"
#include "eap_common/eap_defs.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
#include "drivers/driver.h"
#include "dpp.h"
#include "dpp_i.h"
static const char * dpp_netrole_str(enum dpp_netrole netrole);
#ifdef CONFIG_TESTING_OPTIONS
#ifdef CONFIG_DPP2
int dpp_version_override = 2;
#else
int dpp_version_override = 1;
#endif
enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
#endif /* CONFIG_TESTING_OPTIONS */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
- (defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20700000L)
-/* Compatibility wrappers for older versions. */
-
-#ifdef CONFIG_DPP2
-static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
-{
- if (pkey->type != EVP_PKEY_EC)
- return NULL;
- return pkey->pkey.ec;
-}
-#endif /* CONFIG_DPP2 */
-
-#endif
-
void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
{
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
}
struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
size_t len)
{
struct wpabuf *msg;
msg = wpabuf_alloc(8 + len);
if (!msg)
return NULL;
wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC);
wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
wpabuf_put_be24(msg, OUI_WFA);
wpabuf_put_u8(msg, DPP_OUI_TYPE);
wpabuf_put_u8(msg, 1); /* Crypto Suite */
wpabuf_put_u8(msg, type);
return msg;
}
const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len)
{
u16 id, alen;
const u8 *pos = buf, *end = buf + len;
while (end - pos >= 4) {
id = WPA_GET_LE16(pos);
pos += 2;
alen = WPA_GET_LE16(pos);
pos += 2;
if (alen > end - pos)
return NULL;
if (id == req_id) {
*ret_len = alen;
return pos;
}
pos += alen;
}
return NULL;
}
static const u8 * dpp_get_attr_next(const u8 *prev, const u8 *buf, size_t len,
u16 req_id, u16 *ret_len)
{
u16 id, alen;
const u8 *pos, *end = buf + len;
if (!prev)
pos = buf;
else
pos = prev + WPA_GET_LE16(prev - 2);
while (end - pos >= 4) {
id = WPA_GET_LE16(pos);
pos += 2;
alen = WPA_GET_LE16(pos);
pos += 2;
if (alen > end - pos)
return NULL;
if (id == req_id) {
*ret_len = alen;
return pos;
}
pos += alen;
}
return NULL;
}
int dpp_check_attrs(const u8 *buf, size_t len)
{
const u8 *pos, *end;
int wrapped_data = 0;
pos = buf;
end = buf + len;
while (end - pos >= 4) {
u16 id, alen;
id = WPA_GET_LE16(pos);
pos += 2;
alen = WPA_GET_LE16(pos);
pos += 2;
wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u",
id, alen);
if (alen > end - pos) {
wpa_printf(MSG_DEBUG,
"DPP: Truncated message - not enough room for the attribute - dropped");
return -1;
}
if (wrapped_data) {
wpa_printf(MSG_DEBUG,
"DPP: An unexpected attribute included after the Wrapped Data attribute");
return -1;
}
if (id == DPP_ATTR_WRAPPED_DATA)
wrapped_data = 1;
pos += alen;
}
if (end != pos) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected octets (%d) after the last attribute",
(int) (end - pos));
return -1;
}
return 0;
}
void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info)
{
if (!info)
return;
os_free(info->uri);
os_free(info->info);
os_free(info->chan);
os_free(info->pk);
- EVP_PKEY_free(info->pubkey);
+ crypto_ec_key_deinit(info->pubkey);
str_clear_free(info->configurator_params);
os_free(info);
}
const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type)
{
switch (type) {
case DPP_BOOTSTRAP_QR_CODE:
return "QRCODE";
case DPP_BOOTSTRAP_PKEX:
return "PKEX";
case DPP_BOOTSTRAP_NFC_URI:
return "NFC-URI";
}
return "??";
}
static int dpp_uri_valid_info(const char *info)
{
while (*info) {
unsigned char val = *info++;
if (val < 0x20 || val > 0x7e || val == 0x3b)
return 0;
}
return 1;
}
static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri)
{
bi->uri = os_strdup(uri);
return bi->uri ? 0 : -1;
}
int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
const char *chan_list)
{
const char *pos = chan_list, *pos2;
int opclass = -1, channel, freq;
while (pos && *pos && *pos != ';') {
pos2 = pos;
while (*pos2 >= '0' && *pos2 <= '9')
pos2++;
if (*pos2 == '/') {
opclass = atoi(pos);
pos = pos2 + 1;
}
if (opclass <= 0)
goto fail;
channel = atoi(pos);
if (channel <= 0)
goto fail;
while (*pos >= '0' && *pos <= '9')
pos++;
freq = ieee80211_chan_to_freq(NULL, opclass, channel);
wpa_printf(MSG_DEBUG,
"DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
opclass, channel, freq);
bi->channels_listed = true;
if (freq < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
opclass, channel);
} else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
wpa_printf(MSG_DEBUG,
"DPP: Too many channels in URI channel-list - ignore list");
bi->num_freq = 0;
break;
} else {
bi->freq[bi->num_freq++] = freq;
}
if (*pos == ';' || *pos == '\0')
break;
if (*pos != ',')
goto fail;
pos++;
}
return 0;
fail:
wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list");
return -1;
}
int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac)
{
if (!mac)
return 0;
if (hwaddr_aton2(mac, bi->mac_addr) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac");
return -1;
}
wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr));
return 0;
}
int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info)
{
const char *end;
if (!info)
return 0;
end = os_strchr(info, ';');
if (!end)
end = info + os_strlen(info);
bi->info = os_malloc(end - info + 1);
if (!bi->info)
return -1;
os_memcpy(bi->info, info, end - info);
bi->info[end - info] = '\0';
wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info);
if (!dpp_uri_valid_info(bi->info)) {
wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload");
return -1;
}
return 0;
}
int dpp_parse_uri_version(struct dpp_bootstrap_info *bi, const char *version)
{
#ifdef CONFIG_DPP2
if (!version || DPP_VERSION < 2)
return 0;
if (*version == '1')
bi->version = 1;
else if (*version == '2')
bi->version = 2;
else
wpa_printf(MSG_DEBUG, "DPP: Unknown URI version");
wpa_printf(MSG_DEBUG, "DPP: URI version: %d", bi->version);
#endif /* CONFIG_DPP2 */
return 0;
}
static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
{
u8 *data;
size_t data_len;
int res;
const char *end;
end = os_strchr(info, ';');
if (!end)
return -1;
data = base64_decode(info, end - info, &data_len);
if (!data) {
wpa_printf(MSG_DEBUG,
"DPP: Invalid base64 encoding on URI public-key");
return -1;
}
wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
data, data_len);
res = dpp_get_subject_public_key(bi, data, data_len);
os_free(data);
return res;
}
static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri)
{
const char *pos = uri;
const char *end;
const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
const char *version = NULL;
struct dpp_bootstrap_info *bi;
wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
if (os_strncmp(pos, "DPP:", 4) != 0) {
wpa_printf(MSG_INFO, "DPP: Not a DPP URI");
return NULL;
}
pos += 4;
for (;;) {
end = os_strchr(pos, ';');
if (!end)
break;
if (end == pos) {
/* Handle terminating ";;" and ignore unexpected ";"
* for parsing robustness. */
pos++;
continue;
}
if (pos[0] == 'C' && pos[1] == ':' && !chan_list)
chan_list = pos + 2;
else if (pos[0] == 'M' && pos[1] == ':' && !mac)
mac = pos + 2;
else if (pos[0] == 'I' && pos[1] == ':' && !info)
info = pos + 2;
else if (pos[0] == 'K' && pos[1] == ':' && !pk)
pk = pos + 2;
else if (pos[0] == 'V' && pos[1] == ':' && !version)
version = pos + 2;
else
wpa_hexdump_ascii(MSG_DEBUG,
"DPP: Ignore unrecognized URI parameter",
pos, end - pos);
pos = end + 1;
}
if (!pk) {
wpa_printf(MSG_INFO, "DPP: URI missing public-key");
return NULL;
}
bi = os_zalloc(sizeof(*bi));
if (!bi)
return NULL;
if (dpp_clone_uri(bi, uri) < 0 ||
dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
dpp_parse_uri_mac(bi, mac) < 0 ||
dpp_parse_uri_info(bi, info) < 0 ||
dpp_parse_uri_version(bi, version) < 0 ||
dpp_parse_uri_pk(bi, pk) < 0) {
dpp_bootstrap_info_free(bi);
bi = NULL;
}
return bi;
}
void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status)
{
wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
wpabuf_put_le16(msg, DPP_ATTR_STATUS);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, status);
}
void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash)
{
if (hash) {
wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
wpabuf_put_le16(msg, SHA256_MAC_LEN);
wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
}
}
static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
u16 num_modes, unsigned int freq)
{
u16 m;
int c, flag;
if (!own_modes || !num_modes)
return 1;
for (m = 0; m < num_modes; m++) {
for (c = 0; c < own_modes[m].num_channels; c++) {
if ((unsigned int) own_modes[m].channels[c].freq !=
freq)
continue;
flag = own_modes[m].channels[c].flag;
if (!(flag & (HOSTAPD_CHAN_DISABLED |
HOSTAPD_CHAN_NO_IR |
HOSTAPD_CHAN_RADAR)))
return 1;
}
}
wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq);
return 0;
}
static int freq_included(const unsigned int freqs[], unsigned int num,
unsigned int freq)
{
while (num > 0) {
if (freqs[--num] == freq)
return 1;
}
return 0;
}
static void freq_to_start(unsigned int freqs[], unsigned int num,
unsigned int freq)
{
unsigned int i;
for (i = 0; i < num; i++) {
if (freqs[i] == freq)
break;
}
if (i == 0 || i >= num)
return;
os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0]));
freqs[0] = freq;
}
static int dpp_channel_intersect(struct dpp_authentication *auth,
struct hostapd_hw_modes *own_modes,
u16 num_modes)
{
struct dpp_bootstrap_info *peer_bi = auth->peer_bi;
unsigned int i, freq;
for (i = 0; i < peer_bi->num_freq; i++) {
freq = peer_bi->freq[i];
if (freq_included(auth->freq, auth->num_freq, freq))
continue;
if (dpp_channel_ok_init(own_modes, num_modes, freq))
auth->freq[auth->num_freq++] = freq;
}
if (!auth->num_freq) {
wpa_printf(MSG_INFO,
"DPP: No available channels for initiating DPP Authentication");
return -1;
}
auth->curr_freq = auth->freq[0];
return 0;
}
static int dpp_channel_local_list(struct dpp_authentication *auth,
struct hostapd_hw_modes *own_modes,
u16 num_modes)
{
u16 m;
int c, flag;
unsigned int freq;
auth->num_freq = 0;
if (!own_modes || !num_modes) {
auth->freq[0] = 2412;
auth->freq[1] = 2437;
auth->freq[2] = 2462;
auth->num_freq = 3;
return 0;
}
for (m = 0; m < num_modes; m++) {
for (c = 0; c < own_modes[m].num_channels; c++) {
freq = own_modes[m].channels[c].freq;
flag = own_modes[m].channels[c].flag;
if (flag & (HOSTAPD_CHAN_DISABLED |
HOSTAPD_CHAN_NO_IR |
HOSTAPD_CHAN_RADAR))
continue;
if (freq_included(auth->freq, auth->num_freq, freq))
continue;
auth->freq[auth->num_freq++] = freq;
if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
m = num_modes;
break;
}
}
}
return auth->num_freq == 0 ? -1 : 0;
}
int dpp_prepare_channel_list(struct dpp_authentication *auth,
unsigned int neg_freq,
struct hostapd_hw_modes *own_modes, u16 num_modes)
{
int res;
char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
unsigned int i;
if (!own_modes) {
if (!neg_freq)
return -1;
auth->num_freq = 1;
auth->freq[0] = neg_freq;
auth->curr_freq = neg_freq;
return 0;
}
if (auth->peer_bi->num_freq > 0)
res = dpp_channel_intersect(auth, own_modes, num_modes);
else
res = dpp_channel_local_list(auth, own_modes, num_modes);
if (res < 0)
return res;
/* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most
* likely channels first. */
freq_to_start(auth->freq, auth->num_freq, 2462);
freq_to_start(auth->freq, auth->num_freq, 2412);
freq_to_start(auth->freq, auth->num_freq, 2437);
auth->freq_idx = 0;
auth->curr_freq = auth->freq[0];
pos = freqs;
end = pos + sizeof(freqs);
for (i = 0; i < auth->num_freq; i++) {
res = os_snprintf(pos, end - pos, " %u", auth->freq[i]);
if (os_snprintf_error(end - pos, res))
break;
pos += res;
}
*pos = '\0';
wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s",
freqs);
return 0;
}
int dpp_gen_uri(struct dpp_bootstrap_info *bi)
{
char macstr[ETH_ALEN * 2 + 10];
size_t len;
len = 4; /* "DPP:" */
if (bi->chan)
len += 3 + os_strlen(bi->chan); /* C:...; */
if (is_zero_ether_addr(bi->mac_addr))
macstr[0] = '\0';
else
os_snprintf(macstr, sizeof(macstr), "M:" COMPACT_MACSTR ";",
MAC2STR(bi->mac_addr));
len += os_strlen(macstr); /* M:...; */
if (bi->info)
len += 3 + os_strlen(bi->info); /* I:...; */
#ifdef CONFIG_DPP2
len += 4; /* V:2; */
#endif /* CONFIG_DPP2 */
len += 4 + os_strlen(bi->pk); /* K:...;; */
os_free(bi->uri);
bi->uri = os_malloc(len + 1);
if (!bi->uri)
return -1;
os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%sK:%s;;",
bi->chan ? "C:" : "", bi->chan ? bi->chan : "",
bi->chan ? ";" : "",
macstr,
bi->info ? "I:" : "", bi->info ? bi->info : "",
bi->info ? ";" : "",
DPP_VERSION == 2 ? "V:2;" : "",
bi->pk);
return 0;
}
struct dpp_authentication *
dpp_alloc_auth(struct dpp_global *dpp, void *msg_ctx)
{
struct dpp_authentication *auth;
auth = os_zalloc(sizeof(*auth));
if (!auth)
return NULL;
auth->global = dpp;
auth->msg_ctx = msg_ctx;
auth->conf_resp_status = 255;
return auth;
}
static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth,
const char *json)
{
size_t nonce_len;
size_t json_len, clear_len;
struct wpabuf *clear = NULL, *msg = NULL;
u8 *wrapped;
size_t attr_len;
wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
nonce_len = auth->curve->nonce_len;
if (random_get_bytes(auth->e_nonce, nonce_len)) {
wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
json_len = os_strlen(json);
wpa_hexdump_ascii(MSG_DEBUG, "DPP: configRequest JSON", json, json_len);
/* { E-nonce, configAttrib }ke */
clear_len = 4 + nonce_len + 4 + json_len;
clear = wpabuf_alloc(clear_len);
attr_len = 4 + clear_len + AES_BLOCK_SIZE;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ)
attr_len += 5;
#endif /* CONFIG_TESTING_OPTIONS */
msg = wpabuf_alloc(attr_len);
if (!clear || !msg)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
goto skip_e_nonce;
}
if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce");
wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
wpabuf_put_le16(clear, nonce_len - 1);
wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1);
goto skip_e_nonce;
}
if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
goto skip_wrapped_data;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* E-nonce */
wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
wpabuf_put_le16(clear, nonce_len);
wpabuf_put_data(clear, auth->e_nonce, nonce_len);
#ifdef CONFIG_TESTING_OPTIONS
skip_e_nonce:
if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib");
goto skip_conf_attr_obj;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* configAttrib */
wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
wpabuf_put_le16(clear, json_len);
wpabuf_put_data(clear, json, json_len);
#ifdef CONFIG_TESTING_OPTIONS
skip_conf_attr_obj:
#endif /* CONFIG_TESTING_OPTIONS */
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
/* No AES-SIV AD */
wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
wpabuf_head(clear), wpabuf_len(clear),
0, NULL, NULL, wrapped) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
dpp_build_attr_status(msg, DPP_STATUS_OK);
}
skip_wrapped_data:
#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Configuration Request frame attributes", msg);
wpabuf_free(clear);
return msg;
fail:
wpabuf_free(clear);
wpabuf_free(msg);
return NULL;
}
void dpp_write_adv_proto(struct wpabuf *buf)
{
/* Advertisement Protocol IE */
wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
wpabuf_put_u8(buf, 8); /* Length */
wpabuf_put_u8(buf, 0x7f);
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
wpabuf_put_u8(buf, 5);
wpabuf_put_be24(buf, OUI_WFA);
wpabuf_put_u8(buf, DPP_OUI_TYPE);
wpabuf_put_u8(buf, 0x01);
}
void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
{
/* GAS Query */
wpabuf_put_le16(buf, wpabuf_len(query));
wpabuf_put_buf(buf, query);
}
struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
const char *json)
{
struct wpabuf *buf, *conf_req;
conf_req = dpp_build_conf_req_attr(auth, json);
if (!conf_req) {
wpa_printf(MSG_DEBUG,
"DPP: No configuration request data available");
return NULL;
}
buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req));
if (!buf) {
wpabuf_free(conf_req);
return NULL;
}
dpp_write_adv_proto(buf);
dpp_write_gas_query(buf, conf_req);
wpabuf_free(conf_req);
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf);
return buf;
}
struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
const char *name,
enum dpp_netrole netrole,
const char *mud_url, int *opclasses)
{
size_t len, name_len;
const char *tech = "infra";
const char *dpp_name;
struct wpabuf *buf, *json;
char *csr = NULL;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) {
static const char *bogus_tech = "knfra";
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Config Attr");
tech = bogus_tech;
}
#endif /* CONFIG_TESTING_OPTIONS */
dpp_name = name ? name : "Test";
name_len = os_strlen(dpp_name);
len = 100 + name_len * 6 + 1 + int_array_len(opclasses) * 4;
if (mud_url && mud_url[0])
len += 10 + os_strlen(mud_url);
#ifdef CONFIG_DPP2
if (auth->csr) {
size_t csr_len;
csr = base64_encode_no_lf(wpabuf_head(auth->csr),
wpabuf_len(auth->csr), &csr_len);
if (!csr)
return NULL;
len += 30 + csr_len;
}
#endif /* CONFIG_DPP2 */
json = wpabuf_alloc(len);
if (!json)
return NULL;
json_start_object(json, NULL);
if (json_add_string_escape(json, "name", dpp_name, name_len) < 0) {
wpabuf_free(json);
return NULL;
}
json_value_sep(json);
json_add_string(json, "wi-fi_tech", tech);
json_value_sep(json);
json_add_string(json, "netRole", dpp_netrole_str(netrole));
if (mud_url && mud_url[0]) {
json_value_sep(json);
json_add_string(json, "mudurl", mud_url);
}
if (opclasses) {
int i;
json_value_sep(json);
json_start_array(json, "bandSupport");
for (i = 0; opclasses[i]; i++)
wpabuf_printf(json, "%s%u", i ? "," : "", opclasses[i]);
json_end_array(json);
}
if (csr) {
json_value_sep(json);
json_add_string(json, "pkcs10", csr);
}
json_end_object(json);
buf = dpp_build_conf_req(auth, wpabuf_head(json));
wpabuf_free(json);
os_free(csr);
return buf;
}
static int bin_str_eq(const char *val, size_t len, const char *cmp)
{
return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0;
}
struct dpp_configuration * dpp_configuration_alloc(const char *type)
{
struct dpp_configuration *conf;
const char *end;
size_t len;
conf = os_zalloc(sizeof(*conf));
if (!conf)
goto fail;
end = os_strchr(type, ' ');
if (end)
len = end - type;
else
len = os_strlen(type);
if (bin_str_eq(type, len, "psk"))
conf->akm = DPP_AKM_PSK;
else if (bin_str_eq(type, len, "sae"))
conf->akm = DPP_AKM_SAE;
else if (bin_str_eq(type, len, "psk-sae") ||
bin_str_eq(type, len, "psk+sae"))
conf->akm = DPP_AKM_PSK_SAE;
else if (bin_str_eq(type, len, "sae-dpp") ||
bin_str_eq(type, len, "dpp+sae"))
conf->akm = DPP_AKM_SAE_DPP;
else if (bin_str_eq(type, len, "psk-sae-dpp") ||
bin_str_eq(type, len, "dpp+psk+sae"))
conf->akm = DPP_AKM_PSK_SAE_DPP;
else if (bin_str_eq(type, len, "dpp"))
conf->akm = DPP_AKM_DPP;
else if (bin_str_eq(type, len, "dot1x"))
conf->akm = DPP_AKM_DOT1X;
else
goto fail;
return conf;
fail:
dpp_configuration_free(conf);
return NULL;
}
int dpp_akm_psk(enum dpp_akm akm)
{
return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE ||
akm == DPP_AKM_PSK_SAE_DPP;
}
int dpp_akm_sae(enum dpp_akm akm)
{
return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE ||
akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP;
}
int dpp_akm_legacy(enum dpp_akm akm)
{
return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE ||
akm == DPP_AKM_SAE;
}
int dpp_akm_dpp(enum dpp_akm akm)
{
return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP ||
akm == DPP_AKM_PSK_SAE_DPP;
}
int dpp_akm_ver2(enum dpp_akm akm)
{
return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP;
}
int dpp_configuration_valid(const struct dpp_configuration *conf)
{
if (conf->ssid_len == 0)
return 0;
if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set)
return 0;
if (dpp_akm_sae(conf->akm) && !conf->passphrase)
return 0;
return 1;
}
void dpp_configuration_free(struct dpp_configuration *conf)
{
if (!conf)
return;
str_clear_free(conf->passphrase);
os_free(conf->group_id);
os_free(conf->csrattrs);
bin_clear_free(conf, sizeof(*conf));
}
static int dpp_configuration_parse_helper(struct dpp_authentication *auth,
const char *cmd, int idx)
{
const char *pos, *end;
struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
struct dpp_configuration *conf = NULL;
size_t len;
pos = os_strstr(cmd, " conf=sta-");
if (pos) {
conf_sta = dpp_configuration_alloc(pos + 10);
if (!conf_sta)
goto fail;
conf_sta->netrole = DPP_NETROLE_STA;
conf = conf_sta;
}
pos = os_strstr(cmd, " conf=ap-");
if (pos) {
conf_ap = dpp_configuration_alloc(pos + 9);
if (!conf_ap)
goto fail;
conf_ap->netrole = DPP_NETROLE_AP;
conf = conf_ap;
}
pos = os_strstr(cmd, " conf=configurator");
if (pos)
auth->provision_configurator = 1;
if (!conf)
return 0;
pos = os_strstr(cmd, " ssid=");
if (pos) {
pos += 6;
end = os_strchr(pos, ' ');
conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos);
conf->ssid_len /= 2;
if (conf->ssid_len > sizeof(conf->ssid) ||
hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0)
goto fail;
} else {
#ifdef CONFIG_TESTING_OPTIONS
/* use a default SSID for legacy testing reasons */
os_memcpy(conf->ssid, "test", 4);
conf->ssid_len = 4;
#else /* CONFIG_TESTING_OPTIONS */
goto fail;
#endif /* CONFIG_TESTING_OPTIONS */
}
pos = os_strstr(cmd, " ssid_charset=");
if (pos) {
if (conf_ap) {
wpa_printf(MSG_INFO,
"DPP: ssid64 option (ssid_charset param) not allowed for AP enrollee");
goto fail;
}
conf->ssid_charset = atoi(pos + 14);
}
pos = os_strstr(cmd, " pass=");
if (pos) {
size_t pass_len;
pos += 6;
end = os_strchr(pos, ' ');
pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
pass_len /= 2;
if (pass_len > 63 || pass_len < 8)
goto fail;
conf->passphrase = os_zalloc(pass_len + 1);
if (!conf->passphrase ||
hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0)
goto fail;
}
pos = os_strstr(cmd, " psk=");
if (pos) {
pos += 5;
if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0)
goto fail;
conf->psk_set = 1;
}
pos = os_strstr(cmd, " group_id=");
if (pos) {
size_t group_id_len;
pos += 10;
end = os_strchr(pos, ' ');
group_id_len = end ? (size_t) (end - pos) : os_strlen(pos);
conf->group_id = os_malloc(group_id_len + 1);
if (!conf->group_id)
goto fail;
os_memcpy(conf->group_id, pos, group_id_len);
conf->group_id[group_id_len] = '\0';
}
pos = os_strstr(cmd, " expiry=");
if (pos) {
long int val;
pos += 8;
val = strtol(pos, NULL, 0);
if (val <= 0)
goto fail;
conf->netaccesskey_expiry = val;
}
pos = os_strstr(cmd, " csrattrs=");
if (pos) {
pos += 10;
end = os_strchr(pos, ' ');
len = end ? (size_t) (end - pos) : os_strlen(pos);
conf->csrattrs = os_zalloc(len + 1);
if (!conf->csrattrs)
goto fail;
os_memcpy(conf->csrattrs, pos, len);
}
if (!dpp_configuration_valid(conf))
goto fail;
if (idx == 0) {
auth->conf_sta = conf_sta;
auth->conf_ap = conf_ap;
} else if (idx == 1) {
auth->conf2_sta = conf_sta;
auth->conf2_ap = conf_ap;
} else {
goto fail;
}
return 0;
fail:
dpp_configuration_free(conf_sta);
dpp_configuration_free(conf_ap);
return -1;
}
static int dpp_configuration_parse(struct dpp_authentication *auth,
const char *cmd)
{
const char *pos;
char *tmp;
size_t len;
int res;
pos = os_strstr(cmd, " @CONF-OBJ-SEP@ ");
if (!pos)
return dpp_configuration_parse_helper(auth, cmd, 0);
len = pos - cmd;
tmp = os_malloc(len + 1);
if (!tmp)
goto fail;
os_memcpy(tmp, cmd, len);
tmp[len] = '\0';
res = dpp_configuration_parse_helper(auth, cmd, 0);
str_clear_free(tmp);
if (res)
goto fail;
res = dpp_configuration_parse_helper(auth, cmd + len, 1);
if (res)
goto fail;
return 0;
fail:
dpp_configuration_free(auth->conf_sta);
dpp_configuration_free(auth->conf2_sta);
dpp_configuration_free(auth->conf_ap);
dpp_configuration_free(auth->conf2_ap);
return -1;
}
static struct dpp_configurator *
dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id)
{
struct dpp_configurator *conf;
if (!dpp)
return NULL;
dl_list_for_each(conf, &dpp->configurator,
struct dpp_configurator, list) {
if (conf->id == id)
return conf;
}
return NULL;
}
int dpp_set_configurator(struct dpp_authentication *auth, const char *cmd)
{
const char *pos;
char *tmp = NULL;
int ret = -1;
if (!cmd || auth->configurator_set)
return 0;
auth->configurator_set = 1;
if (cmd[0] != ' ') {
size_t len;
len = os_strlen(cmd);
tmp = os_malloc(len + 2);
if (!tmp)
goto fail;
tmp[0] = ' ';
os_memcpy(tmp + 1, cmd, len + 1);
cmd = tmp;
}
wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
pos = os_strstr(cmd, " configurator=");
if (!auth->conf && pos) {
pos += 14;
auth->conf = dpp_configurator_get_id(auth->global, atoi(pos));
if (!auth->conf) {
wpa_printf(MSG_INFO,
"DPP: Could not find the specified configurator");
goto fail;
}
}
pos = os_strstr(cmd, " conn_status=");
if (pos) {
pos += 13;
auth->send_conn_status = atoi(pos);
}
pos = os_strstr(cmd, " akm_use_selector=");
if (pos) {
pos += 18;
auth->akm_use_selector = atoi(pos);
}
if (dpp_configuration_parse(auth, cmd) < 0) {
wpa_msg(auth->msg_ctx, MSG_INFO,
"DPP: Failed to set configurator parameters");
goto fail;
}
ret = 0;
fail:
os_free(tmp);
return ret;
}
void dpp_auth_deinit(struct dpp_authentication *auth)
{
unsigned int i;
if (!auth)
return;
dpp_configuration_free(auth->conf_ap);
dpp_configuration_free(auth->conf2_ap);
dpp_configuration_free(auth->conf_sta);
dpp_configuration_free(auth->conf2_sta);
- EVP_PKEY_free(auth->own_protocol_key);
- EVP_PKEY_free(auth->peer_protocol_key);
- EVP_PKEY_free(auth->reconfig_old_protocol_key);
+ crypto_ec_key_deinit(auth->own_protocol_key);
+ crypto_ec_key_deinit(auth->peer_protocol_key);
+ crypto_ec_key_deinit(auth->reconfig_old_protocol_key);
wpabuf_free(auth->req_msg);
wpabuf_free(auth->resp_msg);
wpabuf_free(auth->conf_req);
wpabuf_free(auth->reconfig_req_msg);
wpabuf_free(auth->reconfig_resp_msg);
for (i = 0; i < auth->num_conf_obj; i++) {
struct dpp_config_obj *conf = &auth->conf_obj[i];
os_free(conf->connector);
wpabuf_free(conf->c_sign_key);
wpabuf_free(conf->certbag);
wpabuf_free(conf->certs);
wpabuf_free(conf->cacert);
os_free(conf->server_name);
wpabuf_free(conf->pp_key);
}
#ifdef CONFIG_DPP2
dpp_free_asymmetric_key(auth->conf_key_pkg);
os_free(auth->csrattrs);
wpabuf_free(auth->csr);
wpabuf_free(auth->priv_key);
wpabuf_free(auth->cacert);
wpabuf_free(auth->certbag);
os_free(auth->trusted_eap_server_name);
wpabuf_free(auth->conf_resp_tcp);
#endif /* CONFIG_DPP2 */
wpabuf_free(auth->net_access_key);
dpp_bootstrap_info_free(auth->tmp_own_bi);
if (auth->tmp_peer_bi) {
dl_list_del(&auth->tmp_peer_bi->list);
dpp_bootstrap_info_free(auth->tmp_peer_bi);
}
#ifdef CONFIG_TESTING_OPTIONS
os_free(auth->config_obj_override);
os_free(auth->discovery_override);
os_free(auth->groups_override);
#endif /* CONFIG_TESTING_OPTIONS */
bin_clear_free(auth, sizeof(*auth));
}
static struct wpabuf *
dpp_build_conf_start(struct dpp_authentication *auth,
struct dpp_configuration *conf, size_t tailroom)
{
struct wpabuf *buf;
#ifdef CONFIG_TESTING_OPTIONS
if (auth->discovery_override)
tailroom += os_strlen(auth->discovery_override);
#endif /* CONFIG_TESTING_OPTIONS */
buf = wpabuf_alloc(200 + tailroom);
if (!buf)
return NULL;
json_start_object(buf, NULL);
json_add_string(buf, "wi-fi_tech", "infra");
json_value_sep(buf);
#ifdef CONFIG_TESTING_OPTIONS
if (auth->discovery_override) {
wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'",
auth->discovery_override);
wpabuf_put_str(buf, "\"discovery\":");
wpabuf_put_str(buf, auth->discovery_override);
json_value_sep(buf);
return buf;
}
#endif /* CONFIG_TESTING_OPTIONS */
json_start_object(buf, "discovery");
if (((!conf->ssid_charset || auth->peer_version < 2) &&
json_add_string_escape(buf, "ssid", conf->ssid,
conf->ssid_len) < 0) ||
((conf->ssid_charset && auth->peer_version >= 2) &&
json_add_base64url(buf, "ssid64", conf->ssid,
conf->ssid_len) < 0)) {
wpabuf_free(buf);
return NULL;
}
if (conf->ssid_charset > 0) {
json_value_sep(buf);
json_add_int(buf, "ssid_charset", conf->ssid_charset);
}
json_end_object(buf);
json_value_sep(buf);
return buf;
}
-int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
- const char *kid, const struct dpp_curve_params *curve)
+int dpp_build_jwk(struct wpabuf *buf, const char *name,
+ struct crypto_ec_key *key, const char *kid,
+ const struct dpp_curve_params *curve)
{
struct wpabuf *pub;
const u8 *pos;
int ret = -1;
- pub = dpp_get_pubkey_point(key, 0);
+ pub = crypto_ec_key_get_pubkey_point(key, 0);
if (!pub)
goto fail;
json_start_object(buf, name);
json_add_string(buf, "kty", "EC");
json_value_sep(buf);
json_add_string(buf, "crv", curve->jwk_crv);
json_value_sep(buf);
pos = wpabuf_head(pub);
if (json_add_base64url(buf, "x", pos, curve->prime_len) < 0)
goto fail;
json_value_sep(buf);
pos += curve->prime_len;
if (json_add_base64url(buf, "y", pos, curve->prime_len) < 0)
goto fail;
if (kid) {
json_value_sep(buf);
json_add_string(buf, "kid", kid);
}
json_end_object(buf);
ret = 0;
fail:
wpabuf_free(pub);
return ret;
}
static void dpp_build_legacy_cred_params(struct wpabuf *buf,
struct dpp_configuration *conf)
{
if (conf->passphrase && os_strlen(conf->passphrase) < 64) {
json_add_string_escape(buf, "pass", conf->passphrase,
os_strlen(conf->passphrase));
} else if (conf->psk_set) {
char psk[2 * sizeof(conf->psk) + 1];
wpa_snprintf_hex(psk, sizeof(psk),
conf->psk, sizeof(conf->psk));
json_add_string(buf, "psk_hex", psk);
forced_memzero(psk, sizeof(psk));
}
}
static const char * dpp_netrole_str(enum dpp_netrole netrole)
{
switch (netrole) {
case DPP_NETROLE_STA:
return "sta";
case DPP_NETROLE_AP:
return "ap";
case DPP_NETROLE_CONFIGURATOR:
return "configurator";
default:
return "??";
}
}
static struct wpabuf *
dpp_build_conf_obj_dpp(struct dpp_authentication *auth,
struct dpp_configuration *conf)
{
struct wpabuf *buf = NULL;
char *signed_conn = NULL;
size_t tailroom;
const struct dpp_curve_params *curve;
struct wpabuf *dppcon = NULL;
size_t extra_len = 1000;
int incl_legacy;
enum dpp_akm akm;
const char *akm_str;
if (!auth->conf) {
wpa_printf(MSG_INFO,
"DPP: No configurator specified - cannot generate DPP config object");
goto fail;
}
curve = auth->conf->curve;
akm = conf->akm;
if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
wpa_printf(MSG_DEBUG,
"DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2");
akm = DPP_AKM_DPP;
}
#ifdef CONFIG_TESTING_OPTIONS
if (auth->groups_override)
extra_len += os_strlen(auth->groups_override);
#endif /* CONFIG_TESTING_OPTIONS */
if (conf->group_id)
extra_len += os_strlen(conf->group_id);
/* Connector (JSON dppCon object) */
dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3);
if (!dppcon)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
if (auth->groups_override) {
wpabuf_put_u8(dppcon, '{');
if (auth->groups_override) {
wpa_printf(MSG_DEBUG,
"DPP: TESTING - groups override: '%s'",
auth->groups_override);
wpabuf_put_str(dppcon, "\"groups\":");
wpabuf_put_str(dppcon, auth->groups_override);
json_value_sep(dppcon);
}
goto skip_groups;
}
#endif /* CONFIG_TESTING_OPTIONS */
json_start_object(dppcon, NULL);
json_start_array(dppcon, "groups");
json_start_object(dppcon, NULL);
json_add_string(dppcon, "groupId",
conf->group_id ? conf->group_id : "*");
json_value_sep(dppcon);
json_add_string(dppcon, "netRole", dpp_netrole_str(conf->netrole));
json_end_object(dppcon);
json_end_array(dppcon);
json_value_sep(dppcon);
#ifdef CONFIG_TESTING_OPTIONS
skip_groups:
#endif /* CONFIG_TESTING_OPTIONS */
if (!auth->peer_protocol_key ||
dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
auth->curve) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
goto fail;
}
if (conf->netaccesskey_expiry) {
struct os_tm tm;
char expiry[30];
if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to generate expiry string");
goto fail;
}
os_snprintf(expiry, sizeof(expiry),
"%04u-%02u-%02uT%02u:%02u:%02uZ",
tm.year, tm.month, tm.day,
tm.hour, tm.min, tm.sec);
json_value_sep(dppcon);
json_add_string(dppcon, "expiry", expiry);
}
json_end_object(dppcon);
wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
(const char *) wpabuf_head(dppcon));
signed_conn = dpp_sign_connector(auth->conf, dppcon);
if (!signed_conn)
goto fail;
incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm);
tailroom = 1000;
tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
tailroom += os_strlen(signed_conn);
if (incl_legacy)
tailroom += 1000;
if (akm == DPP_AKM_DOT1X) {
if (auth->certbag)
tailroom += 2 * wpabuf_len(auth->certbag);
if (auth->cacert)
tailroom += 2 * wpabuf_len(auth->cacert);
if (auth->trusted_eap_server_name)
tailroom += os_strlen(auth->trusted_eap_server_name);
tailroom += 1000;
}
buf = dpp_build_conf_start(auth, conf, tailroom);
if (!buf)
goto fail;
if (auth->akm_use_selector && dpp_akm_ver2(akm))
akm_str = dpp_akm_selector_str(akm);
else
akm_str = dpp_akm_str(akm);
json_start_object(buf, "cred");
json_add_string(buf, "akm", akm_str);
json_value_sep(buf);
if (incl_legacy) {
dpp_build_legacy_cred_params(buf, conf);
json_value_sep(buf);
}
if (akm == DPP_AKM_DOT1X) {
json_start_object(buf, "entCreds");
if (!auth->certbag)
goto fail;
json_add_base64(buf, "certBag", wpabuf_head(auth->certbag),
wpabuf_len(auth->certbag));
if (auth->cacert) {
json_value_sep(buf);
json_add_base64(buf, "caCert",
wpabuf_head(auth->cacert),
wpabuf_len(auth->cacert));
}
if (auth->trusted_eap_server_name) {
json_value_sep(buf);
json_add_string(buf, "trustedEapServerName",
auth->trusted_eap_server_name);
}
json_value_sep(buf);
json_start_array(buf, "eapMethods");
wpabuf_printf(buf, "%d", EAP_TYPE_TLS);
json_end_array(buf);
json_end_object(buf);
json_value_sep(buf);
}
wpabuf_put_str(buf, "\"signedConnector\":\"");
wpabuf_put_str(buf, signed_conn);
wpabuf_put_str(buf, "\"");
json_value_sep(buf);
if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid,
curve) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK");
goto fail;
}
#ifdef CONFIG_DPP2
if (auth->peer_version >= 2 && auth->conf->pp_key) {
json_value_sep(buf);
if (dpp_build_jwk(buf, "ppKey", auth->conf->pp_key, NULL,
curve) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to build ppKey JWK");
goto fail;
}
}
#endif /* CONFIG_DPP2 */
json_end_object(buf);
json_end_object(buf);
wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
wpabuf_head(buf), wpabuf_len(buf));
out:
os_free(signed_conn);
wpabuf_free(dppcon);
return buf;
fail:
wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object");
wpabuf_free(buf);
buf = NULL;
goto out;
}
static struct wpabuf *
dpp_build_conf_obj_legacy(struct dpp_authentication *auth,
struct dpp_configuration *conf)
{
struct wpabuf *buf;
const char *akm_str;
buf = dpp_build_conf_start(auth, conf, 1000);
if (!buf)
return NULL;
if (auth->akm_use_selector && dpp_akm_ver2(conf->akm))
akm_str = dpp_akm_selector_str(conf->akm);
else
akm_str = dpp_akm_str(conf->akm);
json_start_object(buf, "cred");
json_add_string(buf, "akm", akm_str);
json_value_sep(buf);
dpp_build_legacy_cred_params(buf, conf);
json_end_object(buf);
json_end_object(buf);
wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
wpabuf_head(buf), wpabuf_len(buf));
return buf;
}
static struct wpabuf *
dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole,
int idx, bool cert_req)
{
struct dpp_configuration *conf = NULL;
#ifdef CONFIG_TESTING_OPTIONS
if (auth->config_obj_override) {
if (idx != 0)
return NULL;
wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override");
return wpabuf_alloc_copy(auth->config_obj_override,
os_strlen(auth->config_obj_override));
}
#endif /* CONFIG_TESTING_OPTIONS */
if (idx == 0) {
if (netrole == DPP_NETROLE_STA)
conf = auth->conf_sta;
else if (netrole == DPP_NETROLE_AP)
conf = auth->conf_ap;
} else if (idx == 1) {
if (netrole == DPP_NETROLE_STA)
conf = auth->conf2_sta;
else if (netrole == DPP_NETROLE_AP)
conf = auth->conf2_ap;
}
if (!conf) {
if (idx == 0)
wpa_printf(MSG_DEBUG,
"DPP: No configuration available for Enrollee(%s) - reject configuration request",
dpp_netrole_str(netrole));
return NULL;
}
if (conf->akm == DPP_AKM_DOT1X) {
if (!auth->conf) {
wpa_printf(MSG_DEBUG,
"DPP: No Configurator data available");
return NULL;
}
if (!cert_req && !auth->certbag) {
wpa_printf(MSG_DEBUG,
"DPP: No certificate data available for dot1x configuration");
return NULL;
}
return dpp_build_conf_obj_dpp(auth, conf);
}
if (dpp_akm_dpp(conf->akm) || (auth->peer_version >= 2 && auth->conf))
return dpp_build_conf_obj_dpp(auth, conf);
return dpp_build_conf_obj_legacy(auth, conf);
}
struct wpabuf *
dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
u16 e_nonce_len, enum dpp_netrole netrole, bool cert_req)
{
struct wpabuf *conf = NULL, *conf2 = NULL, *env_data = NULL;
size_t clear_len, attr_len;
struct wpabuf *clear = NULL, *msg = NULL;
u8 *wrapped;
const u8 *addr[1];
size_t len[1];
enum dpp_status_error status;
if (auth->force_conf_resp_status != DPP_STATUS_OK) {
status = auth->force_conf_resp_status;
goto forced_status;
}
if (netrole == DPP_NETROLE_CONFIGURATOR) {
#ifdef CONFIG_DPP2
env_data = dpp_build_enveloped_data(auth);
#endif /* CONFIG_DPP2 */
} else {
conf = dpp_build_conf_obj(auth, netrole, 0, cert_req);
if (conf) {
wpa_hexdump_ascii(MSG_DEBUG,
"DPP: configurationObject JSON",
wpabuf_head(conf), wpabuf_len(conf));
conf2 = dpp_build_conf_obj(auth, netrole, 1, cert_req);
}
}
if (conf || env_data)
status = DPP_STATUS_OK;
else if (!cert_req && netrole == DPP_NETROLE_STA && auth->conf_sta &&
auth->conf_sta->akm == DPP_AKM_DOT1X && !auth->waiting_csr)
status = DPP_STATUS_CSR_NEEDED;
else
status = DPP_STATUS_CONFIGURE_FAILURE;
forced_status:
auth->conf_resp_status = status;
/* { E-nonce, configurationObject[, sendConnStatus]}ke */
clear_len = 4 + e_nonce_len;
if (conf)
clear_len += 4 + wpabuf_len(conf);
if (conf2)
clear_len += 4 + wpabuf_len(conf2);
if (env_data)
clear_len += 4 + wpabuf_len(env_data);
if (auth->peer_version >= 2 && auth->send_conn_status &&
netrole == DPP_NETROLE_STA)
clear_len += 4;
if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta &&
auth->conf_sta->csrattrs)
clear_len += 4 + os_strlen(auth->conf_sta->csrattrs);
clear = wpabuf_alloc(clear_len);
attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP)
attr_len += 5;
#endif /* CONFIG_TESTING_OPTIONS */
msg = wpabuf_alloc(attr_len);
if (!clear || !msg)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
goto skip_e_nonce;
}
if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch");
wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
wpabuf_put_le16(clear, e_nonce_len);
wpabuf_put_data(clear, e_nonce, e_nonce_len - 1);
wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01);
goto skip_e_nonce;
}
if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
goto skip_wrapped_data;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* E-nonce */
wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
wpabuf_put_le16(clear, e_nonce_len);
wpabuf_put_data(clear, e_nonce, e_nonce_len);
#ifdef CONFIG_TESTING_OPTIONS
skip_e_nonce:
if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - Config Object");
goto skip_config_obj;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (conf) {
wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
wpabuf_put_le16(clear, wpabuf_len(conf));
wpabuf_put_buf(clear, conf);
}
if (auth->peer_version >= 2 && conf2) {
wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
wpabuf_put_le16(clear, wpabuf_len(conf2));
wpabuf_put_buf(clear, conf2);
} else if (conf2) {
wpa_printf(MSG_DEBUG,
"DPP: Second Config Object available, but peer does not support more than one");
}
if (env_data) {
wpabuf_put_le16(clear, DPP_ATTR_ENVELOPED_DATA);
wpabuf_put_le16(clear, wpabuf_len(env_data));
wpabuf_put_buf(clear, env_data);
}
if (auth->peer_version >= 2 && auth->send_conn_status &&
netrole == DPP_NETROLE_STA && status == DPP_STATUS_OK) {
wpa_printf(MSG_DEBUG, "DPP: sendConnStatus");
wpabuf_put_le16(clear, DPP_ATTR_SEND_CONN_STATUS);
wpabuf_put_le16(clear, 0);
}
if (status == DPP_STATUS_CSR_NEEDED && auth->conf_sta &&
auth->conf_sta->csrattrs) {
auth->waiting_csr = true;
wpa_printf(MSG_DEBUG, "DPP: CSR Attributes Request");
wpabuf_put_le16(clear, DPP_ATTR_CSR_ATTR_REQ);
wpabuf_put_le16(clear, os_strlen(auth->conf_sta->csrattrs));
wpabuf_put_str(clear, auth->conf_sta->csrattrs);
}
#ifdef CONFIG_TESTING_OPTIONS
skip_config_obj:
if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - Status");
goto skip_status;
}
if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
status = 255;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* DPP Status */
dpp_build_attr_status(msg, status);
#ifdef CONFIG_TESTING_OPTIONS
skip_status:
#endif /* CONFIG_TESTING_OPTIONS */
addr[0] = wpabuf_head(msg);
len[0] = wpabuf_len(msg);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
wpabuf_head(clear), wpabuf_len(clear),
1, addr, len, wrapped) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
dpp_build_attr_status(msg, DPP_STATUS_OK);
}
skip_wrapped_data:
#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Configuration Response attributes", msg);
out:
wpabuf_clear_free(conf);
wpabuf_clear_free(conf2);
wpabuf_clear_free(env_data);
wpabuf_clear_free(clear);
return msg;
fail:
wpabuf_free(msg);
msg = NULL;
goto out;
}
struct wpabuf *
dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
size_t attr_len)
{
const u8 *wrapped_data, *e_nonce, *config_attr;
u16 wrapped_data_len, e_nonce_len, config_attr_len;
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
struct wpabuf *resp = NULL;
struct json_token *root = NULL, *token;
enum dpp_netrole netrole;
struct wpabuf *cert_req = NULL;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) {
wpa_printf(MSG_INFO,
"DPP: TESTING - stop at Config Request");
return NULL;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (dpp_check_attrs(attr_start, attr_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in config request");
return NULL;
}
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
dpp_auth_fail(auth,
"Missing or invalid required Wrapped Data attribute");
return NULL;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
return NULL;
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
0, NULL, NULL, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_ENROLLEE_NONCE,
&e_nonce_len);
if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
dpp_auth_fail(auth,
"Missing or invalid Enrollee Nonce attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
config_attr = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_CONFIG_ATTR_OBJ,
&config_attr_len);
if (!config_attr) {
dpp_auth_fail(auth,
"Missing or invalid Config Attributes attribute");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes",
config_attr, config_attr_len);
root = json_parse((const char *) config_attr, config_attr_len);
if (!root) {
dpp_auth_fail(auth, "Could not parse Config Attributes");
goto fail;
}
token = json_get_member(root, "name");
if (!token || token->type != JSON_STRING) {
dpp_auth_fail(auth, "No Config Attributes - name");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string);
token = json_get_member(root, "wi-fi_tech");
if (!token || token->type != JSON_STRING) {
dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string);
if (os_strcmp(token->string, "infra") != 0) {
wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'",
token->string);
dpp_auth_fail(auth, "Unsupported wi-fi_tech");
goto fail;
}
token = json_get_member(root, "netRole");
if (!token || token->type != JSON_STRING) {
dpp_auth_fail(auth, "No Config Attributes - netRole");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
if (os_strcmp(token->string, "sta") == 0) {
netrole = DPP_NETROLE_STA;
} else if (os_strcmp(token->string, "ap") == 0) {
netrole = DPP_NETROLE_AP;
} else if (os_strcmp(token->string, "configurator") == 0) {
netrole = DPP_NETROLE_CONFIGURATOR;
} else {
wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
token->string);
dpp_auth_fail(auth, "Unsupported netRole");
goto fail;
}
auth->e_netrole = netrole;
token = json_get_member(root, "mudurl");
if (token && token->type == JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: mudurl = '%s'", token->string);
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_MUD_URL "%s",
token->string);
}
token = json_get_member(root, "bandSupport");
if (token && token->type == JSON_ARRAY) {
int *opclass = NULL;
char txt[200], *pos, *end;
int i, res;
wpa_printf(MSG_DEBUG, "DPP: bandSupport");
token = token->child;
while (token) {
if (token->type != JSON_NUMBER) {
wpa_printf(MSG_DEBUG,
"DPP: Invalid bandSupport array member type");
} else {
wpa_printf(MSG_DEBUG,
"DPP: Supported global operating class: %d",
token->number);
int_array_add_unique(&opclass, token->number);
}
token = token->sibling;
}
txt[0] = '\0';
pos = txt;
end = txt + sizeof(txt);
for (i = 0; opclass && opclass[i]; i++) {
res = os_snprintf(pos, end - pos, "%s%d",
pos == txt ? "" : ",", opclass[i]);
if (os_snprintf_error(end - pos, res)) {
*pos = '\0';
break;
}
pos += res;
}
os_free(opclass);
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_BAND_SUPPORT "%s",
txt);
}
#ifdef CONFIG_DPP2
cert_req = json_get_member_base64(root, "pkcs10");
if (cert_req) {
char *txt;
int id;
wpa_hexdump_buf(MSG_DEBUG, "DPP: CertificateRequest", cert_req);
if (dpp_validate_csr(auth, cert_req) < 0) {
wpa_printf(MSG_DEBUG, "DPP: CSR is not valid");
auth->force_conf_resp_status = DPP_STATUS_CSR_BAD;
goto cont;
}
if (auth->peer_bi) {
id = auth->peer_bi->id;
} else if (auth->tmp_peer_bi) {
id = auth->tmp_peer_bi->id;
} else {
struct dpp_bootstrap_info *bi;
bi = os_zalloc(sizeof(*bi));
if (!bi)
goto fail;
bi->id = dpp_next_id(auth->global);
dl_list_add(&auth->global->bootstrap, &bi->list);
auth->tmp_peer_bi = bi;
id = bi->id;
}
wpa_printf(MSG_DEBUG, "DPP: CSR is valid - forward to CA/RA");
txt = base64_encode_no_lf(wpabuf_head(cert_req),
wpabuf_len(cert_req), NULL);
if (!txt)
goto fail;
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_CSR "peer=%d csr=%s",
id, txt);
os_free(txt);
auth->waiting_csr = false;
auth->waiting_cert = true;
goto fail;
}
cont:
#endif /* CONFIG_DPP2 */
resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, netrole,
cert_req);
fail:
wpabuf_free(cert_req);
json_free(root);
os_free(unwrapped);
return resp;
}
static int dpp_parse_cred_legacy(struct dpp_config_obj *conf,
struct json_token *cred)
{
struct json_token *pass, *psk_hex;
wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential");
pass = json_get_member(cred, "pass");
psk_hex = json_get_member(cred, "psk_hex");
if (pass && pass->type == JSON_STRING) {
size_t len = os_strlen(pass->string);
wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase",
pass->string, len);
if (len < 8 || len > 63)
return -1;
os_strlcpy(conf->passphrase, pass->string,
sizeof(conf->passphrase));
} else if (psk_hex && psk_hex->type == JSON_STRING) {
if (dpp_akm_sae(conf->akm) && !dpp_akm_psk(conf->akm)) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected psk_hex with akm=sae");
return -1;
}
if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
hexstr2bin(psk_hex->string, conf->psk, PMK_LEN) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK",
conf->psk, PMK_LEN);
conf->psk_set = 1;
} else {
wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found");
return -1;
}
if (dpp_akm_sae(conf->akm) && !conf->passphrase[0]) {
wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
return -1;
}
return 0;
}
-EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
- const struct dpp_curve_params **key_curve)
+struct crypto_ec_key * dpp_parse_jwk(struct json_token *jwk,
+ const struct dpp_curve_params **key_curve)
{
struct json_token *token;
const struct dpp_curve_params *curve;
struct wpabuf *x = NULL, *y = NULL;
- EC_GROUP *group;
- EVP_PKEY *pkey = NULL;
+ struct crypto_ec_key *key = NULL;
token = json_get_member(jwk, "kty");
if (!token || token->type != JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: No kty in JWK");
goto fail;
}
if (os_strcmp(token->string, "EC") != 0) {
wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'",
token->string);
goto fail;
}
token = json_get_member(jwk, "crv");
if (!token || token->type != JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: No crv in JWK");
goto fail;
}
curve = dpp_get_curve_jwk_crv(token->string);
if (!curve) {
wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'",
token->string);
goto fail;
}
x = json_get_member_base64url(jwk, "x");
if (!x) {
wpa_printf(MSG_DEBUG, "DPP: No x in JWK");
goto fail;
}
wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x);
if (wpabuf_len(x) != curve->prime_len) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected JWK x length %u (expected %u for curve %s)",
(unsigned int) wpabuf_len(x),
(unsigned int) curve->prime_len, curve->name);
goto fail;
}
y = json_get_member_base64url(jwk, "y");
if (!y) {
wpa_printf(MSG_DEBUG, "DPP: No y in JWK");
goto fail;
}
wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y);
if (wpabuf_len(y) != curve->prime_len) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected JWK y length %u (expected %u for curve %s)",
(unsigned int) wpabuf_len(y),
(unsigned int) curve->prime_len, curve->name);
goto fail;
}
- group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
- if (!group) {
- wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK");
+ key = crypto_ec_key_set_pub(curve->ike_group, wpabuf_head(x),
+ wpabuf_head(y), wpabuf_len(x));
+ if (!key)
goto fail;
- }
- pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y),
- wpabuf_len(x));
- EC_GROUP_free(group);
*key_curve = curve;
fail:
wpabuf_free(x);
wpabuf_free(y);
- return pkey;
+ return key;
}
int dpp_key_expired(const char *timestamp, os_time_t *expiry)
{
struct os_time now;
unsigned int year, month, day, hour, min, sec;
os_time_t utime;
const char *pos;
/* ISO 8601 date and time:
* <date>T<time>
* YYYY-MM-DDTHH:MM:SSZ
* YYYY-MM-DDTHH:MM:SS+03:00
*/
if (os_strlen(timestamp) < 19) {
wpa_printf(MSG_DEBUG,
"DPP: Too short timestamp - assume expired key");
return 1;
}
if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u",
&year, &month, &day, &hour, &min, &sec) != 6) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to parse expiration day - assume expired key");
return 1;
}
if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Invalid date/time information - assume expired key");
return 1;
}
pos = timestamp + 19;
if (*pos == 'Z' || *pos == '\0') {
/* In UTC - no need to adjust */
} else if (*pos == '-' || *pos == '+') {
int items;
/* Adjust local time to UTC */
items = sscanf(pos + 1, "%02u:%02u", &hour, &min);
if (items < 1) {
wpa_printf(MSG_DEBUG,
"DPP: Invalid time zone designator (%s) - assume expired key",
pos);
return 1;
}
if (*pos == '-')
utime += 3600 * hour;
if (*pos == '+')
utime -= 3600 * hour;
if (items > 1) {
if (*pos == '-')
utime += 60 * min;
if (*pos == '+')
utime -= 60 * min;
}
} else {
wpa_printf(MSG_DEBUG,
"DPP: Invalid time zone designator (%s) - assume expired key",
pos);
return 1;
}
if (expiry)
*expiry = utime;
if (os_get_time(&now) < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Cannot get current time - assume expired key");
return 1;
}
if (now.sec > utime) {
wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)",
utime, now.sec);
return 1;
}
return 0;
}
static int dpp_parse_connector(struct dpp_authentication *auth,
struct dpp_config_obj *conf,
const unsigned char *payload,
u16 payload_len)
{
struct json_token *root, *groups, *netkey, *token;
int ret = -1;
- EVP_PKEY *key = NULL;
+ struct crypto_ec_key *key = NULL;
const struct dpp_curve_params *curve;
unsigned int rules = 0;
root = json_parse((const char *) payload, payload_len);
if (!root) {
wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
goto fail;
}
groups = json_get_member(root, "groups");
if (!groups || groups->type != JSON_ARRAY) {
wpa_printf(MSG_DEBUG, "DPP: No groups array found");
goto skip_groups;
}
for (token = groups->child; token; token = token->sibling) {
struct json_token *id, *role;
id = json_get_member(token, "groupId");
if (!id || id->type != JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: Missing groupId string");
goto fail;
}
role = json_get_member(token, "netRole");
if (!role || role->type != JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: Missing netRole string");
goto fail;
}
wpa_printf(MSG_DEBUG,
"DPP: connector group: groupId='%s' netRole='%s'",
id->string, role->string);
rules++;
}
skip_groups:
if (!rules) {
wpa_printf(MSG_DEBUG,
"DPP: Connector includes no groups");
goto fail;
}
token = json_get_member(root, "expiry");
if (!token || token->type != JSON_STRING) {
wpa_printf(MSG_DEBUG,
"DPP: No expiry string found - connector does not expire");
} else {
wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
if (dpp_key_expired(token->string,
&auth->net_access_key_expiry)) {
wpa_printf(MSG_DEBUG,
"DPP: Connector (netAccessKey) has expired");
goto fail;
}
}
netkey = json_get_member(root, "netAccessKey");
if (!netkey || netkey->type != JSON_OBJECT) {
wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
goto fail;
}
key = dpp_parse_jwk(netkey, &curve);
if (!key)
goto fail;
dpp_debug_print_key("DPP: Received netAccessKey", key);
- if (EVP_PKEY_cmp(key, auth->own_protocol_key) != 1) {
+ if (crypto_ec_key_cmp(key, auth->own_protocol_key)) {
wpa_printf(MSG_DEBUG,
"DPP: netAccessKey in connector does not match own protocol key");
#ifdef CONFIG_TESTING_OPTIONS
if (auth->ignore_netaccesskey_mismatch) {
wpa_printf(MSG_DEBUG,
"DPP: TESTING - skip netAccessKey mismatch");
} else {
goto fail;
}
#else /* CONFIG_TESTING_OPTIONS */
goto fail;
#endif /* CONFIG_TESTING_OPTIONS */
}
ret = 0;
fail:
- EVP_PKEY_free(key);
+ crypto_ec_key_deinit(key);
json_free(root);
return ret;
}
-static void dpp_copy_csign(struct dpp_config_obj *conf, EVP_PKEY *csign)
+static void dpp_copy_csign(struct dpp_config_obj *conf,
+ struct crypto_ec_key *csign)
{
- unsigned char *der = NULL;
- int der_len;
+ struct wpabuf *c_sign_key;
- der_len = i2d_PUBKEY(csign, &der);
- if (der_len <= 0)
+ c_sign_key = crypto_ec_key_get_subject_public_key(csign);
+ if (!c_sign_key)
return;
+
wpabuf_free(conf->c_sign_key);
- conf->c_sign_key = wpabuf_alloc_copy(der, der_len);
- OPENSSL_free(der);
+ conf->c_sign_key = c_sign_key;
}
-static void dpp_copy_ppkey(struct dpp_config_obj *conf, EVP_PKEY *ppkey)
+static void dpp_copy_ppkey(struct dpp_config_obj *conf,
+ struct crypto_ec_key *ppkey)
{
- unsigned char *der = NULL;
- int der_len;
+ struct wpabuf *pp_key;
- der_len = i2d_PUBKEY(ppkey, &der);
- if (der_len <= 0)
+ pp_key = crypto_ec_key_get_subject_public_key(ppkey);
+ if (!pp_key)
return;
+
wpabuf_free(conf->pp_key);
- conf->pp_key = wpabuf_alloc_copy(der, der_len);
- OPENSSL_free(der);
+ conf->pp_key = pp_key;
}
static void dpp_copy_netaccesskey(struct dpp_authentication *auth,
struct dpp_config_obj *conf)
{
- unsigned char *der = NULL;
- int der_len;
- EC_KEY *eckey;
- EVP_PKEY *own_key;
+ struct wpabuf *net_access_key;
+ struct crypto_ec_key *own_key;
own_key = auth->own_protocol_key;
#ifdef CONFIG_DPP2
if (auth->reconfig_connector_key == DPP_CONFIG_REUSEKEY &&
auth->reconfig_old_protocol_key)
own_key = auth->reconfig_old_protocol_key;
#endif /* CONFIG_DPP2 */
- eckey = EVP_PKEY_get1_EC_KEY(own_key);
- if (!eckey)
- return;
- der_len = i2d_ECPrivateKey(eckey, &der);
- if (der_len <= 0) {
- EC_KEY_free(eckey);
+ net_access_key = crypto_ec_key_get_ecprivate_key(own_key, true);
+ if (!net_access_key)
return;
- }
+
wpabuf_free(auth->net_access_key);
- auth->net_access_key = wpabuf_alloc_copy(der, der_len);
- OPENSSL_free(der);
- EC_KEY_free(eckey);
+ auth->net_access_key = net_access_key;
}
static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
struct dpp_config_obj *conf,
struct json_token *cred)
{
struct dpp_signed_connector_info info;
struct json_token *token, *csign, *ppkey;
int ret = -1;
- EVP_PKEY *csign_pub = NULL, *pp_pub = NULL;
+ struct crypto_ec_key *csign_pub = NULL, *pp_pub = NULL;
const struct dpp_curve_params *key_curve = NULL, *pp_curve = NULL;
const char *signed_connector;
os_memset(&info, 0, sizeof(info));
if (dpp_akm_psk(conf->akm) || dpp_akm_sae(conf->akm)) {
wpa_printf(MSG_DEBUG,
"DPP: Legacy credential included in Connector credential");
if (dpp_parse_cred_legacy(conf, cred) < 0)
return -1;
}
wpa_printf(MSG_DEBUG, "DPP: Connector credential");
csign = json_get_member(cred, "csign");
if (!csign || csign->type != JSON_OBJECT) {
wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON");
goto fail;
}
csign_pub = dpp_parse_jwk(csign, &key_curve);
if (!csign_pub) {
wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK");
goto fail;
}
dpp_debug_print_key("DPP: Received C-sign-key", csign_pub);
ppkey = json_get_member(cred, "ppKey");
if (ppkey && ppkey->type == JSON_OBJECT) {
pp_pub = dpp_parse_jwk(ppkey, &pp_curve);
if (!pp_pub) {
wpa_printf(MSG_DEBUG, "DPP: Failed to parse ppKey JWK");
goto fail;
}
dpp_debug_print_key("DPP: Received ppKey", pp_pub);
if (key_curve != pp_curve) {
wpa_printf(MSG_DEBUG,
"DPP: C-sign-key and ppKey do not use the same curve");
goto fail;
}
}
token = json_get_member(cred, "signedConnector");
if (!token || token->type != JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector",
token->string, os_strlen(token->string));
signed_connector = token->string;
if (os_strchr(signed_connector, '"') ||
os_strchr(signed_connector, '\n')) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected character in signedConnector");
goto fail;
}
if (dpp_process_signed_connector(&info, csign_pub,
signed_connector) != DPP_STATUS_OK)
goto fail;
if (dpp_parse_connector(auth, conf,
info.payload, info.payload_len) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector");
goto fail;
}
os_free(conf->connector);
conf->connector = os_strdup(signed_connector);
dpp_copy_csign(conf, csign_pub);
if (pp_pub)
dpp_copy_ppkey(conf, pp_pub);
if (dpp_akm_dpp(conf->akm) || auth->peer_version >= 2)
dpp_copy_netaccesskey(auth, conf);
ret = 0;
fail:
- EVP_PKEY_free(csign_pub);
- EVP_PKEY_free(pp_pub);
+ crypto_ec_key_deinit(csign_pub);
+ crypto_ec_key_deinit(pp_pub);
os_free(info.payload);
return ret;
}
#ifdef CONFIG_DPP2
static int dpp_parse_cred_dot1x(struct dpp_authentication *auth,
struct dpp_config_obj *conf,
struct json_token *cred)
{
struct json_token *ent, *name;
ent = json_get_member(cred, "entCreds");
if (!ent || ent->type != JSON_OBJECT) {
dpp_auth_fail(auth, "No entCreds in JSON");
return -1;
}
conf->certbag = json_get_member_base64(ent, "certBag");
if (!conf->certbag) {
dpp_auth_fail(auth, "No certBag in JSON");
return -1;
}
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received certBag", conf->certbag);
- conf->certs = dpp_pkcs7_certs(conf->certbag);
+ conf->certs = crypto_pkcs7_get_certificates(conf->certbag);
if (!conf->certs) {
dpp_auth_fail(auth, "No certificates in certBag");
return -1;
}
conf->cacert = json_get_member_base64(ent, "caCert");
if (conf->cacert)
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Received caCert",
conf->cacert);
name = json_get_member(ent, "trustedEapServerName");
if (name &&
(name->type != JSON_STRING ||
has_ctrl_char((const u8 *) name->string,
os_strlen(name->string)))) {
dpp_auth_fail(auth,
"Invalid trustedEapServerName type in JSON");
return -1;
}
if (name && name->string) {
wpa_printf(MSG_DEBUG, "DPP: Received trustedEapServerName: %s",
name->string);
conf->server_name = os_strdup(name->string);
if (!conf->server_name)
return -1;
}
return 0;
}
#endif /* CONFIG_DPP2 */
const char * dpp_akm_str(enum dpp_akm akm)
{
switch (akm) {
case DPP_AKM_DPP:
return "dpp";
case DPP_AKM_PSK:
return "psk";
case DPP_AKM_SAE:
return "sae";
case DPP_AKM_PSK_SAE:
return "psk+sae";
case DPP_AKM_SAE_DPP:
return "dpp+sae";
case DPP_AKM_PSK_SAE_DPP:
return "dpp+psk+sae";
case DPP_AKM_DOT1X:
return "dot1x";
default:
return "??";
}
}
const char * dpp_akm_selector_str(enum dpp_akm akm)
{
switch (akm) {
case DPP_AKM_DPP:
return "506F9A02";
case DPP_AKM_PSK:
return "000FAC02+000FAC06";
case DPP_AKM_SAE:
return "000FAC08";
case DPP_AKM_PSK_SAE:
return "000FAC02+000FAC06+000FAC08";
case DPP_AKM_SAE_DPP:
return "506F9A02+000FAC08";
case DPP_AKM_PSK_SAE_DPP:
return "506F9A02+000FAC08+000FAC02+000FAC06";
case DPP_AKM_DOT1X:
return "000FAC01+000FAC05";
default:
return "??";
}
}
static enum dpp_akm dpp_akm_from_str(const char *akm)
{
const char *pos;
int dpp = 0, psk = 0, sae = 0, dot1x = 0;
if (os_strcmp(akm, "psk") == 0)
return DPP_AKM_PSK;
if (os_strcmp(akm, "sae") == 0)
return DPP_AKM_SAE;
if (os_strcmp(akm, "psk+sae") == 0)
return DPP_AKM_PSK_SAE;
if (os_strcmp(akm, "dpp") == 0)
return DPP_AKM_DPP;
if (os_strcmp(akm, "dpp+sae") == 0)
return DPP_AKM_SAE_DPP;
if (os_strcmp(akm, "dpp+psk+sae") == 0)
return DPP_AKM_PSK_SAE_DPP;
if (os_strcmp(akm, "dot1x") == 0)
return DPP_AKM_DOT1X;
pos = akm;
while (*pos) {
if (os_strlen(pos) < 8)
break;
if (os_strncasecmp(pos, "506F9A02", 8) == 0)
dpp = 1;
else if (os_strncasecmp(pos, "000FAC02", 8) == 0)
psk = 1;
else if (os_strncasecmp(pos, "000FAC06", 8) == 0)
psk = 1;
else if (os_strncasecmp(pos, "000FAC08", 8) == 0)
sae = 1;
else if (os_strncasecmp(pos, "000FAC01", 8) == 0)
dot1x = 1;
else if (os_strncasecmp(pos, "000FAC05", 8) == 0)
dot1x = 1;
pos += 8;
if (*pos != '+')
break;
pos++;
}
if (dpp && psk && sae)
return DPP_AKM_PSK_SAE_DPP;
if (dpp && sae)
return DPP_AKM_SAE_DPP;
if (dpp)
return DPP_AKM_DPP;
if (psk && sae)
return DPP_AKM_PSK_SAE;
if (sae)
return DPP_AKM_SAE;
if (psk)
return DPP_AKM_PSK;
if (dot1x)
return DPP_AKM_DOT1X;
return DPP_AKM_UNKNOWN;
}
static int dpp_parse_conf_obj(struct dpp_authentication *auth,
const u8 *conf_obj, u16 conf_obj_len)
{
int ret = -1;
struct json_token *root, *token, *discovery, *cred;
struct dpp_config_obj *conf;
struct wpabuf *ssid64 = NULL;
int legacy;
root = json_parse((const char *) conf_obj, conf_obj_len);
if (!root)
return -1;
if (root->type != JSON_OBJECT) {
dpp_auth_fail(auth, "JSON root is not an object");
goto fail;
}
token = json_get_member(root, "wi-fi_tech");
if (!token || token->type != JSON_STRING) {
dpp_auth_fail(auth, "No wi-fi_tech string value found");
goto fail;
}
if (os_strcmp(token->string, "infra") != 0) {
wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'",
token->string);
dpp_auth_fail(auth, "Unsupported wi-fi_tech value");
goto fail;
}
discovery = json_get_member(root, "discovery");
if (!discovery || discovery->type != JSON_OBJECT) {
dpp_auth_fail(auth, "No discovery object in JSON");
goto fail;
}
ssid64 = json_get_member_base64url(discovery, "ssid64");
if (ssid64) {
wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid64",
wpabuf_head(ssid64), wpabuf_len(ssid64));
if (wpabuf_len(ssid64) > SSID_MAX_LEN) {
dpp_auth_fail(auth, "Too long discovery::ssid64 value");
goto fail;
}
} else {
token = json_get_member(discovery, "ssid");
if (!token || token->type != JSON_STRING) {
dpp_auth_fail(auth,
"No discovery::ssid string value found");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
token->string, os_strlen(token->string));
if (os_strlen(token->string) > SSID_MAX_LEN) {
dpp_auth_fail(auth,
"Too long discovery::ssid string value");
goto fail;
}
}
if (auth->num_conf_obj == DPP_MAX_CONF_OBJ) {
wpa_printf(MSG_DEBUG,
"DPP: No room for this many Config Objects - ignore this one");
ret = 0;
goto fail;
}
conf = &auth->conf_obj[auth->num_conf_obj++];
if (ssid64) {
conf->ssid_len = wpabuf_len(ssid64);
os_memcpy(conf->ssid, wpabuf_head(ssid64), conf->ssid_len);
} else {
conf->ssid_len = os_strlen(token->string);
os_memcpy(conf->ssid, token->string, conf->ssid_len);
}
token = json_get_member(discovery, "ssid_charset");
if (token && token->type == JSON_NUMBER) {
conf->ssid_charset = token->number;
wpa_printf(MSG_DEBUG, "DPP: ssid_charset=%d",
conf->ssid_charset);
}
cred = json_get_member(root, "cred");
if (!cred || cred->type != JSON_OBJECT) {
dpp_auth_fail(auth, "No cred object in JSON");
goto fail;
}
token = json_get_member(cred, "akm");
if (!token || token->type != JSON_STRING) {
dpp_auth_fail(auth, "No cred::akm string value found");
goto fail;
}
conf->akm = dpp_akm_from_str(token->string);
legacy = dpp_akm_legacy(conf->akm);
if (legacy && auth->peer_version >= 2) {
struct json_token *csign, *s_conn;
csign = json_get_member(cred, "csign");
s_conn = json_get_member(cred, "signedConnector");
if (csign && csign->type == JSON_OBJECT &&
s_conn && s_conn->type == JSON_STRING)
legacy = 0;
}
if (legacy) {
if (dpp_parse_cred_legacy(conf, cred) < 0)
goto fail;
} else if (dpp_akm_dpp(conf->akm) ||
(auth->peer_version >= 2 && dpp_akm_legacy(conf->akm))) {
if (dpp_parse_cred_dpp(auth, conf, cred) < 0)
goto fail;
#ifdef CONFIG_DPP2
} else if (conf->akm == DPP_AKM_DOT1X) {
if (dpp_parse_cred_dot1x(auth, conf, cred) < 0 ||
dpp_parse_cred_dpp(auth, conf, cred) < 0)
goto fail;
#endif /* CONFIG_DPP2 */
} else {
wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
token->string);
dpp_auth_fail(auth, "Unsupported akm");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully");
ret = 0;
fail:
wpabuf_free(ssid64);
json_free(root);
return ret;
}
#ifdef CONFIG_DPP2
static u8 * dpp_get_csr_attrs(const u8 *attrs, size_t attrs_len, size_t *len)
{
const u8 *b64;
u16 b64_len;
b64 = dpp_get_attr(attrs, attrs_len, DPP_ATTR_CSR_ATTR_REQ, &b64_len);
if (!b64)
return NULL;
return base64_decode((const char *) b64, b64_len, len);
}
#endif /* CONFIG_DPP2 */
int dpp_conf_resp_rx(struct dpp_authentication *auth,
const struct wpabuf *resp)
{
const u8 *wrapped_data, *e_nonce, *status, *conf_obj;
u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len;
const u8 *env_data;
u16 env_data_len;
const u8 *addr[1];
size_t len[1];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
int ret = -1;
auth->conf_resp_status = 255;
if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
dpp_auth_fail(auth, "Invalid attribute in config response");
return -1;
}
wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
dpp_auth_fail(auth,
"Missing or invalid required Wrapped Data attribute");
return -1;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
return -1;
addr[0] = wpabuf_head(resp);
len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
1, addr, len, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_ENROLLEE_NONCE,
&e_nonce_len);
if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
dpp_auth_fail(auth,
"Missing or invalid Enrollee Nonce attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
dpp_auth_fail(auth, "Enrollee Nonce mismatch");
goto fail;
}
status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
DPP_ATTR_STATUS, &status_len);
if (!status || status_len < 1) {
dpp_auth_fail(auth,
"Missing or invalid required DPP Status attribute");
goto fail;
}
auth->conf_resp_status = status[0];
wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
#ifdef CONFIG_DPP2
if (status[0] == DPP_STATUS_CSR_NEEDED) {
u8 *csrattrs;
size_t csrattrs_len;
wpa_printf(MSG_DEBUG, "DPP: Configurator requested CSR");
csrattrs = dpp_get_csr_attrs(unwrapped, unwrapped_len,
&csrattrs_len);
if (!csrattrs) {
dpp_auth_fail(auth,
"Missing or invalid CSR Attributes Request attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: CsrAttrs", csrattrs, csrattrs_len);
os_free(auth->csrattrs);
auth->csrattrs = csrattrs;
auth->csrattrs_len = csrattrs_len;
ret = -2;
goto fail;
}
#endif /* CONFIG_DPP2 */
if (status[0] != DPP_STATUS_OK) {
dpp_auth_fail(auth, "Configurator rejected configuration");
goto fail;
}
env_data = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_ENVELOPED_DATA, &env_data_len);
#ifdef CONFIG_DPP2
if (env_data &&
dpp_conf_resp_env_data(auth, env_data, env_data_len) < 0)
goto fail;
#endif /* CONFIG_DPP2 */
conf_obj = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONFIG_OBJ,
&conf_obj_len);
if (!conf_obj && !env_data) {
dpp_auth_fail(auth,
"Missing required Configuration Object attribute");
goto fail;
}
while (conf_obj) {
wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
conf_obj, conf_obj_len);
if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0)
goto fail;
conf_obj = dpp_get_attr_next(conf_obj, unwrapped, unwrapped_len,
DPP_ATTR_CONFIG_OBJ,
&conf_obj_len);
}
#ifdef CONFIG_DPP2
status = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_SEND_CONN_STATUS, &status_len);
if (status) {
wpa_printf(MSG_DEBUG,
"DPP: Configurator requested connection status result");
auth->conn_status_requested = 1;
}
#endif /* CONFIG_DPP2 */
ret = 0;
fail:
os_free(unwrapped);
return ret;
}
#ifdef CONFIG_DPP2
enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
const u8 *hdr,
const u8 *attr_start, size_t attr_len)
{
const u8 *wrapped_data, *status, *e_nonce;
u16 wrapped_data_len, status_len, e_nonce_len;
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
enum dpp_status_error ret = 256;
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
dpp_auth_fail(auth,
"Missing or invalid required Wrapped Data attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
wrapped_data, wrapped_data_len);
attr_len = wrapped_data - 4 - attr_start;
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
addr[1] = attr_start;
len[1] = attr_len;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
goto fail;
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_ENROLLEE_NONCE,
&e_nonce_len);
if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
dpp_auth_fail(auth,
"Missing or invalid Enrollee Nonce attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
dpp_auth_fail(auth, "Enrollee Nonce mismatch");
wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce",
auth->e_nonce, e_nonce_len);
goto fail;
}
status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS,
&status_len);
if (!status || status_len < 1) {
dpp_auth_fail(auth,
"Missing or invalid required DPP Status attribute");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
ret = status[0];
fail:
bin_clear_free(unwrapped, unwrapped_len);
return ret;
}
struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
enum dpp_status_error status)
{
struct wpabuf *msg, *clear;
size_t nonce_len, clear_len, attr_len;
const u8 *addr[2];
size_t len[2];
u8 *wrapped;
nonce_len = auth->curve->nonce_len;
clear_len = 5 + 4 + nonce_len;
attr_len = 4 + clear_len + AES_BLOCK_SIZE;
clear = wpabuf_alloc(clear_len);
msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len);
if (!clear || !msg)
goto fail;
/* DPP Status */
dpp_build_attr_status(clear, status);
/* E-nonce */
wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
wpabuf_put_le16(clear, nonce_len);
wpabuf_put_data(clear, auth->e_nonce, nonce_len);
/* OUI, OUI type, Crypto Suite, DPP frame type */
addr[0] = wpabuf_head_u8(msg) + 2;
len[0] = 3 + 1 + 1 + 1;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
/* Attributes before Wrapped Data (none) */
addr[1] = wpabuf_put(msg, 0);
len[1] = 0;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
/* Wrapped Data */
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
wpabuf_head(clear), wpabuf_len(clear),
2, addr, len, wrapped) < 0)
goto fail;
wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Result attributes", msg);
wpabuf_free(clear);
return msg;
fail:
wpabuf_free(clear);
wpabuf_free(msg);
return NULL;
}
static int valid_channel_list(const char *val)
{
while (*val) {
if (!((*val >= '0' && *val <= '9') ||
*val == '/' || *val == ','))
return 0;
val++;
}
return 1;
}
enum dpp_status_error dpp_conn_status_result_rx(struct dpp_authentication *auth,
const u8 *hdr,
const u8 *attr_start,
size_t attr_len,
u8 *ssid, size_t *ssid_len,
char **channel_list)
{
const u8 *wrapped_data, *status, *e_nonce;
u16 wrapped_data_len, status_len, e_nonce_len;
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
enum dpp_status_error ret = 256;
struct json_token *root = NULL, *token;
struct wpabuf *ssid64;
*ssid_len = 0;
*channel_list = NULL;
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
dpp_auth_fail(auth,
"Missing or invalid required Wrapped Data attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
wrapped_data, wrapped_data_len);
attr_len = wrapped_data - 4 - attr_start;
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
addr[1] = attr_start;
len[1] = attr_len;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
goto fail;
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_ENROLLEE_NONCE,
&e_nonce_len);
if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
dpp_auth_fail(auth,
"Missing or invalid Enrollee Nonce attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
dpp_auth_fail(auth, "Enrollee Nonce mismatch");
wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce",
auth->e_nonce, e_nonce_len);
goto fail;
}
status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_CONN_STATUS,
&status_len);
if (!status) {
dpp_auth_fail(auth,
"Missing required DPP Connection Status attribute");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
status, status_len);
root = json_parse((const char *) status, status_len);
if (!root) {
dpp_auth_fail(auth, "Could not parse connStatus");
goto fail;
}
ssid64 = json_get_member_base64url(root, "ssid64");
if (ssid64 && wpabuf_len(ssid64) <= SSID_MAX_LEN) {
*ssid_len = wpabuf_len(ssid64);
os_memcpy(ssid, wpabuf_head(ssid64), *ssid_len);
}
wpabuf_free(ssid64);
token = json_get_member(root, "channelList");
if (token && token->type == JSON_STRING &&
valid_channel_list(token->string))
*channel_list = os_strdup(token->string);
token = json_get_member(root, "result");
if (!token || token->type != JSON_NUMBER) {
dpp_auth_fail(auth, "No connStatus - result");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: result %d", token->number);
ret = token->number;
fail:
json_free(root);
bin_clear_free(unwrapped, unwrapped_len);
return ret;
}
struct wpabuf * dpp_build_conn_status(enum dpp_status_error result,
const u8 *ssid, size_t ssid_len,
const char *channel_list)
{
struct wpabuf *json;
json = wpabuf_alloc(1000);
if (!json)
return NULL;
json_start_object(json, NULL);
json_add_int(json, "result", result);
if (ssid) {
json_value_sep(json);
if (json_add_base64url(json, "ssid64", ssid, ssid_len) < 0) {
wpabuf_free(json);
return NULL;
}
}
if (channel_list) {
json_value_sep(json);
json_add_string(json, "channelList", channel_list);
}
json_end_object(json);
wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus JSON",
wpabuf_head(json), wpabuf_len(json));
return json;
}
struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth,
enum dpp_status_error result,
const u8 *ssid, size_t ssid_len,
const char *channel_list)
{
struct wpabuf *msg = NULL, *clear = NULL, *json;
size_t nonce_len, clear_len, attr_len;
const u8 *addr[2];
size_t len[2];
u8 *wrapped;
json = dpp_build_conn_status(result, ssid, ssid_len, channel_list);
if (!json)
return NULL;
nonce_len = auth->curve->nonce_len;
clear_len = 5 + 4 + nonce_len + 4 + wpabuf_len(json);
attr_len = 4 + clear_len + AES_BLOCK_SIZE;
clear = wpabuf_alloc(clear_len);
msg = dpp_alloc_msg(DPP_PA_CONNECTION_STATUS_RESULT, attr_len);
if (!clear || !msg)
goto fail;
/* E-nonce */
wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
wpabuf_put_le16(clear, nonce_len);
wpabuf_put_data(clear, auth->e_nonce, nonce_len);
/* DPP Connection Status */
wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS);
wpabuf_put_le16(clear, wpabuf_len(json));
wpabuf_put_buf(clear, json);
/* OUI, OUI type, Crypto Suite, DPP frame type */
addr[0] = wpabuf_head_u8(msg) + 2;
len[0] = 3 + 1 + 1 + 1;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
/* Attributes before Wrapped Data (none) */
addr[1] = wpabuf_put(msg, 0);
len[1] = 0;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
/* Wrapped Data */
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
wpabuf_head(clear), wpabuf_len(clear),
2, addr, len, wrapped) < 0)
goto fail;
wpa_hexdump_buf(MSG_DEBUG, "DPP: Connection Status Result attributes",
msg);
wpabuf_free(json);
wpabuf_free(clear);
return msg;
fail:
wpabuf_free(json);
wpabuf_free(clear);
wpabuf_free(msg);
return NULL;
}
#endif /* CONFIG_DPP2 */
void dpp_configurator_free(struct dpp_configurator *conf)
{
if (!conf)
return;
- EVP_PKEY_free(conf->csign);
+ crypto_ec_key_deinit(conf->csign);
os_free(conf->kid);
os_free(conf->connector);
- EVP_PKEY_free(conf->connector_key);
- EVP_PKEY_free(conf->pp_key);
+ crypto_ec_key_deinit(conf->connector_key);
+ crypto_ec_key_deinit(conf->pp_key);
os_free(conf);
}
int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
size_t buflen)
{
- EC_KEY *eckey;
- int key_len, ret = -1;
- unsigned char *key = NULL;
+ struct wpabuf *key;
+ int ret = -1;
if (!conf->csign)
return -1;
- eckey = EVP_PKEY_get1_EC_KEY(conf->csign);
- if (!eckey)
+ key = crypto_ec_key_get_ecprivate_key(conf->csign, true);
+ if (!key)
return -1;
- key_len = i2d_ECPrivateKey(eckey, &key);
- if (key_len > 0)
- ret = wpa_snprintf_hex(buf, buflen, key, key_len);
+ ret = wpa_snprintf_hex(buf, buflen, wpabuf_head(key), wpabuf_len(key));
- EC_KEY_free(eckey);
- OPENSSL_free(key);
+ wpabuf_clear_free(key);
return ret;
}
static int dpp_configurator_gen_kid(struct dpp_configurator *conf)
{
struct wpabuf *csign_pub = NULL;
const u8 *addr[1];
size_t len[1];
int res;
- csign_pub = dpp_get_pubkey_point(conf->csign, 1);
+ csign_pub = crypto_ec_key_get_pubkey_point(conf->csign, 1);
if (!csign_pub) {
wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key");
return -1;
}
/* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */
addr[0] = wpabuf_head(csign_pub);
len[0] = wpabuf_len(csign_pub);
res = sha256_vector(1, addr, len, conf->kid_hash);
wpabuf_free(csign_pub);
if (res < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to derive kid for C-sign-key");
return -1;
}
conf->kid = base64_url_encode(conf->kid_hash, sizeof(conf->kid_hash),
NULL);
return conf->kid ? 0 : -1;
}
static struct dpp_configurator *
dpp_keygen_configurator(const char *curve, const u8 *privkey,
size_t privkey_len, const u8 *pp_key, size_t pp_key_len)
{
struct dpp_configurator *conf;
conf = os_zalloc(sizeof(*conf));
if (!conf)
return NULL;
conf->curve = dpp_get_curve_name(curve);
if (!conf->curve) {
wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
os_free(conf);
return NULL;
}
if (privkey)
conf->csign = dpp_set_keypair(&conf->curve, privkey,
privkey_len);
else
conf->csign = dpp_gen_keypair(conf->curve);
if (pp_key)
conf->pp_key = dpp_set_keypair(&conf->curve, pp_key,
pp_key_len);
else
conf->pp_key = dpp_gen_keypair(conf->curve);
if (!conf->csign || !conf->pp_key)
goto fail;
conf->own = 1;
if (dpp_configurator_gen_kid(conf) < 0)
goto fail;
return conf;
fail:
dpp_configurator_free(conf);
return NULL;
}
int dpp_configurator_own_config(struct dpp_authentication *auth,
const char *curve, int ap)
{
struct wpabuf *conf_obj;
int ret = -1;
if (!auth->conf) {
wpa_printf(MSG_DEBUG, "DPP: No configurator specified");
return -1;
}
auth->curve = dpp_get_curve_name(curve);
if (!auth->curve) {
wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
return -1;
}
wpa_printf(MSG_DEBUG,
"DPP: Building own configuration/connector with curve %s",
auth->curve->name);
auth->own_protocol_key = dpp_gen_keypair(auth->curve);
if (!auth->own_protocol_key)
return -1;
dpp_copy_netaccesskey(auth, &auth->conf_obj[0]);
auth->peer_protocol_key = auth->own_protocol_key;
dpp_copy_csign(&auth->conf_obj[0], auth->conf->csign);
conf_obj = dpp_build_conf_obj(auth, ap, 0, NULL);
if (!conf_obj) {
wpabuf_free(auth->conf_obj[0].c_sign_key);
auth->conf_obj[0].c_sign_key = NULL;
goto fail;
}
ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
wpabuf_len(conf_obj));
fail:
wpabuf_free(conf_obj);
auth->peer_protocol_key = NULL;
return ret;
}
static int dpp_compatible_netrole(const char *role1, const char *role2)
{
return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) ||
(os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0);
}
static int dpp_connector_compatible_group(struct json_token *root,
const char *group_id,
const char *net_role,
bool reconfig)
{
struct json_token *groups, *token;
groups = json_get_member(root, "groups");
if (!groups || groups->type != JSON_ARRAY)
return 0;
for (token = groups->child; token; token = token->sibling) {
struct json_token *id, *role;
id = json_get_member(token, "groupId");
if (!id || id->type != JSON_STRING)
continue;
role = json_get_member(token, "netRole");
if (!role || role->type != JSON_STRING)
continue;
if (os_strcmp(id->string, "*") != 0 &&
os_strcmp(group_id, "*") != 0 &&
os_strcmp(id->string, group_id) != 0)
continue;
if (reconfig && os_strcmp(net_role, "configurator") == 0)
return 1;
if (!reconfig && dpp_compatible_netrole(role->string, net_role))
return 1;
}
return 0;
}
int dpp_connector_match_groups(struct json_token *own_root,
struct json_token *peer_root, bool reconfig)
{
struct json_token *groups, *token;
groups = json_get_member(peer_root, "groups");
if (!groups || groups->type != JSON_ARRAY) {
wpa_printf(MSG_DEBUG, "DPP: No peer groups array found");
return 0;
}
for (token = groups->child; token; token = token->sibling) {
struct json_token *id, *role;
id = json_get_member(token, "groupId");
if (!id || id->type != JSON_STRING) {
wpa_printf(MSG_DEBUG,
"DPP: Missing peer groupId string");
continue;
}
role = json_get_member(token, "netRole");
if (!role || role->type != JSON_STRING) {
wpa_printf(MSG_DEBUG,
"DPP: Missing peer groups::netRole string");
continue;
}
wpa_printf(MSG_DEBUG,
"DPP: peer connector group: groupId='%s' netRole='%s'",
id->string, role->string);
if (dpp_connector_compatible_group(own_root, id->string,
role->string, reconfig)) {
wpa_printf(MSG_DEBUG,
"DPP: Compatible group/netRole in own connector");
return 1;
}
}
return 0;
}
struct json_token * dpp_parse_own_connector(const char *own_connector)
{
unsigned char *own_conn;
size_t own_conn_len;
const char *pos, *end;
struct json_token *own_root;
pos = os_strchr(own_connector, '.');
if (!pos) {
wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)");
return NULL;
}
pos++;
end = os_strchr(pos, '.');
if (!end) {
wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)");
return NULL;
}
own_conn = base64_url_decode(pos, end - pos, &own_conn_len);
if (!own_conn) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to base64url decode own signedConnector JWS Payload");
return NULL;
}
own_root = json_parse((const char *) own_conn, own_conn_len);
os_free(own_conn);
if (!own_root)
wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector");
return own_root;
}
enum dpp_status_error
dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
const u8 *net_access_key, size_t net_access_key_len,
const u8 *csign_key, size_t csign_key_len,
const u8 *peer_connector, size_t peer_connector_len,
os_time_t *expiry)
{
struct json_token *root = NULL, *netkey, *token;
struct json_token *own_root = NULL;
enum dpp_status_error ret = 255, res;
- EVP_PKEY *own_key = NULL, *peer_key = NULL;
+ struct crypto_ec_key *own_key = NULL, *peer_key = NULL;
struct wpabuf *own_key_pub = NULL;
const struct dpp_curve_params *curve, *own_curve;
struct dpp_signed_connector_info info;
size_t Nx_len;
u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
os_memset(intro, 0, sizeof(*intro));
os_memset(&info, 0, sizeof(info));
if (expiry)
*expiry = 0;
own_key = dpp_set_keypair(&own_curve, net_access_key,
net_access_key_len);
if (!own_key) {
wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
goto fail;
}
own_root = dpp_parse_own_connector(own_connector);
if (!own_root)
goto fail;
res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
peer_connector, peer_connector_len);
if (res != DPP_STATUS_OK) {
ret = res;
goto fail;
}
root = json_parse((const char *) info.payload, info.payload_len);
if (!root) {
wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
if (!dpp_connector_match_groups(own_root, root, false)) {
wpa_printf(MSG_DEBUG,
"DPP: Peer connector does not include compatible group netrole with own connector");
ret = DPP_STATUS_NO_MATCH;
goto fail;
}
token = json_get_member(root, "expiry");
if (!token || token->type != JSON_STRING) {
wpa_printf(MSG_DEBUG,
"DPP: No expiry string found - connector does not expire");
} else {
wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string);
if (dpp_key_expired(token->string, expiry)) {
wpa_printf(MSG_DEBUG,
"DPP: Connector (netAccessKey) has expired");
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
}
netkey = json_get_member(root, "netAccessKey");
if (!netkey || netkey->type != JSON_OBJECT) {
wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
peer_key = dpp_parse_jwk(netkey, &curve);
if (!peer_key) {
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
if (own_curve != curve) {
wpa_printf(MSG_DEBUG,
"DPP: Mismatching netAccessKey curves (%s != %s)",
own_curve->name, curve->name);
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
/* ECDH: N = nk * PK */
if (dpp_ecdh(own_key, peer_key, Nx, &Nx_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
Nx, Nx_len);
/* PMK = HKDF(<>, "DPP PMK", N.x) */
if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK");
goto fail;
}
intro->pmk_len = curve->hash_len;
/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID");
goto fail;
}
ret = DPP_STATUS_OK;
fail:
if (ret != DPP_STATUS_OK)
os_memset(intro, 0, sizeof(*intro));
os_memset(Nx, 0, sizeof(Nx));
os_free(info.payload);
- EVP_PKEY_free(own_key);
+ crypto_ec_key_deinit(own_key);
wpabuf_free(own_key_pub);
- EVP_PKEY_free(peer_key);
+ crypto_ec_key_deinit(peer_key);
json_free(root);
json_free(own_root);
return ret;
}
unsigned int dpp_next_id(struct dpp_global *dpp)
{
struct dpp_bootstrap_info *bi;
unsigned int max_id = 0;
dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
if (bi->id > max_id)
max_id = bi->id;
}
return max_id + 1;
}
static int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id)
{
struct dpp_bootstrap_info *bi, *tmp;
int found = 0;
if (!dpp)
return -1;
dl_list_for_each_safe(bi, tmp, &dpp->bootstrap,
struct dpp_bootstrap_info, list) {
if (id && bi->id != id)
continue;
found = 1;
#ifdef CONFIG_DPP2
if (dpp->remove_bi)
dpp->remove_bi(dpp->cb_ctx, bi);
#endif /* CONFIG_DPP2 */
dl_list_del(&bi->list);
dpp_bootstrap_info_free(bi);
}
if (id == 0)
return 0; /* flush succeeds regardless of entries found */
return found ? 0 : -1;
}
struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
const char *uri)
{
struct dpp_bootstrap_info *bi;
if (!dpp)
return NULL;
bi = dpp_parse_uri(uri);
if (!bi)
return NULL;
bi->type = DPP_BOOTSTRAP_QR_CODE;
bi->id = dpp_next_id(dpp);
dl_list_add(&dpp->bootstrap, &bi->list);
return bi;
}
struct dpp_bootstrap_info * dpp_add_nfc_uri(struct dpp_global *dpp,
const char *uri)
{
struct dpp_bootstrap_info *bi;
if (!dpp)
return NULL;
bi = dpp_parse_uri(uri);
if (!bi)
return NULL;
bi->type = DPP_BOOTSTRAP_NFC_URI;
bi->id = dpp_next_id(dpp);
dl_list_add(&dpp->bootstrap, &bi->list);
return bi;
}
int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
{
char *mac = NULL, *info = NULL, *curve = NULL;
char *key = NULL;
u8 *privkey = NULL;
size_t privkey_len = 0;
int ret = -1;
struct dpp_bootstrap_info *bi;
if (!dpp)
return -1;
bi = os_zalloc(sizeof(*bi));
if (!bi)
goto fail;
if (os_strstr(cmd, "type=qrcode"))
bi->type = DPP_BOOTSTRAP_QR_CODE;
else if (os_strstr(cmd, "type=pkex"))
bi->type = DPP_BOOTSTRAP_PKEX;
else if (os_strstr(cmd, "type=nfc-uri"))
bi->type = DPP_BOOTSTRAP_NFC_URI;
else
goto fail;
bi->chan = get_param(cmd, " chan=");
mac = get_param(cmd, " mac=");
info = get_param(cmd, " info=");
curve = get_param(cmd, " curve=");
key = get_param(cmd, " key=");
if (key) {
privkey_len = os_strlen(key) / 2;
privkey = os_malloc(privkey_len);
if (!privkey ||
hexstr2bin(key, privkey, privkey_len) < 0)
goto fail;
}
if (dpp_keygen(bi, curve, privkey, privkey_len) < 0 ||
dpp_parse_uri_chan_list(bi, bi->chan) < 0 ||
dpp_parse_uri_mac(bi, mac) < 0 ||
dpp_parse_uri_info(bi, info) < 0 ||
dpp_gen_uri(bi) < 0)
goto fail;
bi->id = dpp_next_id(dpp);
dl_list_add(&dpp->bootstrap, &bi->list);
ret = bi->id;
bi = NULL;
fail:
os_free(curve);
os_free(mac);
os_free(info);
str_clear_free(key);
bin_clear_free(privkey, privkey_len);
dpp_bootstrap_info_free(bi);
return ret;
}
struct dpp_bootstrap_info *
dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id)
{
struct dpp_bootstrap_info *bi;
if (!dpp)
return NULL;
dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
if (bi->id == id)
return bi;
}
return NULL;
}
int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id)
{
unsigned int id_val;
if (os_strcmp(id, "*") == 0) {
id_val = 0;
} else {
id_val = atoi(id);
if (id_val == 0)
return -1;
}
return dpp_bootstrap_del(dpp, id_val);
}
const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id)
{
struct dpp_bootstrap_info *bi;
bi = dpp_bootstrap_get_id(dpp, id);
if (!bi)
return NULL;
return bi->uri;
}
int dpp_bootstrap_info(struct dpp_global *dpp, int id,
char *reply, int reply_size)
{
struct dpp_bootstrap_info *bi;
char pkhash[2 * SHA256_MAC_LEN + 1];
bi = dpp_bootstrap_get_id(dpp, id);
if (!bi)
return -1;
wpa_snprintf_hex(pkhash, sizeof(pkhash), bi->pubkey_hash,
SHA256_MAC_LEN);
return os_snprintf(reply, reply_size, "type=%s\n"
"mac_addr=" MACSTR "\n"
"info=%s\n"
"num_freq=%u\n"
"use_freq=%u\n"
"curve=%s\n"
"pkhash=%s\n"
"version=%d\n",
dpp_bootstrap_type_txt(bi->type),
MAC2STR(bi->mac_addr),
bi->info ? bi->info : "",
bi->num_freq,
bi->num_freq == 1 ? bi->freq[0] : 0,
bi->curve->name,
pkhash,
bi->version);
}
int dpp_bootstrap_set(struct dpp_global *dpp, int id, const char *params)
{
struct dpp_bootstrap_info *bi;
bi = dpp_bootstrap_get_id(dpp, id);
if (!bi)
return -1;
str_clear_free(bi->configurator_params);
if (params) {
bi->configurator_params = os_strdup(params);
return bi->configurator_params ? 0 : -1;
}
bi->configurator_params = NULL;
return 0;
}
void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap,
const u8 *r_bootstrap,
struct dpp_bootstrap_info **own_bi,
struct dpp_bootstrap_info **peer_bi)
{
struct dpp_bootstrap_info *bi;
*own_bi = NULL;
*peer_bi = NULL;
if (!dpp)
return;
dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
if (!*own_bi && bi->own &&
os_memcmp(bi->pubkey_hash, r_bootstrap,
SHA256_MAC_LEN) == 0) {
wpa_printf(MSG_DEBUG,
"DPP: Found matching own bootstrapping information");
*own_bi = bi;
}
if (!*peer_bi && !bi->own &&
os_memcmp(bi->pubkey_hash, i_bootstrap,
SHA256_MAC_LEN) == 0) {
wpa_printf(MSG_DEBUG,
"DPP: Found matching peer bootstrapping information");
*peer_bi = bi;
}
if (*own_bi && *peer_bi)
break;
}
}
#ifdef CONFIG_DPP2
struct dpp_bootstrap_info * dpp_bootstrap_find_chirp(struct dpp_global *dpp,
const u8 *hash)
{
struct dpp_bootstrap_info *bi;
if (!dpp)
return NULL;
dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
if (!bi->own && os_memcmp(bi->pubkey_hash_chirp, hash,
SHA256_MAC_LEN) == 0)
return bi;
}
return NULL;
}
#endif /* CONFIG_DPP2 */
static int dpp_nfc_update_bi_channel(struct dpp_bootstrap_info *own_bi,
struct dpp_bootstrap_info *peer_bi)
{
unsigned int i, freq = 0;
enum hostapd_hw_mode mode;
u8 op_class, channel;
char chan[20];
if (peer_bi->num_freq == 0 && !peer_bi->channels_listed)
return 0; /* no channel preference/constraint */
for (i = 0; i < peer_bi->num_freq; i++) {
if ((own_bi->num_freq == 0 && !own_bi->channels_listed) ||
freq_included(own_bi->freq, own_bi->num_freq,
peer_bi->freq[i])) {
freq = peer_bi->freq[i];
break;
}
}
if (!freq) {
wpa_printf(MSG_DEBUG, "DPP: No common channel found");
return -1;
}
mode = ieee80211_freq_to_channel_ext(freq, 0, 0, &op_class, &channel);
if (mode == NUM_HOSTAPD_MODES) {
wpa_printf(MSG_DEBUG,
"DPP: Could not determine operating class or channel number for %u MHz",
freq);
}
wpa_printf(MSG_DEBUG,
"DPP: Selected %u MHz (op_class %u channel %u) as the negotiation channel based on information from NFC negotiated handover",
freq, op_class, channel);
os_snprintf(chan, sizeof(chan), "%u/%u", op_class, channel);
os_free(own_bi->chan);
own_bi->chan = os_strdup(chan);
own_bi->freq[0] = freq;
own_bi->num_freq = 1;
os_free(peer_bi->chan);
peer_bi->chan = os_strdup(chan);
peer_bi->freq[0] = freq;
peer_bi->num_freq = 1;
return dpp_gen_uri(own_bi);
}
static int dpp_nfc_update_bi_key(struct dpp_bootstrap_info *own_bi,
struct dpp_bootstrap_info *peer_bi)
{
if (peer_bi->curve == own_bi->curve)
return 0;
wpa_printf(MSG_DEBUG,
"DPP: Update own bootstrapping key to match peer curve from NFC handover");
- EVP_PKEY_free(own_bi->pubkey);
+ crypto_ec_key_deinit(own_bi->pubkey);
own_bi->pubkey = NULL;
if (dpp_keygen(own_bi, peer_bi->curve->name, NULL, 0) < 0 ||
dpp_gen_uri(own_bi) < 0)
goto fail;
return 0;
fail:
dl_list_del(&own_bi->list);
dpp_bootstrap_info_free(own_bi);
return -1;
}
int dpp_nfc_update_bi(struct dpp_bootstrap_info *own_bi,
struct dpp_bootstrap_info *peer_bi)
{
if (dpp_nfc_update_bi_channel(own_bi, peer_bi) < 0 ||
dpp_nfc_update_bi_key(own_bi, peer_bi) < 0)
return -1;
return 0;
}
static unsigned int dpp_next_configurator_id(struct dpp_global *dpp)
{
struct dpp_configurator *conf;
unsigned int max_id = 0;
dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator,
list) {
if (conf->id > max_id)
max_id = conf->id;
}
return max_id + 1;
}
int dpp_configurator_add(struct dpp_global *dpp, const char *cmd)
{
char *curve = NULL;
char *key = NULL, *ppkey = NULL;
u8 *privkey = NULL, *pp_key = NULL;
size_t privkey_len = 0, pp_key_len = 0;
int ret = -1;
struct dpp_configurator *conf = NULL;
curve = get_param(cmd, " curve=");
key = get_param(cmd, " key=");
ppkey = get_param(cmd, " ppkey=");
if (key) {
privkey_len = os_strlen(key) / 2;
privkey = os_malloc(privkey_len);
if (!privkey ||
hexstr2bin(key, privkey, privkey_len) < 0)
goto fail;
}
if (ppkey) {
pp_key_len = os_strlen(ppkey) / 2;
pp_key = os_malloc(pp_key_len);
if (!pp_key ||
hexstr2bin(ppkey, pp_key, pp_key_len) < 0)
goto fail;
}
conf = dpp_keygen_configurator(curve, privkey, privkey_len,
pp_key, pp_key_len);
if (!conf)
goto fail;
conf->id = dpp_next_configurator_id(dpp);
dl_list_add(&dpp->configurator, &conf->list);
ret = conf->id;
conf = NULL;
fail:
os_free(curve);
str_clear_free(key);
str_clear_free(ppkey);
bin_clear_free(privkey, privkey_len);
bin_clear_free(pp_key, pp_key_len);
dpp_configurator_free(conf);
return ret;
}
static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id)
{
struct dpp_configurator *conf, *tmp;
int found = 0;
if (!dpp)
return -1;
dl_list_for_each_safe(conf, tmp, &dpp->configurator,
struct dpp_configurator, list) {
if (id && conf->id != id)
continue;
found = 1;
dl_list_del(&conf->list);
dpp_configurator_free(conf);
}
if (id == 0)
return 0; /* flush succeeds regardless of entries found */
return found ? 0 : -1;
}
int dpp_configurator_remove(struct dpp_global *dpp, const char *id)
{
unsigned int id_val;
if (os_strcmp(id, "*") == 0) {
id_val = 0;
} else {
id_val = atoi(id);
if (id_val == 0)
return -1;
}
return dpp_configurator_del(dpp, id_val);
}
int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
char *buf, size_t buflen)
{
struct dpp_configurator *conf;
conf = dpp_configurator_get_id(dpp, id);
if (!conf)
return -1;
return dpp_configurator_get_key(conf, buf, buflen);
}
#ifdef CONFIG_DPP2
int dpp_configurator_from_backup(struct dpp_global *dpp,
struct dpp_asymmetric_key *key)
{
struct dpp_configurator *conf;
- const EC_KEY *eckey, *eckey_pp;
- const EC_GROUP *group, *group_pp;
- int nid;
- const struct dpp_curve_params *curve;
+ const struct dpp_curve_params *curve, *curve_pp;
if (!key->csign || !key->pp_key)
return -1;
- eckey = EVP_PKEY_get0_EC_KEY(key->csign);
- if (!eckey)
- return -1;
- group = EC_KEY_get0_group(eckey);
- if (!group)
- return -1;
- nid = EC_GROUP_get_curve_name(group);
- curve = dpp_get_curve_nid(nid);
+
+ curve = dpp_get_curve_ike_group(crypto_ec_key_group(key->csign));
if (!curve) {
wpa_printf(MSG_INFO, "DPP: Unsupported group in c-sign-key");
return -1;
}
- eckey_pp = EVP_PKEY_get0_EC_KEY(key->pp_key);
- if (!eckey_pp)
- return -1;
- group_pp = EC_KEY_get0_group(eckey_pp);
- if (!group_pp)
+
+ curve_pp = dpp_get_curve_ike_group(crypto_ec_key_group(key->pp_key));
+ if (!curve_pp) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported group in ppKey");
return -1;
- if (EC_GROUP_get_curve_name(group) !=
- EC_GROUP_get_curve_name(group_pp)) {
+ }
+
+ if (curve != curve_pp) {
wpa_printf(MSG_INFO,
"DPP: Mismatch in c-sign-key and ppKey groups");
return -1;
}
conf = os_zalloc(sizeof(*conf));
if (!conf)
return -1;
conf->curve = curve;
conf->csign = key->csign;
key->csign = NULL;
conf->pp_key = key->pp_key;
key->pp_key = NULL;
conf->own = 1;
if (dpp_configurator_gen_kid(conf) < 0) {
dpp_configurator_free(conf);
return -1;
}
conf->id = dpp_next_configurator_id(dpp);
dl_list_add(&dpp->configurator, &conf->list);
return conf->id;
}
struct dpp_configurator * dpp_configurator_find_kid(struct dpp_global *dpp,
const u8 *kid)
{
struct dpp_configurator *conf;
if (!dpp)
return NULL;
dl_list_for_each(conf, &dpp->configurator,
struct dpp_configurator, list) {
if (os_memcmp(conf->kid_hash, kid, SHA256_MAC_LEN) == 0)
return conf;
}
return NULL;
}
#endif /* CONFIG_DPP2 */
struct dpp_global * dpp_global_init(struct dpp_global_config *config)
{
struct dpp_global *dpp;
dpp = os_zalloc(sizeof(*dpp));
if (!dpp)
return NULL;
#ifdef CONFIG_DPP2
dpp->cb_ctx = config->cb_ctx;
dpp->remove_bi = config->remove_bi;
#endif /* CONFIG_DPP2 */
dl_list_init(&dpp->bootstrap);
dl_list_init(&dpp->configurator);
#ifdef CONFIG_DPP2
dl_list_init(&dpp->controllers);
dl_list_init(&dpp->tcp_init);
#endif /* CONFIG_DPP2 */
return dpp;
}
void dpp_global_clear(struct dpp_global *dpp)
{
if (!dpp)
return;
dpp_bootstrap_del(dpp, 0);
dpp_configurator_del(dpp, 0);
#ifdef CONFIG_DPP2
dpp_tcp_init_flush(dpp);
dpp_relay_flush_controllers(dpp);
dpp_controller_stop(dpp);
#endif /* CONFIG_DPP2 */
}
void dpp_global_deinit(struct dpp_global *dpp)
{
dpp_global_clear(dpp);
os_free(dpp);
}
#ifdef CONFIG_DPP2
struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi)
{
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "DPP: Build Presence Announcement frame");
msg = dpp_alloc_msg(DPP_PA_PRESENCE_ANNOUNCEMENT, 4 + SHA256_MAC_LEN);
if (!msg)
return NULL;
/* Responder Bootstrapping Key Hash */
dpp_build_attr_r_bootstrap_key_hash(msg, bi->pubkey_hash_chirp);
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Presence Announcement frame attributes", msg);
return msg;
}
void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src,
unsigned int freq, const u8 *hash)
{
char hex[SHA256_MAC_LEN * 2 + 1];
wpa_snprintf_hex(hex, sizeof(hex), hash, SHA256_MAC_LEN);
wpa_msg(msg_ctx, MSG_INFO,
DPP_EVENT_CHIRP_RX "id=%d src=" MACSTR " freq=%u hash=%s",
id, MAC2STR(src), freq, hex);
}
#endif /* CONFIG_DPP2 */
diff --git a/contrib/wpa/src/common/dpp.h b/contrib/wpa/src/common/dpp.h
index 75de3cae93e9..a47c685f64b9 100644
--- a/contrib/wpa/src/common/dpp.h
+++ b/contrib/wpa/src/common/dpp.h
@@ -1,736 +1,734 @@
/*
* DPP functionality shared between hostapd and wpa_supplicant
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef DPP_H
#define DPP_H
#ifdef CONFIG_DPP
-#include <openssl/x509.h>
-
#include "utils/list.h"
#include "common/wpa_common.h"
#include "crypto/sha256.h"
+#include "crypto/crypto.h"
-struct crypto_ecdh;
struct hostapd_ip_addr;
struct dpp_global;
struct json_token;
struct dpp_reconfig_id;
#ifdef CONFIG_TESTING_OPTIONS
#define DPP_VERSION (dpp_version_override)
extern int dpp_version_override;
#else /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_DPP2
#define DPP_VERSION 2
#else
#define DPP_VERSION 1
#endif
#endif /* CONFIG_TESTING_OPTIONS */
#define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
#define DPP_TCP_PORT 8908
enum dpp_public_action_frame_type {
DPP_PA_AUTHENTICATION_REQ = 0,
DPP_PA_AUTHENTICATION_RESP = 1,
DPP_PA_AUTHENTICATION_CONF = 2,
DPP_PA_PEER_DISCOVERY_REQ = 5,
DPP_PA_PEER_DISCOVERY_RESP = 6,
DPP_PA_PKEX_EXCHANGE_REQ = 7,
DPP_PA_PKEX_EXCHANGE_RESP = 8,
DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
DPP_PA_CONFIGURATION_RESULT = 11,
DPP_PA_CONNECTION_STATUS_RESULT = 12,
DPP_PA_PRESENCE_ANNOUNCEMENT = 13,
DPP_PA_RECONFIG_ANNOUNCEMENT = 14,
DPP_PA_RECONFIG_AUTH_REQ = 15,
DPP_PA_RECONFIG_AUTH_RESP = 16,
DPP_PA_RECONFIG_AUTH_CONF = 17,
};
enum dpp_attribute_id {
DPP_ATTR_STATUS = 0x1000,
DPP_ATTR_I_BOOTSTRAP_KEY_HASH = 0x1001,
DPP_ATTR_R_BOOTSTRAP_KEY_HASH = 0x1002,
DPP_ATTR_I_PROTOCOL_KEY = 0x1003,
DPP_ATTR_WRAPPED_DATA = 0x1004,
DPP_ATTR_I_NONCE = 0x1005,
DPP_ATTR_I_CAPABILITIES = 0x1006,
DPP_ATTR_R_NONCE = 0x1007,
DPP_ATTR_R_CAPABILITIES = 0x1008,
DPP_ATTR_R_PROTOCOL_KEY = 0x1009,
DPP_ATTR_I_AUTH_TAG = 0x100A,
DPP_ATTR_R_AUTH_TAG = 0x100B,
DPP_ATTR_CONFIG_OBJ = 0x100C,
DPP_ATTR_CONNECTOR = 0x100D,
DPP_ATTR_CONFIG_ATTR_OBJ = 0x100E,
DPP_ATTR_BOOTSTRAP_KEY = 0x100F,
DPP_ATTR_OWN_NET_NK_HASH = 0x1011,
DPP_ATTR_FINITE_CYCLIC_GROUP = 0x1012,
DPP_ATTR_ENCRYPTED_KEY = 0x1013,
DPP_ATTR_ENROLLEE_NONCE = 0x1014,
DPP_ATTR_CODE_IDENTIFIER = 0x1015,
DPP_ATTR_TRANSACTION_ID = 0x1016,
DPP_ATTR_BOOTSTRAP_INFO = 0x1017,
DPP_ATTR_CHANNEL = 0x1018,
DPP_ATTR_PROTOCOL_VERSION = 0x1019,
DPP_ATTR_ENVELOPED_DATA = 0x101A,
DPP_ATTR_SEND_CONN_STATUS = 0x101B,
DPP_ATTR_CONN_STATUS = 0x101C,
DPP_ATTR_RECONFIG_FLAGS = 0x101D,
DPP_ATTR_C_SIGN_KEY_HASH = 0x101E,
DPP_ATTR_CSR_ATTR_REQ = 0x101F,
DPP_ATTR_A_NONCE = 0x1020,
DPP_ATTR_E_PRIME_ID = 0x1021,
DPP_ATTR_CONFIGURATOR_NONCE = 0x1022,
};
enum dpp_status_error {
DPP_STATUS_OK = 0,
DPP_STATUS_NOT_COMPATIBLE = 1,
DPP_STATUS_AUTH_FAILURE = 2,
DPP_STATUS_UNWRAP_FAILURE = 3,
DPP_STATUS_BAD_GROUP = 4,
DPP_STATUS_CONFIGURE_FAILURE = 5,
DPP_STATUS_RESPONSE_PENDING = 6,
DPP_STATUS_INVALID_CONNECTOR = 7,
DPP_STATUS_NO_MATCH = 8,
DPP_STATUS_CONFIG_REJECTED = 9,
DPP_STATUS_NO_AP = 10,
DPP_STATUS_CONFIGURE_PENDING = 11,
DPP_STATUS_CSR_NEEDED = 12,
DPP_STATUS_CSR_BAD = 13,
};
/* DPP Reconfig Flags object - connectorKey values */
enum dpp_connector_key {
DPP_CONFIG_REUSEKEY = 0,
DPP_CONFIG_REPLACEKEY = 1,
};
#define DPP_CAPAB_ENROLLEE BIT(0)
#define DPP_CAPAB_CONFIGURATOR BIT(1)
#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
#define DPP_BOOTSTRAP_MAX_FREQ 30
#define DPP_MAX_NONCE_LEN 32
#define DPP_MAX_HASH_LEN 64
#define DPP_MAX_SHARED_SECRET_LEN 66
#define DPP_CP_LEN 64
struct dpp_curve_params {
const char *name;
size_t hash_len;
size_t aes_siv_key_len;
size_t nonce_len;
size_t prime_len;
const char *jwk_crv;
u16 ike_group;
const char *jws_alg;
};
enum dpp_bootstrap_type {
DPP_BOOTSTRAP_QR_CODE,
DPP_BOOTSTRAP_PKEX,
DPP_BOOTSTRAP_NFC_URI,
};
struct dpp_bootstrap_info {
struct dl_list list;
unsigned int id;
enum dpp_bootstrap_type type;
char *uri;
u8 mac_addr[ETH_ALEN];
char *chan;
char *info;
char *pk;
unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
unsigned int num_freq;
bool channels_listed;
u8 version;
int own;
- EVP_PKEY *pubkey;
+ struct crypto_ec_key *pubkey;
u8 pubkey_hash[SHA256_MAC_LEN];
u8 pubkey_hash_chirp[SHA256_MAC_LEN];
const struct dpp_curve_params *curve;
unsigned int pkex_t; /* number of failures before dpp_pkex
* instantiation */
int nfc_negotiated; /* whether this has been used in NFC negotiated
* connection handover */
char *configurator_params;
};
#define PKEX_COUNTER_T_LIMIT 5
struct dpp_pkex {
void *msg_ctx;
unsigned int initiator:1;
unsigned int exchange_done:1;
unsigned int failed:1;
struct dpp_bootstrap_info *own_bi;
u8 own_mac[ETH_ALEN];
u8 peer_mac[ETH_ALEN];
char *identifier;
char *code;
- EVP_PKEY *x;
- EVP_PKEY *y;
+ struct crypto_ec_key *x;
+ struct crypto_ec_key *y;
u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
u8 z[DPP_MAX_HASH_LEN];
- EVP_PKEY *peer_bootstrap_key;
+ struct crypto_ec_key *peer_bootstrap_key;
struct wpabuf *exchange_req;
struct wpabuf *exchange_resp;
unsigned int t; /* number of failures on code use */
unsigned int exch_req_wait_time;
unsigned int exch_req_tries;
unsigned int freq;
};
enum dpp_akm {
DPP_AKM_UNKNOWN,
DPP_AKM_DPP,
DPP_AKM_PSK,
DPP_AKM_SAE,
DPP_AKM_PSK_SAE,
DPP_AKM_SAE_DPP,
DPP_AKM_PSK_SAE_DPP,
DPP_AKM_DOT1X,
};
enum dpp_netrole {
DPP_NETROLE_STA,
DPP_NETROLE_AP,
DPP_NETROLE_CONFIGURATOR,
};
struct dpp_configuration {
u8 ssid[32];
size_t ssid_len;
int ssid_charset;
enum dpp_akm akm;
enum dpp_netrole netrole;
/* For DPP configuration (connector) */
os_time_t netaccesskey_expiry;
/* TODO: groups */
char *group_id;
/* For legacy configuration */
char *passphrase;
u8 psk[32];
int psk_set;
char *csrattrs;
};
struct dpp_asymmetric_key {
struct dpp_asymmetric_key *next;
- EVP_PKEY *csign;
- EVP_PKEY *pp_key;
+ struct crypto_ec_key *csign;
+ struct crypto_ec_key *pp_key;
char *config_template;
char *connector_template;
};
#define DPP_MAX_CONF_OBJ 10
struct dpp_authentication {
struct dpp_global *global;
void *msg_ctx;
u8 peer_version;
const struct dpp_curve_params *curve;
struct dpp_bootstrap_info *peer_bi;
struct dpp_bootstrap_info *own_bi;
struct dpp_bootstrap_info *tmp_own_bi;
struct dpp_bootstrap_info *tmp_peer_bi;
u8 waiting_pubkey_hash[SHA256_MAC_LEN];
int response_pending;
int reconfig;
enum dpp_connector_key reconfig_connector_key;
enum dpp_status_error auth_resp_status;
enum dpp_status_error conf_resp_status;
enum dpp_status_error force_conf_resp_status;
u8 peer_mac_addr[ETH_ALEN];
u8 i_nonce[DPP_MAX_NONCE_LEN];
u8 r_nonce[DPP_MAX_NONCE_LEN];
u8 e_nonce[DPP_MAX_NONCE_LEN];
u8 c_nonce[DPP_MAX_NONCE_LEN];
u8 i_capab;
u8 r_capab;
enum dpp_netrole e_netrole;
- EVP_PKEY *own_protocol_key;
- EVP_PKEY *peer_protocol_key;
- EVP_PKEY *reconfig_old_protocol_key;
+ struct crypto_ec_key *own_protocol_key;
+ struct crypto_ec_key *peer_protocol_key;
+ struct crypto_ec_key *reconfig_old_protocol_key;
struct wpabuf *req_msg;
struct wpabuf *resp_msg;
struct wpabuf *reconfig_req_msg;
struct wpabuf *reconfig_resp_msg;
/* Intersection of possible frequencies for initiating DPP
* Authentication exchange */
unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ];
unsigned int num_freq, freq_idx;
unsigned int curr_freq;
unsigned int neg_freq;
unsigned int num_freq_iters;
size_t secret_len;
u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
size_t Mx_len;
u8 Nx[DPP_MAX_SHARED_SECRET_LEN];
size_t Nx_len;
u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
size_t Lx_len;
u8 k1[DPP_MAX_HASH_LEN];
u8 k2[DPP_MAX_HASH_LEN];
u8 ke[DPP_MAX_HASH_LEN];
u8 bk[DPP_MAX_HASH_LEN];
int initiator;
int waiting_auth_resp;
int waiting_auth_conf;
int auth_req_ack;
unsigned int auth_resp_tries;
u8 allowed_roles;
int configurator;
int remove_on_tx_status;
int connect_on_tx_status;
int waiting_conf_result;
int waiting_conn_status_result;
int auth_success;
bool reconfig_success;
struct wpabuf *conf_req;
const struct wpabuf *conf_resp; /* owned by GAS server */
struct wpabuf *conf_resp_tcp;
struct dpp_configuration *conf_ap;
struct dpp_configuration *conf2_ap;
struct dpp_configuration *conf_sta;
struct dpp_configuration *conf2_sta;
int provision_configurator;
struct dpp_configurator *conf;
struct dpp_config_obj {
char *connector; /* received signedConnector */
u8 ssid[SSID_MAX_LEN];
u8 ssid_len;
int ssid_charset;
char passphrase[64];
u8 psk[PMK_LEN];
int psk_set;
enum dpp_akm akm;
struct wpabuf *c_sign_key;
struct wpabuf *certbag;
struct wpabuf *certs;
struct wpabuf *cacert;
char *server_name;
struct wpabuf *pp_key;
} conf_obj[DPP_MAX_CONF_OBJ];
unsigned int num_conf_obj;
struct dpp_asymmetric_key *conf_key_pkg;
struct wpabuf *net_access_key;
os_time_t net_access_key_expiry;
int send_conn_status;
int conn_status_requested;
int akm_use_selector;
int configurator_set;
u8 transaction_id;
u8 *csrattrs;
size_t csrattrs_len;
bool waiting_csr;
struct wpabuf *csr;
struct wpabuf *priv_key; /* DER-encoded private key used for csr */
bool waiting_cert;
char *trusted_eap_server_name;
struct wpabuf *cacert;
struct wpabuf *certbag;
void *cert_resp_ctx;
void *gas_server_ctx;
#ifdef CONFIG_TESTING_OPTIONS
char *config_obj_override;
char *discovery_override;
char *groups_override;
unsigned int ignore_netaccesskey_mismatch:1;
#endif /* CONFIG_TESTING_OPTIONS */
};
struct dpp_configurator {
struct dl_list list;
unsigned int id;
int own;
- EVP_PKEY *csign;
+ struct crypto_ec_key *csign;
u8 kid_hash[SHA256_MAC_LEN];
char *kid;
const struct dpp_curve_params *curve;
char *connector; /* own Connector for reconfiguration */
- EVP_PKEY *connector_key;
- EVP_PKEY *pp_key;
+ struct crypto_ec_key *connector_key;
+ struct crypto_ec_key *pp_key;
};
struct dpp_introduction {
u8 pmkid[PMKID_LEN];
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
};
struct dpp_relay_config {
const struct hostapd_ip_addr *ipaddr;
const u8 *pkhash;
void *msg_ctx;
void *cb_ctx;
void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
size_t len);
void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token, int prot,
struct wpabuf *buf);
};
struct dpp_controller_config {
const char *configurator_params;
int tcp_port;
u8 allowed_roles;
int qr_mutual;
enum dpp_netrole netrole;
void *msg_ctx;
void *cb_ctx;
int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
};
#ifdef CONFIG_TESTING_OPTIONS
enum dpp_test_behavior {
DPP_TEST_DISABLED = 0,
DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ = 1,
DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP = 2,
DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF = 3,
DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ = 4,
DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP = 5,
DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ = 6,
DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP = 7,
DPP_TEST_ZERO_I_CAPAB = 8,
DPP_TEST_ZERO_R_CAPAB = 9,
DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 10,
DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 11,
DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ = 12,
DPP_TEST_NO_I_NONCE_AUTH_REQ = 13,
DPP_TEST_NO_I_CAPAB_AUTH_REQ = 14,
DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ = 15,
DPP_TEST_NO_STATUS_AUTH_RESP = 16,
DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 17,
DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 18,
DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP = 19,
DPP_TEST_NO_R_NONCE_AUTH_RESP = 20,
DPP_TEST_NO_I_NONCE_AUTH_RESP = 21,
DPP_TEST_NO_R_CAPAB_AUTH_RESP = 22,
DPP_TEST_NO_R_AUTH_AUTH_RESP = 23,
DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP = 24,
DPP_TEST_NO_STATUS_AUTH_CONF = 25,
DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 26,
DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 27,
DPP_TEST_NO_I_AUTH_AUTH_CONF = 28,
DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF = 29,
DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP = 30,
DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP = 31,
DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP = 32,
DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF = 33,
DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ = 34,
DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 35,
DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP = 36,
DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 37,
DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ = 38,
DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ = 39,
DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ = 40,
DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP = 41,
DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP = 42,
DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP = 43,
DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 44,
DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 45,
DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP = 46,
DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ = 47,
DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP = 48,
DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ = 49,
DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP = 50,
DPP_TEST_NO_E_NONCE_CONF_REQ = 51,
DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ = 52,
DPP_TEST_NO_WRAPPED_DATA_CONF_REQ = 53,
DPP_TEST_NO_E_NONCE_CONF_RESP = 54,
DPP_TEST_NO_CONFIG_OBJ_CONF_RESP = 55,
DPP_TEST_NO_STATUS_CONF_RESP = 56,
DPP_TEST_NO_WRAPPED_DATA_CONF_RESP = 57,
DPP_TEST_INVALID_STATUS_CONF_RESP = 58,
DPP_TEST_E_NONCE_MISMATCH_CONF_RESP = 59,
DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_REQ = 60,
DPP_TEST_NO_CONNECTOR_PEER_DISC_REQ = 61,
DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP = 62,
DPP_TEST_NO_STATUS_PEER_DISC_RESP = 63,
DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP = 64,
DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF = 65,
DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ = 66,
DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP = 67,
DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 68,
DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 69,
DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 70,
DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 71,
DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 72,
DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 73,
DPP_TEST_INVALID_STATUS_AUTH_RESP = 74,
DPP_TEST_INVALID_STATUS_AUTH_CONF = 75,
DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ = 76,
DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP = 77,
DPP_TEST_INVALID_STATUS_PEER_DISC_RESP = 78,
DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP = 79,
DPP_TEST_INVALID_CONNECTOR_PEER_DISC_REQ = 80,
DPP_TEST_INVALID_I_NONCE_AUTH_REQ = 81,
DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_REQ = 82,
DPP_TEST_INVALID_E_NONCE_CONF_REQ = 83,
DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP = 84,
DPP_TEST_STOP_AT_PKEX_CR_REQ = 85,
DPP_TEST_STOP_AT_PKEX_CR_RESP = 86,
DPP_TEST_STOP_AT_AUTH_REQ = 87,
DPP_TEST_STOP_AT_AUTH_RESP = 88,
DPP_TEST_STOP_AT_AUTH_CONF = 89,
DPP_TEST_STOP_AT_CONF_REQ = 90,
DPP_TEST_REJECT_CONFIG = 91,
};
extern enum dpp_test_behavior dpp_test;
extern u8 dpp_pkex_own_mac_override[ETH_ALEN];
extern u8 dpp_pkex_peer_mac_override[ETH_ALEN];
extern u8 dpp_pkex_ephemeral_key_override[600];
extern size_t dpp_pkex_ephemeral_key_override_len;
extern u8 dpp_protocol_key_override[600];
extern size_t dpp_protocol_key_override_len;
extern u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
extern size_t dpp_nonce_override_len;
#endif /* CONFIG_TESTING_OPTIONS */
void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info);
const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type);
int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
const char *chan_list);
int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac);
int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info);
int dpp_nfc_update_bi(struct dpp_bootstrap_info *own_bi,
struct dpp_bootstrap_info *peer_bi);
struct dpp_authentication *
dpp_alloc_auth(struct dpp_global *dpp, void *msg_ctx);
struct hostapd_hw_modes;
struct dpp_authentication * dpp_auth_init(struct dpp_global *dpp, void *msg_ctx,
struct dpp_bootstrap_info *peer_bi,
struct dpp_bootstrap_info *own_bi,
u8 dpp_allowed_roles,
unsigned int neg_freq,
struct hostapd_hw_modes *own_modes,
u16 num_modes);
struct dpp_authentication *
dpp_auth_req_rx(struct dpp_global *dpp, void *msg_ctx, u8 dpp_allowed_roles,
int qr_mutual, struct dpp_bootstrap_info *peer_bi,
struct dpp_bootstrap_info *own_bi,
unsigned int freq, const u8 *hdr, const u8 *attr_start,
size_t attr_len);
struct wpabuf *
dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len);
struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
const char *json);
struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
const char *name,
enum dpp_netrole netrole,
const char *mud_url, int *opclasses);
int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len);
int dpp_notify_new_qr_code(struct dpp_authentication *auth,
struct dpp_bootstrap_info *peer_bi);
struct dpp_configuration * dpp_configuration_alloc(const char *type);
int dpp_akm_psk(enum dpp_akm akm);
int dpp_akm_sae(enum dpp_akm akm);
int dpp_akm_legacy(enum dpp_akm akm);
int dpp_akm_dpp(enum dpp_akm akm);
int dpp_akm_ver2(enum dpp_akm akm);
int dpp_configuration_valid(const struct dpp_configuration *conf);
void dpp_configuration_free(struct dpp_configuration *conf);
int dpp_set_configurator(struct dpp_authentication *auth, const char *cmd);
void dpp_auth_deinit(struct dpp_authentication *auth);
struct wpabuf *
dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
u16 e_nonce_len, enum dpp_netrole netrole,
bool cert_req);
struct wpabuf *
dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
size_t attr_len);
int dpp_conf_resp_rx(struct dpp_authentication *auth,
const struct wpabuf *resp);
enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
const u8 *hdr,
const u8 *attr_start, size_t attr_len);
struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
enum dpp_status_error status);
enum dpp_status_error dpp_conn_status_result_rx(struct dpp_authentication *auth,
const u8 *hdr,
const u8 *attr_start,
size_t attr_len,
u8 *ssid, size_t *ssid_len,
char **channel_list);
struct wpabuf * dpp_build_conn_status_result(struct dpp_authentication *auth,
enum dpp_status_error result,
const u8 *ssid, size_t ssid_len,
const char *channel_list);
struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
size_t len);
const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
int dpp_check_attrs(const u8 *buf, size_t len);
int dpp_key_expired(const char *timestamp, os_time_t *expiry);
const char * dpp_akm_str(enum dpp_akm akm);
const char * dpp_akm_selector_str(enum dpp_akm akm);
int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf,
size_t buflen);
void dpp_configurator_free(struct dpp_configurator *conf);
int dpp_configurator_own_config(struct dpp_authentication *auth,
const char *curve, int ap);
enum dpp_status_error
dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
const u8 *net_access_key, size_t net_access_key_len,
const u8 *csign_key, size_t csign_key_len,
const u8 *peer_connector, size_t peer_connector_len,
os_time_t *expiry);
struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const char *identifier,
const char *code);
struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const u8 *peer_mac,
const char *identifier,
const char *code,
const u8 *buf, size_t len);
struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
const u8 *peer_mac,
const u8 *buf, size_t len);
struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
const u8 *hdr,
const u8 *buf, size_t len);
int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
const u8 *buf, size_t len);
void dpp_pkex_free(struct dpp_pkex *pkex);
char * dpp_corrupt_connector_signature(const char *connector);
struct dpp_pfs {
struct crypto_ecdh *ecdh;
const struct dpp_curve_params *curve;
struct wpabuf *ie;
struct wpabuf *secret;
};
struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
size_t net_access_key_len);
int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len);
void dpp_pfs_free(struct dpp_pfs *pfs);
struct wpabuf * dpp_build_csr(struct dpp_authentication *auth,
const char *name);
-struct wpabuf * dpp_pkcs7_certs(const struct wpabuf *pkcs7);
int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr);
struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
const char *uri);
struct dpp_bootstrap_info * dpp_add_nfc_uri(struct dpp_global *dpp,
const char *uri);
int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd);
struct dpp_bootstrap_info *
dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id);
int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id);
struct dpp_bootstrap_info *
dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
unsigned int freq);
const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id);
int dpp_bootstrap_info(struct dpp_global *dpp, int id,
char *reply, int reply_size);
int dpp_bootstrap_set(struct dpp_global *dpp, int id, const char *params);
void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap,
const u8 *r_bootstrap,
struct dpp_bootstrap_info **own_bi,
struct dpp_bootstrap_info **peer_bi);
struct dpp_bootstrap_info * dpp_bootstrap_find_chirp(struct dpp_global *dpp,
const u8 *hash);
int dpp_configurator_add(struct dpp_global *dpp, const char *cmd);
int dpp_configurator_remove(struct dpp_global *dpp, const char *id);
int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
char *buf, size_t buflen);
int dpp_configurator_from_backup(struct dpp_global *dpp,
struct dpp_asymmetric_key *key);
struct dpp_configurator * dpp_configurator_find_kid(struct dpp_global *dpp,
const u8 *kid);
int dpp_relay_add_controller(struct dpp_global *dpp,
struct dpp_relay_config *config);
int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
const u8 *buf, size_t len, unsigned int freq,
const u8 *i_bootstrap, const u8 *r_bootstrap,
void *cb_ctx);
int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
size_t data_len);
int dpp_controller_start(struct dpp_global *dpp,
struct dpp_controller_config *config);
void dpp_controller_stop(struct dpp_global *dpp);
+void dpp_controller_stop_for_ctx(struct dpp_global *dpp, void *cb_ctx);
struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp,
unsigned int id);
void dpp_controller_new_qr_code(struct dpp_global *dpp,
struct dpp_bootstrap_info *bi);
int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
const struct hostapd_ip_addr *addr, int port,
const char *name, enum dpp_netrole netrole, void *msg_ctx,
void *cb_ctx,
int (*process_conf_obj)(void *ctx,
struct dpp_authentication *auth));
struct wpabuf * dpp_build_presence_announcement(struct dpp_bootstrap_info *bi);
void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src,
unsigned int freq, const u8 *hash);
struct dpp_global_config {
void *cb_ctx;
void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
};
struct dpp_global * dpp_global_init(struct dpp_global_config *config);
void dpp_global_clear(struct dpp_global *dpp);
void dpp_global_deinit(struct dpp_global *dpp);
/* dpp_reconfig.c */
struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
size_t csign_key_len,
const u8 *net_access_key,
size_t net_access_key_len,
struct dpp_reconfig_id *id);
struct dpp_authentication *
dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
struct dpp_configurator *conf, unsigned int freq, u16 group,
const u8 *a_nonce_attr, size_t a_nonce_len,
const u8 *e_id_attr, size_t e_id_len);
struct dpp_authentication *
dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
const char *own_connector,
const u8 *net_access_key, size_t net_access_key_len,
const u8 *csign_key, size_t csign_key_len,
unsigned int freq, const u8 *hdr,
const u8 *attr_start, size_t attr_len);
struct wpabuf *
dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len);
int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len);
struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key,
size_t csign_key_len,
const u8 *pp_key,
size_t pp_key_len);
int dpp_update_reconfig_id(struct dpp_reconfig_id *id);
void dpp_free_reconfig_id(struct dpp_reconfig_id *id);
#endif /* CONFIG_DPP */
#endif /* DPP_H */
diff --git a/contrib/wpa/src/common/dpp_auth.c b/contrib/wpa/src/common/dpp_auth.c
index 0cabd647fb81..f81f1eecbc7e 100644
--- a/contrib/wpa/src/common/dpp_auth.c
+++ b/contrib/wpa/src/common/dpp_auth.c
@@ -1,1977 +1,1975 @@
/*
* DPP authentication exchange
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
#include "crypto/random.h"
#include "dpp.h"
#include "dpp_i.h"
#ifdef CONFIG_TESTING_OPTIONS
u8 dpp_protocol_key_override[600];
size_t dpp_protocol_key_override_len = 0;
u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
size_t dpp_nonce_override_len = 0;
#endif /* CONFIG_TESTING_OPTIONS */
static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
const u8 *hash)
{
if (hash) {
wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
wpabuf_put_le16(msg, SHA256_MAC_LEN);
wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
}
}
static void dpp_auth_success(struct dpp_authentication *auth)
{
wpa_printf(MSG_DEBUG,
"DPP: Authentication success - clear temporary keys");
os_memset(auth->Mx, 0, sizeof(auth->Mx));
auth->Mx_len = 0;
os_memset(auth->Nx, 0, sizeof(auth->Nx));
auth->Nx_len = 0;
os_memset(auth->Lx, 0, sizeof(auth->Lx));
auth->Lx_len = 0;
os_memset(auth->k1, 0, sizeof(auth->k1));
os_memset(auth->k2, 0, sizeof(auth->k2));
auth->auth_success = 1;
}
static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
const struct wpabuf *pi,
size_t nonce_len,
const u8 *r_pubkey_hash,
const u8 *i_pubkey_hash,
unsigned int neg_freq)
{
struct wpabuf *msg;
u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
u8 *pos;
const u8 *addr[2];
size_t len[2], siv_len, attr_len;
u8 *attr_start, *attr_end;
/* Build DPP Authentication Request frame attributes */
attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
4 + sizeof(wrapped_data);
if (neg_freq > 0)
attr_len += 4 + 2;
#ifdef CONFIG_DPP2
attr_len += 5;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
attr_len += 5;
#endif /* CONFIG_TESTING_OPTIONS */
msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
if (!msg)
return NULL;
attr_start = wpabuf_put(msg, 0);
/* Responder Bootstrapping Key Hash */
dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
/* Initiator Bootstrapping Key Hash */
dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
/* Initiator Protocol Key */
if (pi) {
wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
wpabuf_put_le16(msg, wpabuf_len(pi));
wpabuf_put_buf(msg, pi);
}
/* Channel */
if (neg_freq > 0) {
u8 op_class, channel;
if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
&channel) ==
NUM_HOSTAPD_MODES) {
wpa_printf(MSG_INFO,
"DPP: Unsupported negotiation frequency request: %d",
neg_freq);
wpabuf_free(msg);
return NULL;
}
wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
wpabuf_put_le16(msg, 2);
wpabuf_put_u8(msg, op_class);
wpabuf_put_u8(msg, channel);
}
#ifdef CONFIG_DPP2
/* Protocol Version */
if (DPP_VERSION > 1) {
wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, DPP_VERSION);
}
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
goto skip_wrapped_data;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* Wrapped data ({I-nonce, I-capabilities}k1) */
pos = clear;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
goto skip_i_nonce;
}
if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
pos += 2;
WPA_PUT_LE16(pos, nonce_len - 1);
pos += 2;
os_memcpy(pos, auth->i_nonce, nonce_len - 1);
pos += nonce_len - 1;
goto skip_i_nonce;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* I-nonce */
WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
pos += 2;
WPA_PUT_LE16(pos, nonce_len);
pos += 2;
os_memcpy(pos, auth->i_nonce, nonce_len);
pos += nonce_len;
#ifdef CONFIG_TESTING_OPTIONS
skip_i_nonce:
if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
goto skip_i_capab;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* I-capabilities */
WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
pos += 2;
WPA_PUT_LE16(pos, 1);
pos += 2;
auth->i_capab = auth->allowed_roles;
*pos++ = auth->i_capab;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
pos[-1] = 0;
}
skip_i_capab:
#endif /* CONFIG_TESTING_OPTIONS */
attr_end = wpabuf_put(msg, 0);
/* OUI, OUI type, Crypto Suite, DPP frame type */
addr[0] = wpabuf_head_u8(msg) + 2;
len[0] = 3 + 1 + 1 + 1;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
/* Attributes before Wrapped Data */
addr[1] = attr_start;
len[1] = attr_end - attr_start;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
siv_len = pos - clear;
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
2, addr, len, wrapped_data) < 0) {
wpabuf_free(msg);
return NULL;
}
siv_len += AES_BLOCK_SIZE;
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, siv_len);
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, siv_len);
wpabuf_put_data(msg, wrapped_data, siv_len);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
dpp_build_attr_status(msg, DPP_STATUS_OK);
}
skip_wrapped_data:
#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Authentication Request frame attributes", msg);
return msg;
}
static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
enum dpp_status_error status,
const struct wpabuf *pr,
size_t nonce_len,
const u8 *r_pubkey_hash,
const u8 *i_pubkey_hash,
const u8 *r_nonce, const u8 *i_nonce,
const u8 *wrapped_r_auth,
size_t wrapped_r_auth_len,
const u8 *siv_key)
{
struct wpabuf *msg;
#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
const u8 *addr[2];
size_t len[2], siv_len, attr_len;
u8 *attr_start, *attr_end, *pos;
auth->waiting_auth_conf = 1;
auth->auth_resp_status = status;
auth->auth_resp_tries = 0;
/* Build DPP Authentication Response frame attributes */
attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
#ifdef CONFIG_DPP2
attr_len += 5;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
attr_len += 5;
#endif /* CONFIG_TESTING_OPTIONS */
msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
if (!msg)
return NULL;
attr_start = wpabuf_put(msg, 0);
/* DPP Status */
if (status != 255)
dpp_build_attr_status(msg, status);
/* Responder Bootstrapping Key Hash */
dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
/* Initiator Bootstrapping Key Hash (mutual authentication) */
dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
/* Responder Protocol Key */
if (pr) {
wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
wpabuf_put_le16(msg, wpabuf_len(pr));
wpabuf_put_buf(msg, pr);
}
#ifdef CONFIG_DPP2
/* Protocol Version */
if (auth->peer_version >= 2) {
wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, DPP_VERSION);
}
#endif /* CONFIG_DPP2 */
attr_end = wpabuf_put(msg, 0);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
goto skip_wrapped_data;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
pos = clear;
if (r_nonce) {
/* R-nonce */
WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
pos += 2;
WPA_PUT_LE16(pos, nonce_len);
pos += 2;
os_memcpy(pos, r_nonce, nonce_len);
pos += nonce_len;
}
if (i_nonce) {
/* I-nonce */
WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
pos += 2;
WPA_PUT_LE16(pos, nonce_len);
pos += 2;
os_memcpy(pos, i_nonce, nonce_len);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
pos[nonce_len / 2] ^= 0x01;
}
#endif /* CONFIG_TESTING_OPTIONS */
pos += nonce_len;
}
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
goto skip_r_capab;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* R-capabilities */
WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
pos += 2;
WPA_PUT_LE16(pos, 1);
pos += 2;
auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
DPP_CAPAB_ENROLLEE;
*pos++ = auth->r_capab;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
pos[-1] = 0;
} else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
wpa_printf(MSG_INFO,
"DPP: TESTING - incompatible R-capabilities");
if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
(DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
pos[-1] = 0;
else
pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
DPP_CAPAB_CONFIGURATOR;
}
skip_r_capab:
#endif /* CONFIG_TESTING_OPTIONS */
if (wrapped_r_auth) {
/* {R-auth}ke */
WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
pos += 2;
WPA_PUT_LE16(pos, wrapped_r_auth_len);
pos += 2;
os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
pos += wrapped_r_auth_len;
}
/* OUI, OUI type, Crypto Suite, DPP frame type */
addr[0] = wpabuf_head_u8(msg) + 2;
len[0] = 3 + 1 + 1 + 1;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
/* Attributes before Wrapped Data */
addr[1] = attr_start;
len[1] = attr_end - attr_start;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
siv_len = pos - clear;
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
2, addr, len, wrapped_data) < 0) {
wpabuf_free(msg);
return NULL;
}
siv_len += AES_BLOCK_SIZE;
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, siv_len);
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, siv_len);
wpabuf_put_data(msg, wrapped_data, siv_len);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
dpp_build_attr_status(msg, DPP_STATUS_OK);
}
skip_wrapped_data:
#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Authentication Response frame attributes", msg);
return msg;
}
static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
{
size_t nonce_len;
size_t secret_len;
struct wpabuf *msg, *pr = NULL;
u8 r_auth[4 + DPP_MAX_HASH_LEN];
u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
size_t wrapped_r_auth_len;
int ret = -1;
const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
enum dpp_status_error status = DPP_STATUS_OK;
#ifdef CONFIG_TESTING_OPTIONS
u8 test_hash[SHA256_MAC_LEN];
#endif /* CONFIG_TESTING_OPTIONS */
wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
if (!auth->own_bi)
return -1;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_nonce_override_len > 0) {
wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
nonce_len = dpp_nonce_override_len;
os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
} else {
nonce_len = auth->curve->nonce_len;
if (random_get_bytes(auth->r_nonce, nonce_len)) {
wpa_printf(MSG_ERROR,
"DPP: Failed to generate R-nonce");
goto fail;
}
}
#else /* CONFIG_TESTING_OPTIONS */
nonce_len = auth->curve->nonce_len;
if (random_get_bytes(auth->r_nonce, nonce_len)) {
wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
goto fail;
}
#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
- EVP_PKEY_free(auth->own_protocol_key);
+ crypto_ec_key_deinit(auth->own_protocol_key);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_protocol_key_override_len) {
const struct dpp_curve_params *tmp_curve;
wpa_printf(MSG_INFO,
"DPP: TESTING - override protocol key");
auth->own_protocol_key = dpp_set_keypair(
&tmp_curve, dpp_protocol_key_override,
dpp_protocol_key_override_len);
} else {
auth->own_protocol_key = dpp_gen_keypair(auth->curve);
}
#else /* CONFIG_TESTING_OPTIONS */
auth->own_protocol_key = dpp_gen_keypair(auth->curve);
#endif /* CONFIG_TESTING_OPTIONS */
if (!auth->own_protocol_key)
goto fail;
- pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ pr = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0);
if (!pr)
goto fail;
/* ECDH: N = pR * PI */
if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key,
auth->Nx, &secret_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
auth->Nx, auth->secret_len);
auth->Nx_len = auth->secret_len;
if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
auth->curve->hash_len) < 0)
goto fail;
if (auth->own_bi && auth->peer_bi) {
/* Mutual authentication */
if (dpp_auth_derive_l_responder(auth) < 0)
goto fail;
}
if (dpp_derive_bk_ke(auth) < 0)
goto fail;
/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
r_auth, 4 + auth->curve->hash_len,
0, NULL, NULL, wrapped_r_auth) < 0)
goto fail;
wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
wrapped_r_auth, wrapped_r_auth_len);
w_r_auth = wrapped_r_auth;
r_pubkey_hash = auth->own_bi->pubkey_hash;
if (auth->peer_bi)
i_pubkey_hash = auth->peer_bi->pubkey_hash;
else
i_pubkey_hash = NULL;
i_nonce = auth->i_nonce;
r_nonce = auth->r_nonce;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
r_pubkey_hash = NULL;
} else if (dpp_test ==
DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
wpa_printf(MSG_INFO,
"DPP: TESTING - invalid R-Bootstrap Key Hash");
os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
r_pubkey_hash = test_hash;
} else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
i_pubkey_hash = NULL;
} else if (dpp_test ==
DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
wpa_printf(MSG_INFO,
"DPP: TESTING - invalid I-Bootstrap Key Hash");
if (i_pubkey_hash)
os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
else
os_memset(test_hash, 0, SHA256_MAC_LEN);
test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
i_pubkey_hash = test_hash;
} else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
wpabuf_free(pr);
pr = NULL;
} else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
wpabuf_free(pr);
pr = wpabuf_alloc(2 * auth->curve->prime_len);
if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
goto fail;
} else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
w_r_auth = NULL;
wrapped_r_auth_len = 0;
} else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
status = 255;
} else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
status = 254;
} else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
r_nonce = NULL;
} else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
i_nonce = NULL;
}
#endif /* CONFIG_TESTING_OPTIONS */
msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
r_pubkey_hash, i_pubkey_hash,
r_nonce, i_nonce,
w_r_auth, wrapped_r_auth_len,
auth->k2);
if (!msg)
goto fail;
wpabuf_free(auth->resp_msg);
auth->resp_msg = msg;
ret = 0;
fail:
wpabuf_free(pr);
return ret;
}
static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
enum dpp_status_error status)
{
struct wpabuf *msg;
const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
#ifdef CONFIG_TESTING_OPTIONS
u8 test_hash[SHA256_MAC_LEN];
#endif /* CONFIG_TESTING_OPTIONS */
if (!auth->own_bi)
return -1;
wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
r_pubkey_hash = auth->own_bi->pubkey_hash;
if (auth->peer_bi)
i_pubkey_hash = auth->peer_bi->pubkey_hash;
else
i_pubkey_hash = NULL;
i_nonce = auth->i_nonce;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
r_pubkey_hash = NULL;
} else if (dpp_test ==
DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
wpa_printf(MSG_INFO,
"DPP: TESTING - invalid R-Bootstrap Key Hash");
os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
r_pubkey_hash = test_hash;
} else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
i_pubkey_hash = NULL;
} else if (dpp_test ==
DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
wpa_printf(MSG_INFO,
"DPP: TESTING - invalid I-Bootstrap Key Hash");
if (i_pubkey_hash)
os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
else
os_memset(test_hash, 0, SHA256_MAC_LEN);
test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
i_pubkey_hash = test_hash;
} else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
status = 255;
} else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
i_nonce = NULL;
}
#endif /* CONFIG_TESTING_OPTIONS */
msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
r_pubkey_hash, i_pubkey_hash,
NULL, i_nonce, NULL, 0, auth->k1);
if (!msg)
return -1;
wpabuf_free(auth->resp_msg);
auth->resp_msg = msg;
return 0;
}
struct dpp_authentication *
dpp_auth_req_rx(struct dpp_global *dpp, void *msg_ctx, u8 dpp_allowed_roles,
int qr_mutual, struct dpp_bootstrap_info *peer_bi,
struct dpp_bootstrap_info *own_bi,
unsigned int freq, const u8 *hdr, const u8 *attr_start,
size_t attr_len)
{
- EVP_PKEY *pi = NULL;
- EVP_PKEY_CTX *ctx = NULL;
+ struct crypto_ec_key *pi = NULL;
size_t secret_len;
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
*channel;
u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
i_bootstrap_len, channel_len;
struct dpp_authentication *auth = NULL;
#ifdef CONFIG_DPP2
const u8 *version;
u16 version_len;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
wpa_printf(MSG_INFO,
"DPP: TESTING - stop at Authentication Request");
return NULL;
}
#endif /* CONFIG_TESTING_OPTIONS */
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing or invalid required Wrapped Data attribute");
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
wrapped_data, wrapped_data_len);
attr_len = wrapped_data - 4 - attr_start;
auth = dpp_alloc_auth(dpp, msg_ctx);
if (!auth)
goto fail;
if (peer_bi && peer_bi->configurator_params &&
dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
goto fail;
auth->peer_bi = peer_bi;
auth->own_bi = own_bi;
auth->curve = own_bi->curve;
auth->curr_freq = freq;
auth->peer_version = 1; /* default to the first version */
#ifdef CONFIG_DPP2
version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
&version_len);
if (version && DPP_VERSION > 1) {
if (version_len < 1 || version[0] == 0) {
dpp_auth_fail(auth,
"Invalid Protocol Version attribute");
goto fail;
}
auth->peer_version = version[0];
wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
auth->peer_version);
}
#endif /* CONFIG_DPP2 */
channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
&channel_len);
if (channel) {
int neg_freq;
if (channel_len < 2) {
dpp_auth_fail(auth, "Too short Channel attribute");
goto fail;
}
neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
wpa_printf(MSG_DEBUG,
"DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
channel[0], channel[1], neg_freq);
if (neg_freq < 0) {
dpp_auth_fail(auth,
"Unsupported Channel attribute value");
goto fail;
}
if (auth->curr_freq != (unsigned int) neg_freq) {
wpa_printf(MSG_DEBUG,
"DPP: Changing negotiation channel from %u MHz to %u MHz",
freq, neg_freq);
auth->curr_freq = neg_freq;
}
}
i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
&i_proto_len);
if (!i_proto) {
dpp_auth_fail(auth,
"Missing required Initiator Protocol Key attribute");
goto fail;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
i_proto, i_proto_len);
/* M = bR * PI */
pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
if (!pi) {
dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
goto fail;
}
dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
if (dpp_ecdh(own_bi->pubkey, pi, auth->Mx, &secret_len) < 0)
goto fail;
auth->secret_len = secret_len;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
auth->Mx, auth->secret_len);
auth->Mx_len = auth->secret_len;
if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
auth->curve->hash_len) < 0)
goto fail;
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
addr[1] = attr_start;
len[1] = attr_len;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
goto fail;
if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
&i_nonce_len);
if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
dpp_auth_fail(auth, "Missing or invalid I-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
i_capab = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_I_CAPABILITIES,
&i_capab_len);
if (!i_capab || i_capab_len < 1) {
dpp_auth_fail(auth, "Missing or invalid I-capabilities");
goto fail;
}
auth->i_capab = i_capab[0];
wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
bin_clear_free(unwrapped, unwrapped_len);
unwrapped = NULL;
switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
case DPP_CAPAB_ENROLLEE:
if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
wpa_printf(MSG_DEBUG,
"DPP: Local policy does not allow Configurator role");
goto not_compatible;
}
wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
auth->configurator = 1;
break;
case DPP_CAPAB_CONFIGURATOR:
if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
wpa_printf(MSG_DEBUG,
"DPP: Local policy does not allow Enrollee role");
goto not_compatible;
}
wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
auth->configurator = 0;
break;
case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
auth->configurator = 0;
} else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
auth->configurator = 1;
} else {
wpa_printf(MSG_DEBUG,
"DPP: Local policy does not allow Configurator/Enrollee role");
goto not_compatible;
}
break;
default:
wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
wpa_msg(auth->msg_ctx, MSG_INFO,
DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
auth->i_capab & DPP_CAPAB_ROLE_MASK);
goto fail;
}
auth->peer_protocol_key = pi;
pi = NULL;
if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
char hex[SHA256_MAC_LEN * 2 + 1];
wpa_printf(MSG_DEBUG,
"DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
if (dpp_auth_build_resp_status(auth,
DPP_STATUS_RESPONSE_PENDING) < 0)
goto fail;
i_bootstrap = dpp_get_attr(attr_start, attr_len,
DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
&i_bootstrap_len);
if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
auth->response_pending = 1;
os_memcpy(auth->waiting_pubkey_hash,
i_bootstrap, i_bootstrap_len);
wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
i_bootstrap_len);
} else {
hex[0] = '\0';
}
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
"%s", hex);
return auth;
}
if (dpp_auth_build_resp_ok(auth) < 0)
goto fail;
return auth;
not_compatible:
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
"i-capab=0x%02x", auth->i_capab);
if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
auth->configurator = 1;
else
auth->configurator = 0;
auth->peer_protocol_key = pi;
pi = NULL;
if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
goto fail;
auth->remove_on_tx_status = 1;
return auth;
fail:
bin_clear_free(unwrapped, unwrapped_len);
- EVP_PKEY_free(pi);
- EVP_PKEY_CTX_free(ctx);
+ crypto_ec_key_deinit(pi);
dpp_auth_deinit(auth);
return NULL;
}
int dpp_notify_new_qr_code(struct dpp_authentication *auth,
struct dpp_bootstrap_info *peer_bi)
{
if (!auth || !auth->response_pending ||
os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
SHA256_MAC_LEN) != 0)
return 0;
wpa_printf(MSG_DEBUG,
"DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
MACSTR, MAC2STR(auth->peer_mac_addr));
auth->peer_bi = peer_bi;
if (dpp_auth_build_resp_ok(auth) < 0)
return -1;
return 1;
}
static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
enum dpp_status_error status)
{
struct wpabuf *msg;
u8 i_auth[4 + DPP_MAX_HASH_LEN];
size_t i_auth_len;
u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
size_t r_nonce_len;
const u8 *addr[2];
size_t len[2], attr_len;
u8 *wrapped_i_auth;
u8 *wrapped_r_nonce;
u8 *attr_start, *attr_end;
const u8 *r_pubkey_hash, *i_pubkey_hash;
#ifdef CONFIG_TESTING_OPTIONS
u8 test_hash[SHA256_MAC_LEN];
#endif /* CONFIG_TESTING_OPTIONS */
wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
i_auth_len = 4 + auth->curve->hash_len;
r_nonce_len = 4 + auth->curve->nonce_len;
/* Build DPP Authentication Confirmation frame attributes */
attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
attr_len += 5;
#endif /* CONFIG_TESTING_OPTIONS */
msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
if (!msg)
goto fail;
attr_start = wpabuf_put(msg, 0);
r_pubkey_hash = auth->peer_bi->pubkey_hash;
if (auth->own_bi)
i_pubkey_hash = auth->own_bi->pubkey_hash;
else
i_pubkey_hash = NULL;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
goto skip_status;
} else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
status = 254;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* DPP Status */
dpp_build_attr_status(msg, status);
#ifdef CONFIG_TESTING_OPTIONS
skip_status:
if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
r_pubkey_hash = NULL;
} else if (dpp_test ==
DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
wpa_printf(MSG_INFO,
"DPP: TESTING - invalid R-Bootstrap Key Hash");
os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
r_pubkey_hash = test_hash;
} else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
i_pubkey_hash = NULL;
} else if (dpp_test ==
DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
wpa_printf(MSG_INFO,
"DPP: TESTING - invalid I-Bootstrap Key Hash");
if (i_pubkey_hash)
os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
else
os_memset(test_hash, 0, SHA256_MAC_LEN);
test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
i_pubkey_hash = test_hash;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* Responder Bootstrapping Key Hash */
dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
/* Initiator Bootstrapping Key Hash (mutual authentication) */
dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
goto skip_wrapped_data;
if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
i_auth_len = 0;
#endif /* CONFIG_TESTING_OPTIONS */
attr_end = wpabuf_put(msg, 0);
/* OUI, OUI type, Crypto Suite, DPP frame type */
addr[0] = wpabuf_head_u8(msg) + 2;
len[0] = 3 + 1 + 1 + 1;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
/* Attributes before Wrapped Data */
addr[1] = attr_start;
len[1] = attr_end - attr_start;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
if (status == DPP_STATUS_OK) {
/* I-auth wrapped with ke */
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
goto skip_i_auth;
#endif /* CONFIG_TESTING_OPTIONS */
/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
* 1) */
WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
}
skip_i_auth:
#endif /* CONFIG_TESTING_OPTIONS */
if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
i_auth, i_auth_len,
2, addr, len, wrapped_i_auth) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
} else {
/* R-nonce wrapped with k2 */
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
r_nonce, r_nonce_len,
2, addr, len, wrapped_r_nonce) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
}
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
dpp_build_attr_status(msg, DPP_STATUS_OK);
}
skip_wrapped_data:
#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Authentication Confirmation frame attributes",
msg);
if (status == DPP_STATUS_OK)
dpp_auth_success(auth);
return msg;
fail:
wpabuf_free(msg);
return NULL;
}
static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
{
struct dpp_bootstrap_info *bi;
if (auth->own_bi)
return 0; /* already generated */
bi = os_zalloc(sizeof(*bi));
if (!bi)
return -1;
bi->type = DPP_BOOTSTRAP_QR_CODE;
if (dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0) < 0 ||
dpp_gen_uri(bi) < 0)
goto fail;
wpa_printf(MSG_DEBUG,
"DPP: Auto-generated own bootstrapping key info: URI %s",
bi->uri);
auth->tmp_own_bi = auth->own_bi = bi;
return 0;
fail:
dpp_bootstrap_info_free(bi);
return -1;
}
struct dpp_authentication * dpp_auth_init(struct dpp_global *dpp, void *msg_ctx,
struct dpp_bootstrap_info *peer_bi,
struct dpp_bootstrap_info *own_bi,
u8 dpp_allowed_roles,
unsigned int neg_freq,
struct hostapd_hw_modes *own_modes,
u16 num_modes)
{
struct dpp_authentication *auth;
size_t nonce_len;
size_t secret_len;
struct wpabuf *pi = NULL;
const u8 *r_pubkey_hash, *i_pubkey_hash;
#ifdef CONFIG_TESTING_OPTIONS
u8 test_hash[SHA256_MAC_LEN];
#endif /* CONFIG_TESTING_OPTIONS */
auth = dpp_alloc_auth(dpp, msg_ctx);
if (!auth)
return NULL;
if (peer_bi->configurator_params &&
dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
goto fail;
auth->initiator = 1;
auth->waiting_auth_resp = 1;
auth->allowed_roles = dpp_allowed_roles;
auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
auth->peer_bi = peer_bi;
auth->own_bi = own_bi;
auth->curve = peer_bi->curve;
if (dpp_autogen_bootstrap_key(auth) < 0 ||
dpp_prepare_channel_list(auth, neg_freq, own_modes, num_modes) < 0)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_nonce_override_len > 0) {
wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
nonce_len = dpp_nonce_override_len;
os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
} else {
nonce_len = auth->curve->nonce_len;
if (random_get_bytes(auth->i_nonce, nonce_len)) {
wpa_printf(MSG_ERROR,
"DPP: Failed to generate I-nonce");
goto fail;
}
}
#else /* CONFIG_TESTING_OPTIONS */
nonce_len = auth->curve->nonce_len;
if (random_get_bytes(auth->i_nonce, nonce_len)) {
wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
goto fail;
}
#endif /* CONFIG_TESTING_OPTIONS */
wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_protocol_key_override_len) {
const struct dpp_curve_params *tmp_curve;
wpa_printf(MSG_INFO,
"DPP: TESTING - override protocol key");
auth->own_protocol_key = dpp_set_keypair(
&tmp_curve, dpp_protocol_key_override,
dpp_protocol_key_override_len);
} else {
auth->own_protocol_key = dpp_gen_keypair(auth->curve);
}
#else /* CONFIG_TESTING_OPTIONS */
auth->own_protocol_key = dpp_gen_keypair(auth->curve);
#endif /* CONFIG_TESTING_OPTIONS */
if (!auth->own_protocol_key)
goto fail;
- pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ pi = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0);
if (!pi)
goto fail;
/* ECDH: M = pI * BR */
if (dpp_ecdh(auth->own_protocol_key, auth->peer_bi->pubkey,
auth->Mx, &secret_len) < 0)
goto fail;
auth->secret_len = secret_len;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
auth->Mx, auth->secret_len);
auth->Mx_len = auth->secret_len;
if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
auth->curve->hash_len) < 0)
goto fail;
r_pubkey_hash = auth->peer_bi->pubkey_hash;
i_pubkey_hash = auth->own_bi->pubkey_hash;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
r_pubkey_hash = NULL;
} else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
wpa_printf(MSG_INFO,
"DPP: TESTING - invalid R-Bootstrap Key Hash");
os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
r_pubkey_hash = test_hash;
} else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
i_pubkey_hash = NULL;
} else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
wpa_printf(MSG_INFO,
"DPP: TESTING - invalid I-Bootstrap Key Hash");
os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
i_pubkey_hash = test_hash;
} else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
wpabuf_free(pi);
pi = NULL;
} else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
wpabuf_free(pi);
pi = wpabuf_alloc(2 * auth->curve->prime_len);
if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
goto fail;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (neg_freq && auth->num_freq == 1 && auth->freq[0] == neg_freq)
neg_freq = 0;
auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
i_pubkey_hash, neg_freq);
if (!auth->req_msg)
goto fail;
out:
wpabuf_free(pi);
return auth;
fail:
dpp_auth_deinit(auth);
auth = NULL;
goto out;
}
static void
dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len,
const u8 *wrapped_data, u16 wrapped_data_len,
enum dpp_status_error status)
{
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
const u8 *i_nonce, *r_capab;
u16 i_nonce_len, r_capab_len;
if (status == DPP_STATUS_NOT_COMPATIBLE) {
wpa_printf(MSG_DEBUG,
"DPP: Responder reported incompatible roles");
} else if (status == DPP_STATUS_RESPONSE_PENDING) {
wpa_printf(MSG_DEBUG,
"DPP: Responder reported more time needed");
} else {
wpa_printf(MSG_DEBUG,
"DPP: Responder reported failure (status %d)",
status);
dpp_auth_fail(auth, "Responder reported failure");
return;
}
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
addr[1] = attr_start;
len[1] = attr_len;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
goto fail;
if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
&i_nonce_len);
if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
dpp_auth_fail(auth, "Missing or invalid I-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
dpp_auth_fail(auth, "I-nonce mismatch");
goto fail;
}
r_capab = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_R_CAPABILITIES,
&r_capab_len);
if (!r_capab || r_capab_len < 1) {
dpp_auth_fail(auth, "Missing or invalid R-capabilities");
goto fail;
}
auth->r_capab = r_capab[0];
wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
if (status == DPP_STATUS_NOT_COMPATIBLE) {
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
"r-capab=0x%02x", auth->r_capab);
} else if (status == DPP_STATUS_RESPONSE_PENDING) {
u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
(!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
wpa_msg(auth->msg_ctx, MSG_INFO,
DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
role);
} else {
wpa_printf(MSG_DEBUG,
"DPP: Continue waiting for full DPP Authentication Response");
wpa_msg(auth->msg_ctx, MSG_INFO,
DPP_EVENT_RESPONSE_PENDING "%s",
auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
}
}
fail:
bin_clear_free(unwrapped, unwrapped_len);
}
struct wpabuf *
dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len)
{
- EVP_PKEY *pr;
+ struct crypto_ec_key *pr;
size_t secret_len;
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL, *unwrapped2 = NULL;
size_t unwrapped_len = 0, unwrapped2_len = 0;
const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
*r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
wrapped2_len, r_auth_len;
u8 r_auth2[DPP_MAX_HASH_LEN];
u8 role;
#ifdef CONFIG_DPP2
const u8 *version;
u16 version_len;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
wpa_printf(MSG_INFO,
"DPP: TESTING - stop at Authentication Response");
return NULL;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (!auth->initiator || !auth->peer_bi || auth->reconfig) {
dpp_auth_fail(auth, "Unexpected Authentication Response");
return NULL;
}
auth->waiting_auth_resp = 0;
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
dpp_auth_fail(auth,
"Missing or invalid required Wrapped Data attribute");
return NULL;
}
wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
wrapped_data, wrapped_data_len);
attr_len = wrapped_data - 4 - attr_start;
r_bootstrap = dpp_get_attr(attr_start, attr_len,
DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
dpp_auth_fail(auth,
"Missing or invalid required Responder Bootstrapping Key Hash attribute");
return NULL;
}
wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
r_bootstrap, r_bootstrap_len);
if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
SHA256_MAC_LEN) != 0) {
dpp_auth_fail(auth,
"Unexpected Responder Bootstrapping Key Hash value");
wpa_hexdump(MSG_DEBUG,
"DPP: Expected Responder Bootstrapping Key Hash",
auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
return NULL;
}
i_bootstrap = dpp_get_attr(attr_start, attr_len,
DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
&i_bootstrap_len);
if (i_bootstrap) {
if (i_bootstrap_len != SHA256_MAC_LEN) {
dpp_auth_fail(auth,
"Invalid Initiator Bootstrapping Key Hash attribute");
return NULL;
}
wpa_hexdump(MSG_MSGDUMP,
"DPP: Initiator Bootstrapping Key Hash",
i_bootstrap, i_bootstrap_len);
if (!auth->own_bi ||
os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
SHA256_MAC_LEN) != 0) {
dpp_auth_fail(auth,
"Initiator Bootstrapping Key Hash attribute did not match");
return NULL;
}
} else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
/* PKEX bootstrapping mandates use of mutual authentication */
dpp_auth_fail(auth,
"Missing Initiator Bootstrapping Key Hash attribute");
return NULL;
} else if (auth->own_bi &&
auth->own_bi->type == DPP_BOOTSTRAP_NFC_URI &&
auth->own_bi->nfc_negotiated) {
/* NFC negotiated connection handover bootstrapping mandates
* use of mutual authentication */
dpp_auth_fail(auth,
"Missing Initiator Bootstrapping Key Hash attribute");
return NULL;
}
auth->peer_version = 1; /* default to the first version */
#ifdef CONFIG_DPP2
version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
&version_len);
if (version && DPP_VERSION > 1) {
if (version_len < 1 || version[0] == 0) {
dpp_auth_fail(auth,
"Invalid Protocol Version attribute");
return NULL;
}
auth->peer_version = version[0];
wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
auth->peer_version);
}
#endif /* CONFIG_DPP2 */
status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
&status_len);
if (!status || status_len < 1) {
dpp_auth_fail(auth,
"Missing or invalid required DPP Status attribute");
return NULL;
}
wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
auth->auth_resp_status = status[0];
if (status[0] != DPP_STATUS_OK) {
dpp_auth_resp_rx_status(auth, hdr, attr_start,
attr_len, wrapped_data,
wrapped_data_len, status[0]);
return NULL;
}
if (!i_bootstrap && auth->own_bi) {
wpa_printf(MSG_DEBUG,
"DPP: Responder decided not to use mutual authentication");
auth->own_bi = NULL;
}
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
auth->own_bi != NULL);
r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
&r_proto_len);
if (!r_proto) {
dpp_auth_fail(auth,
"Missing required Responder Protocol Key attribute");
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
r_proto, r_proto_len);
/* N = pI * PR */
pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
if (!pr) {
dpp_auth_fail(auth, "Invalid Responder Protocol Key");
return NULL;
}
dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) {
dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
goto fail;
}
- EVP_PKEY_free(auth->peer_protocol_key);
+ crypto_ec_key_deinit(auth->peer_protocol_key);
auth->peer_protocol_key = pr;
pr = NULL;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
auth->Nx, auth->secret_len);
auth->Nx_len = auth->secret_len;
if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
auth->curve->hash_len) < 0)
goto fail;
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
addr[1] = attr_start;
len[1] = attr_len;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
goto fail;
if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
&r_nonce_len);
if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
&i_nonce_len);
if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
dpp_auth_fail(auth, "Missing or invalid I-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
dpp_auth_fail(auth, "I-nonce mismatch");
goto fail;
}
if (auth->own_bi) {
/* Mutual authentication */
if (dpp_auth_derive_l_initiator(auth) < 0)
goto fail;
}
r_capab = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_R_CAPABILITIES,
&r_capab_len);
if (!r_capab || r_capab_len < 1) {
dpp_auth_fail(auth, "Missing or invalid R-capabilities");
goto fail;
}
auth->r_capab = r_capab[0];
wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
if ((auth->allowed_roles ==
(DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
(role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
/* Peer selected its role, so move from "either role" to the
* role that is compatible with peer's selection. */
auth->configurator = role == DPP_CAPAB_ENROLLEE;
wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
auth->configurator ? "Configurator" : "Enrollee");
} else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
(!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Unexpected role in R-capabilities 0x%02x",
role);
if (role != DPP_CAPAB_ENROLLEE &&
role != DPP_CAPAB_CONFIGURATOR)
goto fail;
bin_clear_free(unwrapped, unwrapped_len);
auth->remove_on_tx_status = 1;
return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
}
wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
dpp_auth_fail(auth,
"Missing or invalid Secondary Wrapped Data");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped2, wrapped2_len);
if (dpp_derive_bk_ke(auth) < 0)
goto fail;
unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
unwrapped2 = os_malloc(unwrapped2_len);
if (!unwrapped2)
goto fail;
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped2, wrapped2_len,
0, NULL, NULL, unwrapped2) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped2, unwrapped2_len);
if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
dpp_auth_fail(auth,
"Invalid attribute in secondary unwrapped data");
goto fail;
}
r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
&r_auth_len);
if (!r_auth || r_auth_len != auth->curve->hash_len) {
dpp_auth_fail(auth,
"Missing or invalid Responder Authenticating Tag");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
r_auth, r_auth_len);
/* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
if (dpp_gen_r_auth(auth, r_auth2) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
r_auth2, r_auth_len);
if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
bin_clear_free(unwrapped, unwrapped_len);
bin_clear_free(unwrapped2, unwrapped2_len);
auth->remove_on_tx_status = 1;
return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
}
bin_clear_free(unwrapped, unwrapped_len);
bin_clear_free(unwrapped2, unwrapped2_len);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
wpa_printf(MSG_INFO,
"DPP: TESTING - Authentication Response in place of Confirm");
if (dpp_auth_build_resp_ok(auth) < 0)
return NULL;
return wpabuf_dup(auth->resp_msg);
}
#endif /* CONFIG_TESTING_OPTIONS */
return dpp_auth_build_conf(auth, DPP_STATUS_OK);
fail:
bin_clear_free(unwrapped, unwrapped_len);
bin_clear_free(unwrapped2, unwrapped2_len);
- EVP_PKEY_free(pr);
+ crypto_ec_key_deinit(pr);
return NULL;
}
static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
const u8 *hdr,
const u8 *attr_start, size_t attr_len,
const u8 *wrapped_data,
u16 wrapped_data_len,
enum dpp_status_error status)
{
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
const u8 *r_nonce;
u16 r_nonce_len;
/* Authentication Confirm failure cases are expected to include
* {R-nonce}k2 in the Wrapped Data attribute. */
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
addr[1] = attr_start;
len[1] = attr_len;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped) {
dpp_auth_fail(auth, "Authentication failed");
goto fail;
}
if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
&r_nonce_len);
if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
goto fail;
}
if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
r_nonce, r_nonce_len);
wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
auth->r_nonce, r_nonce_len);
dpp_auth_fail(auth, "R-nonce mismatch");
goto fail;
}
if (status == DPP_STATUS_NOT_COMPATIBLE)
dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
else if (status == DPP_STATUS_AUTH_FAILURE)
dpp_auth_fail(auth, "Peer reported authentication failure)");
fail:
bin_clear_free(unwrapped, unwrapped_len);
return -1;
}
int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len)
{
const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
i_auth_len;
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
u8 i_auth2[DPP_MAX_HASH_LEN];
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
wpa_printf(MSG_INFO,
"DPP: TESTING - stop at Authentication Confirm");
return -1;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (auth->initiator || !auth->own_bi || !auth->waiting_auth_conf ||
auth->reconfig) {
wpa_printf(MSG_DEBUG,
"DPP: initiator=%d own_bi=%d waiting_auth_conf=%d",
auth->initiator, !!auth->own_bi,
auth->waiting_auth_conf);
dpp_auth_fail(auth, "Unexpected Authentication Confirm");
return -1;
}
auth->waiting_auth_conf = 0;
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
dpp_auth_fail(auth,
"Missing or invalid required Wrapped Data attribute");
return -1;
}
wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
wrapped_data, wrapped_data_len);
attr_len = wrapped_data - 4 - attr_start;
r_bootstrap = dpp_get_attr(attr_start, attr_len,
DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
dpp_auth_fail(auth,
"Missing or invalid required Responder Bootstrapping Key Hash attribute");
return -1;
}
wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
r_bootstrap, r_bootstrap_len);
if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
SHA256_MAC_LEN) != 0) {
wpa_hexdump(MSG_DEBUG,
"DPP: Expected Responder Bootstrapping Key Hash",
auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
dpp_auth_fail(auth,
"Responder Bootstrapping Key Hash mismatch");
return -1;
}
i_bootstrap = dpp_get_attr(attr_start, attr_len,
DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
&i_bootstrap_len);
if (i_bootstrap) {
if (i_bootstrap_len != SHA256_MAC_LEN) {
dpp_auth_fail(auth,
"Invalid Initiator Bootstrapping Key Hash attribute");
return -1;
}
wpa_hexdump(MSG_MSGDUMP,
"DPP: Initiator Bootstrapping Key Hash",
i_bootstrap, i_bootstrap_len);
if (!auth->peer_bi ||
os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
SHA256_MAC_LEN) != 0) {
dpp_auth_fail(auth,
"Initiator Bootstrapping Key Hash mismatch");
return -1;
}
} else if (auth->peer_bi) {
/* Mutual authentication and peer did not include its
* Bootstrapping Key Hash attribute. */
dpp_auth_fail(auth,
"Missing Initiator Bootstrapping Key Hash attribute");
return -1;
}
status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
&status_len);
if (!status || status_len < 1) {
dpp_auth_fail(auth,
"Missing or invalid required DPP Status attribute");
return -1;
}
wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
status[0] == DPP_STATUS_AUTH_FAILURE)
return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
attr_len, wrapped_data,
wrapped_data_len, status[0]);
if (status[0] != DPP_STATUS_OK) {
dpp_auth_fail(auth, "Authentication failed");
return -1;
}
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
addr[1] = attr_start;
len[1] = attr_len;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
return -1;
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
&i_auth_len);
if (!i_auth || i_auth_len != auth->curve->hash_len) {
dpp_auth_fail(auth,
"Missing or invalid Initiator Authenticating Tag");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
i_auth, i_auth_len);
/* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
if (dpp_gen_i_auth(auth, i_auth2) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
i_auth2, i_auth_len);
if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
goto fail;
}
bin_clear_free(unwrapped, unwrapped_len);
dpp_auth_success(auth);
return 0;
fail:
bin_clear_free(unwrapped, unwrapped_len);
return -1;
}
diff --git a/contrib/wpa/src/common/dpp_backup.c b/contrib/wpa/src/common/dpp_backup.c
index 947a5e9ea33e..fb3f776d2ea2 100644
--- a/contrib/wpa/src/common/dpp_backup.c
+++ b/contrib/wpa/src/common/dpp_backup.c
@@ -1,1265 +1,1210 @@
/*
* DPP configurator backup
* Copyright (c) 2019-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
-#include <openssl/opensslv.h>
-#include <openssl/err.h>
#include "utils/common.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
#include "tls/asn1.h"
#include "dpp.h"
#include "dpp_i.h"
#ifdef CONFIG_DPP2
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
- (defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20700000L)
-/* Compatibility wrappers for older versions. */
-
-static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
-{
- if (pkey->type != EVP_PKEY_EC)
- return NULL;
- return pkey->pkey.ec;
-}
-
-#endif
-
-
void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key)
{
while (key) {
struct dpp_asymmetric_key *next = key->next;
- EVP_PKEY_free(key->csign);
- EVP_PKEY_free(key->pp_key);
+ crypto_ec_key_deinit(key->csign);
+ crypto_ec_key_deinit(key->pp_key);
str_clear_free(key->config_template);
str_clear_free(key->connector_template);
os_free(key);
key = next;
}
}
static struct wpabuf * dpp_build_conf_params(struct dpp_configurator *conf)
{
struct wpabuf *buf, *priv_key = NULL;
size_t len;
/* TODO: proper template values */
const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
const char *connector_template = NULL;
- EC_KEY *eckey;
- unsigned char *der = NULL;
- int der_len;
if (!conf->pp_key)
return NULL;
- eckey = EVP_PKEY_get0_EC_KEY(conf->pp_key);
- if (!eckey)
- return NULL;
- EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
- der_len = i2d_ECPrivateKey(eckey, &der);
- if (der_len > 0)
- priv_key = wpabuf_alloc_copy(der, der_len);
- OPENSSL_free(der);
+ priv_key = crypto_ec_key_get_ecprivate_key(conf->pp_key, false);
if (!priv_key)
- goto fail;
+ return NULL;
len = 100 + os_strlen(conf_template);
if (connector_template)
len += os_strlen(connector_template);
if (priv_key)
len += wpabuf_len(priv_key);
buf = wpabuf_alloc(len);
if (!buf)
goto fail;
/*
* DPPConfigurationParameters ::= SEQUENCE {
* privacyProtectionKey PrivateKey,
* configurationTemplate UTF8String,
* connectorTemplate UTF8String OPTIONAL}
*/
/* PrivateKey ::= OCTET STRING */
asn1_put_octet_string(buf, priv_key);
asn1_put_utf8string(buf, conf_template);
if (connector_template)
asn1_put_utf8string(buf, connector_template);
wpabuf_clear_free(priv_key);
return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
fail:
wpabuf_clear_free(priv_key);
return NULL;
}
static struct wpabuf * dpp_build_attribute(struct dpp_configurator *conf)
{
struct wpabuf *conf_params, *attr;
/*
* aa-DPPConfigurationParameters ATTRIBUTE ::=
* { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
*
* Attribute ::= SEQUENCE {
* type OBJECT IDENTIFIER,
* values SET SIZE(1..MAX) OF Type
*/
conf_params = dpp_build_conf_params(conf);
conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
ASN1_TAG_SET);
if (!conf_params)
return NULL;
attr = wpabuf_alloc(100 + wpabuf_len(conf_params));
if (!attr) {
wpabuf_clear_free(conf_params);
return NULL;
}
asn1_put_oid(attr, &asn1_dpp_config_params_oid);
wpabuf_put_buf(attr, conf_params);
wpabuf_clear_free(conf_params);
return asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
}
static struct wpabuf * dpp_build_key_alg(const struct dpp_curve_params *curve)
{
const struct asn1_oid *oid;
struct wpabuf *params, *res;
switch (curve->ike_group) {
case 19:
oid = &asn1_prime256v1_oid;
break;
case 20:
oid = &asn1_secp384r1_oid;
break;
case 21:
oid = &asn1_secp521r1_oid;
break;
case 28:
oid = &asn1_brainpoolP256r1_oid;
break;
case 29:
oid = &asn1_brainpoolP384r1_oid;
break;
case 30:
oid = &asn1_brainpoolP512r1_oid;
break;
default:
return NULL;
}
params = wpabuf_alloc(20);
if (!params)
return NULL;
asn1_put_oid(params, oid); /* namedCurve */
res = asn1_build_alg_id(&asn1_ec_public_key_oid, params);
wpabuf_free(params);
return res;
}
static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth)
{
struct wpabuf *key = NULL, *attr, *alg, *priv_key = NULL;
- EC_KEY *eckey;
- unsigned char *der = NULL;
- int der_len;
- eckey = EVP_PKEY_get0_EC_KEY(auth->conf->csign);
- if (!eckey)
+ priv_key = crypto_ec_key_get_ecprivate_key(auth->conf->csign, false);
+ if (!priv_key)
return NULL;
- EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
- der_len = i2d_ECPrivateKey(eckey, &der);
- if (der_len > 0)
- priv_key = wpabuf_alloc_copy(der, der_len);
- OPENSSL_free(der);
-
alg = dpp_build_key_alg(auth->conf->curve);
/* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
attr = dpp_build_attribute(auth->conf);
attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
if (!priv_key || !attr || !alg)
goto fail;
/*
* OneAsymmetricKey ::= SEQUENCE {
* version Version,
* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
* privateKey PrivateKey,
* attributes [0] Attributes OPTIONAL,
* ...,
* [[2: publicKey [1] BIT STRING OPTIONAL ]],
* ...
* }
*/
key = wpabuf_alloc(100 + wpabuf_len(alg) + wpabuf_len(priv_key) +
wpabuf_len(attr));
if (!key)
goto fail;
asn1_put_integer(key, 0); /* version = v1(0) */
/* PrivateKeyAlgorithmIdentifier */
wpabuf_put_buf(key, alg);
/* PrivateKey ::= OCTET STRING */
asn1_put_octet_string(key, priv_key);
/* [0] Attributes OPTIONAL */
asn1_put_hdr(key, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0, wpabuf_len(attr));
wpabuf_put_buf(key, attr);
fail:
wpabuf_clear_free(attr);
wpabuf_clear_free(priv_key);
wpabuf_free(alg);
/*
* DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
*
* AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
*
* OneAsymmetricKey ::= SEQUENCE
*/
return asn1_encaps(asn1_encaps(key,
ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE),
ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
}
static struct wpabuf * dpp_build_pbkdf2_alg_id(const struct wpabuf *salt,
size_t hash_len)
{
struct wpabuf *params = NULL, *buf = NULL, *prf = NULL;
const struct asn1_oid *oid;
/*
* PBKDF2-params ::= SEQUENCE {
* salt CHOICE {
* specified OCTET STRING,
* otherSource AlgorithmIdentifier}
* iterationCount INTEGER (1..MAX),
* keyLength INTEGER (1..MAX),
* prf AlgorithmIdentifier}
*
* salt is an 64 octet value, iterationCount is 1000, keyLength is based
* on Configurator signing key length, prf is
* id-hmacWithSHA{256,384,512} based on Configurator signing key.
*/
if (hash_len == 32)
oid = &asn1_pbkdf2_hmac_sha256_oid;
else if (hash_len == 48)
oid = &asn1_pbkdf2_hmac_sha384_oid;
else if (hash_len == 64)
oid = &asn1_pbkdf2_hmac_sha512_oid;
else
goto fail;
prf = asn1_build_alg_id(oid, NULL);
if (!prf)
goto fail;
params = wpabuf_alloc(100 + wpabuf_len(salt) + wpabuf_len(prf));
if (!params)
goto fail;
asn1_put_octet_string(params, salt); /* salt.specified */
asn1_put_integer(params, 1000); /* iterationCount */
asn1_put_integer(params, hash_len); /* keyLength */
wpabuf_put_buf(params, prf);
params = asn1_encaps(params, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
if (!params)
goto fail;
buf = asn1_build_alg_id(&asn1_pbkdf2_oid, params);
fail:
wpabuf_free(params);
wpabuf_free(prf);
return buf;
}
static struct wpabuf *
dpp_build_pw_recipient_info(struct dpp_authentication *auth, size_t hash_len,
const struct wpabuf *cont_enc_key)
{
struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
*key_enc_alg = NULL, *salt;
u8 kek[DPP_MAX_HASH_LEN];
u8 key[DPP_MAX_HASH_LEN];
size_t key_len;
int res;
salt = wpabuf_alloc(64);
if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
goto fail;
wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
key_len = auth->curve->hash_len;
/* password = HKDF-Expand(bk, "Enveloped Data Password", length) */
res = dpp_hkdf_expand(key_len, auth->bk, key_len,
"Enveloped Data Password", key, key_len);
if (res < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
kek, hash_len)) {
wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
kek, hash_len);
enc_key = wpabuf_alloc(hash_len + AES_BLOCK_SIZE);
if (!enc_key ||
aes_siv_encrypt(kek, hash_len, wpabuf_head(cont_enc_key),
wpabuf_len(cont_enc_key), 0, NULL, NULL,
wpabuf_put(enc_key, hash_len + AES_BLOCK_SIZE)) < 0)
goto fail;
wpa_hexdump_buf(MSG_DEBUG, "DPP: encryptedKey", enc_key);
/*
* PasswordRecipientInfo ::= SEQUENCE {
* version CMSVersion,
* keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
* encryptedKey EncryptedKey}
*
* version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
* parameters contains PBKDF2-params SEQUENCE.
*/
key_der_alg = dpp_build_pbkdf2_alg_id(salt, hash_len);
key_enc_alg = asn1_build_alg_id(&asn1_aes_siv_cmac_aead_256_oid, NULL);
if (!key_der_alg || !key_enc_alg)
goto fail;
pwri = wpabuf_alloc(100 + wpabuf_len(key_der_alg) +
wpabuf_len(key_enc_alg) + wpabuf_len(enc_key));
if (!pwri)
goto fail;
/* version = 0 */
asn1_put_integer(pwri, 0);
/* [0] KeyDerivationAlgorithmIdentifier */
asn1_put_hdr(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0,
wpabuf_len(key_der_alg));
wpabuf_put_buf(pwri, key_der_alg);
/* KeyEncryptionAlgorithmIdentifier */
wpabuf_put_buf(pwri, key_enc_alg);
/* EncryptedKey ::= OCTET STRING */
asn1_put_octet_string(pwri, enc_key);
fail:
wpabuf_clear_free(key_der_alg);
wpabuf_free(key_enc_alg);
wpabuf_free(enc_key);
wpabuf_free(salt);
forced_memzero(kek, sizeof(kek));
return asn1_encaps(pwri, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
}
static struct wpabuf *
dpp_build_recipient_info(struct dpp_authentication *auth, size_t hash_len,
const struct wpabuf *cont_enc_key)
{
struct wpabuf *pwri;
/*
* RecipientInfo ::= CHOICE {
* ktri KeyTransRecipientInfo,
* kari [1] KeyAgreeRecipientInfo,
* kekri [2] KEKRecipientInfo,
* pwri [3] PasswordRecipientInfo,
* ori [4] OtherRecipientInfo}
*
* Shall always use the pwri CHOICE.
*/
pwri = dpp_build_pw_recipient_info(auth, hash_len, cont_enc_key);
return asn1_encaps(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
}
static struct wpabuf *
dpp_build_enc_cont_info(struct dpp_authentication *auth, size_t hash_len,
const struct wpabuf *cont_enc_key)
{
struct wpabuf *key_pkg, *enc_cont_info = NULL, *enc_cont = NULL,
*enc_alg;
const struct asn1_oid *oid;
size_t enc_cont_len;
/*
* EncryptedContentInfo ::= SEQUENCE {
* contentType ContentType,
* contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
*/
if (hash_len == 32)
oid = &asn1_aes_siv_cmac_aead_256_oid;
else if (hash_len == 48)
oid = &asn1_aes_siv_cmac_aead_384_oid;
else if (hash_len == 64)
oid = &asn1_aes_siv_cmac_aead_512_oid;
else
return NULL;
key_pkg = dpp_build_key_pkg(auth);
enc_alg = asn1_build_alg_id(oid, NULL);
if (!key_pkg || !enc_alg)
goto fail;
wpa_hexdump_buf_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
key_pkg);
enc_cont_len = wpabuf_len(key_pkg) + AES_BLOCK_SIZE;
enc_cont = wpabuf_alloc(enc_cont_len);
if (!enc_cont ||
aes_siv_encrypt(wpabuf_head(cont_enc_key), wpabuf_len(cont_enc_key),
wpabuf_head(key_pkg), wpabuf_len(key_pkg),
0, NULL, NULL,
wpabuf_put(enc_cont, enc_cont_len)) < 0)
goto fail;
enc_cont_info = wpabuf_alloc(100 + wpabuf_len(enc_alg) +
wpabuf_len(enc_cont));
if (!enc_cont_info)
goto fail;
/* ContentType ::= OBJECT IDENTIFIER */
asn1_put_oid(enc_cont_info, &asn1_dpp_asymmetric_key_package_oid);
/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
wpabuf_put_buf(enc_cont_info, enc_alg);
/* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
* EncryptedContent ::= OCTET STRING */
asn1_put_hdr(enc_cont_info, ASN1_CLASS_CONTEXT_SPECIFIC, 0, 0,
wpabuf_len(enc_cont));
wpabuf_put_buf(enc_cont_info, enc_cont);
fail:
wpabuf_clear_free(key_pkg);
wpabuf_free(enc_cont);
wpabuf_free(enc_alg);
return enc_cont_info;
}
static struct wpabuf * dpp_gen_random(size_t len)
{
struct wpabuf *key;
key = wpabuf_alloc(len);
if (!key || os_get_random(wpabuf_put(key, len), len) < 0) {
wpabuf_free(key);
key = NULL;
}
wpa_hexdump_buf_key(MSG_DEBUG, "DPP: content-encryption key", key);
return key;
}
struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth)
{
struct wpabuf *env = NULL;
struct wpabuf *recipient_info = NULL, *enc_cont_info = NULL;
struct wpabuf *cont_enc_key = NULL;
size_t hash_len;
if (!auth->conf) {
wpa_printf(MSG_DEBUG,
"DPP: No Configurator instance selected for the session - cannot build DPPEnvelopedData");
return NULL;
}
if (!auth->provision_configurator) {
wpa_printf(MSG_DEBUG,
"DPP: Configurator provisioning not allowed");
return NULL;
}
wpa_printf(MSG_DEBUG, "DPP: Building DPPEnvelopedData");
hash_len = auth->conf->curve->hash_len;
cont_enc_key = dpp_gen_random(hash_len);
if (!cont_enc_key)
goto fail;
recipient_info = dpp_build_recipient_info(auth, hash_len, cont_enc_key);
enc_cont_info = dpp_build_enc_cont_info(auth, hash_len, cont_enc_key);
if (!recipient_info || !enc_cont_info)
goto fail;
env = wpabuf_alloc(wpabuf_len(recipient_info) +
wpabuf_len(enc_cont_info) +
100);
if (!env)
goto fail;
/*
* DPPEnvelopedData ::= EnvelopedData
*
* EnvelopedData ::= SEQUENCE {
* version CMSVersion,
* originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
* recipientInfos RecipientInfos,
* encryptedContentInfo EncryptedContentInfo,
* unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
*
* For DPP, version is 3, both originatorInfo and
* unprotectedAttrs are omitted, and recipientInfos contains a single
* RecipientInfo.
*/
/* EnvelopedData.version = 3 */
asn1_put_integer(env, 3);
/* RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo */
asn1_put_set(env, recipient_info);
/* EncryptedContentInfo ::= SEQUENCE */
asn1_put_sequence(env, enc_cont_info);
env = asn1_encaps(env, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: DPPEnvelopedData", env);
out:
wpabuf_clear_free(cont_enc_key);
wpabuf_clear_free(recipient_info);
wpabuf_free(enc_cont_info);
return env;
fail:
wpabuf_free(env);
env = NULL;
goto out;
}
struct dpp_enveloped_data {
const u8 *enc_cont;
size_t enc_cont_len;
const u8 *enc_key;
size_t enc_key_len;
const u8 *salt;
size_t pbkdf2_key_len;
size_t prf_hash_len;
};
static int dpp_parse_recipient_infos(const u8 *pos, size_t len,
struct dpp_enveloped_data *data)
{
struct asn1_hdr hdr;
const u8 *end = pos + len;
const u8 *next, *e_end;
struct asn1_oid oid;
int val;
const u8 *params;
size_t params_len;
wpa_hexdump(MSG_MSGDUMP, "DPP: RecipientInfos", pos, len);
/*
* RecipientInfo ::= CHOICE {
* ktri KeyTransRecipientInfo,
* kari [1] KeyAgreeRecipientInfo,
* kekri [2] KEKRecipientInfo,
* pwri [3] PasswordRecipientInfo,
* ori [4] OtherRecipientInfo}
*
* Shall always use the pwri CHOICE.
*/
if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
!asn1_is_cs_tag(&hdr, 3)) {
asn1_unexpected(&hdr, "DPP: Expected CHOICE [3] (pwri)");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: PasswordRecipientInfo",
hdr.payload, hdr.length);
pos = hdr.payload;
end = pos + hdr.length;
/*
* PasswordRecipientInfo ::= SEQUENCE {
* version CMSVersion,
* keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
* encryptedKey EncryptedKey}
*
* version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
* parameters contains PBKDF2-params SEQUENCE.
*/
if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
return -1;
pos = hdr.payload;
if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
return -1;
if (val != 0) {
wpa_printf(MSG_DEBUG, "DPP: pwri.version != 0");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Remaining PasswordRecipientInfo after version",
pos, end - pos);
if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
!asn1_is_cs_tag(&hdr, 0)) {
asn1_unexpected(&hdr,
"DPP: Expected keyDerivationAlgorithm [0]");
return -1;
}
pos = hdr.payload;
e_end = pos + hdr.length;
/* KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier */
if (asn1_get_alg_id(pos, e_end - pos, &oid, &params, &params_len,
&next) < 0)
return -1;
if (!asn1_oid_equal(&oid, &asn1_pbkdf2_oid)) {
char buf[80];
asn1_oid_to_str(&oid, buf, sizeof(buf));
wpa_printf(MSG_DEBUG,
"DPP: Unexpected KeyDerivationAlgorithmIdentifier %s",
buf);
return -1;
}
/*
* PBKDF2-params ::= SEQUENCE {
* salt CHOICE {
* specified OCTET STRING,
* otherSource AlgorithmIdentifier}
* iterationCount INTEGER (1..MAX),
* keyLength INTEGER (1..MAX),
* prf AlgorithmIdentifier}
*
* salt is an 64 octet value, iterationCount is 1000, keyLength is based
* on Configurator signing key length, prf is
* id-hmacWithSHA{256,384,512} based on Configurator signing key.
*/
if (!params ||
asn1_get_sequence(params, params_len, &hdr, &e_end) < 0)
return -1;
pos = hdr.payload;
if (asn1_get_next(pos, e_end - pos, &hdr) < 0 ||
!asn1_is_octetstring(&hdr)) {
asn1_unexpected(&hdr,
"DPP: Expected OCTETSTRING (salt.specified)");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: salt.specified",
hdr.payload, hdr.length);
if (hdr.length != 64) {
wpa_printf(MSG_DEBUG, "DPP: Unexpected salt length %u",
hdr.length);
return -1;
}
data->salt = hdr.payload;
pos = hdr.payload + hdr.length;
if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
return -1;
if (val != 1000) {
wpa_printf(MSG_DEBUG, "DPP: Unexpected iterationCount %d", val);
return -1;
}
if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
return -1;
if (val != 32 && val != 48 && val != 64) {
wpa_printf(MSG_DEBUG, "DPP: Unexpected keyLength %d", val);
return -1;
}
data->pbkdf2_key_len = val;
if (asn1_get_sequence(pos, e_end - pos, &hdr, NULL) < 0 ||
asn1_get_oid(hdr.payload, hdr.length, &oid, &pos) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Could not parse prf");
return -1;
}
if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha256_oid)) {
data->prf_hash_len = 32;
} else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha384_oid)) {
data->prf_hash_len = 48;
} else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha512_oid)) {
data->prf_hash_len = 64;
} else {
char buf[80];
asn1_oid_to_str(&oid, buf, sizeof(buf));
wpa_printf(MSG_DEBUG, "DPP: Unexpected PBKDF2-params.prf %s",
buf);
return -1;
}
pos = next;
/* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier
*
* KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
*
* id-alg-AES-SIV-CMAC-aed-256, id-alg-AES-SIV-CMAC-aed-384, or
* id-alg-AES-SIV-CMAC-aed-512. */
if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
return -1;
if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
char buf[80];
asn1_oid_to_str(&oid, buf, sizeof(buf));
wpa_printf(MSG_DEBUG,
"DPP: Unexpected KeyEncryptionAlgorithmIdentifier %s",
buf);
return -1;
}
/*
* encryptedKey EncryptedKey
*
* EncryptedKey ::= OCTET STRING
*/
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
!asn1_is_octetstring(&hdr)) {
asn1_unexpected(&hdr,
"DPP: Expected OCTETSTRING (pwri.encryptedKey)");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: pwri.encryptedKey",
hdr.payload, hdr.length);
data->enc_key = hdr.payload;
data->enc_key_len = hdr.length;
return 0;
}
static int dpp_parse_encrypted_content_info(const u8 *pos, const u8 *end,
struct dpp_enveloped_data *data)
{
struct asn1_hdr hdr;
struct asn1_oid oid;
/*
* EncryptedContentInfo ::= SEQUENCE {
* contentType ContentType,
* contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
*/
if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
return -1;
wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContentInfo",
hdr.payload, hdr.length);
if (pos < end) {
wpa_hexdump(MSG_DEBUG,
"DPP: Unexpected extra data after EncryptedContentInfo",
pos, end - pos);
return -1;
}
end = pos;
pos = hdr.payload;
/* ContentType ::= OBJECT IDENTIFIER */
if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Could not parse ContentType");
return -1;
}
if (!asn1_oid_equal(&oid, &asn1_dpp_asymmetric_key_package_oid)) {
char buf[80];
asn1_oid_to_str(&oid, buf, sizeof(buf));
wpa_printf(MSG_DEBUG, "DPP: Unexpected ContentType %s", buf);
return -1;
}
/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
return -1;
if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
char buf[80];
asn1_oid_to_str(&oid, buf, sizeof(buf));
wpa_printf(MSG_DEBUG,
"DPP: Unexpected ContentEncryptionAlgorithmIdentifier %s",
buf);
return -1;
}
/* ignore optional parameters */
/* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
* EncryptedContent ::= OCTET STRING */
if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.constructed ||
!asn1_is_cs_tag(&hdr, 0)) {
asn1_unexpected(&hdr,
"DPP: Expected [0] IMPLICIT (EncryptedContent)");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContent",
hdr.payload, hdr.length);
data->enc_cont = hdr.payload;
data->enc_cont_len = hdr.length;
return 0;
}
static int dpp_parse_enveloped_data(const u8 *env_data, size_t env_data_len,
struct dpp_enveloped_data *data)
{
struct asn1_hdr hdr;
const u8 *pos, *end;
int val;
os_memset(data, 0, sizeof(*data));
/*
* DPPEnvelopedData ::= EnvelopedData
*
* EnvelopedData ::= SEQUENCE {
* version CMSVersion,
* originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
* recipientInfos RecipientInfos,
* encryptedContentInfo EncryptedContentInfo,
* unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
*
* CMSVersion ::= INTEGER
*
* RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo
*
* For DPP, version is 3, both originatorInfo and
* unprotectedAttrs are omitted, and recipientInfos contains a single
* RecipientInfo.
*/
if (asn1_get_sequence(env_data, env_data_len, &hdr, &end) < 0)
return -1;
pos = hdr.payload;
if (end < env_data + env_data_len) {
wpa_hexdump(MSG_DEBUG,
"DPP: Unexpected extra data after DPPEnvelopedData",
end, env_data + env_data_len - end);
return -1;
}
if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
return -1;
if (val != 3) {
wpa_printf(MSG_DEBUG, "DPP: EnvelopedData.version != 3");
return -1;
}
if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_set(&hdr)) {
asn1_unexpected(&hdr,
"DPP: Expected SET (RecipientInfos)");
return -1;
}
if (dpp_parse_recipient_infos(hdr.payload, hdr.length, data) < 0)
return -1;
return dpp_parse_encrypted_content_info(hdr.payload + hdr.length, end,
data);
}
static struct dpp_asymmetric_key *
dpp_parse_one_asymmetric_key(const u8 *buf, size_t len)
{
struct asn1_hdr hdr;
const u8 *pos = buf, *end = buf + len, *next;
int val;
const u8 *params;
size_t params_len;
struct asn1_oid oid;
char txt[80];
struct dpp_asymmetric_key *key;
- EC_KEY *eckey;
wpa_hexdump_key(MSG_MSGDUMP, "DPP: OneAsymmetricKey", buf, len);
key = os_zalloc(sizeof(*key));
if (!key)
return NULL;
/*
* OneAsymmetricKey ::= SEQUENCE {
* version Version,
* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
* privateKey PrivateKey,
* attributes [0] Attributes OPTIONAL,
* ...,
* [[2: publicKey [1] BIT STRING OPTIONAL ]],
* ...
* }
*/
if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
goto fail;
pos = hdr.payload;
/* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
goto fail;
if (val != 0 && val != 1) {
wpa_printf(MSG_DEBUG,
"DPP: Unsupported DPPAsymmetricKeyPackage version %d",
val);
goto fail;
}
/* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier */
if (asn1_get_alg_id(pos, end - pos, &oid, &params, &params_len,
&pos) < 0)
goto fail;
if (!asn1_oid_equal(&oid, &asn1_ec_public_key_oid)) {
asn1_oid_to_str(&oid, txt, sizeof(txt));
wpa_printf(MSG_DEBUG,
"DPP: Unsupported PrivateKeyAlgorithmIdentifier %s",
txt);
goto fail;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: PrivateKeyAlgorithmIdentifier params",
params, params_len);
/*
* ECParameters ::= CHOICE {
* namedCurve OBJECT IDENTIFIER
* -- implicitCurve NULL
* -- specifiedCurve SpecifiedECDomain}
*/
if (!params || asn1_get_oid(params, params_len, &oid, &next) < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Could not parse ECParameters.namedCurve");
goto fail;
}
asn1_oid_to_str(&oid, txt, sizeof(txt));
wpa_printf(MSG_MSGDUMP, "DPP: namedCurve %s", txt);
/* Assume the curve is identified within ECPrivateKey, so that this
* separate indication is not really needed. */
/*
* PrivateKey ::= OCTET STRING
* (Contains DER encoding of ECPrivateKey)
*/
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
!asn1_is_octetstring(&hdr)) {
asn1_unexpected(&hdr,
"DPP: Expected OCTETSTRING (PrivateKey)");
goto fail;
}
wpa_hexdump_key(MSG_MSGDUMP, "DPP: PrivateKey",
hdr.payload, hdr.length);
pos = hdr.payload + hdr.length;
- eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
- if (!eckey) {
- wpa_printf(MSG_INFO,
- "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- key->csign = EVP_PKEY_new();
- if (!key->csign || EVP_PKEY_assign_EC_KEY(key->csign, eckey) != 1) {
- EC_KEY_free(eckey);
+ key->csign = crypto_ec_key_parse_priv(hdr.payload, hdr.length);
+ if (!key->csign)
goto fail;
- }
if (wpa_debug_show_keys)
dpp_debug_print_key("DPP: Received c-sign-key", key->csign);
/*
* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
*
* Exactly one instance of type Attribute in OneAsymmetricKey.
*/
if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
!asn1_is_cs_tag(&hdr, 0)) {
asn1_unexpected(&hdr, "DPP: Expected [0] Attributes");
goto fail;
}
wpa_hexdump_key(MSG_MSGDUMP, "DPP: Attributes",
hdr.payload, hdr.length);
if (hdr.payload + hdr.length < end) {
wpa_hexdump_key(MSG_MSGDUMP,
"DPP: Ignore additional data at the end of OneAsymmetricKey",
hdr.payload + hdr.length,
end - (hdr.payload + hdr.length));
}
pos = hdr.payload;
end = hdr.payload + hdr.length;
if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_set(&hdr)) {
asn1_unexpected(&hdr, "DPP: Expected SET (Attributes)");
goto fail;
}
if (hdr.payload + hdr.length < end) {
wpa_hexdump_key(MSG_MSGDUMP,
"DPP: Ignore additional data at the end of OneAsymmetricKey (after SET)",
hdr.payload + hdr.length,
end - (hdr.payload + hdr.length));
}
pos = hdr.payload;
end = hdr.payload + hdr.length;
/*
* OneAsymmetricKeyAttributes ATTRIBUTE ::= {
* aa-DPPConfigurationParameters,
* ... -- For local profiles
* }
*
* aa-DPPConfigurationParameters ATTRIBUTE ::=
* { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
*
* Attribute ::= SEQUENCE {
* type OBJECT IDENTIFIER,
* values SET SIZE(1..MAX) OF Type
*
* Exactly one instance of ATTRIBUTE in attrValues.
*/
if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
goto fail;
if (pos < end) {
wpa_hexdump_key(MSG_MSGDUMP,
"DPP: Ignore additional data at the end of ATTRIBUTE",
pos, end - pos);
}
end = pos;
pos = hdr.payload;
if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0)
goto fail;
if (!asn1_oid_equal(&oid, &asn1_dpp_config_params_oid)) {
asn1_oid_to_str(&oid, txt, sizeof(txt));
wpa_printf(MSG_DEBUG,
"DPP: Unexpected Attribute identifier %s", txt);
goto fail;
}
if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_set(&hdr)) {
asn1_unexpected(&hdr, "DPP: Expected SET (Attribute)");
goto fail;
}
pos = hdr.payload;
end = hdr.payload + hdr.length;
/*
* DPPConfigurationParameters ::= SEQUENCE {
* privacyProtectionKey PrivateKey,
* configurationTemplate UTF8String,
* connectorTemplate UTF8String OPTIONAL}
*/
wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPConfigurationParameters",
pos, end - pos);
if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
goto fail;
if (pos < end) {
wpa_hexdump_key(MSG_MSGDUMP,
"DPP: Ignore additional data after DPPConfigurationParameters",
pos, end - pos);
}
end = pos;
pos = hdr.payload;
/*
* PrivateKey ::= OCTET STRING
* (Contains DER encoding of ECPrivateKey)
*/
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
!asn1_is_octetstring(&hdr)) {
asn1_unexpected(&hdr, "DPP: Expected OCTETSTRING (PrivateKey)");
goto fail;
}
wpa_hexdump_key(MSG_MSGDUMP, "DPP: privacyProtectionKey",
hdr.payload, hdr.length);
pos = hdr.payload + hdr.length;
- eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
- if (!eckey) {
- wpa_printf(MSG_INFO,
- "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- key->pp_key = EVP_PKEY_new();
- if (!key->pp_key || EVP_PKEY_assign_EC_KEY(key->pp_key, eckey) != 1) {
- EC_KEY_free(eckey);
+ key->pp_key = crypto_ec_key_parse_priv(hdr.payload, hdr.length);
+ if (!key->pp_key)
goto fail;
- }
if (wpa_debug_show_keys)
dpp_debug_print_key("DPP: Received privacyProtectionKey",
key->pp_key);
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
!asn1_is_utf8string(&hdr)) {
asn1_unexpected(&hdr,
"DPP: Expected UTF8STRING (configurationTemplate)");
goto fail;
}
wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: configurationTemplate",
hdr.payload, hdr.length);
key->config_template = os_zalloc(hdr.length + 1);
if (!key->config_template)
goto fail;
os_memcpy(key->config_template, hdr.payload, hdr.length);
pos = hdr.payload + hdr.length;
if (pos < end) {
if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
!asn1_is_utf8string(&hdr)) {
asn1_unexpected(&hdr,
"DPP: Expected UTF8STRING (connectorTemplate)");
goto fail;
}
wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: connectorTemplate",
hdr.payload, hdr.length);
key->connector_template = os_zalloc(hdr.length + 1);
if (!key->connector_template)
goto fail;
os_memcpy(key->connector_template, hdr.payload, hdr.length);
}
return key;
fail:
wpa_printf(MSG_DEBUG, "DPP: Failed to parse OneAsymmetricKey");
dpp_free_asymmetric_key(key);
return NULL;
}
static struct dpp_asymmetric_key *
dpp_parse_dpp_asymmetric_key_package(const u8 *key_pkg, size_t key_pkg_len)
{
struct asn1_hdr hdr;
const u8 *pos = key_pkg, *end = key_pkg + key_pkg_len;
struct dpp_asymmetric_key *first = NULL, *last = NULL, *key;
wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
key_pkg, key_pkg_len);
/*
* DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
*
* AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
*/
while (pos < end) {
if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0 ||
!(key = dpp_parse_one_asymmetric_key(hdr.payload,
hdr.length))) {
dpp_free_asymmetric_key(first);
return NULL;
}
if (!last) {
first = last = key;
} else {
last->next = key;
last = key;
}
}
return first;
}
int dpp_conf_resp_env_data(struct dpp_authentication *auth,
const u8 *env_data, size_t env_data_len)
{
u8 key[DPP_MAX_HASH_LEN];
size_t key_len;
u8 kek[DPP_MAX_HASH_LEN];
u8 cont_encr_key[DPP_MAX_HASH_LEN];
size_t cont_encr_key_len;
int res;
u8 *key_pkg;
size_t key_pkg_len;
struct dpp_enveloped_data data;
struct dpp_asymmetric_key *keys;
wpa_hexdump(MSG_DEBUG, "DPP: DPPEnvelopedData", env_data, env_data_len);
if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
return -1;
key_len = auth->curve->hash_len;
/* password = HKDF-Expand(bk, "Enveloped Data Password", length) */
res = dpp_hkdf_expand(key_len, auth->bk, key_len,
"Enveloped Data Password", key, key_len);
if (res < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
kek, data.pbkdf2_key_len)) {
wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
kek, data.pbkdf2_key_len);
if (data.enc_key_len < AES_BLOCK_SIZE ||
data.enc_key_len > sizeof(cont_encr_key) + AES_BLOCK_SIZE) {
wpa_printf(MSG_DEBUG, "DPP: Invalid encryptedKey length");
return -1;
}
res = aes_siv_decrypt(kek, data.pbkdf2_key_len,
data.enc_key, data.enc_key_len,
0, NULL, NULL, cont_encr_key);
forced_memzero(kek, data.pbkdf2_key_len);
if (res < 0) {
wpa_printf(MSG_DEBUG,
"DPP: AES-SIV decryption of encryptedKey failed");
return -1;
}
cont_encr_key_len = data.enc_key_len - AES_BLOCK_SIZE;
wpa_hexdump_key(MSG_DEBUG, "DPP: content-encryption key",
cont_encr_key, cont_encr_key_len);
if (data.enc_cont_len < AES_BLOCK_SIZE)
return -1;
key_pkg_len = data.enc_cont_len - AES_BLOCK_SIZE;
key_pkg = os_malloc(key_pkg_len);
if (!key_pkg)
return -1;
res = aes_siv_decrypt(cont_encr_key, cont_encr_key_len,
data.enc_cont, data.enc_cont_len,
0, NULL, NULL, key_pkg);
forced_memzero(cont_encr_key, cont_encr_key_len);
if (res < 0) {
bin_clear_free(key_pkg, key_pkg_len);
wpa_printf(MSG_DEBUG,
"DPP: AES-SIV decryption of encryptedContent failed");
return -1;
}
keys = dpp_parse_dpp_asymmetric_key_package(key_pkg, key_pkg_len);
bin_clear_free(key_pkg, key_pkg_len);
dpp_free_asymmetric_key(auth->conf_key_pkg);
auth->conf_key_pkg = keys;
return keys != NULL;
}
#endif /* CONFIG_DPP2 */
diff --git a/contrib/wpa/src/common/dpp_crypto.c b/contrib/wpa/src/common/dpp_crypto.c
index c75fc78711a8..da59730eb7b7 100644
--- a/contrib/wpa/src/common/dpp_crypto.c
+++ b/contrib/wpa/src/common/dpp_crypto.c
@@ -1,3329 +1,2436 @@
/*
* DPP crypto functionality
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
-#include <openssl/opensslv.h>
-#include <openssl/err.h>
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
-#include <openssl/pem.h>
#include "utils/common.h"
#include "utils/base64.h"
#include "utils/json.h"
#include "common/ieee802_11_defs.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
#include "crypto/sha384.h"
#include "crypto/sha512.h"
+#include "tls/asn1.h"
#include "dpp.h"
#include "dpp_i.h"
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
- (defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20700000L)
-/* Compatibility wrappers for older versions. */
-
-static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
-{
- sig->r = r;
- sig->s = s;
- return 1;
-}
-
-
-static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
- const BIGNUM **ps)
-{
- if (pr)
- *pr = sig->r;
- if (ps)
- *ps = sig->s;
-}
-
-
-static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
-{
- if (pkey->type != EVP_PKEY_EC)
- return NULL;
- return pkey->pkey.ec;
-}
-
-#endif
-
static const struct dpp_curve_params dpp_curves[] = {
/* The mandatory to support and the default NIST P-256 curve needs to
* be the first entry on this list. */
{ "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
{ "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
{ "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
{ "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
{ "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
{ "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
{ NULL, 0, 0, 0, 0, NULL, 0, NULL }
};
const struct dpp_curve_params * dpp_get_curve_name(const char *name)
{
int i;
if (!name)
return &dpp_curves[0];
for (i = 0; dpp_curves[i].name; i++) {
if (os_strcmp(name, dpp_curves[i].name) == 0 ||
(dpp_curves[i].jwk_crv &&
os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
return &dpp_curves[i];
}
return NULL;
}
const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name)
{
int i;
for (i = 0; dpp_curves[i].name; i++) {
if (dpp_curves[i].jwk_crv &&
os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
return &dpp_curves[i];
}
return NULL;
}
-static const struct dpp_curve_params *
-dpp_get_curve_oid(const ASN1_OBJECT *poid)
-{
- ASN1_OBJECT *oid;
- int i;
-
- for (i = 0; dpp_curves[i].name; i++) {
- oid = OBJ_txt2obj(dpp_curves[i].name, 0);
- if (oid && OBJ_cmp(poid, oid) == 0)
- return &dpp_curves[i];
- }
- return NULL;
-}
-
-
-const struct dpp_curve_params * dpp_get_curve_nid(int nid)
-{
- int i, tmp;
-
- if (!nid)
- return NULL;
- for (i = 0; dpp_curves[i].name; i++) {
- tmp = OBJ_txt2nid(dpp_curves[i].name);
- if (tmp == nid)
- return &dpp_curves[i];
- }
- return NULL;
-}
-
-
const struct dpp_curve_params * dpp_get_curve_ike_group(u16 group)
{
int i;
for (i = 0; dpp_curves[i].name; i++) {
if (dpp_curves[i].ike_group == group)
return &dpp_curves[i];
}
return NULL;
}
-void dpp_debug_print_point(const char *title, const EC_GROUP *group,
- const EC_POINT *point)
-{
- BIGNUM *x, *y;
- BN_CTX *ctx;
- char *x_str = NULL, *y_str = NULL;
-
- if (!wpa_debug_show_keys)
- return;
-
- ctx = BN_CTX_new();
- x = BN_new();
- y = BN_new();
- if (!ctx || !x || !y ||
- EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
- goto fail;
-
- x_str = BN_bn2hex(x);
- y_str = BN_bn2hex(y);
- if (!x_str || !y_str)
- goto fail;
-
- wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
-
-fail:
- OPENSSL_free(x_str);
- OPENSSL_free(y_str);
- BN_free(x);
- BN_free(y);
- BN_CTX_free(ctx);
-}
-
-
-void dpp_debug_print_key(const char *title, EVP_PKEY *key)
+void dpp_debug_print_key(const char *title, struct crypto_ec_key *key)
{
- EC_KEY *eckey;
- BIO *out;
- size_t rlen;
- char *txt;
- int res;
- unsigned char *der = NULL;
- int der_len;
- const EC_GROUP *group;
- const EC_POINT *point;
-
- out = BIO_new(BIO_s_mem());
- if (!out)
- return;
-
- EVP_PKEY_print_private(out, key, 0, NULL);
- rlen = BIO_ctrl_pending(out);
- txt = os_malloc(rlen + 1);
- if (txt) {
- res = BIO_read(out, txt, rlen);
- if (res > 0) {
- txt[res] = '\0';
- wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
- }
- os_free(txt);
- }
- BIO_free(out);
-
- eckey = EVP_PKEY_get1_EC_KEY(key);
- if (!eckey)
- return;
+ struct wpabuf *der = NULL;
- group = EC_KEY_get0_group(eckey);
- point = EC_KEY_get0_public_key(eckey);
- if (group && point)
- dpp_debug_print_point(title, group, point);
+ crypto_ec_key_debug_print(key, title);
- der_len = i2d_ECPrivateKey(eckey, &der);
- if (der_len > 0)
- wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
- OPENSSL_free(der);
- if (der_len <= 0) {
- der = NULL;
- der_len = i2d_EC_PUBKEY(eckey, &der);
- if (der_len > 0)
- wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
- OPENSSL_free(der);
+ der = crypto_ec_key_get_ecprivate_key(key, true);
+ if (der) {
+ wpa_hexdump_buf_key(MSG_DEBUG, "DPP: ECPrivateKey", der);
+ } else {
+ der = crypto_ec_key_get_subject_public_key(key);
+ if (der)
+ wpa_hexdump_buf_key(MSG_DEBUG, "DPP: EC_PUBKEY", der);
}
- EC_KEY_free(eckey);
+ wpabuf_clear_free(der);
}
static int dpp_hash_vector(const struct dpp_curve_params *curve,
size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac)
{
if (curve->hash_len == 32)
return sha256_vector(num_elem, addr, len, mac);
if (curve->hash_len == 48)
return sha384_vector(num_elem, addr, len, mac);
if (curve->hash_len == 64)
return sha512_vector(num_elem, addr, len, mac);
return -1;
}
int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
const char *label, u8 *out, size_t outlen)
{
if (hash_len == 32)
return hmac_sha256_kdf(secret, secret_len, NULL,
(const u8 *) label, os_strlen(label),
out, outlen);
if (hash_len == 48)
return hmac_sha384_kdf(secret, secret_len, NULL,
(const u8 *) label, os_strlen(label),
out, outlen);
if (hash_len == 64)
return hmac_sha512_kdf(secret, secret_len, NULL,
(const u8 *) label, os_strlen(label),
out, outlen);
return -1;
}
int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac)
{
if (hash_len == 32)
return hmac_sha256_vector(key, key_len, num_elem, addr, len,
mac);
if (hash_len == 48)
return hmac_sha384_vector(key, key_len, num_elem, addr, len,
mac);
if (hash_len == 64)
return hmac_sha512_vector(key, key_len, num_elem, addr, len,
mac);
return -1;
}
static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
const u8 *data, size_t data_len, u8 *mac)
{
if (hash_len == 32)
return hmac_sha256(key, key_len, data, data_len, mac);
if (hash_len == 48)
return hmac_sha384(key, key_len, data, data_len, mac);
if (hash_len == 64)
return hmac_sha512(key, key_len, data, data_len, mac);
return -1;
}
#ifdef CONFIG_DPP2
static int dpp_pbkdf2_f(size_t hash_len,
const u8 *password, size_t password_len,
const u8 *salt, size_t salt_len,
unsigned int iterations, unsigned int count, u8 *digest)
{
unsigned char tmp[DPP_MAX_HASH_LEN], tmp2[DPP_MAX_HASH_LEN];
unsigned int i;
size_t j;
u8 count_buf[4];
const u8 *addr[2];
size_t len[2];
addr[0] = salt;
len[0] = salt_len;
addr[1] = count_buf;
len[1] = 4;
/* F(P, S, c, i) = U1 xor U2 xor ... Uc
* U1 = PRF(P, S || i)
* U2 = PRF(P, U1)
* Uc = PRF(P, Uc-1)
*/
WPA_PUT_BE32(count_buf, count);
if (dpp_hmac_vector(hash_len, password, password_len, 2, addr, len,
tmp))
return -1;
os_memcpy(digest, tmp, hash_len);
for (i = 1; i < iterations; i++) {
if (dpp_hmac(hash_len, password, password_len, tmp, hash_len,
tmp2))
return -1;
os_memcpy(tmp, tmp2, hash_len);
for (j = 0; j < hash_len; j++)
digest[j] ^= tmp2[j];
}
return 0;
}
int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
const u8 *salt, size_t salt_len, unsigned int iterations,
u8 *buf, size_t buflen)
{
unsigned int count = 0;
unsigned char *pos = buf;
size_t left = buflen, plen;
unsigned char digest[DPP_MAX_HASH_LEN];
while (left > 0) {
count++;
if (dpp_pbkdf2_f(hash_len, password, password_len,
salt, salt_len, iterations, count, digest))
return -1;
plen = left > hash_len ? hash_len : left;
os_memcpy(pos, digest, plen);
pos += plen;
left -= plen;
}
return 0;
}
#endif /* CONFIG_DPP2 */
-int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
-{
- int num_bytes, offset;
-
- num_bytes = BN_num_bytes(bn);
- if ((size_t) num_bytes > len)
- return -1;
- offset = len - num_bytes;
- os_memset(pos, 0, offset);
- BN_bn2bin(bn, pos + offset);
- return 0;
-}
-
-
-struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
-{
- int len, res;
- EC_KEY *eckey;
- struct wpabuf *buf;
- unsigned char *pos;
-
- eckey = EVP_PKEY_get1_EC_KEY(pkey);
- if (!eckey)
- return NULL;
- EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
- len = i2o_ECPublicKey(eckey, NULL);
- if (len <= 0) {
- wpa_printf(MSG_ERROR,
- "DDP: Failed to determine public key encoding length");
- EC_KEY_free(eckey);
- return NULL;
- }
-
- buf = wpabuf_alloc(len);
- if (!buf) {
- EC_KEY_free(eckey);
- return NULL;
- }
-
- pos = wpabuf_put(buf, len);
- res = i2o_ECPublicKey(eckey, &pos);
- EC_KEY_free(eckey);
- if (res != len) {
- wpa_printf(MSG_ERROR,
- "DDP: Failed to encode public key (res=%d/%d)",
- res, len);
- wpabuf_free(buf);
- return NULL;
- }
-
- if (!prefix) {
- /* Remove 0x04 prefix to match DPP definition */
- pos = wpabuf_mhead(buf);
- os_memmove(pos, pos + 1, len - 1);
- buf->used--;
- }
-
- return buf;
-}
-
-
-EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
- const u8 *buf_x, const u8 *buf_y,
- size_t len)
-{
- EC_KEY *eckey = NULL;
- BN_CTX *ctx;
- EC_POINT *point = NULL;
- BIGNUM *x = NULL, *y = NULL;
- EVP_PKEY *pkey = NULL;
-
- ctx = BN_CTX_new();
- if (!ctx) {
- wpa_printf(MSG_ERROR, "DPP: Out of memory");
- return NULL;
- }
-
- point = EC_POINT_new(group);
- x = BN_bin2bn(buf_x, len, NULL);
- y = BN_bin2bn(buf_y, len, NULL);
- if (!point || !x || !y) {
- wpa_printf(MSG_ERROR, "DPP: Out of memory");
- goto fail;
- }
-
- if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
- wpa_printf(MSG_ERROR,
- "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (!EC_POINT_is_on_curve(group, point, ctx) ||
- EC_POINT_is_at_infinity(group, point)) {
- wpa_printf(MSG_ERROR, "DPP: Invalid point");
- goto fail;
- }
- dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
-
- eckey = EC_KEY_new();
- if (!eckey ||
- EC_KEY_set_group(eckey, group) != 1 ||
- EC_KEY_set_public_key(eckey, point) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to set EC_KEY: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
-
- pkey = EVP_PKEY_new();
- if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
- wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
- goto fail;
- }
-
-out:
- BN_free(x);
- BN_free(y);
- EC_KEY_free(eckey);
- EC_POINT_free(point);
- BN_CTX_free(ctx);
- return pkey;
-fail:
- EVP_PKEY_free(pkey);
- pkey = NULL;
- goto out;
-}
-
-
-EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len)
+struct crypto_ec_key * dpp_set_pubkey_point(struct crypto_ec_key *group_key,
+ const u8 *buf, size_t len)
{
- const EC_KEY *eckey;
- const EC_GROUP *group;
- EVP_PKEY *pkey = NULL;
+ int ike_group = crypto_ec_key_group(group_key);
if (len & 1)
return NULL;
- eckey = EVP_PKEY_get0_EC_KEY(group_key);
- if (!eckey) {
- wpa_printf(MSG_ERROR,
- "DPP: Could not get EC_KEY from group_key");
+ if (ike_group < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
return NULL;
}
- group = EC_KEY_get0_group(eckey);
- if (group)
- pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
- len / 2);
- else
- wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
-
- return pkey;
+ return crypto_ec_key_set_pub(ike_group, buf, buf + len / 2, len / 2);
}
-EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
+struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve)
{
- EVP_PKEY_CTX *kctx = NULL;
- EC_KEY *ec_params = NULL;
- EVP_PKEY *params = NULL, *key = NULL;
- int nid;
+ struct crypto_ec_key *key;
wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
- nid = OBJ_txt2nid(curve->name);
- if (nid == NID_undef) {
- wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
- return NULL;
- }
-
- ec_params = EC_KEY_new_by_curve_name(nid);
- if (!ec_params) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate EC_KEY parameters");
- goto fail;
- }
- EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
- params = EVP_PKEY_new();
- if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to generate EVP_PKEY parameters");
- goto fail;
- }
-
- kctx = EVP_PKEY_CTX_new(params, NULL);
- if (!kctx ||
- EVP_PKEY_keygen_init(kctx) != 1 ||
- EVP_PKEY_keygen(kctx, &key) != 1) {
- wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
- key = NULL;
- goto fail;
- }
-
- if (wpa_debug_show_keys)
- dpp_debug_print_key("Own generated key", key);
+ key = crypto_ec_key_gen(curve->ike_group);
+ if (key && wpa_debug_show_keys)
+ dpp_debug_print_key("Own generated key", key);
-fail:
- EC_KEY_free(ec_params);
- EVP_PKEY_free(params);
- EVP_PKEY_CTX_free(kctx);
return key;
}
-EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
- const u8 *privkey, size_t privkey_len)
+struct crypto_ec_key * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len)
{
- EVP_PKEY *pkey;
- EC_KEY *eckey;
- const EC_GROUP *group;
- int nid;
+ struct crypto_ec_key *key;
+ int group;
- pkey = EVP_PKEY_new();
- if (!pkey)
- return NULL;
- eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
- if (!eckey) {
- wpa_printf(MSG_INFO,
- "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- EVP_PKEY_free(pkey);
+ key = crypto_ec_key_parse_priv(privkey, privkey_len);
+ if (!key) {
+ wpa_printf(MSG_INFO, "DPP: Failed to parse private key");
return NULL;
}
- group = EC_KEY_get0_group(eckey);
- if (!group) {
- EC_KEY_free(eckey);
- EVP_PKEY_free(pkey);
+
+ group = crypto_ec_key_group(key);
+ if (group < 0) {
+ crypto_ec_key_deinit(key);
return NULL;
}
- nid = EC_GROUP_get_curve_name(group);
- *curve = dpp_get_curve_nid(nid);
+
+ *curve = dpp_get_curve_ike_group(group);
if (!*curve) {
wpa_printf(MSG_INFO,
- "DPP: Unsupported curve (nid=%d) in pre-assigned key",
- nid);
- EC_KEY_free(eckey);
- EVP_PKEY_free(pkey);
- return NULL;
- }
-
- if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
- EC_KEY_free(eckey);
- EVP_PKEY_free(pkey);
+ "DPP: Unsupported curve (group=%d) in pre-assigned key",
+ group);
+ crypto_ec_key_deinit(key);
return NULL;
}
- return pkey;
-}
-
-
-typedef struct {
- /* AlgorithmIdentifier ecPublicKey with optional parameters present
- * as an OID identifying the curve */
- X509_ALGOR *alg;
- /* Compressed format public key per ANSI X9.63 */
- ASN1_BIT_STRING *pub_key;
-} DPP_BOOTSTRAPPING_KEY;
-
-ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
- ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
- ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
-} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
-
-IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
-
-
-static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
-{
- unsigned char *der = NULL;
- int der_len;
- const EC_KEY *eckey;
- struct wpabuf *ret = NULL;
- size_t len;
- const EC_GROUP *group;
- const EC_POINT *point;
- BN_CTX *ctx;
- DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
- int nid;
-
- ctx = BN_CTX_new();
- eckey = EVP_PKEY_get0_EC_KEY(key);
- if (!ctx || !eckey)
- goto fail;
-
- group = EC_KEY_get0_group(eckey);
- point = EC_KEY_get0_public_key(eckey);
- if (!group || !point)
- goto fail;
- dpp_debug_print_point("DPP: bootstrap public key", group, point);
- nid = EC_GROUP_get_curve_name(group);
-
- bootstrap = DPP_BOOTSTRAPPING_KEY_new();
- if (!bootstrap ||
- X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
- V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
- goto fail;
-
- len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
- NULL, 0, ctx);
- if (len == 0)
- goto fail;
-
- der = OPENSSL_malloc(len);
- if (!der)
- goto fail;
- len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
- der, len, ctx);
-
- OPENSSL_free(bootstrap->pub_key->data);
- bootstrap->pub_key->data = der;
- der = NULL;
- bootstrap->pub_key->length = len;
- /* No unused bits */
- bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
- bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
-
- der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
- if (der_len <= 0) {
- wpa_printf(MSG_ERROR,
- "DDP: Failed to build DER encoded public key");
- goto fail;
- }
- ret = wpabuf_alloc_copy(der, der_len);
-fail:
- DPP_BOOTSTRAPPING_KEY_free(bootstrap);
- OPENSSL_free(der);
- BN_CTX_free(ctx);
- return ret;
+ return key;
}
int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
{
struct wpabuf *der;
int res;
- der = dpp_bootstrap_key_der(bi->pubkey);
+ der = crypto_ec_key_get_subject_public_key(bi->pubkey);
if (!der)
return -1;
wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
der);
res = dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der));
if (res < 0)
wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
wpabuf_free(der);
return res;
}
int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
const u8 *privkey, size_t privkey_len)
{
char *base64 = NULL;
char *pos, *end;
size_t len;
struct wpabuf *der = NULL;
bi->curve = dpp_get_curve_name(curve);
if (!bi->curve) {
wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
return -1;
}
if (privkey)
bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
else
bi->pubkey = dpp_gen_keypair(bi->curve);
if (!bi->pubkey)
goto fail;
bi->own = 1;
- der = dpp_bootstrap_key_der(bi->pubkey);
+ der = crypto_ec_key_get_subject_public_key(bi->pubkey);
if (!der)
goto fail;
wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
der);
if (dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der)) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
goto fail;
}
base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
wpabuf_free(der);
der = NULL;
if (!base64)
goto fail;
pos = base64;
end = pos + len;
for (;;) {
pos = os_strchr(pos, '\n');
if (!pos)
break;
os_memmove(pos, pos + 1, end - pos);
}
os_free(bi->pk);
bi->pk = base64;
return 0;
fail:
os_free(base64);
wpabuf_free(der);
return -1;
}
int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len)
{
u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
const char *info = "first intermediate key";
int res;
/* k1 = HKDF(<>, "first intermediate key", M.x) */
/* HKDF-Extract(<>, M.x) */
os_memset(salt, 0, hash_len);
if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
prk, hash_len);
/* HKDF-Expand(PRK, info, L) */
res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
os_memset(prk, 0, hash_len);
if (res < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
k1, hash_len);
return 0;
}
int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len)
{
u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
const char *info = "second intermediate key";
int res;
/* k2 = HKDF(<>, "second intermediate key", N.x) */
/* HKDF-Extract(<>, N.x) */
os_memset(salt, 0, hash_len);
res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
if (res < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
prk, hash_len);
/* HKDF-Expand(PRK, info, L) */
res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
os_memset(prk, 0, hash_len);
if (res < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
k2, hash_len);
return 0;
}
int dpp_derive_bk_ke(struct dpp_authentication *auth)
{
unsigned int hash_len = auth->curve->hash_len;
size_t nonce_len = auth->curve->nonce_len;
u8 nonces[2 * DPP_MAX_NONCE_LEN];
const char *info_ke = "DPP Key";
int res;
const u8 *addr[3];
size_t len[3];
size_t num_elem = 0;
if (!auth->Mx_len || !auth->Nx_len) {
wpa_printf(MSG_DEBUG,
"DPP: Mx/Nx not available - cannot derive ke");
return -1;
}
/* bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
os_memcpy(nonces, auth->i_nonce, nonce_len);
os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
addr[num_elem] = auth->Mx;
len[num_elem] = auth->Mx_len;
num_elem++;
addr[num_elem] = auth->Nx;
len[num_elem] = auth->Nx_len;
num_elem++;
if (auth->peer_bi && auth->own_bi) {
if (!auth->Lx_len) {
wpa_printf(MSG_DEBUG,
"DPP: Lx not available - cannot derive ke");
return -1;
}
addr[num_elem] = auth->Lx;
len[num_elem] = auth->secret_len;
num_elem++;
}
res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
num_elem, addr, len, auth->bk);
if (res < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG,
"DPP: bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x])",
auth->bk, hash_len);
/* ke = HKDF-Expand(bk, "DPP Key", length) */
res = dpp_hkdf_expand(hash_len, auth->bk, hash_len, info_ke, auth->ke,
hash_len);
if (res < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG,
"DPP: ke = HKDF-Expand(bk, \"DPP Key\", length)",
auth->ke, hash_len);
return 0;
}
-int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, u8 *secret, size_t *secret_len)
+int dpp_ecdh(struct crypto_ec_key *own, struct crypto_ec_key *peer,
+ u8 *secret, size_t *secret_len)
{
- EVP_PKEY_CTX *ctx;
+ struct crypto_ecdh *ecdh;
+ struct wpabuf *peer_pub, *secret_buf = NULL;
int ret = -1;
- ERR_clear_error();
*secret_len = 0;
- ctx = EVP_PKEY_CTX_new(own, NULL);
- if (!ctx) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ ecdh = crypto_ecdh_init2(crypto_ec_key_group(own), own);
+ if (!ecdh) {
+ wpa_printf(MSG_ERROR, "DPP: crypto_ecdh_init2() failed");
return -1;
}
- if (EVP_PKEY_derive_init(ctx) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) {
+ peer_pub = crypto_ec_key_get_pubkey_point(peer, 0);
+ if (!peer_pub) {
wpa_printf(MSG_ERROR,
- "DPP: EVP_PKEY_derive_set_peet failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ "DPP: crypto_ec_key_get_pubkey_point() failed");
goto fail;
}
- if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ secret_buf = crypto_ecdh_set_peerkey(ecdh, 1, wpabuf_head(peer_pub),
+ wpabuf_len(peer_pub));
+ if (!secret_buf) {
+ wpa_printf(MSG_ERROR, "DPP: crypto_ecdh_set_peerkey() failed");
goto fail;
}
- if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
- u8 buf[200];
- int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG;
-
- /* It looks like OpenSSL can return unexpectedly large buffer
- * need for shared secret from EVP_PKEY_derive(NULL) in some
- * cases. For example, group 19 has shown cases where secret_len
- * is set to 72 even though the actual length ends up being
- * updated to 32 when EVP_PKEY_derive() is called with a buffer
- * for the value. Work around this by trying to fetch the value
- * and continue if it is within supported range even when the
- * initial buffer need is claimed to be larger. */
- wpa_printf(level,
- "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
- (int) *secret_len);
- if (*secret_len > 200)
- goto fail;
- if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
- wpa_printf(MSG_ERROR,
- "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
- (int) *secret_len);
- goto fail;
- }
- wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change",
- buf, *secret_len);
- os_memcpy(secret, buf, *secret_len);
- forced_memzero(buf, sizeof(buf));
- goto done;
- }
-
- if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) {
- wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ if (wpabuf_len(secret_buf) > DPP_MAX_SHARED_SECRET_LEN) {
+ wpa_printf(MSG_ERROR, "DPP: ECDH secret longer than expected");
goto fail;
}
-done:
+ *secret_len = wpabuf_len(secret_buf);
+ os_memcpy(secret, wpabuf_head(secret_buf), wpabuf_len(secret_buf));
ret = 0;
fail:
- EVP_PKEY_CTX_free(ctx);
+ wpabuf_clear_free(secret_buf);
+ wpabuf_free(peer_pub);
+ crypto_ecdh_deinit(ecdh);
return ret;
}
int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
const u8 *data, size_t data_len)
{
const u8 *addr[2];
size_t len[2];
addr[0] = data;
len[0] = data_len;
if (sha256_vector(1, addr, len, bi->pubkey_hash) < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
bi->pubkey_hash, SHA256_MAC_LEN);
addr[0] = (const u8 *) "chirp";
len[0] = 5;
addr[1] = data;
len[1] = data_len;
if (sha256_vector(2, addr, len, bi->pubkey_hash_chirp) < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "DPP: Public key hash (chirp)",
bi->pubkey_hash_chirp, SHA256_MAC_LEN);
return 0;
}
int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
const u8 *data, size_t data_len)
{
- EVP_PKEY *pkey;
- const unsigned char *p;
- int res;
- X509_PUBKEY *pub = NULL;
- ASN1_OBJECT *ppkalg;
- const unsigned char *pk;
- int ppklen;
- X509_ALGOR *pa;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
- (defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20800000L)
- ASN1_OBJECT *pa_oid;
-#else
- const ASN1_OBJECT *pa_oid;
-#endif
- const void *pval;
- int ptype;
- const ASN1_OBJECT *poid;
- char buf[100];
+ struct crypto_ec_key *key;
if (dpp_bi_pubkey_hash(bi, data, data_len) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
return -1;
}
- /* DER encoded ASN.1 SubjectPublicKeyInfo
- *
- * SubjectPublicKeyInfo ::= SEQUENCE {
- * algorithm AlgorithmIdentifier,
- * subjectPublicKey BIT STRING }
- *
- * AlgorithmIdentifier ::= SEQUENCE {
- * algorithm OBJECT IDENTIFIER,
- * parameters ANY DEFINED BY algorithm OPTIONAL }
- *
- * subjectPublicKey = compressed format public key per ANSI X9.63
- * algorithm = ecPublicKey (1.2.840.10045.2.1)
- * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
- * prime256v1 (1.2.840.10045.3.1.7)
- */
-
- p = data;
- pkey = d2i_PUBKEY(NULL, &p, data_len);
-
- if (!pkey) {
+ key = crypto_ec_key_parse_pub(data, data_len);
+ if (!key) {
wpa_printf(MSG_DEBUG,
"DPP: Could not parse URI public-key SubjectPublicKeyInfo");
return -1;
}
- if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
- wpa_printf(MSG_DEBUG,
- "DPP: SubjectPublicKeyInfo does not describe an EC key");
- EVP_PKEY_free(pkey);
- return -1;
- }
-
- res = X509_PUBKEY_set(&pub, pkey);
- if (res != 1) {
- wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
- goto fail;
- }
-
- res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
- if (res != 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not extract SubjectPublicKeyInfo parameters");
- goto fail;
- }
- res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
- if (res < 0 || (size_t) res >= sizeof(buf)) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not extract SubjectPublicKeyInfo algorithm");
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
- if (os_strcmp(buf, "id-ecPublicKey") != 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Unsupported SubjectPublicKeyInfo algorithm");
- goto fail;
- }
-
- X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
- if (ptype != V_ASN1_OBJECT) {
- wpa_printf(MSG_DEBUG,
- "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
- goto fail;
- }
- poid = pval;
- res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
- if (res < 0 || (size_t) res >= sizeof(buf)) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
- goto fail;
- }
- wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
- bi->curve = dpp_get_curve_oid(poid);
+ bi->curve = dpp_get_curve_ike_group(crypto_ec_key_group(key));
if (!bi->curve) {
wpa_printf(MSG_DEBUG,
- "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
- buf);
+ "DPP: Unsupported SubjectPublicKeyInfo curve: group %d",
+ crypto_ec_key_group(key));
goto fail;
}
- wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
-
- X509_PUBKEY_free(pub);
- bi->pubkey = pkey;
+ bi->pubkey = key;
return 0;
fail:
- X509_PUBKEY_free(pub);
- EVP_PKEY_free(pkey);
+ crypto_ec_key_deinit(key);
return -1;
}
static struct wpabuf *
dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
const u8 *prot_hdr, u16 prot_hdr_len,
- const EVP_MD **ret_md)
+ int *hash_func)
{
struct json_token *root, *token;
struct wpabuf *kid = NULL;
root = json_parse((const char *) prot_hdr, prot_hdr_len);
if (!root) {
wpa_printf(MSG_DEBUG,
"DPP: JSON parsing failed for JWS Protected Header");
goto fail;
}
if (root->type != JSON_OBJECT) {
wpa_printf(MSG_DEBUG,
"DPP: JWS Protected Header root is not an object");
goto fail;
}
token = json_get_member(root, "typ");
if (!token || token->type != JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
token->string);
if (os_strcmp(token->string, "dppCon") != 0) {
wpa_printf(MSG_DEBUG,
"DPP: Unsupported JWS Protected Header typ=%s",
token->string);
goto fail;
}
token = json_get_member(root, "alg");
if (!token || token->type != JSON_STRING) {
wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
token->string);
if (os_strcmp(token->string, curve->jws_alg) != 0) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
token->string, curve->jws_alg);
goto fail;
}
if (os_strcmp(token->string, "ES256") == 0 ||
- os_strcmp(token->string, "BS256") == 0)
- *ret_md = EVP_sha256();
- else if (os_strcmp(token->string, "ES384") == 0 ||
- os_strcmp(token->string, "BS384") == 0)
- *ret_md = EVP_sha384();
- else if (os_strcmp(token->string, "ES512") == 0 ||
- os_strcmp(token->string, "BS512") == 0)
- *ret_md = EVP_sha512();
- else
- *ret_md = NULL;
- if (!*ret_md) {
+ os_strcmp(token->string, "BS256") == 0) {
+ *hash_func = CRYPTO_HASH_ALG_SHA256;
+ } else if (os_strcmp(token->string, "ES384") == 0 ||
+ os_strcmp(token->string, "BS384") == 0) {
+ *hash_func = CRYPTO_HASH_ALG_SHA384;
+ } else if (os_strcmp(token->string, "ES512") == 0 ||
+ os_strcmp(token->string, "BS512") == 0) {
+ *hash_func = CRYPTO_HASH_ALG_SHA512;
+ } else {
+ *hash_func = -1;
wpa_printf(MSG_DEBUG,
"DPP: Unsupported JWS Protected Header alg=%s",
token->string);
goto fail;
}
kid = json_get_member_base64url(root, "kid");
if (!kid) {
wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
goto fail;
}
wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
kid);
fail:
json_free(root);
return kid;
}
-static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
+static int dpp_check_pubkey_match(struct crypto_ec_key *pub,
+ struct wpabuf *r_hash)
{
struct wpabuf *uncomp;
int res;
u8 hash[SHA256_MAC_LEN];
const u8 *addr[1];
size_t len[1];
if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
return -1;
- uncomp = dpp_get_pubkey_point(pub, 1);
+ uncomp = crypto_ec_key_get_pubkey_point(pub, 1);
if (!uncomp)
return -1;
addr[0] = wpabuf_head(uncomp);
len[0] = wpabuf_len(uncomp);
wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
addr[0], len[0]);
res = sha256_vector(1, addr, len, hash);
wpabuf_free(uncomp);
if (res < 0)
return -1;
if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
wpa_printf(MSG_DEBUG,
"DPP: Received hash value does not match calculated public key hash value");
wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
hash, SHA256_MAC_LEN);
return -1;
}
return 0;
}
enum dpp_status_error
dpp_process_signed_connector(struct dpp_signed_connector_info *info,
- EVP_PKEY *csign_pub, const char *connector)
+ struct crypto_ec_key *csign_pub,
+ const char *connector)
{
enum dpp_status_error ret = 255;
const char *pos, *end, *signed_start, *signed_end;
struct wpabuf *kid = NULL;
unsigned char *prot_hdr = NULL, *signature = NULL;
- size_t prot_hdr_len = 0, signature_len = 0;
- const EVP_MD *sign_md = NULL;
- unsigned char *der = NULL;
- int der_len;
- int res;
- EVP_MD_CTX *md_ctx = NULL;
- ECDSA_SIG *sig = NULL;
- BIGNUM *r = NULL, *s = NULL;
+ size_t prot_hdr_len = 0, signature_len = 0, signed_len;
+ int res, hash_func = -1;
const struct dpp_curve_params *curve;
- const EC_KEY *eckey;
- const EC_GROUP *group;
- int nid;
+ u8 *hash = NULL;
- eckey = EVP_PKEY_get0_EC_KEY(csign_pub);
- if (!eckey)
- goto fail;
- group = EC_KEY_get0_group(eckey);
- if (!group)
- goto fail;
- nid = EC_GROUP_get_curve_name(group);
- curve = dpp_get_curve_nid(nid);
+ curve = dpp_get_curve_ike_group(crypto_ec_key_group(csign_pub));
if (!curve)
goto fail;
wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
os_memset(info, 0, sizeof(*info));
signed_start = pos = connector;
end = os_strchr(pos, '.');
if (!end) {
wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
prot_hdr = base64_url_decode(pos, end - pos, &prot_hdr_len);
if (!prot_hdr) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to base64url decode signedConnector JWS Protected Header");
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG,
"DPP: signedConnector - JWS Protected Header",
prot_hdr, prot_hdr_len);
- kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
+ kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &hash_func);
if (!kid) {
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
if (wpabuf_len(kid) != SHA256_MAC_LEN) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
(unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
pos = end + 1;
end = os_strchr(pos, '.');
if (!end) {
wpa_printf(MSG_DEBUG,
"DPP: Missing dot(2) in signedConnector");
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
signed_end = end - 1;
info->payload = base64_url_decode(pos, end - pos, &info->payload_len);
if (!info->payload) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to base64url decode signedConnector JWS Payload");
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG,
"DPP: signedConnector - JWS Payload",
info->payload, info->payload_len);
pos = end + 1;
signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
if (!signature) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to base64url decode signedConnector signature");
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
signature, signature_len);
if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
ret = DPP_STATUS_NO_MATCH;
goto fail;
}
if (signature_len & 0x01) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected signedConnector signature length (%d)",
(int) signature_len);
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
- /* JWS Signature encodes the signature (r,s) as two octet strings. Need
- * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
- r = BN_bin2bn(signature, signature_len / 2, NULL);
- s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
- sig = ECDSA_SIG_new();
- if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
+ hash = os_malloc(curve->hash_len);
+ if (!hash)
goto fail;
- r = NULL;
- s = NULL;
- der_len = i2d_ECDSA_SIG(sig, &der);
- if (der_len <= 0) {
- wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
- goto fail;
- }
- wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
- md_ctx = EVP_MD_CTX_create();
- if (!md_ctx)
+ signed_len = signed_end - signed_start + 1;
+ if (hash_func == CRYPTO_HASH_ALG_SHA256)
+ res = sha256_vector(1, (const u8 **) &signed_start, &signed_len,
+ hash);
+ else if (hash_func == CRYPTO_HASH_ALG_SHA384)
+ res = sha384_vector(1, (const u8 **) &signed_start, &signed_len,
+ hash);
+ else if (hash_func == CRYPTO_HASH_ALG_SHA512)
+ res = sha512_vector(1, (const u8 **) &signed_start, &signed_len,
+ hash);
+ else
goto fail;
- ERR_clear_error();
- if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
- signed_end - signed_start + 1) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ if (res)
goto fail;
- }
- res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
+
+ res = crypto_ec_key_verify_signature_r_s(csign_pub,
+ hash, curve->hash_len,
+ signature, signature_len / 2,
+ signature + signature_len / 2,
+ signature_len / 2);
if (res != 1) {
wpa_printf(MSG_DEBUG,
- "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
- res, ERR_error_string(ERR_get_error(), NULL));
+ "DPP: signedConnector signature check failed (res=%d)",
+ res);
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
ret = DPP_STATUS_OK;
fail:
- EVP_MD_CTX_destroy(md_ctx);
+ os_free(hash);
os_free(prot_hdr);
wpabuf_free(kid);
os_free(signature);
- ECDSA_SIG_free(sig);
- BN_free(r);
- BN_free(s);
- OPENSSL_free(der);
return ret;
}
enum dpp_status_error
dpp_check_signed_connector(struct dpp_signed_connector_info *info,
const u8 *csign_key, size_t csign_key_len,
const u8 *peer_connector, size_t peer_connector_len)
{
- const unsigned char *p;
- EVP_PKEY *csign = NULL;
+ struct crypto_ec_key *csign;
char *signed_connector = NULL;
enum dpp_status_error res = DPP_STATUS_INVALID_CONNECTOR;
- p = csign_key;
- csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ csign = crypto_ec_key_parse_pub(csign_key, csign_key_len);
if (!csign) {
wpa_printf(MSG_ERROR,
"DPP: Failed to parse local C-sign-key information");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
peer_connector, peer_connector_len);
signed_connector = os_malloc(peer_connector_len + 1);
if (!signed_connector)
goto fail;
os_memcpy(signed_connector, peer_connector, peer_connector_len);
signed_connector[peer_connector_len] = '\0';
res = dpp_process_signed_connector(info, csign, signed_connector);
fail:
os_free(signed_connector);
- EVP_PKEY_free(csign);
+ crypto_ec_key_deinit(csign);
return res;
}
int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
{
struct wpabuf *pix, *prx, *bix, *brx;
const u8 *addr[7];
size_t len[7];
size_t i, num_elem = 0;
size_t nonce_len;
u8 zero = 0;
int res = -1;
/* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
nonce_len = auth->curve->nonce_len;
if (auth->initiator) {
- pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ pix = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = crypto_ec_key_get_pubkey_point(auth->peer_protocol_key,
+ 0);
if (auth->own_bi)
- bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ bix = crypto_ec_key_get_pubkey_point(
+ auth->own_bi->pubkey, 0);
else
bix = NULL;
- brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ brx = crypto_ec_key_get_pubkey_point(auth->peer_bi->pubkey, 0);
} else {
- pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ pix = crypto_ec_key_get_pubkey_point(auth->peer_protocol_key,
+ 0);
+ prx = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0);
if (auth->peer_bi)
- bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ bix = crypto_ec_key_get_pubkey_point(
+ auth->peer_bi->pubkey, 0);
else
bix = NULL;
- brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ brx = crypto_ec_key_get_pubkey_point(auth->own_bi->pubkey, 0);
}
if (!pix || !prx || !brx)
goto fail;
addr[num_elem] = auth->i_nonce;
len[num_elem] = nonce_len;
num_elem++;
addr[num_elem] = auth->r_nonce;
len[num_elem] = nonce_len;
num_elem++;
addr[num_elem] = wpabuf_head(pix);
len[num_elem] = wpabuf_len(pix) / 2;
num_elem++;
addr[num_elem] = wpabuf_head(prx);
len[num_elem] = wpabuf_len(prx) / 2;
num_elem++;
if (bix) {
addr[num_elem] = wpabuf_head(bix);
len[num_elem] = wpabuf_len(bix) / 2;
num_elem++;
}
addr[num_elem] = wpabuf_head(brx);
len[num_elem] = wpabuf_len(brx) / 2;
num_elem++;
addr[num_elem] = &zero;
len[num_elem] = 1;
num_elem++;
wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
for (i = 0; i < num_elem; i++)
wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
if (res == 0)
wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
auth->curve->hash_len);
fail:
wpabuf_free(pix);
wpabuf_free(prx);
wpabuf_free(bix);
wpabuf_free(brx);
return res;
}
int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
{
struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
const u8 *addr[7];
size_t len[7];
size_t i, num_elem = 0;
size_t nonce_len;
u8 one = 1;
int res = -1;
/* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
nonce_len = auth->curve->nonce_len;
if (auth->initiator) {
- pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ pix = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = crypto_ec_key_get_pubkey_point(auth->peer_protocol_key,
+ 0);
if (auth->own_bi)
- bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ bix = crypto_ec_key_get_pubkey_point(
+ auth->own_bi->pubkey, 0);
else
bix = NULL;
if (!auth->peer_bi)
goto fail;
- brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ brx = crypto_ec_key_get_pubkey_point(auth->peer_bi->pubkey, 0);
} else {
- pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
- prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ pix = crypto_ec_key_get_pubkey_point(auth->peer_protocol_key,
+ 0);
+ prx = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0);
if (auth->peer_bi)
- bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ bix = crypto_ec_key_get_pubkey_point(
+ auth->peer_bi->pubkey, 0);
else
bix = NULL;
if (!auth->own_bi)
goto fail;
- brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ brx = crypto_ec_key_get_pubkey_point(auth->own_bi->pubkey, 0);
}
if (!pix || !prx || !brx)
goto fail;
addr[num_elem] = auth->r_nonce;
len[num_elem] = nonce_len;
num_elem++;
addr[num_elem] = auth->i_nonce;
len[num_elem] = nonce_len;
num_elem++;
addr[num_elem] = wpabuf_head(prx);
len[num_elem] = wpabuf_len(prx) / 2;
num_elem++;
addr[num_elem] = wpabuf_head(pix);
len[num_elem] = wpabuf_len(pix) / 2;
num_elem++;
addr[num_elem] = wpabuf_head(brx);
len[num_elem] = wpabuf_len(brx) / 2;
num_elem++;
if (bix) {
addr[num_elem] = wpabuf_head(bix);
len[num_elem] = wpabuf_len(bix) / 2;
num_elem++;
}
addr[num_elem] = &one;
len[num_elem] = 1;
num_elem++;
wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
for (i = 0; i < num_elem; i++)
wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
if (res == 0)
wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
auth->curve->hash_len);
fail:
wpabuf_free(pix);
wpabuf_free(prx);
wpabuf_free(bix);
wpabuf_free(brx);
return res;
}
int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
{
- const EC_GROUP *group;
- EC_POINT *l = NULL;
- const EC_KEY *BI, *bR, *pR;
- const EC_POINT *BI_point;
- BN_CTX *bnctx;
- BIGNUM *lx, *sum, *q;
- const BIGNUM *bR_bn, *pR_bn;
+ struct crypto_ec *ec;
+ struct crypto_ec_point *L = NULL;
+ const struct crypto_ec_point *BI;
+ const struct crypto_bignum *bR, *pR, *q;
+ struct crypto_bignum *sum = NULL, *lx = NULL;
int ret = -1;
/* L = ((bR + pR) modulo q) * BI */
- bnctx = BN_CTX_new();
- sum = BN_new();
- q = BN_new();
- lx = BN_new();
- if (!bnctx || !sum || !q || !lx)
- goto fail;
- BI = EVP_PKEY_get0_EC_KEY(auth->peer_bi->pubkey);
- if (!BI)
- goto fail;
- BI_point = EC_KEY_get0_public_key(BI);
- group = EC_KEY_get0_group(BI);
- if (!group)
+ ec = crypto_ec_init(crypto_ec_key_group(auth->peer_bi->pubkey));
+ if (!ec)
goto fail;
- bR = EVP_PKEY_get0_EC_KEY(auth->own_bi->pubkey);
- pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
- if (!bR || !pR)
+ q = crypto_ec_get_order(ec);
+ BI = crypto_ec_key_get_public_key(auth->peer_bi->pubkey);
+ bR = crypto_ec_key_get_private_key(auth->own_bi->pubkey);
+ pR = crypto_ec_key_get_private_key(auth->own_protocol_key);
+ sum = crypto_bignum_init();
+ L = crypto_ec_point_init(ec);
+ lx = crypto_bignum_init();
+ if (!q || !BI || !bR || !pR || !sum || !L || !lx ||
+ crypto_bignum_addmod(bR, pR, q, sum) ||
+ crypto_ec_point_mul(ec, BI, sum, L) ||
+ crypto_ec_point_x(ec, L, lx) ||
+ crypto_bignum_to_bin(lx, auth->Lx, sizeof(auth->Lx),
+ auth->secret_len) < 0)
goto fail;
- bR_bn = EC_KEY_get0_private_key(bR);
- pR_bn = EC_KEY_get0_private_key(pR);
- if (!bR_bn || !pR_bn)
- goto fail;
- if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
- BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
- goto fail;
- l = EC_POINT_new(group);
- if (!l ||
- EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
- bnctx) != 1) {
- wpa_printf(MSG_ERROR,
- "OpenSSL: failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
- goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
auth->Lx_len = auth->secret_len;
ret = 0;
fail:
- EC_POINT_clear_free(l);
- BN_clear_free(lx);
- BN_clear_free(sum);
- BN_free(q);
- BN_CTX_free(bnctx);
+ crypto_bignum_deinit(lx, 1);
+ crypto_bignum_deinit(sum, 1);
+ crypto_ec_point_deinit(L, 1);
+ crypto_ec_deinit(ec);
return ret;
}
int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
{
- const EC_GROUP *group;
- EC_POINT *l = NULL, *sum = NULL;
- const EC_KEY *bI, *BR, *PR;
- const EC_POINT *BR_point, *PR_point;
- BN_CTX *bnctx;
- BIGNUM *lx;
- const BIGNUM *bI_bn;
+ struct crypto_ec *ec;
+ struct crypto_ec_point *L = NULL, *sum = NULL;
+ const struct crypto_ec_point *BR, *PR;
+ const struct crypto_bignum *bI;
+ struct crypto_bignum *lx = NULL;
int ret = -1;
/* L = bI * (BR + PR) */
- bnctx = BN_CTX_new();
- lx = BN_new();
- if (!bnctx || !lx)
+ ec = crypto_ec_init(crypto_ec_key_group(auth->peer_bi->pubkey));
+ if (!ec)
goto fail;
- BR = EVP_PKEY_get0_EC_KEY(auth->peer_bi->pubkey);
- PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
- if (!BR || !PR)
- goto fail;
- BR_point = EC_KEY_get0_public_key(BR);
- PR_point = EC_KEY_get0_public_key(PR);
- bI = EVP_PKEY_get0_EC_KEY(auth->own_bi->pubkey);
- if (!bI)
- goto fail;
- group = EC_KEY_get0_group(bI);
- bI_bn = EC_KEY_get0_private_key(bI);
- if (!group || !bI_bn)
- goto fail;
- sum = EC_POINT_new(group);
- l = EC_POINT_new(group);
- if (!sum || !l ||
- EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
- EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
- bnctx) != 1) {
- wpa_printf(MSG_ERROR,
- "OpenSSL: failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ BR = crypto_ec_key_get_public_key(auth->peer_bi->pubkey);
+ PR = crypto_ec_key_get_public_key(auth->peer_protocol_key);
+ bI = crypto_ec_key_get_private_key(auth->own_bi->pubkey);
+ sum = crypto_ec_point_init(ec);
+ L = crypto_ec_point_init(ec);
+ lx = crypto_bignum_init();
+ if (!BR || !PR || !bI || !sum || !L || !lx ||
+ crypto_ec_point_add(ec, BR, PR, sum) ||
+ crypto_ec_point_mul(ec, sum, bI, L) ||
+ crypto_ec_point_x(ec, L, lx) ||
+ crypto_bignum_to_bin(lx, auth->Lx, sizeof(auth->Lx),
+ auth->secret_len) < 0)
goto fail;
- }
- if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
- goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
auth->Lx_len = auth->secret_len;
ret = 0;
fail:
- EC_POINT_clear_free(l);
- EC_POINT_clear_free(sum);
- BN_clear_free(lx);
- BN_CTX_free(bnctx);
+ crypto_bignum_deinit(lx, 1);
+ crypto_ec_point_deinit(sum, 1);
+ crypto_ec_point_deinit(L, 1);
+ crypto_ec_deinit(ec);
return ret;
}
int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, unsigned int hash_len)
{
u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
const char *info = "DPP PMK";
int res;
/* PMK = HKDF(<>, "DPP PMK", N.x) */
/* HKDF-Extract(<>, N.x) */
os_memset(salt, 0, hash_len);
if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
prk, hash_len);
/* HKDF-Expand(PRK, info, L) */
res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
os_memset(prk, 0, hash_len);
if (res < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
pmk, hash_len);
return 0;
}
int dpp_derive_pmkid(const struct dpp_curve_params *curve,
- EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
+ struct crypto_ec_key *own_key,
+ struct crypto_ec_key *peer_key, u8 *pmkid)
{
struct wpabuf *nkx, *pkx;
int ret = -1, res;
const u8 *addr[2];
size_t len[2];
u8 hash[SHA256_MAC_LEN];
/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
- nkx = dpp_get_pubkey_point(own_key, 0);
- pkx = dpp_get_pubkey_point(peer_key, 0);
+ nkx = crypto_ec_key_get_pubkey_point(own_key, 0);
+ pkx = crypto_ec_key_get_pubkey_point(peer_key, 0);
if (!nkx || !pkx)
goto fail;
addr[0] = wpabuf_head(nkx);
len[0] = wpabuf_len(nkx) / 2;
addr[1] = wpabuf_head(pkx);
len[1] = wpabuf_len(pkx) / 2;
if (len[0] != len[1])
goto fail;
if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
addr[0] = wpabuf_head(pkx);
addr[1] = wpabuf_head(nkx);
}
wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
res = sha256_vector(2, addr, len, hash);
if (res < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
os_memcpy(pmkid, hash, PMKID_LEN);
wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
ret = 0;
fail:
wpabuf_free(nkx);
wpabuf_free(pkx);
return ret;
}
/* Role-specific elements for PKEX */
/* NIST P-256 */
static const u8 pkex_init_x_p256[32] = {
0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
};
static const u8 pkex_init_y_p256[32] = {
0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
};
static const u8 pkex_resp_x_p256[32] = {
0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
};
static const u8 pkex_resp_y_p256[32] = {
0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
};
/* NIST P-384 */
static const u8 pkex_init_x_p384[48] = {
0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
};
static const u8 pkex_init_y_p384[48] = {
0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
};
static const u8 pkex_resp_x_p384[48] = {
0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
};
static const u8 pkex_resp_y_p384[48] = {
0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
};
/* NIST P-521 */
static const u8 pkex_init_x_p521[66] = {
0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
0x97, 0x76
};
static const u8 pkex_init_y_p521[66] = {
0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
0x03, 0xa8
};
static const u8 pkex_resp_x_p521[66] = {
0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
0x84, 0xb4
};
static const u8 pkex_resp_y_p521[66] = {
0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
0xce, 0xe1
};
/* Brainpool P-256r1 */
static const u8 pkex_init_x_bp_p256r1[32] = {
0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
};
static const u8 pkex_init_y_bp_p256r1[32] = {
0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
};
static const u8 pkex_resp_x_bp_p256r1[32] = {
0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
};
static const u8 pkex_resp_y_bp_p256r1[32] = {
0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
};
/* Brainpool P-384r1 */
static const u8 pkex_init_x_bp_p384r1[48] = {
0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
};
static const u8 pkex_init_y_bp_p384r1[48] = {
0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
};
static const u8 pkex_resp_x_bp_p384r1[48] = {
0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
};
static const u8 pkex_resp_y_bp_p384r1[48] = {
0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
};
/* Brainpool P-512r1 */
static const u8 pkex_init_x_bp_p512r1[64] = {
0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
};
static const u8 pkex_init_y_bp_p512r1[64] = {
0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
};
static const u8 pkex_resp_x_bp_p512r1[64] = {
0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
};
static const u8 pkex_resp_y_bp_p512r1[64] = {
0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
};
-static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
- int init)
+static struct crypto_ec_key *
+dpp_pkex_get_role_elem(const struct dpp_curve_params *curve, int init)
{
- EC_GROUP *group;
- size_t len = curve->prime_len;
const u8 *x, *y;
- EVP_PKEY *res;
switch (curve->ike_group) {
case 19:
x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
break;
case 20:
x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
break;
case 21:
x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
break;
case 28:
x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
break;
case 29:
x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
break;
case 30:
x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
break;
default:
return NULL;
}
- group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
- if (!group)
- return NULL;
- res = dpp_set_pubkey_point_group(group, x, y, len);
- EC_GROUP_free(group);
- return res;
+ return crypto_ec_key_set_pub(curve->ike_group, x, y, curve->prime_len);
}
-EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
- const u8 *mac_init, const char *code,
- const char *identifier, BN_CTX *bnctx,
- EC_GROUP **ret_group)
+struct crypto_ec_point *
+dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, const u8 *mac_init,
+ const char *code, const char *identifier,
+ struct crypto_ec **ret_ec)
{
u8 hash[DPP_MAX_HASH_LEN];
const u8 *addr[3];
size_t len[3];
unsigned int num_elem = 0;
- EC_POINT *Qi = NULL;
- EVP_PKEY *Pi = NULL;
- const EC_KEY *Pi_ec;
- const EC_POINT *Pi_point;
- BIGNUM *hash_bn = NULL;
- const EC_GROUP *group = NULL;
- EC_GROUP *group2 = NULL;
+ struct crypto_ec_point *Qi = NULL;
+ struct crypto_ec_key *Pi_key = NULL;
+ const struct crypto_ec_point *Pi = NULL;
+ struct crypto_bignum *hash_bn = NULL;
+ struct crypto_ec *ec = NULL;
/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
addr[num_elem] = mac_init;
len[num_elem] = ETH_ALEN;
num_elem++;
if (identifier) {
wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
identifier);
addr[num_elem] = (const u8 *) identifier;
len[num_elem] = os_strlen(identifier);
num_elem++;
}
wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
addr[num_elem] = (const u8 *) code;
len[num_elem] = os_strlen(code);
num_elem++;
if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG,
"DPP: H(MAC-Initiator | [identifier |] code)",
hash, curve->hash_len);
- Pi = dpp_pkex_get_role_elem(curve, 1);
- if (!Pi)
- goto fail;
- dpp_debug_print_key("DPP: Pi", Pi);
- Pi_ec = EVP_PKEY_get0_EC_KEY(Pi);
- if (!Pi_ec)
+ Pi_key = dpp_pkex_get_role_elem(curve, 1);
+ if (!Pi_key)
goto fail;
- Pi_point = EC_KEY_get0_public_key(Pi_ec);
+ dpp_debug_print_key("DPP: Pi", Pi_key);
- group = EC_KEY_get0_group(Pi_ec);
- if (!group)
+ ec = crypto_ec_init(curve->ike_group);
+ if (!ec)
goto fail;
- group2 = EC_GROUP_dup(group);
- if (!group2)
- goto fail;
- Qi = EC_POINT_new(group2);
- if (!Qi) {
- EC_GROUP_free(group2);
- goto fail;
- }
- hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
- if (!hash_bn ||
- EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
+
+ Pi = crypto_ec_key_get_public_key(Pi_key);
+ Qi = crypto_ec_point_init(ec);
+ hash_bn = crypto_bignum_init_set(hash, curve->hash_len);
+ if (!Pi || !Qi || !hash_bn || crypto_ec_point_mul(ec, Pi, hash_bn, Qi))
goto fail;
- if (EC_POINT_is_at_infinity(group, Qi)) {
+
+ if (crypto_ec_point_is_at_infinity(ec, Qi)) {
wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
goto fail;
}
- dpp_debug_print_point("DPP: Qi", group, Qi);
+ crypto_ec_point_debug_print(ec, Qi, "DPP: Qi");
out:
- EVP_PKEY_free(Pi);
- BN_clear_free(hash_bn);
- if (ret_group && Qi)
- *ret_group = group2;
+ crypto_ec_key_deinit(Pi_key);
+ crypto_bignum_deinit(hash_bn, 1);
+ if (ret_ec && Qi)
+ *ret_ec = ec;
else
- EC_GROUP_free(group2);
+ crypto_ec_deinit(ec);
return Qi;
fail:
- EC_POINT_free(Qi);
+ crypto_ec_point_deinit(Qi, 1);
Qi = NULL;
goto out;
}
-EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
- const u8 *mac_resp, const char *code,
- const char *identifier, BN_CTX *bnctx,
- EC_GROUP **ret_group)
+struct crypto_ec_point *
+dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
+ const char *code, const char *identifier,
+ struct crypto_ec **ret_ec)
{
u8 hash[DPP_MAX_HASH_LEN];
const u8 *addr[3];
size_t len[3];
unsigned int num_elem = 0;
- EC_POINT *Qr = NULL;
- EVP_PKEY *Pr = NULL;
- const EC_KEY *Pr_ec;
- const EC_POINT *Pr_point;
- BIGNUM *hash_bn = NULL;
- const EC_GROUP *group = NULL;
- EC_GROUP *group2 = NULL;
+ struct crypto_ec_point *Qr = NULL;
+ struct crypto_ec_key *Pr_key = NULL;
+ const struct crypto_ec_point *Pr = NULL;
+ struct crypto_bignum *hash_bn = NULL;
+ struct crypto_ec *ec = NULL;
/* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
addr[num_elem] = mac_resp;
len[num_elem] = ETH_ALEN;
num_elem++;
if (identifier) {
wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
identifier);
addr[num_elem] = (const u8 *) identifier;
len[num_elem] = os_strlen(identifier);
num_elem++;
}
wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
addr[num_elem] = (const u8 *) code;
len[num_elem] = os_strlen(code);
num_elem++;
if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG,
"DPP: H(MAC-Responder | [identifier |] code)",
hash, curve->hash_len);
- Pr = dpp_pkex_get_role_elem(curve, 0);
- if (!Pr)
- goto fail;
- dpp_debug_print_key("DPP: Pr", Pr);
- Pr_ec = EVP_PKEY_get0_EC_KEY(Pr);
- if (!Pr_ec)
+ Pr_key = dpp_pkex_get_role_elem(curve, 0);
+ if (!Pr_key)
goto fail;
- Pr_point = EC_KEY_get0_public_key(Pr_ec);
+ dpp_debug_print_key("DPP: Pr", Pr_key);
- group = EC_KEY_get0_group(Pr_ec);
- if (!group)
+ ec = crypto_ec_init(curve->ike_group);
+ if (!ec)
goto fail;
- group2 = EC_GROUP_dup(group);
- if (!group2)
- goto fail;
- Qr = EC_POINT_new(group2);
- if (!Qr) {
- EC_GROUP_free(group2);
- goto fail;
- }
- hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
- if (!hash_bn ||
- EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
+
+ Pr = crypto_ec_key_get_public_key(Pr_key);
+ Qr = crypto_ec_point_init(ec);
+ hash_bn = crypto_bignum_init_set(hash, curve->hash_len);
+ if (!Pr || !Qr || !hash_bn || crypto_ec_point_mul(ec, Pr, hash_bn, Qr))
goto fail;
- if (EC_POINT_is_at_infinity(group, Qr)) {
+
+ if (crypto_ec_point_is_at_infinity(ec, Qr)) {
wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
goto fail;
}
- dpp_debug_print_point("DPP: Qr", group, Qr);
+ crypto_ec_point_debug_print(ec, Qr, "DPP: Qr");
+
out:
- EVP_PKEY_free(Pr);
- BN_clear_free(hash_bn);
- if (ret_group && Qr)
- *ret_group = group2;
+ crypto_ec_key_deinit(Pr_key);
+ crypto_bignum_deinit(hash_bn, 1);
+ if (ret_ec && Qr)
+ *ret_ec = ec;
else
- EC_GROUP_free(group2);
+ crypto_ec_deinit(ec);
return Qr;
fail:
- EC_POINT_free(Qr);
+ crypto_ec_point_deinit(Qr, 1);
Qr = NULL;
goto out;
}
int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
const u8 *Mx, size_t Mx_len,
const u8 *Nx, size_t Nx_len,
const char *code,
const u8 *Kx, size_t Kx_len,
u8 *z, unsigned int hash_len)
{
u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
int res;
u8 *info, *pos;
size_t info_len;
/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
*/
/* HKDF-Extract(<>, IKM=K.x) */
os_memset(salt, 0, hash_len);
if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
prk, hash_len);
info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
info = os_malloc(info_len);
if (!info)
return -1;
pos = info;
os_memcpy(pos, mac_init, ETH_ALEN);
pos += ETH_ALEN;
os_memcpy(pos, mac_resp, ETH_ALEN);
pos += ETH_ALEN;
os_memcpy(pos, Mx, Mx_len);
pos += Mx_len;
os_memcpy(pos, Nx, Nx_len);
pos += Nx_len;
os_memcpy(pos, code, os_strlen(code));
/* HKDF-Expand(PRK, info, L) */
if (hash_len == 32)
res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
z, hash_len);
else if (hash_len == 48)
res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
z, hash_len);
else if (hash_len == 64)
res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
z, hash_len);
else
res = -1;
os_free(info);
os_memset(prk, 0, hash_len);
if (res < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
z, hash_len);
return 0;
}
int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
const u8 *net_access_key,
size_t net_access_key_len,
struct json_token *peer_net_access_key)
{
- BN_CTX *bnctx = NULL;
- EVP_PKEY *own_key = NULL, *peer_key = NULL;
- BIGNUM *sum = NULL, *q = NULL, *mx = NULL;
- EC_POINT *m = NULL;
- const EC_KEY *cR, *pR;
- const EC_GROUP *group;
- const BIGNUM *cR_bn, *pR_bn;
- const EC_POINT *CI_point;
- const EC_KEY *CI;
+ struct crypto_ec_key *own_key = NULL, *peer_key = NULL;
+ struct crypto_bignum *sum = NULL;
+ const struct crypto_bignum *q, *cR, *pR;
+ struct crypto_ec *ec = NULL;
+ struct crypto_ec_point *M = NULL;
+ const struct crypto_ec_point *CI;
u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
u8 prk[DPP_MAX_HASH_LEN];
const struct dpp_curve_params *curve;
int res = -1;
u8 nonces[2 * DPP_MAX_NONCE_LEN];
own_key = dpp_set_keypair(&auth->curve, net_access_key,
net_access_key_len);
if (!own_key) {
dpp_auth_fail(auth, "Failed to parse own netAccessKey");
goto fail;
}
peer_key = dpp_parse_jwk(peer_net_access_key, &curve);
if (!peer_key)
goto fail;
dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
if (auth->curve != curve) {
wpa_printf(MSG_DEBUG,
"DPP: Mismatching netAccessKey curves (own=%s != peer=%s)",
auth->curve->name, curve->name);
goto fail;
}
auth->own_protocol_key = dpp_gen_keypair(curve);
if (!auth->own_protocol_key)
goto fail;
if (random_get_bytes(auth->e_nonce, auth->curve->nonce_len)) {
wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "DPP: E-nonce",
auth->e_nonce, auth->curve->nonce_len);
/* M = { cR + pR } * CI */
- cR = EVP_PKEY_get0_EC_KEY(own_key);
- pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
- if (!pR)
- goto fail;
- group = EC_KEY_get0_group(pR);
- bnctx = BN_CTX_new();
- sum = BN_new();
- mx = BN_new();
- q = BN_new();
- m = EC_POINT_new(group);
- if (!cR || !bnctx || !sum || !mx || !q || !m)
- goto fail;
- cR_bn = EC_KEY_get0_private_key(cR);
- pR_bn = EC_KEY_get0_private_key(pR);
- if (!cR_bn || !pR_bn)
- goto fail;
- CI = EVP_PKEY_get0_EC_KEY(peer_key);
- CI_point = EC_KEY_get0_public_key(CI);
- if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
- BN_mod_add(sum, cR_bn, pR_bn, q, bnctx) != 1 ||
- EC_POINT_mul(group, m, NULL, CI_point, sum, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
- bnctx) != 1) {
- wpa_printf(MSG_ERROR,
- "OpenSSL: failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ ec = crypto_ec_init(curve->ike_group);
+ if (!ec)
goto fail;
- }
- if (dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
+ sum = crypto_bignum_init();
+ q = crypto_ec_get_order(ec);
+ M = crypto_ec_point_init(ec);
+ cR = crypto_ec_key_get_private_key(own_key);
+ pR = crypto_ec_key_get_private_key(auth->own_protocol_key);
+ CI = crypto_ec_key_get_public_key(peer_key);
+ if (!sum || !q || !M || !cR || !pR || !CI ||
+ crypto_bignum_addmod(cR, pR, q, sum) ||
+ crypto_ec_point_mul(ec, CI, sum, M) ||
+ crypto_ec_point_to_bin(ec, M, Mx, NULL)) {
+ wpa_printf(MSG_ERROR, "DPP: Error during M computation");
goto fail;
+ }
wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
/* ke = HKDF(C-nonce | E-nonce, "dpp reconfig key", M.x) */
/* HKDF-Extract(C-nonce | E-nonce, M.x) */
os_memcpy(nonces, auth->c_nonce, curve->nonce_len);
os_memcpy(&nonces[curve->nonce_len], auth->e_nonce, curve->nonce_len);
if (dpp_hmac(curve->hash_len, nonces, 2 * curve->nonce_len,
Mx, curve->prime_len, prk) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
/* HKDF-Expand(PRK, "dpp reconfig key", L) */
if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
"dpp reconfig key", auth->ke, curve->hash_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG,
"DPP: ke = HKDF(C-nonce | E-nonce, \"dpp reconfig key\", M.x)",
auth->ke, curve->hash_len);
res = 0;
- EVP_PKEY_free(auth->reconfig_old_protocol_key);
+ crypto_ec_key_deinit(auth->reconfig_old_protocol_key);
auth->reconfig_old_protocol_key = own_key;
own_key = NULL;
fail:
forced_memzero(prk, sizeof(prk));
forced_memzero(Mx, sizeof(Mx));
- EC_POINT_clear_free(m);
- BN_free(q);
- BN_clear_free(mx);
- BN_clear_free(sum);
- EVP_PKEY_free(own_key);
- EVP_PKEY_free(peer_key);
- BN_CTX_free(bnctx);
+ crypto_ec_point_deinit(M, 1);
+ crypto_bignum_deinit(sum, 1);
+ crypto_ec_key_deinit(own_key);
+ crypto_ec_key_deinit(peer_key);
+ crypto_ec_deinit(ec);
return res;
}
int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
const u8 *r_proto, u16 r_proto_len,
struct json_token *net_access_key)
{
- BN_CTX *bnctx = NULL;
- EVP_PKEY *pr = NULL, *peer_key = NULL;
- EC_POINT *sum = NULL, *m = NULL;
- BIGNUM *mx = NULL;
- const EC_KEY *cI, *CR, *PR;
- const EC_GROUP *group;
- const EC_POINT *CR_point, *PR_point;
- const BIGNUM *cI_bn;
+ struct crypto_ec_key *pr = NULL, *peer_key = NULL;
+ const struct crypto_ec_point *CR, *PR;
+ const struct crypto_bignum *cI;
+ struct crypto_ec *ec = NULL;
+ struct crypto_ec_point *sum = NULL, *M = NULL;
u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
u8 prk[DPP_MAX_HASH_LEN];
int res = -1;
const struct dpp_curve_params *curve;
u8 nonces[2 * DPP_MAX_NONCE_LEN];
pr = dpp_set_pubkey_point(auth->conf->connector_key,
r_proto, r_proto_len);
if (!pr) {
dpp_auth_fail(auth, "Invalid Responder Protocol Key");
goto fail;
}
dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
- EVP_PKEY_free(auth->peer_protocol_key);
+ crypto_ec_key_deinit(auth->peer_protocol_key);
auth->peer_protocol_key = pr;
pr = NULL;
peer_key = dpp_parse_jwk(net_access_key, &curve);
if (!peer_key)
goto fail;
dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
if (auth->curve != curve) {
wpa_printf(MSG_DEBUG,
"DPP: Mismatching netAccessKey curves (own=%s != peer=%s)",
auth->curve->name, curve->name);
goto fail;
}
/* M = cI * { CR + PR } */
- cI = EVP_PKEY_get0_EC_KEY(auth->conf->connector_key);
- cI_bn = EC_KEY_get0_private_key(cI);
- group = EC_KEY_get0_group(cI);
- bnctx = BN_CTX_new();
- sum = EC_POINT_new(group);
- m = EC_POINT_new(group);
- mx = BN_new();
- CR = EVP_PKEY_get0_EC_KEY(peer_key);
- PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
- CR_point = EC_KEY_get0_public_key(CR);
- PR_point = EC_KEY_get0_public_key(PR);
- if (!bnctx || !sum || !m || !mx ||
- EC_POINT_add(group, sum, CR_point, PR_point, bnctx) != 1 ||
- EC_POINT_mul(group, m, NULL, sum, cI_bn, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
- bnctx) != 1 ||
- dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
+ ec = crypto_ec_init(curve->ike_group);
+ if (!ec)
goto fail;
+ cI = crypto_ec_key_get_private_key(auth->conf->connector_key);
+ sum = crypto_ec_point_init(ec);
+ M = crypto_ec_point_init(ec);
+ CR = crypto_ec_key_get_public_key(peer_key);
+ PR = crypto_ec_key_get_public_key(auth->peer_protocol_key);
+ if (!cI || !sum || !M || !CR || !PR ||
+ crypto_ec_point_add(ec, CR, PR, sum) ||
+ crypto_ec_point_mul(ec, sum, cI, M) ||
+ crypto_ec_point_to_bin(ec, M, Mx, NULL)) {
+ wpa_printf(MSG_ERROR, "DPP: Error during M computation");
+ goto fail;
+ }
+
wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
/* ke = HKDF(C-nonce | E-nonce, "dpp reconfig key", M.x) */
/* HKDF-Extract(C-nonce | E-nonce, M.x) */
os_memcpy(nonces, auth->c_nonce, curve->nonce_len);
os_memcpy(&nonces[curve->nonce_len], auth->e_nonce, curve->nonce_len);
if (dpp_hmac(curve->hash_len, nonces, 2 * curve->nonce_len,
Mx, curve->prime_len, prk) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
/* HKDF-Expand(PRK, "dpp reconfig key", L) */
if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
"dpp reconfig key", auth->ke, curve->hash_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG,
"DPP: ke = HKDF(C-nonce | E-nonce, \"dpp reconfig key\", M.x)",
auth->ke, curve->hash_len);
res = 0;
fail:
forced_memzero(prk, sizeof(prk));
forced_memzero(Mx, sizeof(Mx));
- EVP_PKEY_free(pr);
- EVP_PKEY_free(peer_key);
- EC_POINT_clear_free(sum);
- EC_POINT_clear_free(m);
- BN_clear_free(mx);
- BN_CTX_free(bnctx);
+ crypto_ec_key_deinit(pr);
+ crypto_ec_key_deinit(peer_key);
+ crypto_ec_point_deinit(sum, 1);
+ crypto_ec_point_deinit(M, 1);
+ crypto_ec_deinit(ec);
return res;
}
static char *
dpp_build_jws_prot_hdr(struct dpp_configurator *conf, size_t *signed1_len)
{
struct wpabuf *jws_prot_hdr;
char *signed1;
jws_prot_hdr = wpabuf_alloc(100);
if (!jws_prot_hdr)
return NULL;
json_start_object(jws_prot_hdr, NULL);
json_add_string(jws_prot_hdr, "typ", "dppCon");
json_value_sep(jws_prot_hdr);
json_add_string(jws_prot_hdr, "kid", conf->kid);
json_value_sep(jws_prot_hdr);
json_add_string(jws_prot_hdr, "alg", conf->curve->jws_alg);
json_end_object(jws_prot_hdr);
signed1 = base64_url_encode(wpabuf_head(jws_prot_hdr),
wpabuf_len(jws_prot_hdr),
signed1_len);
wpabuf_free(jws_prot_hdr);
return signed1;
}
static char *
dpp_build_conn_signature(struct dpp_configurator *conf,
const char *signed1, size_t signed1_len,
const char *signed2, size_t signed2_len,
size_t *signed3_len)
{
const struct dpp_curve_params *curve;
+ struct wpabuf *sig = NULL;
char *signed3 = NULL;
- unsigned char *signature = NULL;
- const unsigned char *p;
- size_t signature_len;
- EVP_MD_CTX *md_ctx = NULL;
- ECDSA_SIG *sig = NULL;
char *dot = ".";
- const EVP_MD *sign_md;
- const BIGNUM *r, *s;
+ const u8 *vector[3];
+ size_t vector_len[3];
+ u8 *hash;
+ int ret;
+
+ vector[0] = (const u8 *) signed1;
+ vector[1] = (const u8 *) dot;
+ vector[2] = (const u8 *) signed2;
+ vector_len[0] = signed1_len;
+ vector_len[1] = 1;
+ vector_len[2] = signed2_len;
curve = conf->curve;
+ hash = os_malloc(curve->hash_len);
+ if (!hash)
+ goto fail;
if (curve->hash_len == SHA256_MAC_LEN) {
- sign_md = EVP_sha256();
+ ret = sha256_vector(3, vector, vector_len, hash);
} else if (curve->hash_len == SHA384_MAC_LEN) {
- sign_md = EVP_sha384();
+ ret = sha384_vector(3, vector, vector_len, hash);
} else if (curve->hash_len == SHA512_MAC_LEN) {
- sign_md = EVP_sha512();
+ ret = sha512_vector(3, vector, vector_len, hash);
} else {
wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
goto fail;
}
-
- md_ctx = EVP_MD_CTX_create();
- if (!md_ctx)
- goto fail;
-
- ERR_clear_error();
- if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL, conf->csign) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
- EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
- EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "DPP: Hash computation failed");
goto fail;
}
- signature = os_malloc(signature_len);
- if (!signature)
- goto fail;
- if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
- wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ wpa_hexdump(MSG_DEBUG, "DPP: Hash value for Connector signature",
+ hash, curve->hash_len);
+
+ sig = crypto_ec_key_sign_r_s(conf->csign, hash, curve->hash_len);
+ if (!sig) {
+ wpa_printf(MSG_ERROR, "DPP: Signature computation failed");
goto fail;
}
- wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
- signature, signature_len);
- /* Convert to raw coordinates r,s */
- p = signature;
- sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
- if (!sig)
- goto fail;
- ECDSA_SIG_get0(sig, &r, &s);
- if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
- dpp_bn2bin_pad(s, signature + curve->prime_len,
- curve->prime_len) < 0)
- goto fail;
- signature_len = 2 * curve->prime_len;
+
wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
- signature, signature_len);
- signed3 = base64_url_encode(signature, signature_len, signed3_len);
+ wpabuf_head(sig), wpabuf_len(sig));
+ signed3 = base64_url_encode(wpabuf_head(sig), wpabuf_len(sig),
+ signed3_len);
+
fail:
- EVP_MD_CTX_destroy(md_ctx);
- ECDSA_SIG_free(sig);
- os_free(signature);
+ os_free(hash);
+ wpabuf_free(sig);
return signed3;
}
char * dpp_sign_connector(struct dpp_configurator *conf,
const struct wpabuf *dppcon)
{
char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
char *signed_conn = NULL, *pos;
size_t signed1_len, signed2_len, signed3_len;
signed1 = dpp_build_jws_prot_hdr(conf, &signed1_len);
signed2 = base64_url_encode(wpabuf_head(dppcon), wpabuf_len(dppcon),
&signed2_len);
if (!signed1 || !signed2)
goto fail;
signed3 = dpp_build_conn_signature(conf, signed1, signed1_len,
signed2, signed2_len, &signed3_len);
if (!signed3)
goto fail;
signed_conn = os_malloc(signed1_len + signed2_len + signed3_len + 3);
if (!signed_conn)
goto fail;
pos = signed_conn;
os_memcpy(pos, signed1, signed1_len);
pos += signed1_len;
*pos++ = '.';
os_memcpy(pos, signed2, signed2_len);
pos += signed2_len;
*pos++ = '.';
os_memcpy(pos, signed3, signed3_len);
pos += signed3_len;
*pos = '\0';
fail:
os_free(signed1);
os_free(signed2);
os_free(signed3);
return signed_conn;
}
#ifdef CONFIG_DPP2
struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
size_t net_access_key_len)
{
struct wpabuf *pub = NULL;
- EVP_PKEY *own_key;
+ struct crypto_ec_key *own_key;
struct dpp_pfs *pfs;
pfs = os_zalloc(sizeof(*pfs));
if (!pfs)
return NULL;
own_key = dpp_set_keypair(&pfs->curve, net_access_key,
net_access_key_len);
if (!own_key) {
wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
goto fail;
}
- EVP_PKEY_free(own_key);
+ crypto_ec_key_deinit(own_key);
pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
if (!pfs->ecdh)
goto fail;
pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
if (!pub)
goto fail;
pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
if (!pfs->ie)
goto fail;
wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
wpabuf_put_buf(pfs->ie, pub);
wpabuf_free(pub);
wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
pfs->ie);
return pfs;
fail:
wpabuf_free(pub);
dpp_pfs_free(pfs);
return NULL;
}
int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
{
if (peer_ie_len < 2)
return -1;
if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
return -1;
}
pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
peer_ie_len - 2);
pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
if (!pfs->secret) {
wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
return -1;
}
wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
return 0;
}
void dpp_pfs_free(struct dpp_pfs *pfs)
{
if (!pfs)
return;
crypto_ecdh_deinit(pfs->ecdh);
wpabuf_free(pfs->ie);
wpabuf_clear_free(pfs->secret);
os_free(pfs);
}
struct wpabuf * dpp_build_csr(struct dpp_authentication *auth, const char *name)
{
- X509_REQ *req = NULL;
+ struct crypto_csr *csr = NULL;
struct wpabuf *buf = NULL;
- unsigned char *der;
- int der_len;
- EVP_PKEY *key;
- const EVP_MD *sign_md;
+ struct crypto_ec_key *key;
unsigned int hash_len = auth->curve->hash_len;
- EC_KEY *eckey;
- BIO *out = NULL;
+ struct wpabuf *priv_key;
u8 cp[DPP_CP_LEN];
- char *password;
+ char *password = NULL;
size_t password_len;
- int res;
+ int hash_sign_algo;
/* TODO: use auth->csrattrs */
/* TODO: support generation of a new private key if csrAttrs requests
* a specific group to be used */
key = auth->own_protocol_key;
- eckey = EVP_PKEY_get1_EC_KEY(key);
- if (!eckey)
- goto fail;
- der = NULL;
- der_len = i2d_ECPrivateKey(eckey, &der);
- if (der_len <= 0)
+ priv_key = crypto_ec_key_get_ecprivate_key(key, true);
+ if (!priv_key)
goto fail;
wpabuf_free(auth->priv_key);
- auth->priv_key = wpabuf_alloc_copy(der, der_len);
- OPENSSL_free(der);
- if (!auth->priv_key)
- goto fail;
+ auth->priv_key = priv_key;
- req = X509_REQ_new();
- if (!req || !X509_REQ_set_pubkey(req, key))
+ csr = crypto_csr_init();
+ if (!csr || crypto_csr_set_ec_public_key(csr, key))
goto fail;
- if (name) {
- X509_NAME *n;
-
- n = X509_REQ_get_subject_name(req);
- if (!n)
- goto fail;
-
- if (X509_NAME_add_entry_by_txt(
- n, "CN", MBSTRING_UTF8,
- (const unsigned char *) name, -1, -1, 0) != 1)
- goto fail;
- }
+ if (name && crypto_csr_set_name(csr, CSR_NAME_CN, name))
+ goto fail;
/* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */
if (dpp_hkdf_expand(hash_len, auth->bk, hash_len,
"CSR challengePassword", cp, DPP_CP_LEN) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG,
"DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)",
cp, DPP_CP_LEN);
password = base64_encode_no_lf(cp, DPP_CP_LEN, &password_len);
forced_memzero(cp, DPP_CP_LEN);
- if (!password)
- goto fail;
-
- res = X509_REQ_add1_attr_by_NID(req, NID_pkcs9_challengePassword,
- V_ASN1_UTF8STRING,
- (const unsigned char *) password,
- password_len);
- bin_clear_free(password, password_len);
- if (!res)
+ if (!password ||
+ crypto_csr_set_attribute(csr, CSR_ATTR_CHALLENGE_PASSWORD,
+ ASN1_TAG_UTF8STRING, (const u8 *) password,
+ password_len))
goto fail;
- /* TODO */
-
/* TODO: hash func selection based on csrAttrs */
if (hash_len == SHA256_MAC_LEN) {
- sign_md = EVP_sha256();
+ hash_sign_algo = CRYPTO_HASH_ALG_SHA256;
} else if (hash_len == SHA384_MAC_LEN) {
- sign_md = EVP_sha384();
+ hash_sign_algo = CRYPTO_HASH_ALG_SHA384;
} else if (hash_len == SHA512_MAC_LEN) {
- sign_md = EVP_sha512();
+ hash_sign_algo = CRYPTO_HASH_ALG_SHA512;
} else {
wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
goto fail;
}
- if (!X509_REQ_sign(req, key, sign_md))
- goto fail;
-
- der = NULL;
- der_len = i2d_X509_REQ(req, &der);
- if (der_len < 0)
+ buf = crypto_csr_sign(csr, key, hash_sign_algo);
+ if (!buf)
goto fail;
- buf = wpabuf_alloc_copy(der, der_len);
- OPENSSL_free(der);
-
wpa_hexdump_buf(MSG_DEBUG, "DPP: CSR", buf);
fail:
- BIO_free_all(out);
- X509_REQ_free(req);
+ bin_clear_free(password, password_len);
+ crypto_csr_deinit(csr);
return buf;
}
-struct wpabuf * dpp_pkcs7_certs(const struct wpabuf *pkcs7)
-{
-#ifdef OPENSSL_IS_BORINGSSL
- CBS pkcs7_cbs;
-#else /* OPENSSL_IS_BORINGSSL */
- PKCS7 *p7 = NULL;
- const unsigned char *p = wpabuf_head(pkcs7);
-#endif /* OPENSSL_IS_BORINGSSL */
- STACK_OF(X509) *certs;
- int i, num;
- BIO *out = NULL;
- size_t rlen;
- struct wpabuf *pem = NULL;
- int res;
-
-#ifdef OPENSSL_IS_BORINGSSL
- certs = sk_X509_new_null();
- if (!certs)
- goto fail;
- CBS_init(&pkcs7_cbs, wpabuf_head(pkcs7), wpabuf_len(pkcs7));
- if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
- wpa_printf(MSG_INFO, "DPP: Could not parse PKCS#7 object: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-#else /* OPENSSL_IS_BORINGSSL */
- p7 = d2i_PKCS7(NULL, &p, wpabuf_len(pkcs7));
- if (!p7) {
- wpa_printf(MSG_INFO, "DPP: Could not parse PKCS#7 object: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
-
- switch (OBJ_obj2nid(p7->type)) {
- case NID_pkcs7_signed:
- certs = p7->d.sign->cert;
- break;
- case NID_pkcs7_signedAndEnveloped:
- certs = p7->d.signed_and_enveloped->cert;
- break;
- default:
- certs = NULL;
- break;
- }
-#endif /* OPENSSL_IS_BORINGSSL */
-
- if (!certs || ((num = sk_X509_num(certs)) == 0)) {
- wpa_printf(MSG_INFO,
- "DPP: No certificates found in PKCS#7 object");
- goto fail;
- }
-
- out = BIO_new(BIO_s_mem());
- if (!out)
- goto fail;
-
- for (i = 0; i < num; i++) {
- X509 *cert = sk_X509_value(certs, i);
-
- PEM_write_bio_X509(out, cert);
- }
-
- rlen = BIO_ctrl_pending(out);
- pem = wpabuf_alloc(rlen);
- if (!pem)
- goto fail;
- res = BIO_read(out, wpabuf_put(pem, 0), rlen);
- if (res <= 0) {
- wpabuf_free(pem);
- pem = NULL;
- goto fail;
- }
- wpabuf_put(pem, res);
-
-fail:
-#ifdef OPENSSL_IS_BORINGSSL
- if (certs)
- sk_X509_pop_free(certs, X509_free);
-#else /* OPENSSL_IS_BORINGSSL */
- PKCS7_free(p7);
-#endif /* OPENSSL_IS_BORINGSSL */
- if (out)
- BIO_free_all(out);
-
- return pem;
-}
-
-
-int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr)
+int dpp_validate_csr(struct dpp_authentication *auth,
+ const struct wpabuf *csrbuf)
{
- X509_REQ *req;
- const unsigned char *pos;
- EVP_PKEY *pkey;
- int res, loc, ret = -1;
- X509_ATTRIBUTE *attr;
- ASN1_TYPE *type;
- ASN1_STRING *str;
- unsigned char *utf8 = NULL;
+ struct crypto_csr *csr;
+ const u8 *attr;
+ size_t attr_len;
+ int attr_type;
unsigned char *cp = NULL;
size_t cp_len;
u8 exp_cp[DPP_CP_LEN];
unsigned int hash_len = auth->curve->hash_len;
+ int ret = -1;
- pos = wpabuf_head(csr);
- req = d2i_X509_REQ(NULL, &pos, wpabuf_len(csr));
- if (!req) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to parse CSR");
- return -1;
- }
-
- pkey = X509_REQ_get_pubkey(req);
- if (!pkey) {
- wpa_printf(MSG_DEBUG, "DPP: Failed to get public key from CSR");
- goto fail;
- }
-
- res = X509_REQ_verify(req, pkey);
- EVP_PKEY_free(pkey);
- if (res != 1) {
- wpa_printf(MSG_DEBUG,
- "DPP: CSR does not have a valid signature");
- goto fail;
- }
-
- loc = X509_REQ_get_attr_by_NID(req, NID_pkcs9_challengePassword, -1);
- if (loc < 0) {
+ csr = crypto_csr_verify(csrbuf);
+ if (!csr) {
wpa_printf(MSG_DEBUG,
- "DPP: CSR does not include challengePassword");
+ "DPP: CSR invalid or invalid signature");
goto fail;
}
- attr = X509_REQ_get_attr(req, loc);
+ attr = crypto_csr_get_attribute(csr, CSR_ATTR_CHALLENGE_PASSWORD,
+ &attr_len, &attr_type);
if (!attr) {
wpa_printf(MSG_DEBUG,
- "DPP: Could not get challengePassword attribute");
- goto fail;
- }
-
- type = X509_ATTRIBUTE_get0_type(attr, 0);
- if (!type) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not get challengePassword attribute type");
+ "DPP: CSR does not include challengePassword");
goto fail;
}
-
- res = ASN1_TYPE_get(type);
/* This is supposed to be UTF8String, but allow other strings as well
* since challengePassword is using ASCII (base64 encoded). */
- if (res != V_ASN1_UTF8STRING && res != V_ASN1_PRINTABLESTRING &&
- res != V_ASN1_IA5STRING) {
+ if (attr_type != ASN1_TAG_UTF8STRING &&
+ attr_type != ASN1_TAG_PRINTABLESTRING &&
+ attr_type != ASN1_TAG_IA5STRING) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected challengePassword attribute type %d",
- res);
- goto fail;
- }
-
- str = X509_ATTRIBUTE_get0_data(attr, 0, res, NULL);
- if (!str) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not get ASN.1 string for challengePassword");
+ attr_type);
goto fail;
}
- res = ASN1_STRING_to_UTF8(&utf8, str);
- if (res < 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Could not get UTF8 version of challengePassword");
- goto fail;
- }
-
- cp = base64_decode((const char *) utf8, res, &cp_len);
- OPENSSL_free(utf8);
+ cp = base64_decode((const char *) attr, attr_len, &cp_len);
if (!cp) {
wpa_printf(MSG_DEBUG,
"DPP: Could not base64 decode challengePassword");
goto fail;
}
if (cp_len != DPP_CP_LEN) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected cp length (%zu) in CSR challengePassword",
cp_len);
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "DPP: cp from CSR challengePassword",
cp, cp_len);
/* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */
if (dpp_hkdf_expand(hash_len, auth->bk, hash_len,
"CSR challengePassword", exp_cp, DPP_CP_LEN) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG,
"DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)",
exp_cp, DPP_CP_LEN);
if (os_memcmp_const(cp, exp_cp, DPP_CP_LEN) != 0) {
wpa_printf(MSG_DEBUG,
"DPP: CSR challengePassword does not match calculated cp");
goto fail;
}
ret = 0;
fail:
os_free(cp);
- X509_REQ_free(req);
+ crypto_csr_deinit(csr);
return ret;
}
struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key,
size_t csign_key_len,
const u8 *pp_key,
size_t pp_key_len)
{
- const unsigned char *p;
- EVP_PKEY *csign = NULL, *ppkey = NULL;
+ struct crypto_ec_key *csign = NULL, *ppkey = NULL;
struct dpp_reconfig_id *id = NULL;
- BN_CTX *ctx = NULL;
- BIGNUM *bn = NULL, *q = NULL;
- const EC_KEY *eckey;
- const EC_GROUP *group;
- EC_POINT *e_id = NULL;
-
- p = csign_key;
- csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ struct crypto_ec *ec = NULL;
+ const struct crypto_bignum *q;
+ struct crypto_bignum *bn = NULL;
+ struct crypto_ec_point *e_id = NULL;
+ const struct crypto_ec_point *generator;
+
+ csign = crypto_ec_key_parse_pub(csign_key, csign_key_len);
if (!csign)
goto fail;
if (!pp_key)
goto fail;
- p = pp_key;
- ppkey = d2i_PUBKEY(NULL, &p, pp_key_len);
+ ppkey = crypto_ec_key_parse_pub(pp_key, pp_key_len);
if (!ppkey)
goto fail;
- eckey = EVP_PKEY_get0_EC_KEY(csign);
- if (!eckey)
- goto fail;
- group = EC_KEY_get0_group(eckey);
- if (!group)
+ ec = crypto_ec_init(crypto_ec_key_group(csign));
+ if (!ec)
goto fail;
- e_id = EC_POINT_new(group);
- ctx = BN_CTX_new();
- bn = BN_new();
- q = BN_new();
- if (!e_id || !ctx || !bn || !q ||
- !EC_GROUP_get_order(group, q, ctx) ||
- !BN_rand_range(bn, q) ||
- !EC_POINT_mul(group, e_id, bn, NULL, NULL, ctx))
+ e_id = crypto_ec_point_init(ec);
+ bn = crypto_bignum_init();
+ q = crypto_ec_get_order(ec);
+ generator = crypto_ec_get_generator(ec);
+ if (!e_id || !bn || !q || !generator ||
+ crypto_bignum_rand(bn, q) ||
+ crypto_ec_point_mul(ec, generator, bn, e_id))
goto fail;
- dpp_debug_print_point("DPP: Generated random point E-id", group, e_id);
+ crypto_ec_point_debug_print(ec, e_id,
+ "DPP: Generated random point E-id");
id = os_zalloc(sizeof(*id));
if (!id)
goto fail;
- id->group = group;
+
+ id->ec = ec;
+ ec = NULL;
id->e_id = e_id;
e_id = NULL;
id->csign = csign;
csign = NULL;
id->pp_key = ppkey;
ppkey = NULL;
fail:
- EC_POINT_free(e_id);
- EVP_PKEY_free(csign);
- EVP_PKEY_free(ppkey);
- BN_clear_free(bn);
- BN_CTX_free(ctx);
+ crypto_ec_point_deinit(e_id, 1);
+ crypto_ec_key_deinit(csign);
+ crypto_ec_key_deinit(ppkey);
+ crypto_bignum_deinit(bn, 1);
+ crypto_ec_deinit(ec);
return id;
}
-static EVP_PKEY * dpp_pkey_from_point(const EC_GROUP *group,
- const EC_POINT *point)
-{
- EC_KEY *eckey;
- EVP_PKEY *pkey = NULL;
-
- eckey = EC_KEY_new();
- if (!eckey ||
- EC_KEY_set_group(eckey, group) != 1 ||
- EC_KEY_set_public_key(eckey, point) != 1) {
- wpa_printf(MSG_ERROR,
- "DPP: Failed to set EC_KEY: %s",
- ERR_error_string(ERR_get_error(), NULL));
- goto fail;
- }
- EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
-
- pkey = EVP_PKEY_new();
- if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
- wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
- EVP_PKEY_free(pkey);
- pkey = NULL;
- goto fail;
- }
-
-fail:
- EC_KEY_free(eckey);
- return pkey;
-}
-
-
int dpp_update_reconfig_id(struct dpp_reconfig_id *id)
{
- BN_CTX *ctx = NULL;
- BIGNUM *bn = NULL, *q = NULL;
- EC_POINT *e_prime_id = NULL, *a_nonce = NULL;
+ const struct crypto_bignum *q;
+ struct crypto_bignum *bn;
+ const struct crypto_ec_point *pp, *generator;
+ struct crypto_ec_point *e_prime_id, *a_nonce;
int ret = -1;
- const EC_KEY *pp;
- const EC_POINT *pp_point;
-
- pp = EVP_PKEY_get0_EC_KEY(id->pp_key);
- if (!pp)
- goto fail;
- pp_point = EC_KEY_get0_public_key(pp);
- e_prime_id = EC_POINT_new(id->group);
- a_nonce = EC_POINT_new(id->group);
- ctx = BN_CTX_new();
- bn = BN_new();
- q = BN_new();
+
+ pp = crypto_ec_key_get_public_key(id->pp_key);
+ e_prime_id = crypto_ec_point_init(id->ec);
+ a_nonce = crypto_ec_point_init(id->ec);
+ bn = crypto_bignum_init();
+ q = crypto_ec_get_order(id->ec);
+ generator = crypto_ec_get_generator(id->ec);
+
/* Generate random 0 <= a-nonce < q
* A-NONCE = a-nonce * G
* E'-id = E-id + a-nonce * P_pk */
- if (!pp_point || !e_prime_id || !a_nonce || !ctx || !bn || !q ||
- !EC_GROUP_get_order(id->group, q, ctx) ||
- !BN_rand_range(bn, q) || /* bn = a-nonce */
- !EC_POINT_mul(id->group, a_nonce, bn, NULL, NULL, ctx) ||
- !EC_POINT_mul(id->group, e_prime_id, NULL, pp_point, bn, ctx) ||
- !EC_POINT_add(id->group, e_prime_id, id->e_id, e_prime_id, ctx))
- goto fail;
-
- dpp_debug_print_point("DPP: Generated A-NONCE", id->group, a_nonce);
- dpp_debug_print_point("DPP: Encrypted E-id to E'-id",
- id->group, e_prime_id);
-
- EVP_PKEY_free(id->a_nonce);
- EVP_PKEY_free(id->e_prime_id);
- id->a_nonce = dpp_pkey_from_point(id->group, a_nonce);
- id->e_prime_id = dpp_pkey_from_point(id->group, e_prime_id);
+ if (!pp || !e_prime_id || !a_nonce || !bn || !q || !generator ||
+ crypto_bignum_rand(bn, q) || /* bn = a-nonce */
+ crypto_ec_point_mul(id->ec, generator, bn, a_nonce) ||
+ crypto_ec_point_mul(id->ec, pp, bn, e_prime_id) ||
+ crypto_ec_point_add(id->ec, id->e_id, e_prime_id, e_prime_id))
+ goto fail;
+
+ crypto_ec_point_debug_print(id->ec, a_nonce,
+ "DPP: Generated A-NONCE");
+ crypto_ec_point_debug_print(id->ec, e_prime_id,
+ "DPP: Encrypted E-id to E'-id");
+
+ crypto_ec_key_deinit(id->a_nonce);
+ crypto_ec_key_deinit(id->e_prime_id);
+ id->a_nonce = crypto_ec_key_set_pub_point(id->ec, a_nonce);
+ id->e_prime_id = crypto_ec_key_set_pub_point(id->ec, e_prime_id);
if (!id->a_nonce || !id->e_prime_id)
goto fail;
ret = 0;
fail:
- EC_POINT_free(e_prime_id);
- EC_POINT_free(a_nonce);
- BN_clear_free(bn);
- BN_CTX_free(ctx);
+ crypto_ec_point_deinit(e_prime_id, 1);
+ crypto_ec_point_deinit(a_nonce, 1);
+ crypto_bignum_deinit(bn, 1);
return ret;
}
void dpp_free_reconfig_id(struct dpp_reconfig_id *id)
{
if (id) {
- EC_POINT_clear_free(id->e_id);
- EVP_PKEY_free(id->csign);
- EVP_PKEY_free(id->a_nonce);
- EVP_PKEY_free(id->e_prime_id);
- EVP_PKEY_free(id->pp_key);
+ crypto_ec_point_deinit(id->e_id, 1);
+ crypto_ec_key_deinit(id->csign);
+ crypto_ec_key_deinit(id->a_nonce);
+ crypto_ec_key_deinit(id->e_prime_id);
+ crypto_ec_key_deinit(id->pp_key);
+ crypto_ec_deinit(id->ec);
os_free(id);
}
}
-EC_POINT * dpp_decrypt_e_id(EVP_PKEY *ppkey, EVP_PKEY *a_nonce,
- EVP_PKEY *e_prime_id)
+struct crypto_ec_point * dpp_decrypt_e_id(struct crypto_ec_key *ppkey,
+ struct crypto_ec_key *a_nonce,
+ struct crypto_ec_key *e_prime_id)
{
- const EC_KEY *pp_ec, *a_nonce_ec, *e_prime_id_ec;
- const BIGNUM *pp_bn;
- const EC_GROUP *group;
- EC_POINT *e_id = NULL;
- const EC_POINT *a_nonce_point, *e_prime_id_point;
- BN_CTX *ctx = NULL;
+ struct crypto_ec *ec;
+ const struct crypto_bignum *pp;
+ struct crypto_ec_point *e_id = NULL;
+ const struct crypto_ec_point *a_nonce_point, *e_prime_id_point;
if (!ppkey)
return NULL;
/* E-id = E'-id - s_C * A-NONCE */
- pp_ec = EVP_PKEY_get0_EC_KEY(ppkey);
- a_nonce_ec = EVP_PKEY_get0_EC_KEY(a_nonce);
- e_prime_id_ec = EVP_PKEY_get0_EC_KEY(e_prime_id);
- if (!pp_ec || !a_nonce_ec || !e_prime_id_ec)
+ ec = crypto_ec_init(crypto_ec_key_group(ppkey));
+ if (!ec)
return NULL;
- pp_bn = EC_KEY_get0_private_key(pp_ec);
- group = EC_KEY_get0_group(pp_ec);
- a_nonce_point = EC_KEY_get0_public_key(a_nonce_ec);
- e_prime_id_point = EC_KEY_get0_public_key(e_prime_id_ec);
- ctx = BN_CTX_new();
- if (!pp_bn || !group || !a_nonce_point || !e_prime_id_point || !ctx)
- goto fail;
- e_id = EC_POINT_new(group);
- if (!e_id ||
- !EC_POINT_mul(group, e_id, NULL, a_nonce_point, pp_bn, ctx) ||
- !EC_POINT_invert(group, e_id, ctx) ||
- !EC_POINT_add(group, e_id, e_prime_id_point, e_id, ctx)) {
- EC_POINT_clear_free(e_id);
+
+ pp = crypto_ec_key_get_private_key(ppkey);
+ a_nonce_point = crypto_ec_key_get_public_key(a_nonce);
+ e_prime_id_point = crypto_ec_key_get_public_key(e_prime_id);
+ e_id = crypto_ec_point_init(ec);
+ if (!pp || !a_nonce_point || !e_prime_id_point || !e_id ||
+ crypto_ec_point_mul(ec, a_nonce_point, pp, e_id) ||
+ crypto_ec_point_invert(ec, e_id) ||
+ crypto_ec_point_add(ec, e_id, e_prime_id_point, e_id)) {
+ crypto_ec_point_deinit(e_id, 1);
goto fail;
}
- dpp_debug_print_point("DPP: Decrypted E-id", group, e_id);
+ crypto_ec_point_debug_print(ec, e_id, "DPP: Decrypted E-id");
fail:
- BN_CTX_free(ctx);
+ crypto_ec_deinit(ec);
return e_id;
}
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
int dpp_test_gen_invalid_key(struct wpabuf *msg,
const struct dpp_curve_params *curve)
{
- BN_CTX *ctx;
- BIGNUM *x, *y;
+ struct crypto_ec *ec;
+ struct crypto_ec_key *key = NULL;
+ const struct crypto_ec_point *pub_key;
+ struct crypto_ec_point *p = NULL;
+ u8 *x, *y;
int ret = -1;
- EC_GROUP *group;
- EC_POINT *point;
- group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
- if (!group)
- return -1;
-
- ctx = BN_CTX_new();
- point = EC_POINT_new(group);
- x = BN_new();
- y = BN_new();
- if (!ctx || !point || !x || !y)
+ ec = crypto_ec_init(curve->ike_group);
+ x = wpabuf_put(msg, curve->prime_len);
+ y = wpabuf_put(msg, curve->prime_len);
+ if (!ec)
goto fail;
- if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
+retry:
+ /* Generate valid key pair */
+ key = crypto_ec_key_gen(curve->ike_group);
+ if (!key)
goto fail;
- /* Generate a random y coordinate that results in a point that is not
- * on the curve. */
- for (;;) {
- if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
- goto fail;
+ /* Retrieve public key coordinates */
+ pub_key = crypto_ec_key_get_public_key(key);
+ if (!pub_key)
+ goto fail;
- if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
- ctx) != 1) {
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
- /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
- * return an error from EC_POINT_set_affine_coordinates_GFp()
- * when the point is not on the curve. */
- break;
-#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
- goto fail;
-#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
- }
+ crypto_ec_point_to_bin(ec, pub_key, x, y);
- if (!EC_POINT_is_on_curve(group, point, ctx))
- break;
+ /* And corrupt them */
+ y[curve->prime_len - 1] ^= 0x01;
+ p = crypto_ec_point_from_bin(ec, x);
+ if (p && crypto_ec_point_is_on_curve(ec, p)) {
+ crypto_ec_point_deinit(p, 0);
+ p = NULL;
+ goto retry;
}
- if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0 ||
- dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0)
- goto fail;
-
ret = 0;
fail:
- if (ret < 0)
- wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
- BN_free(x);
- BN_free(y);
- EC_POINT_free(point);
- BN_CTX_free(ctx);
- EC_GROUP_free(group);
-
+ crypto_ec_point_deinit(p, 0);
+ crypto_ec_key_deinit(key);
+ crypto_ec_deinit(ec);
return ret;
}
char * dpp_corrupt_connector_signature(const char *connector)
{
char *tmp, *pos, *signed3 = NULL;
unsigned char *signature = NULL;
size_t signature_len = 0, signed3_len;
tmp = os_zalloc(os_strlen(connector) + 5);
if (!tmp)
goto fail;
os_memcpy(tmp, connector, os_strlen(connector));
pos = os_strchr(tmp, '.');
if (!pos)
goto fail;
pos = os_strchr(pos + 1, '.');
if (!pos)
goto fail;
pos++;
wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
pos);
signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
if (!signature || signature_len == 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
signature, signature_len);
signature[signature_len - 1] ^= 0x01;
wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
signature, signature_len);
signed3 = base64_url_encode(signature, signature_len, &signed3_len);
if (!signed3)
goto fail;
os_memcpy(pos, signed3, signed3_len);
pos[signed3_len] = '\0';
wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
pos);
out:
os_free(signature);
os_free(signed3);
return tmp;
fail:
os_free(tmp);
tmp = NULL;
goto out;
}
#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/contrib/wpa/src/common/dpp_i.h b/contrib/wpa/src/common/dpp_i.h
index af12467a5d92..087878a508cb 100644
--- a/contrib/wpa/src/common/dpp_i.h
+++ b/contrib/wpa/src/common/dpp_i.h
@@ -1,160 +1,158 @@
/*
* DPP module internal definitions
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef DPP_I_H
#define DPP_I_H
#ifdef CONFIG_DPP
struct dpp_global {
void *msg_ctx;
struct dl_list bootstrap; /* struct dpp_bootstrap_info */
struct dl_list configurator; /* struct dpp_configurator */
#ifdef CONFIG_DPP2
struct dl_list controllers; /* struct dpp_relay_controller */
struct dpp_controller *controller;
struct dl_list tcp_init; /* struct dpp_connection */
void *cb_ctx;
int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
#endif /* CONFIG_DPP2 */
};
/* dpp.c */
void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status);
void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash);
unsigned int dpp_next_id(struct dpp_global *dpp);
struct wpabuf * dpp_build_conn_status(enum dpp_status_error result,
const u8 *ssid, size_t ssid_len,
const char *channel_list);
struct json_token * dpp_parse_own_connector(const char *own_connector);
int dpp_connector_match_groups(struct json_token *own_root,
struct json_token *peer_root, bool reconfig);
-int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
- const char *kid, const struct dpp_curve_params *curve);
-EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
- const struct dpp_curve_params **key_curve);
+int dpp_build_jwk(struct wpabuf *buf, const char *name,
+ struct crypto_ec_key *key, const char *kid,
+ const struct dpp_curve_params *curve);
+struct crypto_ec_key * dpp_parse_jwk(struct json_token *jwk,
+ const struct dpp_curve_params **key_curve);
int dpp_prepare_channel_list(struct dpp_authentication *auth,
unsigned int neg_freq,
struct hostapd_hw_modes *own_modes, u16 num_modes);
void dpp_auth_fail(struct dpp_authentication *auth, const char *txt);
int dpp_gen_uri(struct dpp_bootstrap_info *bi);
void dpp_write_adv_proto(struct wpabuf *buf);
void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query);
/* dpp_backup.c */
void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key);
struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth);
int dpp_conf_resp_env_data(struct dpp_authentication *auth,
const u8 *env_data, size_t env_data_len);
/* dpp_crypto.c */
struct dpp_signed_connector_info {
unsigned char *payload;
size_t payload_len;
};
enum dpp_status_error
dpp_process_signed_connector(struct dpp_signed_connector_info *info,
- EVP_PKEY *csign_pub, const char *connector);
+ struct crypto_ec_key *csign_pub,
+ const char *connector);
enum dpp_status_error
dpp_check_signed_connector(struct dpp_signed_connector_info *info,
const u8 *csign_key, size_t csign_key_len,
const u8 *peer_connector, size_t peer_connector_len);
const struct dpp_curve_params * dpp_get_curve_name(const char *name);
const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name);
-const struct dpp_curve_params * dpp_get_curve_nid(int nid);
const struct dpp_curve_params * dpp_get_curve_ike_group(u16 group);
int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
const u8 *data, size_t data_len);
-struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix);
-EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
- const u8 *buf_x, const u8 *buf_y,
- size_t len);
-EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len);
-int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len);
+struct crypto_ec_key * dpp_set_pubkey_point(struct crypto_ec_key *group_key,
+ const u8 *buf, size_t len);
int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
const char *label, u8 *out, size_t outlen);
int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac);
-int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, u8 *secret, size_t *secret_len);
-void dpp_debug_print_point(const char *title, const EC_GROUP *group,
- const EC_POINT *point);
-void dpp_debug_print_key(const char *title, EVP_PKEY *key);
+int dpp_ecdh(struct crypto_ec_key *own, struct crypto_ec_key *peer,
+ u8 *secret, size_t *secret_len);
+void dpp_debug_print_key(const char *title, struct crypto_ec_key *key);
int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
const u8 *salt, size_t salt_len, unsigned int iterations,
u8 *buf, size_t buflen);
int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
const u8 *data, size_t data_len);
int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
const u8 *privkey, size_t privkey_len);
-EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
- const u8 *privkey, size_t privkey_len);
-EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve);
+struct crypto_ec_key * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len);
+struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve);
int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len);
int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len);
int dpp_derive_bk_ke(struct dpp_authentication *auth);
int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth);
int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth);
int dpp_auth_derive_l_responder(struct dpp_authentication *auth);
int dpp_auth_derive_l_initiator(struct dpp_authentication *auth);
int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, unsigned int hash_len);
int dpp_derive_pmkid(const struct dpp_curve_params *curve,
- EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid);
-EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
- const u8 *mac_init, const char *code,
- const char *identifier, BN_CTX *bnctx,
- EC_GROUP **ret_group);
-EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
- const u8 *mac_resp, const char *code,
- const char *identifier, BN_CTX *bnctx,
- EC_GROUP **ret_group);
+ struct crypto_ec_key *own_key,
+ struct crypto_ec_key *peer_key, u8 *pmkid);
+struct crypto_ec_point *
+dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, const u8 *mac_init,
+ const char *code, const char *identifier,
+ struct crypto_ec **ret_ec);
+struct crypto_ec_point *
+dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
+ const char *code, const char *identifier,
+ struct crypto_ec **ret_ec);
int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
const u8 *Mx, size_t Mx_len,
const u8 *Nx, size_t Nx_len,
const char *code,
const u8 *Kx, size_t Kx_len,
u8 *z, unsigned int hash_len);
int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
const u8 *net_access_key,
size_t net_access_key_len,
struct json_token *peer_net_access_key);
int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
const u8 *r_proto, u16 r_proto_len,
struct json_token *net_access_key);
-EC_POINT * dpp_decrypt_e_id(EVP_PKEY *ppkey, EVP_PKEY *a_nonce,
- EVP_PKEY *e_prime_id);
+struct crypto_ec_point * dpp_decrypt_e_id(struct crypto_ec_key *ppkey,
+ struct crypto_ec_key *a_nonce,
+ struct crypto_ec_key *e_prime_id);
char * dpp_sign_connector(struct dpp_configurator *conf,
const struct wpabuf *dppcon);
int dpp_test_gen_invalid_key(struct wpabuf *msg,
const struct dpp_curve_params *curve);
struct dpp_reconfig_id {
- const EC_GROUP *group;
- EC_POINT *e_id; /* E-id */
- EVP_PKEY *csign;
- EVP_PKEY *a_nonce; /* A-NONCE */
- EVP_PKEY *e_prime_id; /* E'-id */
- EVP_PKEY *pp_key;
+ struct crypto_ec *ec;
+ struct crypto_ec_point *e_id; /* E-id */
+ struct crypto_ec_key *csign;
+ struct crypto_ec_key *a_nonce; /* A-NONCE */
+ struct crypto_ec_key *e_prime_id; /* E'-id */
+ struct crypto_ec_key *pp_key;
};
/* dpp_tcp.c */
void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
void *timeout_ctx);
void dpp_tcp_init_flush(struct dpp_global *dpp);
void dpp_relay_flush_controllers(struct dpp_global *dpp);
#endif /* CONFIG_DPP */
#endif /* DPP_I_H */
diff --git a/contrib/wpa/src/common/dpp_pkex.c b/contrib/wpa/src/common/dpp_pkex.c
index 807ab7d0a1ce..06532b5457bd 100644
--- a/contrib/wpa/src/common/dpp_pkex.c
+++ b/contrib/wpa/src/common/dpp_pkex.c
@@ -1,1324 +1,1262 @@
/*
* DPP PKEX functionality
* Copyright (c) 2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
-#include <openssl/opensslv.h>
-#include <openssl/err.h>
#include "utils/common.h"
#include "common/wpa_ctrl.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
#include "crypto/crypto.h"
#include "dpp.h"
#include "dpp_i.h"
#ifdef CONFIG_TESTING_OPTIONS
u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
u8 dpp_pkex_ephemeral_key_override[600];
size_t dpp_pkex_ephemeral_key_override_len = 0;
#endif /* CONFIG_TESTING_OPTIONS */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
- (defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20700000L)
-/* Compatibility wrappers for older versions. */
-
-static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
-{
- if (pkey->type != EVP_PKEY_EC)
- return NULL;
- return pkey->pkey.ec;
-}
-
-#endif
-
static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
{
- const EC_KEY *X_ec;
- const EC_POINT *X_point;
- BN_CTX *bnctx = NULL;
- EC_GROUP *group = NULL;
- EC_POINT *Qi = NULL, *M = NULL;
- struct wpabuf *M_buf = NULL;
- BIGNUM *Mx = NULL, *My = NULL;
+ struct crypto_ec *ec = NULL;
+ const struct crypto_ec_point *X;
+ struct crypto_ec_point *Qi = NULL, *M = NULL;
+ u8 *Mx, *My;
struct wpabuf *msg = NULL;
size_t attr_len;
const struct dpp_curve_params *curve = pkex->own_bi->curve;
wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
- bnctx = BN_CTX_new();
- if (!bnctx)
- goto fail;
Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
- pkex->identifier, bnctx, &group);
+ pkex->identifier, &ec);
if (!Qi)
goto fail;
/* Generate a random ephemeral keypair x/X */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_pkex_ephemeral_key_override_len) {
const struct dpp_curve_params *tmp_curve;
wpa_printf(MSG_INFO,
"DPP: TESTING - override ephemeral key x/X");
pkex->x = dpp_set_keypair(&tmp_curve,
dpp_pkex_ephemeral_key_override,
dpp_pkex_ephemeral_key_override_len);
} else {
pkex->x = dpp_gen_keypair(curve);
}
#else /* CONFIG_TESTING_OPTIONS */
pkex->x = dpp_gen_keypair(curve);
#endif /* CONFIG_TESTING_OPTIONS */
if (!pkex->x)
goto fail;
/* M = X + Qi */
- X_ec = EVP_PKEY_get0_EC_KEY(pkex->x);
- if (!X_ec)
+ X = crypto_ec_key_get_public_key(pkex->x);
+ M = crypto_ec_point_init(ec);
+ if (!X || !M)
goto fail;
- X_point = EC_KEY_get0_public_key(X_ec);
- if (!X_point)
- goto fail;
- dpp_debug_print_point("DPP: X", group, X_point);
- M = EC_POINT_new(group);
- Mx = BN_new();
- My = BN_new();
- if (!M || !Mx || !My ||
- EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
+ crypto_ec_point_debug_print(ec, X, "DPP: X");
+
+ if (crypto_ec_point_add(ec, X, Qi, M))
goto fail;
- dpp_debug_print_point("DPP: M", group, M);
+ crypto_ec_point_debug_print(ec, M, "DPP: M");
/* Initiator -> Responder: group, [identifier,] M */
attr_len = 4 + 2;
if (pkex->identifier)
attr_len += 4 + os_strlen(pkex->identifier);
attr_len += 4 + 2 * curve->prime_len;
msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
if (!msg)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
goto skip_finite_cyclic_group;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* Finite Cyclic Group attribute */
wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
wpabuf_put_le16(msg, 2);
wpabuf_put_le16(msg, curve->ike_group);
#ifdef CONFIG_TESTING_OPTIONS
skip_finite_cyclic_group:
#endif /* CONFIG_TESTING_OPTIONS */
/* Code Identifier attribute */
if (pkex->identifier) {
wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
wpabuf_put_le16(msg, os_strlen(pkex->identifier));
wpabuf_put_str(msg, pkex->identifier);
}
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
goto out;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* M in Encrypted Key attribute */
wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
wpabuf_put_le16(msg, 2 * curve->prime_len);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
if (dpp_test_gen_invalid_key(msg, curve) < 0)
goto fail;
goto out;
}
#endif /* CONFIG_TESTING_OPTIONS */
- if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0 ||
- dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
- dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0)
+ Mx = wpabuf_put(msg, curve->prime_len);
+ My = wpabuf_put(msg, curve->prime_len);
+ if (crypto_ec_point_to_bin(ec, M, Mx, My))
goto fail;
+ os_memcpy(pkex->Mx, Mx, curve->prime_len);
+
out:
- wpabuf_free(M_buf);
- EC_POINT_free(M);
- EC_POINT_free(Qi);
- BN_clear_free(Mx);
- BN_clear_free(My);
- BN_CTX_free(bnctx);
- EC_GROUP_free(group);
+ crypto_ec_point_deinit(M, 1);
+ crypto_ec_point_deinit(Qi, 1);
+ crypto_ec_deinit(ec);
return msg;
fail:
wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
wpabuf_free(msg);
msg = NULL;
goto out;
}
static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
{
wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
}
struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const char *identifier,
const char *code)
{
struct dpp_pkex *pkex;
#ifdef CONFIG_TESTING_OPTIONS
if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
MAC2STR(dpp_pkex_own_mac_override));
own_mac = dpp_pkex_own_mac_override;
}
#endif /* CONFIG_TESTING_OPTIONS */
pkex = os_zalloc(sizeof(*pkex));
if (!pkex)
return NULL;
pkex->msg_ctx = msg_ctx;
pkex->initiator = 1;
pkex->own_bi = bi;
os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
if (identifier) {
pkex->identifier = os_strdup(identifier);
if (!pkex->identifier)
goto fail;
}
pkex->code = os_strdup(code);
if (!pkex->code)
goto fail;
pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
if (!pkex->exchange_req)
goto fail;
return pkex;
fail:
dpp_pkex_free(pkex);
return NULL;
}
static struct wpabuf *
dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
enum dpp_status_error status,
- const BIGNUM *Nx, const BIGNUM *Ny)
+ const u8 *Nx, const u8 *Ny)
{
struct wpabuf *msg = NULL;
size_t attr_len;
const struct dpp_curve_params *curve = pkex->own_bi->curve;
/* Initiator -> Responder: DPP Status, [identifier,] N */
attr_len = 4 + 1;
if (pkex->identifier)
attr_len += 4 + os_strlen(pkex->identifier);
attr_len += 4 + 2 * curve->prime_len;
msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
if (!msg)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
goto skip_status;
}
if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
status = 255;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* DPP Status */
dpp_build_attr_status(msg, status);
#ifdef CONFIG_TESTING_OPTIONS
skip_status:
#endif /* CONFIG_TESTING_OPTIONS */
/* Code Identifier attribute */
if (pkex->identifier) {
wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
wpabuf_put_le16(msg, os_strlen(pkex->identifier));
wpabuf_put_str(msg, pkex->identifier);
}
if (status != DPP_STATUS_OK)
goto skip_encrypted_key;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
goto skip_encrypted_key;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* N in Encrypted Key attribute */
wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
wpabuf_put_le16(msg, 2 * curve->prime_len);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
if (dpp_test_gen_invalid_key(msg, curve) < 0)
goto fail;
goto skip_encrypted_key;
}
#endif /* CONFIG_TESTING_OPTIONS */
- if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0 ||
- dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
- dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
- curve->prime_len) < 0)
- goto fail;
+ wpabuf_put_data(msg, Nx, curve->prime_len);
+ wpabuf_put_data(msg, Ny, curve->prime_len);
+ os_memcpy(pkex->Nx, Nx, curve->prime_len);
skip_encrypted_key:
if (status == DPP_STATUS_BAD_GROUP) {
/* Finite Cyclic Group attribute */
wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
wpabuf_put_le16(msg, 2);
wpabuf_put_le16(msg, curve->ike_group);
}
return msg;
fail:
wpabuf_free(msg);
return NULL;
}
static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len,
const char *identifier)
{
if (!attr_id && identifier) {
wpa_printf(MSG_DEBUG,
"DPP: No PKEX code identifier received, but expected one");
return 0;
}
if (attr_id && !identifier) {
wpa_printf(MSG_DEBUG,
"DPP: PKEX code identifier received, but not expecting one");
return 0;
}
if (attr_id && identifier &&
(os_strlen(identifier) != attr_id_len ||
os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
return 0;
}
return 1;
}
struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const u8 *peer_mac,
const char *identifier,
const char *code,
const u8 *buf, size_t len)
{
const u8 *attr_group, *attr_id, *attr_key;
u16 attr_group_len, attr_id_len, attr_key_len;
const struct dpp_curve_params *curve = bi->curve;
u16 ike_group;
struct dpp_pkex *pkex = NULL;
- EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
- BN_CTX *bnctx = NULL;
- EC_GROUP *group = NULL;
- BIGNUM *Mx = NULL, *My = NULL;
- const EC_KEY *Y_ec;
- EC_KEY *X_ec = NULL;
- const EC_POINT *Y_point;
- BIGNUM *Nx = NULL, *Ny = NULL;
+ struct crypto_ec_point *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL,
+ *N = NULL;
+ struct crypto_ec *ec = NULL;
+ const struct crypto_ec_point *Y;
+ u8 *x_coord = NULL, *y_coord = NULL;
u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
size_t Kx_len;
int res;
if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"PKEX counter t limit reached - ignore message");
return NULL;
}
#ifdef CONFIG_TESTING_OPTIONS
if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
MAC2STR(dpp_pkex_peer_mac_override));
peer_mac = dpp_pkex_peer_mac_override;
}
if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
MAC2STR(dpp_pkex_own_mac_override));
own_mac = dpp_pkex_own_mac_override;
}
#endif /* CONFIG_TESTING_OPTIONS */
attr_id_len = 0;
attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
&attr_id_len);
if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier))
return NULL;
attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
&attr_group_len);
if (!attr_group || attr_group_len != 2) {
wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing or invalid Finite Cyclic Group attribute");
return NULL;
}
ike_group = WPA_GET_LE16(attr_group);
if (ike_group != curve->ike_group) {
wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Mismatching PKEX curve: peer=%u own=%u",
ike_group, curve->ike_group);
pkex = os_zalloc(sizeof(*pkex));
if (!pkex)
goto fail;
pkex->own_bi = bi;
pkex->failed = 1;
pkex->exchange_resp = dpp_pkex_build_exchange_resp(
pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
if (!pkex->exchange_resp)
goto fail;
return pkex;
}
/* M in Encrypted Key attribute */
attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
&attr_key_len);
if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing Encrypted Key attribute");
return NULL;
}
/* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
- bnctx = BN_CTX_new();
- if (!bnctx)
- goto fail;
- Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
- &group);
+ Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, &ec);
if (!Qi)
goto fail;
/* X' = M - Qi */
- X = EC_POINT_new(group);
- M = EC_POINT_new(group);
- Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
- My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
- if (!X || !M || !Mx || !My ||
- EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, M) ||
- !EC_POINT_is_on_curve(group, M, bnctx) ||
- EC_POINT_invert(group, Qi, bnctx) != 1 ||
- EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, X) ||
- !EC_POINT_is_on_curve(group, X, bnctx)) {
+ X = crypto_ec_point_init(ec);
+ M = crypto_ec_point_from_bin(ec, attr_key);
+ if (!X || !M ||
+ crypto_ec_point_is_at_infinity(ec, M) ||
+ !crypto_ec_point_is_on_curve(ec, M) ||
+ crypto_ec_point_invert(ec, Qi) ||
+ crypto_ec_point_add(ec, M, Qi, X) ||
+ crypto_ec_point_is_at_infinity(ec, X) ||
+ !crypto_ec_point_is_on_curve(ec, X)) {
wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Invalid Encrypted Key value");
bi->pkex_t++;
goto fail;
}
- dpp_debug_print_point("DPP: M", group, M);
- dpp_debug_print_point("DPP: X'", group, X);
+ crypto_ec_point_debug_print(ec, M, "DPP: M");
+ crypto_ec_point_debug_print(ec, X, "DPP: X'");
pkex = os_zalloc(sizeof(*pkex));
if (!pkex)
goto fail;
pkex->t = bi->pkex_t;
pkex->msg_ctx = msg_ctx;
pkex->own_bi = bi;
os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
if (identifier) {
pkex->identifier = os_strdup(identifier);
if (!pkex->identifier)
goto fail;
}
pkex->code = os_strdup(code);
if (!pkex->code)
goto fail;
os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
- X_ec = EC_KEY_new();
- if (!X_ec ||
- EC_KEY_set_group(X_ec, group) != 1 ||
- EC_KEY_set_public_key(X_ec, X) != 1)
+ x_coord = os_malloc(curve->prime_len);
+ y_coord = os_malloc(curve->prime_len);
+ if (!x_coord || !y_coord ||
+ crypto_ec_point_to_bin(ec, X, x_coord, y_coord))
goto fail;
- pkex->x = EVP_PKEY_new();
- if (!pkex->x ||
- EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
+
+ pkex->x = crypto_ec_key_set_pub(curve->ike_group, x_coord,
+ y_coord, crypto_ec_prime_len(ec));
+ if (!pkex->x)
goto fail;
/* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
- Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
+ Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, NULL);
if (!Qr)
goto fail;
/* Generate a random ephemeral keypair y/Y */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_pkex_ephemeral_key_override_len) {
const struct dpp_curve_params *tmp_curve;
wpa_printf(MSG_INFO,
"DPP: TESTING - override ephemeral key y/Y");
pkex->y = dpp_set_keypair(&tmp_curve,
dpp_pkex_ephemeral_key_override,
dpp_pkex_ephemeral_key_override_len);
} else {
pkex->y = dpp_gen_keypair(curve);
}
#else /* CONFIG_TESTING_OPTIONS */
pkex->y = dpp_gen_keypair(curve);
#endif /* CONFIG_TESTING_OPTIONS */
if (!pkex->y)
goto fail;
/* N = Y + Qr */
- Y_ec = EVP_PKEY_get0_EC_KEY(pkex->y);
- if (!Y_ec)
+ Y = crypto_ec_key_get_public_key(pkex->y);
+ if (!Y)
goto fail;
- Y_point = EC_KEY_get0_public_key(Y_ec);
- if (!Y_point)
- goto fail;
- dpp_debug_print_point("DPP: Y", group, Y_point);
- N = EC_POINT_new(group);
- Nx = BN_new();
- Ny = BN_new();
- if (!N || !Nx || !Ny ||
- EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
- EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
+ crypto_ec_point_debug_print(ec, Y, "DPP: Y");
+
+ N = crypto_ec_point_init(ec);
+ if (!N ||
+ crypto_ec_point_add(ec, Y, Qr, N) ||
+ crypto_ec_point_to_bin(ec, N, x_coord, y_coord))
goto fail;
- dpp_debug_print_point("DPP: N", group, N);
+ crypto_ec_point_debug_print(ec, N, "DPP: N");
pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
- Nx, Ny);
+ x_coord, y_coord);
if (!pkex->exchange_resp)
goto fail;
/* K = y * X' */
if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
Kx, Kx_len);
/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
*/
res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
pkex->Mx, curve->prime_len,
pkex->Nx, curve->prime_len, pkex->code,
Kx, Kx_len, pkex->z, curve->hash_len);
os_memset(Kx, 0, Kx_len);
if (res < 0)
goto fail;
pkex->exchange_done = 1;
out:
- BN_CTX_free(bnctx);
- EC_POINT_free(Qi);
- EC_POINT_free(Qr);
- BN_free(Mx);
- BN_free(My);
- BN_free(Nx);
- BN_free(Ny);
- EC_POINT_free(M);
- EC_POINT_free(N);
- EC_POINT_free(X);
- EC_KEY_free(X_ec);
- EC_GROUP_free(group);
+ os_free(x_coord);
+ os_free(y_coord);
+ crypto_ec_point_deinit(Qi, 1);
+ crypto_ec_point_deinit(Qr, 1);
+ crypto_ec_point_deinit(M, 1);
+ crypto_ec_point_deinit(N, 1);
+ crypto_ec_point_deinit(X, 1);
+ crypto_ec_deinit(ec);
return pkex;
fail:
wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
dpp_pkex_free(pkex);
pkex = NULL;
goto out;
}
static struct wpabuf *
dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
const struct wpabuf *A_pub, const u8 *u)
{
const struct dpp_curve_params *curve = pkex->own_bi->curve;
struct wpabuf *msg = NULL;
size_t clear_len, attr_len;
struct wpabuf *clear = NULL;
u8 *wrapped;
u8 octet;
const u8 *addr[2];
size_t len[2];
/* {A, u, [bootstrapping info]}z */
clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
clear = wpabuf_alloc(clear_len);
attr_len = 4 + clear_len + AES_BLOCK_SIZE;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
attr_len += 5;
#endif /* CONFIG_TESTING_OPTIONS */
msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
if (!clear || !msg)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
goto skip_bootstrap_key;
}
if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
wpabuf_put_le16(clear, 2 * curve->prime_len);
if (dpp_test_gen_invalid_key(clear, curve) < 0)
goto fail;
goto skip_bootstrap_key;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* A in Bootstrap Key attribute */
wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
wpabuf_put_le16(clear, wpabuf_len(A_pub));
wpabuf_put_buf(clear, A_pub);
#ifdef CONFIG_TESTING_OPTIONS
skip_bootstrap_key:
if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
goto skip_i_auth_tag;
}
if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
wpabuf_put_le16(clear, curve->hash_len);
wpabuf_put_data(clear, u, curve->hash_len - 1);
wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
goto skip_i_auth_tag;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* u in I-Auth tag attribute */
wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
wpabuf_put_le16(clear, curve->hash_len);
wpabuf_put_data(clear, u, curve->hash_len);
#ifdef CONFIG_TESTING_OPTIONS
skip_i_auth_tag:
if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
goto skip_wrapped_data;
}
#endif /* CONFIG_TESTING_OPTIONS */
addr[0] = wpabuf_head_u8(msg) + 2;
len[0] = DPP_HDR_LEN;
octet = 0;
addr[1] = &octet;
len[1] = sizeof(octet);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
if (aes_siv_encrypt(pkex->z, curve->hash_len,
wpabuf_head(clear), wpabuf_len(clear),
2, addr, len, wrapped) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
dpp_build_attr_status(msg, DPP_STATUS_OK);
}
skip_wrapped_data:
#endif /* CONFIG_TESTING_OPTIONS */
out:
wpabuf_free(clear);
return msg;
fail:
wpabuf_free(msg);
msg = NULL;
goto out;
}
struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
const u8 *peer_mac,
const u8 *buf, size_t buflen)
{
const u8 *attr_status, *attr_id, *attr_key, *attr_group;
u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
- EC_GROUP *group = NULL;
- BN_CTX *bnctx = NULL;
+ struct crypto_ec *ec = NULL;
struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
const struct dpp_curve_params *curve = pkex->own_bi->curve;
- EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
- BIGNUM *Nx = NULL, *Ny = NULL;
- EC_KEY *Y_ec = NULL;
+ struct crypto_ec_point *Qr = NULL, *Y = NULL, *N = NULL;
+ u8 *x_coord = NULL, *y_coord = NULL;
size_t Jx_len, Kx_len;
u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
const u8 *addr[4];
size_t len[4];
u8 u[DPP_MAX_HASH_LEN];
int res;
if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
return NULL;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
wpa_printf(MSG_INFO,
"DPP: TESTING - stop at PKEX Exchange Response");
pkex->failed = 1;
return NULL;
}
if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
MAC2STR(dpp_pkex_peer_mac_override));
peer_mac = dpp_pkex_peer_mac_override;
}
#endif /* CONFIG_TESTING_OPTIONS */
os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
&attr_status_len);
if (!attr_status || attr_status_len != 1) {
dpp_pkex_fail(pkex, "No DPP Status attribute");
return NULL;
}
wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
attr_group = dpp_get_attr(buf, buflen,
DPP_ATTR_FINITE_CYCLIC_GROUP,
&attr_group_len);
if (attr_group && attr_group_len == 2) {
wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Peer indicated mismatching PKEX group - proposed %u",
WPA_GET_LE16(attr_group));
return NULL;
}
}
if (attr_status[0] != DPP_STATUS_OK) {
dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
return NULL;
}
attr_id_len = 0;
attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
&attr_id_len);
if (!dpp_pkex_identifier_match(attr_id, attr_id_len,
pkex->identifier)) {
dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
return NULL;
}
/* N in Encrypted Key attribute */
attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
&attr_key_len);
if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
return NULL;
}
/* Qr = H(MAC-Responder | [identifier |] code) * Pr */
- bnctx = BN_CTX_new();
- if (!bnctx)
- goto fail;
Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
- pkex->identifier, bnctx, &group);
+ pkex->identifier, &ec);
if (!Qr)
goto fail;
/* Y' = N - Qr */
- Y = EC_POINT_new(group);
- N = EC_POINT_new(group);
- Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
- Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
- if (!Y || !N || !Nx || !Ny ||
- EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, N) ||
- !EC_POINT_is_on_curve(group, N, bnctx) ||
- EC_POINT_invert(group, Qr, bnctx) != 1 ||
- EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
- EC_POINT_is_at_infinity(group, Y) ||
- !EC_POINT_is_on_curve(group, Y, bnctx)) {
+ Y = crypto_ec_point_init(ec);
+ N = crypto_ec_point_from_bin(ec, attr_key);
+ if (!Y || !N ||
+ crypto_ec_point_is_at_infinity(ec, N) ||
+ !crypto_ec_point_is_on_curve(ec, N) ||
+ crypto_ec_point_invert(ec, Qr) ||
+ crypto_ec_point_add(ec, N, Qr, Y) ||
+ crypto_ec_point_is_at_infinity(ec, Y) ||
+ !crypto_ec_point_is_on_curve(ec, Y)) {
dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
pkex->t++;
goto fail;
}
- dpp_debug_print_point("DPP: N", group, N);
- dpp_debug_print_point("DPP: Y'", group, Y);
+ crypto_ec_point_debug_print(ec, N, "DPP: N");
+ crypto_ec_point_debug_print(ec, Y, "DPP: Y'");
pkex->exchange_done = 1;
/* ECDH: J = a * Y' */
- Y_ec = EC_KEY_new();
- if (!Y_ec ||
- EC_KEY_set_group(Y_ec, group) != 1 ||
- EC_KEY_set_public_key(Y_ec, Y) != 1)
+ x_coord = os_malloc(curve->prime_len);
+ y_coord = os_malloc(curve->prime_len);
+ if (!x_coord || !y_coord ||
+ crypto_ec_point_to_bin(ec, Y, x_coord, y_coord))
goto fail;
- pkex->y = EVP_PKEY_new();
- if (!pkex->y ||
- EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
+ pkex->y = crypto_ec_key_set_pub(curve->ike_group, x_coord, y_coord,
+ curve->prime_len);
+ if (!pkex->y)
goto fail;
if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
Jx, Jx_len);
/* u = HMAC(J.x, MAC-Initiator | A.x | Y'.x | X.x) */
- A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
- Y_pub = dpp_get_pubkey_point(pkex->y, 0);
- X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ A_pub = crypto_ec_key_get_pubkey_point(pkex->own_bi->pubkey, 0);
+ Y_pub = crypto_ec_key_get_pubkey_point(pkex->y, 0);
+ X_pub = crypto_ec_key_get_pubkey_point(pkex->x, 0);
if (!A_pub || !Y_pub || !X_pub)
goto fail;
addr[0] = pkex->own_mac;
len[0] = ETH_ALEN;
addr[1] = wpabuf_head(A_pub);
len[1] = wpabuf_len(A_pub) / 2;
addr[2] = wpabuf_head(Y_pub);
len[2] = wpabuf_len(Y_pub) / 2;
addr[3] = wpabuf_head(X_pub);
len[3] = wpabuf_len(X_pub) / 2;
if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
/* K = x * Y' */
if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
Kx, Kx_len);
/* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
*/
res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
pkex->Mx, curve->prime_len,
attr_key /* N.x */, attr_key_len / 2,
pkex->code, Kx, Kx_len,
pkex->z, curve->hash_len);
os_memset(Kx, 0, Kx_len);
if (res < 0)
goto fail;
msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
if (!msg)
goto fail;
out:
wpabuf_free(A_pub);
wpabuf_free(X_pub);
wpabuf_free(Y_pub);
- EC_POINT_free(Qr);
- EC_POINT_free(Y);
- EC_POINT_free(N);
- BN_free(Nx);
- BN_free(Ny);
- EC_KEY_free(Y_ec);
- BN_CTX_free(bnctx);
- EC_GROUP_free(group);
+ os_free(x_coord);
+ os_free(y_coord);
+ crypto_ec_point_deinit(Qr, 1);
+ crypto_ec_point_deinit(Y, 1);
+ crypto_ec_point_deinit(N, 1);
+ crypto_ec_deinit(ec);
return msg;
fail:
wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
goto out;
}
static struct wpabuf *
dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
const struct wpabuf *B_pub, const u8 *v)
{
const struct dpp_curve_params *curve = pkex->own_bi->curve;
struct wpabuf *msg = NULL;
const u8 *addr[2];
size_t len[2];
u8 octet;
u8 *wrapped;
struct wpabuf *clear = NULL;
size_t clear_len, attr_len;
/* {B, v [bootstrapping info]}z */
clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
clear = wpabuf_alloc(clear_len);
attr_len = 4 + clear_len + AES_BLOCK_SIZE;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
attr_len += 5;
#endif /* CONFIG_TESTING_OPTIONS */
msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
if (!clear || !msg)
goto fail;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
goto skip_bootstrap_key;
}
if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
wpabuf_put_le16(clear, 2 * curve->prime_len);
if (dpp_test_gen_invalid_key(clear, curve) < 0)
goto fail;
goto skip_bootstrap_key;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* B in Bootstrap Key attribute */
wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
wpabuf_put_le16(clear, wpabuf_len(B_pub));
wpabuf_put_buf(clear, B_pub);
#ifdef CONFIG_TESTING_OPTIONS
skip_bootstrap_key:
if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
goto skip_r_auth_tag;
}
if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
wpabuf_put_le16(clear, curve->hash_len);
wpabuf_put_data(clear, v, curve->hash_len - 1);
wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
goto skip_r_auth_tag;
}
#endif /* CONFIG_TESTING_OPTIONS */
/* v in R-Auth tag attribute */
wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
wpabuf_put_le16(clear, curve->hash_len);
wpabuf_put_data(clear, v, curve->hash_len);
#ifdef CONFIG_TESTING_OPTIONS
skip_r_auth_tag:
if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
goto skip_wrapped_data;
}
#endif /* CONFIG_TESTING_OPTIONS */
addr[0] = wpabuf_head_u8(msg) + 2;
len[0] = DPP_HDR_LEN;
octet = 1;
addr[1] = &octet;
len[1] = sizeof(octet);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
if (aes_siv_encrypt(pkex->z, curve->hash_len,
wpabuf_head(clear), wpabuf_len(clear),
2, addr, len, wrapped) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
dpp_build_attr_status(msg, DPP_STATUS_OK);
}
skip_wrapped_data:
#endif /* CONFIG_TESTING_OPTIONS */
out:
wpabuf_free(clear);
return msg;
fail:
wpabuf_free(msg);
msg = NULL;
goto out;
}
struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
const u8 *hdr,
const u8 *buf, size_t buflen)
{
const struct dpp_curve_params *curve = pkex->own_bi->curve;
size_t Jx_len, Lx_len;
u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
const u8 *wrapped_data, *b_key, *peer_u;
u16 wrapped_data_len, b_key_len, peer_u_len = 0;
const u8 *addr[4];
size_t len[4];
u8 octet;
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
struct wpabuf *B_pub = NULL;
u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
wpa_printf(MSG_INFO,
"DPP: TESTING - stop at PKEX CR Request");
pkex->failed = 1;
return NULL;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (!pkex->exchange_done || pkex->failed ||
pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
goto fail;
wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
dpp_pkex_fail(pkex,
"Missing or invalid required Wrapped Data attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
goto fail;
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
octet = 0;
addr[1] = &octet;
len[1] = sizeof(octet);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
if (aes_siv_decrypt(pkex->z, curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_pkex_fail(pkex,
"AES-SIV decryption failed - possible PKEX code mismatch");
pkex->failed = 1;
pkex->t++;
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
goto fail;
}
b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
&b_key_len);
if (!b_key || b_key_len != 2 * curve->prime_len) {
dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
goto fail;
}
pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
b_key_len);
if (!pkex->peer_bootstrap_key) {
dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
goto fail;
}
dpp_debug_print_key("DPP: Peer bootstrap public key",
pkex->peer_bootstrap_key);
/* ECDH: J' = y * A' */
if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
Jx, Jx_len);
/* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
- A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
- Y_pub = dpp_get_pubkey_point(pkex->y, 0);
- X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ A_pub = crypto_ec_key_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+ Y_pub = crypto_ec_key_get_pubkey_point(pkex->y, 0);
+ X_pub = crypto_ec_key_get_pubkey_point(pkex->x, 0);
if (!A_pub || !Y_pub || !X_pub)
goto fail;
addr[0] = pkex->peer_mac;
len[0] = ETH_ALEN;
addr[1] = wpabuf_head(A_pub);
len[1] = wpabuf_len(A_pub) / 2;
addr[2] = wpabuf_head(Y_pub);
len[2] = wpabuf_len(Y_pub) / 2;
addr[3] = wpabuf_head(X_pub);
len[3] = wpabuf_len(X_pub) / 2;
if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
goto fail;
peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
&peer_u_len);
if (!peer_u || peer_u_len != curve->hash_len ||
os_memcmp(peer_u, u, curve->hash_len) != 0) {
dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
u, curve->hash_len);
wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
pkex->t++;
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
/* ECDH: L = b * X' */
if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
Lx, Lx_len);
/* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
- B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+ B_pub = crypto_ec_key_get_pubkey_point(pkex->own_bi->pubkey, 0);
if (!B_pub)
goto fail;
addr[0] = pkex->own_mac;
len[0] = ETH_ALEN;
addr[1] = wpabuf_head(B_pub);
len[1] = wpabuf_len(B_pub) / 2;
addr[2] = wpabuf_head(X_pub);
len[2] = wpabuf_len(X_pub) / 2;
addr[3] = wpabuf_head(Y_pub);
len[3] = wpabuf_len(Y_pub) / 2;
if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
if (!msg)
goto fail;
out:
os_free(unwrapped);
wpabuf_free(A_pub);
wpabuf_free(B_pub);
wpabuf_free(X_pub);
wpabuf_free(Y_pub);
return msg;
fail:
wpa_printf(MSG_DEBUG,
"DPP: PKEX Commit-Reveal Request processing failed");
goto out;
}
int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
const u8 *buf, size_t buflen)
{
const struct dpp_curve_params *curve = pkex->own_bi->curve;
const u8 *wrapped_data, *b_key, *peer_v;
u16 wrapped_data_len, b_key_len, peer_v_len = 0;
const u8 *addr[4];
size_t len[4];
u8 octet;
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
int ret = -1;
u8 v[DPP_MAX_HASH_LEN];
size_t Lx_len;
u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
wpa_printf(MSG_INFO,
"DPP: TESTING - stop at PKEX CR Response");
pkex->failed = 1;
goto fail;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (!pkex->exchange_done || pkex->failed ||
pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
goto fail;
wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
dpp_pkex_fail(pkex,
"Missing or invalid required Wrapped Data attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
goto fail;
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
octet = 1;
addr[1] = &octet;
len[1] = sizeof(octet);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
if (aes_siv_decrypt(pkex->z, curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_pkex_fail(pkex,
"AES-SIV decryption failed - possible PKEX code mismatch");
pkex->t++;
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
goto fail;
}
b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
&b_key_len);
if (!b_key || b_key_len != 2 * curve->prime_len) {
dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
goto fail;
}
pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
b_key_len);
if (!pkex->peer_bootstrap_key) {
dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
goto fail;
}
dpp_debug_print_key("DPP: Peer bootstrap public key",
pkex->peer_bootstrap_key);
/* ECDH: L' = x * B' */
if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
Lx, Lx_len);
/* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
- B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
- X_pub = dpp_get_pubkey_point(pkex->x, 0);
- Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ B_pub = crypto_ec_key_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+ X_pub = crypto_ec_key_get_pubkey_point(pkex->x, 0);
+ Y_pub = crypto_ec_key_get_pubkey_point(pkex->y, 0);
if (!B_pub || !X_pub || !Y_pub)
goto fail;
addr[0] = pkex->peer_mac;
len[0] = ETH_ALEN;
addr[1] = wpabuf_head(B_pub);
len[1] = wpabuf_len(B_pub) / 2;
addr[2] = wpabuf_head(X_pub);
len[2] = wpabuf_len(X_pub) / 2;
addr[3] = wpabuf_head(Y_pub);
len[3] = wpabuf_len(Y_pub) / 2;
if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
goto fail;
peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
&peer_v_len);
if (!peer_v || peer_v_len != curve->hash_len ||
os_memcmp(peer_v, v, curve->hash_len) != 0) {
dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
v, curve->hash_len);
wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
pkex->t++;
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
ret = 0;
out:
wpabuf_free(B_pub);
wpabuf_free(X_pub);
wpabuf_free(Y_pub);
os_free(unwrapped);
return ret;
fail:
goto out;
}
struct dpp_bootstrap_info *
dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
unsigned int freq)
{
struct dpp_bootstrap_info *bi;
bi = os_zalloc(sizeof(*bi));
if (!bi)
return NULL;
bi->id = dpp_next_id(dpp);
bi->type = DPP_BOOTSTRAP_PKEX;
os_memcpy(bi->mac_addr, peer, ETH_ALEN);
bi->num_freq = 1;
bi->freq[0] = freq;
bi->curve = pkex->own_bi->curve;
bi->pubkey = pkex->peer_bootstrap_key;
pkex->peer_bootstrap_key = NULL;
if (dpp_bootstrap_key_hash(bi) < 0) {
dpp_bootstrap_info_free(bi);
return NULL;
}
dpp_pkex_free(pkex);
dl_list_add(&dpp->bootstrap, &bi->list);
return bi;
}
void dpp_pkex_free(struct dpp_pkex *pkex)
{
if (!pkex)
return;
os_free(pkex->identifier);
os_free(pkex->code);
- EVP_PKEY_free(pkex->x);
- EVP_PKEY_free(pkex->y);
- EVP_PKEY_free(pkex->peer_bootstrap_key);
+ crypto_ec_key_deinit(pkex->x);
+ crypto_ec_key_deinit(pkex->y);
+ crypto_ec_key_deinit(pkex->peer_bootstrap_key);
wpabuf_free(pkex->exchange_req);
wpabuf_free(pkex->exchange_resp);
os_free(pkex);
}
diff --git a/contrib/wpa/src/common/dpp_reconfig.c b/contrib/wpa/src/common/dpp_reconfig.c
index c4a027363fce..7137bc5fdd43 100644
--- a/contrib/wpa/src/common/dpp_reconfig.c
+++ b/contrib/wpa/src/common/dpp_reconfig.c
@@ -1,958 +1,954 @@
/*
* DPP reconfiguration
* Copyright (c) 2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
-#include <openssl/opensslv.h>
-#include <openssl/err.h>
#include "utils/common.h"
#include "utils/json.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
#include "dpp.h"
#include "dpp_i.h"
#ifdef CONFIG_DPP2
static void dpp_build_attr_csign_key_hash(struct wpabuf *msg, const u8 *hash)
{
if (hash) {
wpa_printf(MSG_DEBUG, "DPP: Configurator C-sign key Hash");
wpabuf_put_le16(msg, DPP_ATTR_C_SIGN_KEY_HASH);
wpabuf_put_le16(msg, SHA256_MAC_LEN);
wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
}
}
struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
size_t csign_key_len,
const u8 *net_access_key,
size_t net_access_key_len,
struct dpp_reconfig_id *id)
{
struct wpabuf *msg = NULL;
- EVP_PKEY *csign = NULL;
- const unsigned char *p;
+ struct crypto_ec_key *csign = NULL;
struct wpabuf *uncomp;
u8 hash[SHA256_MAC_LEN];
const u8 *addr[1];
size_t len[1];
int res;
size_t attr_len;
const struct dpp_curve_params *own_curve;
- EVP_PKEY *own_key;
+ struct crypto_ec_key *own_key;
struct wpabuf *a_nonce = NULL, *e_id = NULL;
wpa_printf(MSG_DEBUG, "DPP: Build Reconfig Announcement frame");
own_key = dpp_set_keypair(&own_curve, net_access_key,
net_access_key_len);
if (!own_key) {
wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
goto fail;
}
- p = csign_key;
- csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ csign = crypto_ec_key_parse_pub(csign_key, csign_key_len);
if (!csign) {
wpa_printf(MSG_ERROR,
"DPP: Failed to parse local C-sign-key information");
goto fail;
}
- uncomp = dpp_get_pubkey_point(csign, 1);
- EVP_PKEY_free(csign);
+ uncomp = crypto_ec_key_get_pubkey_point(csign, 1);
+ crypto_ec_key_deinit(csign);
if (!uncomp)
goto fail;
addr[0] = wpabuf_head(uncomp);
len[0] = wpabuf_len(uncomp);
wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed C-sign key", addr[0], len[0]);
res = sha256_vector(1, addr, len, hash);
wpabuf_free(uncomp);
if (res < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "DPP: kid = SHA256(uncompressed C-sign key)",
hash, SHA256_MAC_LEN);
if (dpp_update_reconfig_id(id) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to generate E'-id");
goto fail;
}
- a_nonce = dpp_get_pubkey_point(id->a_nonce, 0);
- e_id = dpp_get_pubkey_point(id->e_prime_id, 0);
+ a_nonce = crypto_ec_key_get_pubkey_point(id->a_nonce, 0);
+ e_id = crypto_ec_key_get_pubkey_point(id->e_prime_id, 0);
if (!a_nonce || !e_id)
goto fail;
attr_len = 4 + SHA256_MAC_LEN;
attr_len += 4 + 2;
attr_len += 4 + wpabuf_len(a_nonce);
attr_len += 4 + wpabuf_len(e_id);
msg = dpp_alloc_msg(DPP_PA_RECONFIG_ANNOUNCEMENT, attr_len);
if (!msg)
goto fail;
/* Configurator C-sign key Hash */
dpp_build_attr_csign_key_hash(msg, hash);
/* Finite Cyclic Group attribute */
wpa_printf(MSG_DEBUG, "DPP: Finite Cyclic Group: %u",
own_curve->ike_group);
wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
wpabuf_put_le16(msg, 2);
wpabuf_put_le16(msg, own_curve->ike_group);
/* A-NONCE */
wpabuf_put_le16(msg, DPP_ATTR_A_NONCE);
wpabuf_put_le16(msg, wpabuf_len(a_nonce));
wpabuf_put_buf(msg, a_nonce);
/* E'-id */
wpabuf_put_le16(msg, DPP_ATTR_E_PRIME_ID);
wpabuf_put_le16(msg, wpabuf_len(e_id));
wpabuf_put_buf(msg, e_id);
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Reconfig Announcement frame attributes", msg);
fail:
wpabuf_free(a_nonce);
wpabuf_free(e_id);
- EVP_PKEY_free(own_key);
+ crypto_ec_key_deinit(own_key);
return msg;
}
static struct wpabuf * dpp_reconfig_build_req(struct dpp_authentication *auth)
{
struct wpabuf *msg;
size_t attr_len;
/* Build DPP Reconfig Authentication Request frame attributes */
attr_len = 4 + 1 + 4 + 1 + 4 + os_strlen(auth->conf->connector) +
4 + auth->curve->nonce_len;
msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_REQ, attr_len);
if (!msg)
return NULL;
/* Transaction ID */
wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, auth->transaction_id);
/* Protocol Version */
wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, DPP_VERSION);
/* DPP Connector */
wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
wpabuf_put_le16(msg, os_strlen(auth->conf->connector));
wpabuf_put_str(msg, auth->conf->connector);
/* C-nonce */
wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE);
wpabuf_put_le16(msg, auth->curve->nonce_len);
wpabuf_put_data(msg, auth->c_nonce, auth->curve->nonce_len);
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Reconfig Authentication Request frame attributes",
msg);
return msg;
}
static int
dpp_configurator_build_own_connector(struct dpp_configurator *conf,
const struct dpp_curve_params *curve)
{
struct wpabuf *dppcon = NULL;
int ret = -1;
if (conf->connector)
return 0; /* already generated */
wpa_printf(MSG_DEBUG,
"DPP: Sign own Configurator Connector for reconfiguration with curve %s",
conf->curve->name);
conf->connector_key = dpp_gen_keypair(curve);
if (!conf->connector_key)
goto fail;
/* Connector (JSON dppCon object) */
dppcon = wpabuf_alloc(1000 + 2 * curve->prime_len * 4 / 3);
if (!dppcon)
goto fail;
json_start_object(dppcon, NULL);
json_start_array(dppcon, "groups");
json_start_object(dppcon, NULL);
json_add_string(dppcon, "groupId", "*");
json_value_sep(dppcon);
json_add_string(dppcon, "netRole", "configurator");
json_end_object(dppcon);
json_end_array(dppcon);
json_value_sep(dppcon);
if (dpp_build_jwk(dppcon, "netAccessKey", conf->connector_key, NULL,
curve) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
goto fail;
}
json_end_object(dppcon);
wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
(const char *) wpabuf_head(dppcon));
conf->connector = dpp_sign_connector(conf, dppcon);
if (!conf->connector)
goto fail;
wpa_printf(MSG_DEBUG, "DPP: signedConnector: %s", conf->connector);
ret = 0;
fail:
wpabuf_free(dppcon);
return ret;
}
struct dpp_authentication *
dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
struct dpp_configurator *conf, unsigned int freq, u16 group,
const u8 *a_nonce_attr, size_t a_nonce_len,
const u8 *e_id_attr, size_t e_id_len)
{
struct dpp_authentication *auth;
const struct dpp_curve_params *curve;
- EVP_PKEY *a_nonce, *e_prime_id;
- EC_POINT *e_id;
+ struct crypto_ec_key *a_nonce, *e_prime_id;
+ struct crypto_ec_point *e_id;
curve = dpp_get_curve_ike_group(group);
if (!curve) {
wpa_printf(MSG_DEBUG,
"DPP: Unsupported group %u - cannot reconfigure",
group);
return NULL;
}
if (!a_nonce_attr) {
wpa_printf(MSG_INFO, "DPP: Missing required A-NONCE attribute");
return NULL;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: A-NONCE", a_nonce_attr, a_nonce_len);
a_nonce = dpp_set_pubkey_point(conf->csign, a_nonce_attr, a_nonce_len);
if (!a_nonce) {
wpa_printf(MSG_INFO, "DPP: Invalid A-NONCE");
return NULL;
}
dpp_debug_print_key("A-NONCE", a_nonce);
if (!e_id_attr) {
wpa_printf(MSG_INFO, "DPP: Missing required E'-id attribute");
return NULL;
}
e_prime_id = dpp_set_pubkey_point(conf->csign, e_id_attr, e_id_len);
if (!e_prime_id) {
wpa_printf(MSG_INFO, "DPP: Invalid E'-id");
- EVP_PKEY_free(a_nonce);
+ crypto_ec_key_deinit(a_nonce);
return NULL;
}
dpp_debug_print_key("E'-id", e_prime_id);
e_id = dpp_decrypt_e_id(conf->pp_key, a_nonce, e_prime_id);
- EVP_PKEY_free(a_nonce);
- EVP_PKEY_free(e_prime_id);
+ crypto_ec_key_deinit(a_nonce);
+ crypto_ec_key_deinit(e_prime_id);
if (!e_id) {
wpa_printf(MSG_INFO, "DPP: Could not decrypt E'-id");
return NULL;
}
/* TODO: could use E-id to determine whether reconfiguration with this
* Enrollee has already been started and is waiting for updated
* configuration instead of replying again before such configuration
* becomes available */
- EC_POINT_clear_free(e_id);
+ crypto_ec_point_deinit(e_id, 1);
auth = dpp_alloc_auth(dpp, msg_ctx);
if (!auth)
return NULL;
auth->conf = conf;
auth->reconfig = 1;
auth->initiator = 1;
auth->waiting_auth_resp = 1;
auth->allowed_roles = DPP_CAPAB_CONFIGURATOR;
auth->configurator = 1;
auth->curve = curve;
auth->transaction_id = 1;
if (freq && dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
goto fail;
if (dpp_configurator_build_own_connector(conf, curve) < 0)
goto fail;
if (random_get_bytes(auth->c_nonce, auth->curve->nonce_len)) {
wpa_printf(MSG_ERROR, "DPP: Failed to generate C-nonce");
goto fail;
}
auth->reconfig_req_msg = dpp_reconfig_build_req(auth);
if (!auth->reconfig_req_msg)
goto fail;
out:
return auth;
fail:
dpp_auth_deinit(auth);
auth = NULL;
goto out;
}
static int dpp_reconfig_build_resp(struct dpp_authentication *auth,
const char *own_connector,
struct wpabuf *conn_status)
{
struct wpabuf *msg = NULL, *clear, *pr = NULL;
u8 *attr_start, *attr_end;
size_t clear_len, attr_len, len[2];
const u8 *addr[2];
u8 *wrapped;
int res = -1;
/* Build DPP Reconfig Authentication Response frame attributes */
clear_len = 4 + auth->curve->nonce_len +
4 + wpabuf_len(conn_status);
clear = wpabuf_alloc(clear_len);
if (!clear)
goto fail;
/* C-nonce (wrapped) */
wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE);
wpabuf_put_le16(clear, auth->curve->nonce_len);
wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len);
/* Connection Status (wrapped) */
wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS);
wpabuf_put_le16(clear, wpabuf_len(conn_status));
wpabuf_put_buf(clear, conn_status);
- pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ pr = crypto_ec_key_get_pubkey_point(auth->own_protocol_key, 0);
if (!pr)
goto fail;
attr_len = 4 + 1 + 4 + 1 +
4 + os_strlen(own_connector) +
4 + auth->curve->nonce_len +
4 + wpabuf_len(pr) +
4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_RESP, attr_len);
if (!msg)
goto fail;
attr_start = wpabuf_put(msg, 0);
/* Transaction ID */
wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, auth->transaction_id);
/* Protocol Version */
wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
wpabuf_put_le16(msg, 1);
wpabuf_put_u8(msg, DPP_VERSION);
/* R-Connector */
wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
wpabuf_put_le16(msg, os_strlen(own_connector));
wpabuf_put_str(msg, own_connector);
/* E-nonce */
wpabuf_put_le16(msg, DPP_ATTR_ENROLLEE_NONCE);
wpabuf_put_le16(msg, auth->curve->nonce_len);
wpabuf_put_data(msg, auth->e_nonce, auth->curve->nonce_len);
/* Responder Protocol Key (Pr) */
wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
wpabuf_put_le16(msg, wpabuf_len(pr));
wpabuf_put_buf(msg, pr);
attr_end = wpabuf_put(msg, 0);
/* OUI, OUI type, Crypto Suite, DPP frame type */
addr[0] = wpabuf_head_u8(msg) + 2;
len[0] = 3 + 1 + 1 + 1;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
/* Attributes before Wrapped Data */
addr[1] = attr_start;
len[1] = attr_end - attr_start;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
/* Wrapped Data: {C-nonce, E-nonce, Connection Status}ke */
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
wpabuf_head(clear), wpabuf_len(clear),
2, addr, len, wrapped) < 0)
goto fail;
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Reconfig Authentication Response frame attributes",
msg);
wpabuf_free(auth->reconfig_resp_msg);
auth->reconfig_resp_msg = msg;
res = 0;
out:
wpabuf_free(clear);
wpabuf_free(pr);
return res;
fail:
wpabuf_free(msg);
goto out;
}
struct dpp_authentication *
dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
const char *own_connector,
const u8 *net_access_key, size_t net_access_key_len,
const u8 *csign_key, size_t csign_key_len,
unsigned int freq, const u8 *hdr,
const u8 *attr_start, size_t attr_len)
{
struct dpp_authentication *auth = NULL;
const u8 *trans_id, *version, *i_connector, *c_nonce;
u16 trans_id_len, version_len, i_connector_len, c_nonce_len;
struct dpp_signed_connector_info info;
enum dpp_status_error res;
struct json_token *root = NULL, *own_root = NULL, *token;
unsigned char *own_conn = NULL;
struct wpabuf *conn_status = NULL;
os_memset(&info, 0, sizeof(info));
trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
&trans_id_len);
if (!trans_id || trans_id_len != 1) {
wpa_printf(MSG_DEBUG,
"DPP: Peer did not include Transaction ID");
goto fail;
}
version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
&version_len);
if (!version || version_len < 1 || version[0] < 2) {
wpa_printf(MSG_DEBUG,
"DPP: Missing or invalid Protocol Version attribute");
goto fail;
}
i_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
&i_connector_len);
if (!i_connector) {
wpa_printf(MSG_DEBUG, "DPP: Missing I-Connector attribute");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: I-Connector",
i_connector, i_connector_len);
c_nonce = dpp_get_attr(attr_start, attr_len,
DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
if (!c_nonce || c_nonce_len > DPP_MAX_NONCE_LEN) {
wpa_printf(MSG_DEBUG,
"DPP: Missing or invalid C-nonce attribute");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
i_connector, i_connector_len);
if (res != DPP_STATUS_OK) {
wpa_printf(MSG_DEBUG, "DPP: Invalid I-Connector");
goto fail;
}
root = json_parse((const char *) info.payload, info.payload_len);
own_root = dpp_parse_own_connector(own_connector);
if (!root || !own_root ||
!dpp_connector_match_groups(own_root, root, true)) {
wpa_printf(MSG_DEBUG,
"DPP: I-Connector does not include compatible group netrole with own connector");
goto fail;
}
token = json_get_member(root, "expiry");
if (token && token->type == JSON_STRING &&
dpp_key_expired(token->string, NULL)) {
wpa_printf(MSG_DEBUG,
"DPP: I-Connector (netAccessKey) has expired");
goto fail;
}
token = json_get_member(root, "netAccessKey");
if (!token || token->type != JSON_OBJECT) {
wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
goto fail;
}
auth = dpp_alloc_auth(dpp, msg_ctx);
if (!auth)
return NULL;
auth->reconfig = 1;
auth->allowed_roles = DPP_CAPAB_ENROLLEE;
if (dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
goto fail;
auth->transaction_id = trans_id[0];
auth->peer_version = version[0];
wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
auth->peer_version);
os_memcpy(auth->c_nonce, c_nonce, c_nonce_len);
if (dpp_reconfig_derive_ke_responder(auth, net_access_key,
net_access_key_len, token) < 0)
goto fail;
if (c_nonce_len != auth->curve->nonce_len) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected C-nonce length %u (curve nonce len %zu)",
c_nonce_len, auth->curve->nonce_len);
goto fail;
}
/* Build Connection Status object */
/* TODO: Get appropriate result value */
/* TODO: ssid64 and channelList */
conn_status = dpp_build_conn_status(DPP_STATUS_NO_AP, NULL, 0, NULL);
if (!conn_status)
goto fail;
if (dpp_reconfig_build_resp(auth, own_connector, conn_status) < 0)
goto fail;
out:
os_free(info.payload);
os_free(own_conn);
json_free(root);
json_free(own_root);
wpabuf_free(conn_status);
return auth;
fail:
dpp_auth_deinit(auth);
auth = NULL;
goto out;
}
struct wpabuf *
dpp_reconfig_build_conf(struct dpp_authentication *auth)
{
struct wpabuf *msg = NULL, *clear;
u8 *attr_start, *attr_end;
size_t clear_len, attr_len, len[2];
const u8 *addr[2];
u8 *wrapped;
u8 flags;
/* Build DPP Reconfig Authentication Confirm frame attributes */
clear_len = 4 + 1 + 4 + 1 + 2 * (4 + auth->curve->nonce_len) +
4 + 1;
clear = wpabuf_alloc(clear_len);
if (!clear)
goto fail;
/* Transaction ID */
wpabuf_put_le16(clear, DPP_ATTR_TRANSACTION_ID);
wpabuf_put_le16(clear, 1);
wpabuf_put_u8(clear, auth->transaction_id);
/* Protocol Version */
wpabuf_put_le16(clear, DPP_ATTR_PROTOCOL_VERSION);
wpabuf_put_le16(clear, 1);
wpabuf_put_u8(clear, auth->peer_version);
/* C-nonce (wrapped) */
wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE);
wpabuf_put_le16(clear, auth->curve->nonce_len);
wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len);
/* E-nonce (wrapped) */
wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
wpabuf_put_le16(clear, auth->curve->nonce_len);
wpabuf_put_data(clear, auth->e_nonce, auth->curve->nonce_len);
/* Reconfig-Flags (wrapped) */
flags = DPP_CONFIG_REPLACEKEY;
wpabuf_put_le16(clear, DPP_ATTR_RECONFIG_FLAGS);
wpabuf_put_le16(clear, 1);
wpabuf_put_u8(clear, flags);
attr_len = 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
attr_len += 4 + 1;
msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_CONF, attr_len);
if (!msg)
goto fail;
attr_start = wpabuf_put(msg, 0);
/* DPP Status */
dpp_build_attr_status(msg, DPP_STATUS_OK);
attr_end = wpabuf_put(msg, 0);
/* OUI, OUI type, Crypto Suite, DPP frame type */
addr[0] = wpabuf_head_u8(msg) + 2;
len[0] = 3 + 1 + 1 + 1;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
/* Attributes before Wrapped Data */
addr[1] = attr_start;
len[1] = attr_end - attr_start;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
/* Wrapped Data */
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
wpabuf_head(clear), wpabuf_len(clear),
2, addr, len, wrapped) < 0)
goto fail;
wpa_hexdump_buf(MSG_DEBUG,
"DPP: Reconfig Authentication Confirm frame attributes",
msg);
out:
wpabuf_free(clear);
return msg;
fail:
wpabuf_free(msg);
msg = NULL;
goto out;
}
struct wpabuf *
dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len)
{
const u8 *trans_id, *version, *r_connector, *r_proto, *wrapped_data,
*c_nonce, *e_nonce, *conn_status;
u16 trans_id_len, version_len, r_connector_len, r_proto_len,
wrapped_data_len, c_nonce_len, e_nonce_len, conn_status_len;
struct wpabuf *conf = NULL;
char *signed_connector = NULL;
struct dpp_signed_connector_info info;
enum dpp_status_error res;
struct json_token *root = NULL, *token, *conn_status_json = NULL;
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
os_memset(&info, 0, sizeof(info));
if (!auth->reconfig || !auth->configurator)
goto fail;
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
dpp_auth_fail(auth,
"Missing or invalid required Wrapped Data attribute");
goto fail;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
wrapped_data, wrapped_data_len);
attr_len = wrapped_data - 4 - attr_start;
trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
&trans_id_len);
if (!trans_id || trans_id_len != 1) {
dpp_auth_fail(auth, "Peer did not include Transaction ID");
goto fail;
}
if (trans_id[0] != auth->transaction_id) {
dpp_auth_fail(auth, "Transaction ID mismatch");
goto fail;
}
version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
&version_len);
if (!version || version_len < 1 || version[0] < 2) {
dpp_auth_fail(auth,
"Missing or invalid Protocol Version attribute");
goto fail;
}
auth->peer_version = version[0];
wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
auth->peer_version);
r_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
&r_connector_len);
if (!r_connector) {
dpp_auth_fail(auth, " Missing R-Connector attribute");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: R-Connector",
r_connector, r_connector_len);
e_nonce = dpp_get_attr(attr_start, attr_len,
DPP_ATTR_ENROLLEE_NONCE, &e_nonce_len);
if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
dpp_auth_fail(auth, "Missing or invalid E-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len);
os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
&r_proto_len);
if (!r_proto) {
dpp_auth_fail(auth,
"Missing required Responder Protocol Key attribute");
goto fail;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
r_proto, r_proto_len);
signed_connector = os_malloc(r_connector_len + 1);
if (!signed_connector)
goto fail;
os_memcpy(signed_connector, r_connector, r_connector_len);
signed_connector[r_connector_len] = '\0';
res = dpp_process_signed_connector(&info, auth->conf->csign,
signed_connector);
if (res != DPP_STATUS_OK) {
dpp_auth_fail(auth, "Invalid R-Connector");
goto fail;
}
root = json_parse((const char *) info.payload, info.payload_len);
if (!root) {
dpp_auth_fail(auth, "Invalid Connector payload");
goto fail;
}
/* Do not check netAccessKey expiration for reconfiguration to allow
* expired Connector to be updated. */
token = json_get_member(root, "netAccessKey");
if (!token || token->type != JSON_OBJECT) {
dpp_auth_fail(auth, "No netAccessKey object found");
goto fail;
}
if (dpp_reconfig_derive_ke_initiator(auth, r_proto, r_proto_len,
token) < 0)
goto fail;
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
addr[1] = attr_start;
len[1] = attr_len;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
goto fail;
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
c_nonce = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
if (!c_nonce || c_nonce_len != auth->curve->nonce_len ||
os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) {
dpp_auth_fail(auth, "Missing or invalid C-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
conn_status = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_CONN_STATUS, &conn_status_len);
if (!conn_status) {
dpp_auth_fail(auth, "Missing Connection Status attribute");
goto fail;
}
wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus",
conn_status, conn_status_len);
conn_status_json = json_parse((const char *) conn_status,
conn_status_len);
if (!conn_status_json) {
dpp_auth_fail(auth, "Could not parse connStatus");
goto fail;
}
/* TODO: use connStatus information */
conf = dpp_reconfig_build_conf(auth);
if (conf)
auth->reconfig_success = true;
out:
json_free(root);
json_free(conn_status_json);
bin_clear_free(unwrapped, unwrapped_len);
os_free(info.payload);
os_free(signed_connector);
return conf;
fail:
wpabuf_free(conf);
conf = NULL;
goto out;
}
int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len)
{
const u8 *trans_id, *version, *wrapped_data, *c_nonce, *e_nonce,
*reconfig_flags, *status;
u16 trans_id_len, version_len, wrapped_data_len, c_nonce_len,
e_nonce_len, reconfig_flags_len, status_len;
const u8 *addr[2];
size_t len[2];
u8 *unwrapped = NULL;
size_t unwrapped_len = 0;
int res = -1;
u8 flags;
if (!auth->reconfig || auth->configurator)
goto fail;
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
&wrapped_data_len);
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
dpp_auth_fail(auth,
"Missing or invalid required Wrapped Data attribute");
goto fail;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
wrapped_data, wrapped_data_len);
attr_len = wrapped_data - 4 - attr_start;
status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
&status_len);
if (!status || status_len < 1) {
dpp_auth_fail(auth,
"Missing or invalid required DPP Status attribute");
goto fail;
}
wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
if (status[0] != DPP_STATUS_OK) {
dpp_auth_fail(auth,
"Reconfiguration did not complete successfully");
goto fail;
}
addr[0] = hdr;
len[0] = DPP_HDR_LEN;
addr[1] = attr_start;
len[1] = attr_len;
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
wrapped_data, wrapped_data_len);
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
unwrapped = os_malloc(unwrapped_len);
if (!unwrapped)
goto fail;
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
wrapped_data, wrapped_data_len,
2, addr, len, unwrapped) < 0) {
dpp_auth_fail(auth, "AES-SIV decryption failed");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
unwrapped, unwrapped_len);
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
goto fail;
}
trans_id = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_TRANSACTION_ID, &trans_id_len);
if (!trans_id || trans_id_len != 1 ||
trans_id[0] != auth->transaction_id) {
dpp_auth_fail(auth,
"Peer did not include valid Transaction ID");
goto fail;
}
version = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_PROTOCOL_VERSION, &version_len);
if (!version || version_len < 1 || version[0] != DPP_VERSION) {
dpp_auth_fail(auth,
"Missing or invalid Protocol Version attribute");
goto fail;
}
c_nonce = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
if (!c_nonce || c_nonce_len != auth->curve->nonce_len ||
os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) {
dpp_auth_fail(auth, "Missing or invalid C-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_ENROLLEE_NONCE, &e_nonce_len);
if (!e_nonce || e_nonce_len != auth->curve->nonce_len ||
os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
dpp_auth_fail(auth, "Missing or invalid E-nonce");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len);
reconfig_flags = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_RECONFIG_FLAGS,
&reconfig_flags_len);
if (!reconfig_flags || reconfig_flags_len < 1) {
dpp_auth_fail(auth, "Missing or invalid Reconfig-Flags");
goto fail;
}
flags = reconfig_flags[0] & BIT(0);
wpa_printf(MSG_DEBUG, "DPP: Reconfig Flags connectorKey=%u", flags);
auth->reconfig_connector_key = flags;
auth->reconfig_success = true;
res = 0;
fail:
bin_clear_free(unwrapped, unwrapped_len);
return res;
}
#endif /* CONFIG_DPP2 */
diff --git a/contrib/wpa/src/common/dpp_tcp.c b/contrib/wpa/src/common/dpp_tcp.c
index c373f107791c..fb8ef1c5bfaf 100644
--- a/contrib/wpa/src/common/dpp_tcp.c
+++ b/contrib/wpa/src/common/dpp_tcp.c
@@ -1,1824 +1,1827 @@
/*
* DPP over TCP
* Copyright (c) 2019-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include <fcntl.h>
#include "utils/common.h"
#include "utils/ip_addr.h"
#include "utils/eloop.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "dpp.h"
#include "dpp_i.h"
#ifdef CONFIG_DPP2
struct dpp_connection {
struct dl_list list;
struct dpp_controller *ctrl;
struct dpp_relay_controller *relay;
struct dpp_global *global;
struct dpp_authentication *auth;
void *msg_ctx;
void *cb_ctx;
int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
int sock;
u8 mac_addr[ETH_ALEN];
unsigned int freq;
u8 msg_len[4];
size_t msg_len_octets;
struct wpabuf *msg;
struct wpabuf *msg_out;
size_t msg_out_pos;
unsigned int read_eloop:1;
unsigned int write_eloop:1;
unsigned int on_tcp_tx_complete_gas_done:1;
unsigned int on_tcp_tx_complete_remove:1;
unsigned int on_tcp_tx_complete_auth_ok:1;
unsigned int gas_comeback_in_progress:1;
u8 gas_dialog_token;
char *name;
enum dpp_netrole netrole;
};
/* Remote Controller */
struct dpp_relay_controller {
struct dl_list list;
struct dpp_global *global;
u8 pkhash[SHA256_MAC_LEN];
struct hostapd_ip_addr ipaddr;
void *msg_ctx;
void *cb_ctx;
void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
size_t len);
void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
int prot, struct wpabuf *buf);
struct dl_list conn; /* struct dpp_connection */
};
/* Local Controller */
struct dpp_controller {
struct dpp_global *global;
u8 allowed_roles;
int qr_mutual;
int sock;
struct dl_list conn; /* struct dpp_connection */
char *configurator_params;
enum dpp_netrole netrole;
void *msg_ctx;
void *cb_ctx;
int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
};
static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
static void dpp_controller_auth_success(struct dpp_connection *conn,
int initiator);
static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx);
static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx);
static void dpp_relay_conn_timeout(void *eloop_ctx, void *timeout_ctx);
static void dpp_connection_free(struct dpp_connection *conn)
{
if (conn->sock >= 0) {
wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d",
conn->sock);
eloop_unregister_sock(conn->sock, EVENT_TYPE_READ);
eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
close(conn->sock);
}
eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
conn, NULL);
eloop_cancel_timeout(dpp_tcp_build_csr, conn, NULL);
eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
eloop_cancel_timeout(dpp_relay_conn_timeout, conn, NULL);
wpabuf_free(conn->msg);
wpabuf_free(conn->msg_out);
dpp_auth_deinit(conn->auth);
os_free(conn->name);
os_free(conn);
}
static void dpp_connection_remove(struct dpp_connection *conn)
{
dl_list_del(&conn->list);
dpp_connection_free(conn);
}
int dpp_relay_add_controller(struct dpp_global *dpp,
struct dpp_relay_config *config)
{
struct dpp_relay_controller *ctrl;
if (!dpp)
return -1;
ctrl = os_zalloc(sizeof(*ctrl));
if (!ctrl)
return -1;
dl_list_init(&ctrl->conn);
ctrl->global = dpp;
os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
ctrl->msg_ctx = config->msg_ctx;
ctrl->cb_ctx = config->cb_ctx;
ctrl->tx = config->tx;
ctrl->gas_resp_tx = config->gas_resp_tx;
dl_list_add(&dpp->controllers, &ctrl->list);
return 0;
}
static struct dpp_relay_controller *
dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash)
{
struct dpp_relay_controller *ctrl;
if (!dpp)
return NULL;
dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
list) {
if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0)
return ctrl;
}
return NULL;
}
static struct dpp_relay_controller *
dpp_relay_controller_get_ctx(struct dpp_global *dpp, void *cb_ctx)
{
struct dpp_relay_controller *ctrl;
if (!dpp)
return NULL;
dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
list) {
if (cb_ctx == ctrl->cb_ctx)
return ctrl;
}
return NULL;
}
static void dpp_controller_gas_done(struct dpp_connection *conn)
{
struct dpp_authentication *auth = conn->auth;
if (auth->waiting_csr) {
wpa_printf(MSG_DEBUG, "DPP: Waiting for CSR");
conn->on_tcp_tx_complete_gas_done = 0;
return;
}
if (auth->peer_version >= 2 &&
auth->conf_resp_status == DPP_STATUS_OK) {
wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
auth->waiting_conf_result = 1;
return;
}
wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
dpp_connection_remove(conn);
}
static int dpp_tcp_send(struct dpp_connection *conn)
{
int res;
if (!conn->msg_out) {
eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
conn->write_eloop = 0;
return -1;
}
res = send(conn->sock,
wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos,
wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0);
if (res < 0) {
wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s",
strerror(errno));
dpp_connection_remove(conn);
return -1;
}
conn->msg_out_pos += res;
if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) {
wpa_printf(MSG_DEBUG,
"DPP: %u/%u bytes of message sent to Controller",
(unsigned int) conn->msg_out_pos,
(unsigned int) wpabuf_len(conn->msg_out));
if (!conn->write_eloop &&
eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
dpp_conn_tx_ready, conn, NULL) == 0)
conn->write_eloop = 1;
return 1;
}
wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP");
wpabuf_free(conn->msg_out);
conn->msg_out = NULL;
conn->msg_out_pos = 0;
eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
conn->write_eloop = 0;
if (!conn->read_eloop &&
eloop_register_sock(conn->sock, EVENT_TYPE_READ,
dpp_controller_rx, conn, NULL) == 0)
conn->read_eloop = 1;
if (conn->on_tcp_tx_complete_remove) {
dpp_connection_remove(conn);
} else if (conn->auth && (conn->ctrl || conn->auth->configurator) &&
conn->on_tcp_tx_complete_gas_done) {
dpp_controller_gas_done(conn);
} else if (conn->on_tcp_tx_complete_auth_ok) {
conn->on_tcp_tx_complete_auth_ok = 0;
dpp_controller_auth_success(conn, 1);
}
return 0;
}
static int dpp_tcp_send_msg(struct dpp_connection *conn,
const struct wpabuf *msg)
{
wpabuf_free(conn->msg_out);
conn->msg_out_pos = 0;
conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1);
if (!conn->msg_out)
return -1;
wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1);
wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1,
wpabuf_len(msg) - 1);
if (dpp_tcp_send(conn) == 1) {
if (!conn->write_eloop) {
if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
dpp_conn_tx_ready,
conn, NULL) < 0)
return -1;
conn->write_eloop = 1;
}
}
return 0;
}
static void dpp_controller_start_gas_client(struct dpp_connection *conn)
{
struct dpp_authentication *auth = conn->auth;
struct wpabuf *buf;
const char *dpp_name;
dpp_name = conn->name ? conn->name : "Test";
buf = dpp_build_conf_req_helper(auth, dpp_name, conn->netrole, NULL,
NULL);
if (!buf) {
wpa_printf(MSG_DEBUG,
"DPP: No configuration request data available");
return;
}
dpp_tcp_send_msg(conn, buf);
wpabuf_free(buf);
}
static void dpp_controller_auth_success(struct dpp_connection *conn,
int initiator)
{
struct dpp_authentication *auth = conn->auth;
if (!auth)
return;
wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
wpa_msg(conn->msg_ctx, MSG_INFO,
DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
wpa_printf(MSG_INFO,
"DPP: TESTING - stop at Authentication Confirm");
if (auth->configurator) {
/* Prevent GAS response */
auth->auth_success = 0;
}
return;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (!auth->configurator)
dpp_controller_start_gas_client(conn);
}
static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
{
struct dpp_connection *conn = eloop_ctx;
wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock);
dpp_tcp_send(conn);
}
static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen,
const struct hostapd_ip_addr *ipaddr,
int port)
{
struct sockaddr_in *dst;
#ifdef CONFIG_IPV6
struct sockaddr_in6 *dst6;
#endif /* CONFIG_IPV6 */
switch (ipaddr->af) {
case AF_INET:
dst = (struct sockaddr_in *) addr;
os_memset(dst, 0, sizeof(*dst));
dst->sin_family = AF_INET;
dst->sin_addr.s_addr = ipaddr->u.v4.s_addr;
dst->sin_port = htons(port);
*addrlen = sizeof(*dst);
break;
#ifdef CONFIG_IPV6
case AF_INET6:
dst6 = (struct sockaddr_in6 *) addr;
os_memset(dst6, 0, sizeof(*dst6));
dst6->sin6_family = AF_INET6;
os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6,
sizeof(struct in6_addr));
dst6->sin6_port = htons(port);
*addrlen = sizeof(*dst6);
break;
#endif /* CONFIG_IPV6 */
default:
return -1;
}
return 0;
}
static void dpp_relay_conn_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct dpp_connection *conn = eloop_ctx;
wpa_printf(MSG_DEBUG,
"DPP: Timeout while waiting for relayed connection to complete");
dpp_connection_remove(conn);
}
static struct dpp_connection *
dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src,
unsigned int freq)
{
struct dpp_connection *conn;
struct sockaddr_storage addr;
socklen_t addrlen;
char txt[100];
if (dl_list_len(&ctrl->conn) >= 15) {
wpa_printf(MSG_DEBUG,
"DPP: Too many ongoing Relay connections to the Controller - cannot start a new one");
return NULL;
}
if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen,
&ctrl->ipaddr, DPP_TCP_PORT) < 0)
return NULL;
conn = os_zalloc(sizeof(*conn));
if (!conn)
return NULL;
conn->global = ctrl->global;
conn->relay = ctrl;
conn->msg_ctx = ctrl->msg_ctx;
conn->cb_ctx = ctrl->global->cb_ctx;
os_memcpy(conn->mac_addr, src, ETH_ALEN);
conn->freq = freq;
conn->sock = socket(AF_INET, SOCK_STREAM, 0);
if (conn->sock < 0)
goto fail;
wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s",
conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
strerror(errno));
goto fail;
}
if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) {
if (errno != EINPROGRESS) {
wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
strerror(errno));
goto fail;
}
/*
* Continue connecting in the background; eloop will call us
* once the connection is ready (or failed).
*/
}
if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
dpp_conn_tx_ready, conn, NULL) < 0)
goto fail;
conn->write_eloop = 1;
eloop_cancel_timeout(dpp_relay_conn_timeout, conn, NULL);
eloop_register_timeout(20, 0, dpp_relay_conn_timeout, conn, NULL);
dl_list_add(&ctrl->conn, &conn->list);
return conn;
fail:
dpp_connection_free(conn);
return NULL;
}
static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len)
{
struct wpabuf *msg;
msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len);
if (!msg)
return NULL;
wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len);
wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
wpabuf_put_data(msg, hdr, DPP_HDR_LEN);
wpabuf_put_data(msg, buf, len);
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
return msg;
}
static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr,
const u8 *buf, size_t len)
{
u8 type = hdr[DPP_HDR_LEN - 1];
wpa_printf(MSG_DEBUG,
"DPP: Continue already established Relay/Controller connection for this session");
wpabuf_free(conn->msg_out);
conn->msg_out_pos = 0;
conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
if (!conn->msg_out) {
dpp_connection_remove(conn);
return -1;
}
/* TODO: for proto ver 1, need to do remove connection based on GAS Resp
* TX status */
if (type == DPP_PA_CONFIGURATION_RESULT)
conn->on_tcp_tx_complete_remove = 1;
dpp_tcp_send(conn);
return 0;
}
int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
const u8 *buf, size_t len, unsigned int freq,
const u8 *i_bootstrap, const u8 *r_bootstrap,
void *cb_ctx)
{
struct dpp_relay_controller *ctrl;
struct dpp_connection *conn;
u8 type = hdr[DPP_HDR_LEN - 1];
/* Check if there is an already started session for this peer and if so,
* continue that session (send this over TCP) and return 0.
*/
if (type != DPP_PA_PEER_DISCOVERY_REQ &&
type != DPP_PA_PEER_DISCOVERY_RESP &&
type != DPP_PA_PRESENCE_ANNOUNCEMENT &&
type != DPP_PA_RECONFIG_ANNOUNCEMENT) {
dl_list_for_each(ctrl, &dpp->controllers,
struct dpp_relay_controller, list) {
dl_list_for_each(conn, &ctrl->conn,
struct dpp_connection, list) {
if (os_memcmp(src, conn->mac_addr,
ETH_ALEN) == 0)
return dpp_relay_tx(conn, hdr, buf, len);
}
}
}
if (type == DPP_PA_PRESENCE_ANNOUNCEMENT ||
type == DPP_PA_RECONFIG_ANNOUNCEMENT) {
/* TODO: Could send this to all configured Controllers. For now,
* only the first Controller is supported. */
ctrl = dpp_relay_controller_get_ctx(dpp, cb_ctx);
} else {
if (!r_bootstrap)
return -1;
ctrl = dpp_relay_controller_get(dpp, r_bootstrap);
}
if (!ctrl)
return -1;
wpa_printf(MSG_DEBUG,
"DPP: Authentication Request for a configured Controller");
conn = dpp_relay_new_conn(ctrl, src, freq);
if (!conn)
return -1;
conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
if (!conn->msg_out) {
dpp_connection_remove(conn);
return -1;
}
/* Message will be sent in dpp_conn_tx_ready() */
return 0;
}
int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
size_t data_len)
{
struct dpp_relay_controller *ctrl;
struct dpp_connection *conn, *found = NULL;
struct wpabuf *msg;
/* Check if there is a successfully completed authentication for this
* and if so, continue that session (send this over TCP) and return 0.
*/
dl_list_for_each(ctrl, &dpp->controllers,
struct dpp_relay_controller, list) {
if (found)
break;
dl_list_for_each(conn, &ctrl->conn,
struct dpp_connection, list) {
if (os_memcmp(src, conn->mac_addr,
ETH_ALEN) == 0) {
found = conn;
break;
}
}
}
if (!found)
return -1;
msg = wpabuf_alloc(4 + 1 + data_len);
if (!msg)
return -1;
wpabuf_put_be32(msg, 1 + data_len);
wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ);
wpabuf_put_data(msg, data, data_len);
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
wpabuf_free(conn->msg_out);
conn->msg_out_pos = 0;
conn->msg_out = msg;
dpp_tcp_send(conn);
return 0;
}
static void dpp_controller_free(struct dpp_controller *ctrl)
{
struct dpp_connection *conn, *tmp;
if (!ctrl)
return;
dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
list)
dpp_connection_remove(conn);
if (ctrl->sock >= 0) {
close(ctrl->sock);
eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ);
}
os_free(ctrl->configurator_params);
os_free(ctrl);
}
static int dpp_controller_rx_auth_req(struct dpp_connection *conn,
const u8 *hdr, const u8 *buf, size_t len)
{
const u8 *r_bootstrap, *i_bootstrap;
u16 r_bootstrap_len, i_bootstrap_len;
struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
if (!conn->ctrl)
return 0;
wpa_printf(MSG_DEBUG, "DPP: Authentication Request");
r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
wpa_printf(MSG_INFO,
"Missing or invalid required Responder Bootstrapping Key Hash attribute");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
r_bootstrap, r_bootstrap_len);
i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
&i_bootstrap_len);
if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
wpa_printf(MSG_INFO,
"Missing or invalid required Initiator Bootstrapping Key Hash attribute");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
i_bootstrap, i_bootstrap_len);
/* Try to find own and peer bootstrapping key matches based on the
* received hash values */
dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap,
&own_bi, &peer_bi);
if (!own_bi) {
wpa_printf(MSG_INFO,
"No matching own bootstrapping key found - ignore message");
return -1;
}
if (conn->auth) {
wpa_printf(MSG_INFO,
"Already in DPP authentication exchange - ignore new one");
return 0;
}
conn->auth = dpp_auth_req_rx(conn->ctrl->global, conn->msg_ctx,
conn->ctrl->allowed_roles,
conn->ctrl->qr_mutual,
peer_bi, own_bi, -1, hdr, buf, len);
if (!conn->auth) {
wpa_printf(MSG_DEBUG, "DPP: No response generated");
return -1;
}
if (dpp_set_configurator(conn->auth,
- conn->ctrl->configurator_params) < 0) {
- dpp_connection_remove(conn);
+ conn->ctrl->configurator_params) < 0)
return -1;
- }
return dpp_tcp_send_msg(conn, conn->auth->resp_msg);
}
static int dpp_controller_rx_auth_resp(struct dpp_connection *conn,
const u8 *hdr, const u8 *buf, size_t len)
{
struct dpp_authentication *auth = conn->auth;
struct wpabuf *msg;
int res;
if (!auth)
return -1;
wpa_printf(MSG_DEBUG, "DPP: Authentication Response");
msg = dpp_auth_resp_rx(auth, hdr, buf, len);
if (!msg) {
if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
wpa_printf(MSG_DEBUG,
"DPP: Start wait for full response");
return 0;
}
wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
- dpp_connection_remove(conn);
return -1;
}
conn->on_tcp_tx_complete_auth_ok = 1;
res = dpp_tcp_send_msg(conn, msg);
wpabuf_free(msg);
return res;
}
static int dpp_controller_rx_auth_conf(struct dpp_connection *conn,
const u8 *hdr, const u8 *buf, size_t len)
{
struct dpp_authentication *auth = conn->auth;
wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation");
if (!auth) {
wpa_printf(MSG_DEBUG,
"DPP: No DPP Authentication in progress - drop");
return -1;
}
if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
return -1;
}
dpp_controller_auth_success(conn, 0);
return 0;
}
void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
void *timeout_ctx)
{
struct dpp_connection *conn = eloop_ctx;
if (!conn->auth->waiting_conf_result)
return;
wpa_printf(MSG_DEBUG,
"DPP: Timeout while waiting for Connection Status Result");
wpa_msg(conn->msg_ctx, MSG_INFO,
DPP_EVENT_CONN_STATUS_RESULT "timeout");
dpp_connection_remove(conn);
}
static int dpp_controller_rx_conf_result(struct dpp_connection *conn,
const u8 *hdr, const u8 *buf,
size_t len)
{
struct dpp_authentication *auth = conn->auth;
enum dpp_status_error status;
void *msg_ctx = conn->msg_ctx;
if (!conn->ctrl && (!auth || !auth->configurator))
return 0;
wpa_printf(MSG_DEBUG, "DPP: Configuration Result");
if (!auth || !auth->waiting_conf_result) {
wpa_printf(MSG_DEBUG,
"DPP: No DPP Configuration waiting for result - drop");
return -1;
}
status = dpp_conf_result_rx(auth, hdr, buf, len);
if (status == DPP_STATUS_OK && auth->send_conn_status) {
wpa_msg(msg_ctx, MSG_INFO,
DPP_EVENT_CONF_SENT "wait_conn_status=1");
wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
eloop_cancel_timeout(
dpp_controller_conn_status_result_wait_timeout,
conn, NULL);
eloop_register_timeout(
16, 0, dpp_controller_conn_status_result_wait_timeout,
conn, NULL);
return 0;
}
if (status == DPP_STATUS_OK)
wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
else
wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
return -1; /* to remove the completed connection */
}
static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn,
const u8 *hdr, const u8 *buf,
size_t len)
{
struct dpp_authentication *auth = conn->auth;
enum dpp_status_error status;
u8 ssid[SSID_MAX_LEN];
size_t ssid_len = 0;
char *channel_list = NULL;
if (!conn->ctrl)
return 0;
wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
if (!auth || !auth->waiting_conn_status_result) {
wpa_printf(MSG_DEBUG,
"DPP: No DPP Configuration waiting for connection status result - drop");
return -1;
}
status = dpp_conn_status_result_rx(auth, hdr, buf, len,
ssid, &ssid_len, &channel_list);
wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT
"result=%d ssid=%s channel_list=%s",
status, wpa_ssid_txt(ssid, ssid_len),
channel_list ? channel_list : "N/A");
os_free(channel_list);
return -1; /* to remove the completed connection */
}
static int dpp_controller_rx_presence_announcement(struct dpp_connection *conn,
const u8 *hdr, const u8 *buf,
size_t len)
{
const u8 *r_bootstrap;
u16 r_bootstrap_len;
struct dpp_bootstrap_info *peer_bi;
struct dpp_authentication *auth;
struct dpp_global *dpp = conn->ctrl->global;
if (conn->auth) {
wpa_printf(MSG_DEBUG,
"DPP: Ignore Presence Announcement during ongoing Authentication");
return -1;
}
wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
&r_bootstrap_len);
if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing or invalid required Responder Bootstrapping Key Hash attribute");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
r_bootstrap, r_bootstrap_len);
peer_bi = dpp_bootstrap_find_chirp(dpp, r_bootstrap);
if (!peer_bi) {
wpa_printf(MSG_DEBUG,
"DPP: No matching bootstrapping information found");
return -1;
}
auth = dpp_auth_init(dpp, conn->msg_ctx, peer_bi, NULL,
DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
if (!auth)
return -1;
if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
dpp_auth_deinit(auth);
- dpp_connection_remove(conn);
return -1;
}
conn->auth = auth;
return dpp_tcp_send_msg(conn, conn->auth->req_msg);
}
static int dpp_controller_rx_reconfig_announcement(struct dpp_connection *conn,
const u8 *hdr, const u8 *buf,
size_t len)
{
const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
struct dpp_configurator *conf;
struct dpp_global *dpp = conn->ctrl->global;
struct dpp_authentication *auth;
u16 group;
if (conn->auth) {
wpa_printf(MSG_DEBUG,
"DPP: Ignore Reconfig Announcement during ongoing Authentication");
return -1;
}
wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement");
csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
&csign_hash_len);
if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing or invalid required Configurator C-sign key Hash attribute");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
csign_hash, csign_hash_len);
conf = dpp_configurator_find_kid(dpp, csign_hash);
if (!conf) {
wpa_printf(MSG_DEBUG,
"DPP: No matching Configurator information found");
return -1;
}
fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
&fcgroup_len);
if (!fcgroup || fcgroup_len != 2) {
wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Missing or invalid required Finite Cyclic Group attribute");
return -1;
}
group = WPA_GET_LE16(fcgroup);
wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
auth = dpp_reconfig_init(dpp, conn->msg_ctx, conf, 0, group,
a_nonce, a_nonce_len, e_id, e_id_len);
if (!auth)
return -1;
if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
dpp_auth_deinit(auth);
return -1;
}
conn->auth = auth;
return dpp_tcp_send_msg(conn, auth->reconfig_req_msg);
}
static int dpp_controller_rx_reconfig_auth_resp(struct dpp_connection *conn,
const u8 *hdr, const u8 *buf,
size_t len)
{
struct dpp_authentication *auth = conn->auth;
struct wpabuf *conf;
int res;
wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response");
if (!auth || !auth->reconfig || !auth->configurator) {
wpa_printf(MSG_DEBUG,
"DPP: No DPP Reconfig Authentication in progress - drop");
return -1;
}
conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
if (!conf)
return -1;
res = dpp_tcp_send_msg(conn, conf);
wpabuf_free(conf);
return res;
}
static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg,
size_t len)
{
const u8 *pos, *end;
u8 type;
wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP");
pos = msg;
end = msg + len;
if (end - pos < DPP_HDR_LEN ||
WPA_GET_BE24(pos) != OUI_WFA ||
pos[3] != DPP_OUI_TYPE) {
wpa_printf(MSG_DEBUG, "DPP: Unrecognized header");
return -1;
}
if (pos[4] != 1) {
wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u",
pos[4]);
return -1;
}
type = pos[5];
wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type);
pos += DPP_HDR_LEN;
wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes",
pos, end - pos);
if (dpp_check_attrs(pos, end - pos) < 0)
return -1;
if (conn->relay) {
wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr,
conn->freq, msg, len);
return 0;
}
switch (type) {
case DPP_PA_AUTHENTICATION_REQ:
return dpp_controller_rx_auth_req(conn, msg, pos, end - pos);
case DPP_PA_AUTHENTICATION_RESP:
return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos);
case DPP_PA_AUTHENTICATION_CONF:
return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos);
case DPP_PA_CONFIGURATION_RESULT:
return dpp_controller_rx_conf_result(conn, msg, pos, end - pos);
case DPP_PA_CONNECTION_STATUS_RESULT:
return dpp_controller_rx_conn_status_result(conn, msg, pos,
end - pos);
case DPP_PA_PRESENCE_ANNOUNCEMENT:
return dpp_controller_rx_presence_announcement(conn, msg, pos,
end - pos);
case DPP_PA_RECONFIG_ANNOUNCEMENT:
return dpp_controller_rx_reconfig_announcement(conn, msg, pos,
end - pos);
case DPP_PA_RECONFIG_AUTH_RESP:
return dpp_controller_rx_reconfig_auth_resp(conn, msg, pos,
end - pos);
default:
/* TODO: missing messages types */
wpa_printf(MSG_DEBUG,
"DPP: Unsupported frame subtype %d", type);
return -1;
}
}
static int dpp_tcp_send_comeback_delay(struct dpp_connection *conn, u8 action)
{
struct wpabuf *buf;
size_t len = 18;
if (action == WLAN_PA_GAS_COMEBACK_RESP)
len++;
buf = wpabuf_alloc(4 + len);
if (!buf)
return -1;
wpabuf_put_be32(buf, len);
wpabuf_put_u8(buf, action);
wpabuf_put_u8(buf, conn->gas_dialog_token);
wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
if (action == WLAN_PA_GAS_COMEBACK_RESP)
wpabuf_put_u8(buf, 0);
wpabuf_put_le16(buf, 500); /* GAS Comeback Delay */
dpp_write_adv_proto(buf);
wpabuf_put_le16(buf, 0); /* Query Response Length */
/* Send Config Response over TCP */
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
wpabuf_free(conn->msg_out);
conn->msg_out_pos = 0;
conn->msg_out = buf;
dpp_tcp_send(conn);
return 0;
}
static int dpp_tcp_send_gas_resp(struct dpp_connection *conn, u8 action,
struct wpabuf *resp)
{
struct wpabuf *buf;
size_t len;
if (!resp)
return -1;
len = 18 + wpabuf_len(resp);
if (action == WLAN_PA_GAS_COMEBACK_RESP)
len++;
buf = wpabuf_alloc(4 + len);
if (!buf) {
wpabuf_free(resp);
return -1;
}
wpabuf_put_be32(buf, len);
wpabuf_put_u8(buf, action);
wpabuf_put_u8(buf, conn->gas_dialog_token);
wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
if (action == WLAN_PA_GAS_COMEBACK_RESP)
wpabuf_put_u8(buf, 0);
wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
dpp_write_adv_proto(buf);
dpp_write_gas_query(buf, resp);
wpabuf_free(resp);
/* Send Config Response over TCP; GAS fragmentation is taken care of by
* the Relay */
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
wpabuf_free(conn->msg_out);
conn->msg_out_pos = 0;
conn->msg_out = buf;
conn->on_tcp_tx_complete_gas_done = 1;
dpp_tcp_send(conn);
return 0;
}
static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
size_t len)
{
const u8 *pos, *end, *next;
const u8 *adv_proto;
u16 slen;
struct wpabuf *resp;
struct dpp_authentication *auth = conn->auth;
if (len < 1 + 2)
return -1;
wpa_printf(MSG_DEBUG,
"DPP: Received DPP Configuration Request over TCP");
if (!auth || (!conn->ctrl && !auth->configurator) ||
(!auth->auth_success && !auth->reconfig_success)) {
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
return -1;
}
pos = msg;
end = msg + len;
conn->gas_dialog_token = *pos++;
adv_proto = pos++;
slen = *pos++;
if (*adv_proto != WLAN_EID_ADV_PROTO ||
slen > end - pos || slen < 2)
return -1;
next = pos + slen;
pos++; /* skip QueryRespLenLimit and PAME-BI */
if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
return -1;
pos = next;
/* Query Request */
if (end - pos < 2)
return -1;
slen = WPA_GET_LE16(pos);
pos += 2;
if (slen > end - pos)
return -1;
resp = dpp_conf_req_rx(auth, pos, slen);
if (!resp && auth->waiting_cert) {
wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
conn->gas_comeback_in_progress = 1;
return dpp_tcp_send_comeback_delay(conn,
WLAN_PA_GAS_INITIAL_RESP);
}
return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_INITIAL_RESP, resp);
}
static int dpp_controller_rx_gas_comeback_req(struct dpp_connection *conn,
const u8 *msg, size_t len)
{
u8 dialog_token;
struct dpp_authentication *auth = conn->auth;
struct wpabuf *resp;
if (len < 1)
return -1;
wpa_printf(MSG_DEBUG,
"DPP: Received DPP Configuration Request over TCP (comeback)");
if (!auth || (!conn->ctrl && !auth->configurator) ||
(!auth->auth_success && !auth->reconfig_success) ||
!conn->gas_comeback_in_progress) {
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
return -1;
}
dialog_token = msg[0];
if (dialog_token != conn->gas_dialog_token) {
wpa_printf(MSG_DEBUG, "DPP: Dialog token mismatch (%u != %u)",
dialog_token, conn->gas_dialog_token);
return -1;
}
if (!auth->conf_resp_tcp) {
wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
return dpp_tcp_send_comeback_delay(conn,
WLAN_PA_GAS_COMEBACK_RESP);
}
wpa_printf(MSG_DEBUG,
"DPP: Configuration response is ready to be sent out");
resp = auth->conf_resp_tcp;
auth->conf_resp_tcp = NULL;
return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_COMEBACK_RESP, resp);
}
static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx)
{
struct dpp_connection *conn = eloop_ctx;
struct dpp_authentication *auth = conn->auth;
if (!auth || !auth->csrattrs)
return;
wpa_printf(MSG_DEBUG, "DPP: Build CSR");
wpabuf_free(auth->csr);
/* TODO: Additional information needed for CSR based on csrAttrs */
auth->csr = dpp_build_csr(auth, conn->name ? conn->name : "Test");
if (!auth->csr) {
dpp_connection_remove(conn);
return;
}
dpp_controller_start_gas_client(conn);
}
static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp)
{
struct dpp_authentication *auth = conn->auth;
int res;
struct wpabuf *msg;
enum dpp_status_error status;
wpa_printf(MSG_DEBUG,
"DPP: Configuration Response for local stack from TCP");
if (auth)
res = dpp_conf_resp_rx(auth, resp);
else
res = -1;
wpabuf_free(resp);
if (res == -2) {
wpa_printf(MSG_DEBUG, "DPP: CSR needed");
eloop_register_timeout(0, 0, dpp_tcp_build_csr, conn, NULL);
return 0;
}
if (res < 0) {
wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
return -1;
}
if (conn->process_conf_obj)
res = conn->process_conf_obj(conn->cb_ctx, auth);
else
res = 0;
if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK)
return -1;
wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK;
msg = dpp_build_conf_result(auth, status);
if (!msg)
return -1;
conn->on_tcp_tx_complete_remove = 1;
res = dpp_tcp_send_msg(conn, msg);
wpabuf_free(msg);
/* This exchange will be terminated in the TX status handler */
return res;
}
static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx)
{
struct dpp_connection *conn = eloop_ctx;
struct dpp_authentication *auth = conn->auth;
struct wpabuf *msg;
if (!auth)
return;
wpa_printf(MSG_DEBUG, "DPP: Send GAS Comeback Request");
msg = wpabuf_alloc(4 + 2);
if (!msg)
return;
wpabuf_put_be32(msg, 2);
wpabuf_put_u8(msg, WLAN_PA_GAS_COMEBACK_REQ);
wpabuf_put_u8(msg, conn->gas_dialog_token);
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
wpabuf_free(conn->msg_out);
conn->msg_out_pos = 0;
conn->msg_out = msg;
dpp_tcp_send(conn);
}
static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
size_t len, bool comeback)
{
struct wpabuf *buf;
u8 dialog_token;
const u8 *pos, *end, *next, *adv_proto;
u16 status, slen, comeback_delay;
if (len < (size_t) (5 + 2 + (comeback ? 1 : 0)))
return -1;
wpa_printf(MSG_DEBUG,
"DPP: Received DPP Configuration Response over TCP");
pos = msg;
end = msg + len;
dialog_token = *pos++;
status = WPA_GET_LE16(pos);
if (status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status);
return -1;
}
pos += 2;
if (comeback)
pos++; /* ignore Fragment ID */
comeback_delay = WPA_GET_LE16(pos);
pos += 2;
adv_proto = pos++;
slen = *pos++;
if (*adv_proto != WLAN_EID_ADV_PROTO ||
slen > end - pos || slen < 2)
return -1;
next = pos + slen;
pos++; /* skip QueryRespLenLimit and PAME-BI */
if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
return -1;
pos = next;
/* Query Response */
if (end - pos < 2)
return -1;
slen = WPA_GET_LE16(pos);
pos += 2;
if (slen > end - pos)
return -1;
if (comeback_delay) {
unsigned int secs, usecs;
conn->gas_dialog_token = dialog_token;
secs = (comeback_delay * 1024) / 1000000;
usecs = comeback_delay * 1024 - secs * 1000000;
wpa_printf(MSG_DEBUG, "DPP: Comeback delay: %u",
comeback_delay);
eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
eloop_register_timeout(secs, usecs, dpp_tcp_gas_query_comeback,
conn, NULL);
return 0;
}
buf = wpabuf_alloc(slen);
if (!buf)
return -1;
wpabuf_put_data(buf, pos, slen);
if (!conn->relay &&
(!conn->ctrl || (conn->ctrl->allowed_roles & DPP_CAPAB_ENROLLEE)))
return dpp_tcp_rx_gas_resp(conn, buf);
if (!conn->relay) {
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
wpabuf_free(buf);
return -1;
}
wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr,
dialog_token, 0, buf);
return 0;
}
static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx)
{
struct dpp_connection *conn = eloop_ctx;
int res;
const u8 *pos;
wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)",
sd);
if (conn->msg_len_octets < 4) {
u32 msglen;
res = recv(sd, &conn->msg_len[conn->msg_len_octets],
4 - conn->msg_len_octets, 0);
if (res < 0) {
wpa_printf(MSG_DEBUG, "DPP: recv failed: %s",
strerror(errno));
dpp_connection_remove(conn);
return;
}
if (res == 0) {
wpa_printf(MSG_DEBUG,
"DPP: No more data available over TCP");
dpp_connection_remove(conn);
return;
}
wpa_printf(MSG_DEBUG,
"DPP: Received %d/%d octet(s) of message length field",
res, (int) (4 - conn->msg_len_octets));
conn->msg_len_octets += res;
if (conn->msg_len_octets < 4) {
wpa_printf(MSG_DEBUG,
"DPP: Need %d more octets of message length field",
(int) (4 - conn->msg_len_octets));
return;
}
msglen = WPA_GET_BE32(conn->msg_len);
wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen);
if (msglen > 65535) {
wpa_printf(MSG_INFO, "DPP: Unexpectedly long message");
dpp_connection_remove(conn);
return;
}
wpabuf_free(conn->msg);
conn->msg = wpabuf_alloc(msglen);
}
if (!conn->msg) {
wpa_printf(MSG_DEBUG,
"DPP: No buffer available for receiving the message");
dpp_connection_remove(conn);
return;
}
wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload",
(unsigned int) wpabuf_tailroom(conn->msg));
res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0);
if (res < 0) {
wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno));
dpp_connection_remove(conn);
return;
}
if (res == 0) {
wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP");
dpp_connection_remove(conn);
return;
}
wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res);
wpabuf_put(conn->msg, res);
if (wpabuf_tailroom(conn->msg) > 0) {
wpa_printf(MSG_DEBUG,
"DPP: Need %u more octets of message payload",
(unsigned int) wpabuf_tailroom(conn->msg));
return;
}
conn->msg_len_octets = 0;
wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg);
if (wpabuf_len(conn->msg) < 1) {
dpp_connection_remove(conn);
return;
}
pos = wpabuf_head(conn->msg);
switch (*pos) {
case WLAN_PA_VENDOR_SPECIFIC:
if (dpp_controller_rx_action(conn, pos + 1,
wpabuf_len(conn->msg) - 1) < 0)
dpp_connection_remove(conn);
break;
case WLAN_PA_GAS_INITIAL_REQ:
if (dpp_controller_rx_gas_req(conn, pos + 1,
wpabuf_len(conn->msg) - 1) < 0)
dpp_connection_remove(conn);
break;
case WLAN_PA_GAS_INITIAL_RESP:
case WLAN_PA_GAS_COMEBACK_RESP:
if (dpp_rx_gas_resp(conn, pos + 1,
wpabuf_len(conn->msg) - 1,
*pos == WLAN_PA_GAS_COMEBACK_RESP) < 0)
dpp_connection_remove(conn);
break;
case WLAN_PA_GAS_COMEBACK_REQ:
if (dpp_controller_rx_gas_comeback_req(
conn, pos + 1, wpabuf_len(conn->msg) - 1) < 0)
dpp_connection_remove(conn);
break;
default:
wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u",
*pos);
break;
}
}
static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
{
struct dpp_controller *ctrl = eloop_ctx;
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
int fd;
struct dpp_connection *conn;
wpa_printf(MSG_DEBUG, "DPP: New TCP connection");
fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len);
if (fd < 0) {
wpa_printf(MSG_DEBUG,
"DPP: Failed to accept new connection: %s",
strerror(errno));
return;
}
wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
conn = os_zalloc(sizeof(*conn));
if (!conn)
goto fail;
conn->global = ctrl->global;
conn->ctrl = ctrl;
conn->msg_ctx = ctrl->msg_ctx;
conn->cb_ctx = ctrl->cb_ctx;
conn->process_conf_obj = ctrl->process_conf_obj;
conn->sock = fd;
conn->netrole = ctrl->netrole;
if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
strerror(errno));
goto fail;
}
if (eloop_register_sock(conn->sock, EVENT_TYPE_READ,
dpp_controller_rx, conn, NULL) < 0)
goto fail;
conn->read_eloop = 1;
/* TODO: eloop timeout to expire connections that do not complete in
* reasonable time */
dl_list_add(&ctrl->conn, &conn->list);
return;
fail:
close(fd);
os_free(conn);
}
int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
const struct hostapd_ip_addr *addr, int port, const char *name,
enum dpp_netrole netrole, void *msg_ctx, void *cb_ctx,
int (*process_conf_obj)(void *ctx,
struct dpp_authentication *auth))
{
struct dpp_connection *conn;
struct sockaddr_storage saddr;
socklen_t addrlen;
const u8 *hdr, *pos, *end;
char txt[100];
wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d",
hostapd_ip_txt(addr, txt, sizeof(txt)), port);
if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen,
addr, port) < 0) {
dpp_auth_deinit(auth);
return -1;
}
conn = os_zalloc(sizeof(*conn));
if (!conn) {
dpp_auth_deinit(auth);
return -1;
}
conn->msg_ctx = msg_ctx;
conn->cb_ctx = cb_ctx;
conn->process_conf_obj = process_conf_obj;
conn->name = os_strdup(name ? name : "Test");
conn->netrole = netrole;
conn->global = dpp;
conn->auth = auth;
conn->sock = socket(AF_INET, SOCK_STREAM, 0);
if (conn->sock < 0)
goto fail;
if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
strerror(errno));
goto fail;
}
if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) {
if (errno != EINPROGRESS) {
wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
strerror(errno));
goto fail;
}
/*
* Continue connecting in the background; eloop will call us
* once the connection is ready (or failed).
*/
}
if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
dpp_conn_tx_ready, conn, NULL) < 0)
goto fail;
conn->write_eloop = 1;
hdr = wpabuf_head(auth->req_msg);
end = hdr + wpabuf_len(auth->req_msg);
hdr += 2; /* skip Category and Actiom */
pos = hdr + DPP_HDR_LEN;
conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos);
if (!conn->msg_out)
goto fail;
/* Message will be sent in dpp_conn_tx_ready() */
/* TODO: eloop timeout to clear a connection if it does not complete
* properly */
dl_list_add(&dpp->tcp_init, &conn->list);
return 0;
fail:
dpp_connection_free(conn);
return -1;
}
int dpp_controller_start(struct dpp_global *dpp,
struct dpp_controller_config *config)
{
struct dpp_controller *ctrl;
int on = 1;
struct sockaddr_in sin;
int port;
if (!dpp || dpp->controller)
return -1;
ctrl = os_zalloc(sizeof(*ctrl));
if (!ctrl)
return -1;
ctrl->global = dpp;
if (config->configurator_params)
ctrl->configurator_params =
os_strdup(config->configurator_params);
dl_list_init(&ctrl->conn);
ctrl->allowed_roles = config->allowed_roles;
ctrl->qr_mutual = config->qr_mutual;
ctrl->netrole = config->netrole;
ctrl->msg_ctx = config->msg_ctx;
ctrl->cb_ctx = config->cb_ctx;
ctrl->process_conf_obj = config->process_conf_obj;
ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
if (ctrl->sock < 0)
goto fail;
if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR,
&on, sizeof(on)) < 0) {
wpa_printf(MSG_DEBUG,
"DPP: setsockopt(SO_REUSEADDR) failed: %s",
strerror(errno));
/* try to continue anyway */
}
if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) {
wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
strerror(errno));
goto fail;
}
/* TODO: IPv6 */
os_memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT;
sin.sin_port = htons(port);
if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
wpa_printf(MSG_INFO,
"DPP: Failed to bind Controller TCP port: %s",
strerror(errno));
goto fail;
}
if (listen(ctrl->sock, 10 /* max backlog */) < 0 ||
fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 ||
eloop_register_sock(ctrl->sock, EVENT_TYPE_READ,
dpp_controller_tcp_cb, ctrl, NULL))
goto fail;
dpp->controller = ctrl;
wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port);
return 0;
fail:
dpp_controller_free(ctrl);
return -1;
}
void dpp_controller_stop(struct dpp_global *dpp)
{
if (dpp) {
dpp_controller_free(dpp->controller);
dpp->controller = NULL;
}
}
+void dpp_controller_stop_for_ctx(struct dpp_global *dpp, void *cb_ctx)
+{
+ if (dpp && dpp->controller && dpp->controller->cb_ctx == cb_ctx)
+ dpp_controller_stop(dpp);
+}
+
+
static bool dpp_tcp_peer_id_match(struct dpp_authentication *auth,
unsigned int id)
{
return auth &&
((auth->peer_bi && auth->peer_bi->id == id) ||
(auth->tmp_peer_bi && auth->tmp_peer_bi->id == id));
}
static struct dpp_authentication * dpp_tcp_get_auth(struct dpp_global *dpp,
unsigned int id)
{
struct dpp_connection *conn;
dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) {
if (dpp_tcp_peer_id_match(conn->auth, id))
return conn->auth;
}
return NULL;
}
struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp,
unsigned int id)
{
struct dpp_controller *ctrl = dpp->controller;
struct dpp_connection *conn;
if (!ctrl)
return dpp_tcp_get_auth(dpp, id);
dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
if (dpp_tcp_peer_id_match(conn->auth, id))
return conn->auth;
}
return dpp_tcp_get_auth(dpp, id);
}
void dpp_controller_new_qr_code(struct dpp_global *dpp,
struct dpp_bootstrap_info *bi)
{
struct dpp_controller *ctrl = dpp->controller;
struct dpp_connection *conn;
if (!ctrl)
return;
dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
struct dpp_authentication *auth = conn->auth;
if (!auth->response_pending ||
dpp_notify_new_qr_code(auth, bi) != 1)
continue;
wpa_printf(MSG_DEBUG,
"DPP: Sending out pending authentication response");
dpp_tcp_send_msg(conn, conn->auth->resp_msg);
}
}
void dpp_tcp_init_flush(struct dpp_global *dpp)
{
struct dpp_connection *conn, *tmp;
dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection,
list)
dpp_connection_remove(conn);
}
static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
{
struct dpp_connection *conn, *tmp;
dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
list)
dpp_connection_remove(conn);
os_free(ctrl);
}
void dpp_relay_flush_controllers(struct dpp_global *dpp)
{
struct dpp_relay_controller *ctrl, *tmp;
if (!dpp)
return;
dl_list_for_each_safe(ctrl, tmp, &dpp->controllers,
struct dpp_relay_controller, list) {
dl_list_del(&ctrl->list);
dpp_relay_controller_free(ctrl);
}
}
#endif /* CONFIG_DPP2 */
diff --git a/contrib/wpa/src/common/hw_features_common.c b/contrib/wpa/src/common/hw_features_common.c
index ad2aebfef5b5..e7ac3b2aac6b 100644
--- a/contrib/wpa/src/common/hw_features_common.c
+++ b/contrib/wpa/src/common/hw_features_common.c
@@ -1,812 +1,823 @@
/*
* Common hostapd/wpa_supplicant HW features
* Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
* Copyright (c) 2015, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "defs.h"
#include "ieee802_11_defs.h"
#include "ieee802_11_common.h"
#include "hw_features_common.h"
struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
int chan, int *freq)
{
int i;
if (freq)
*freq = 0;
if (!mode)
return NULL;
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *ch = &mode->channels[i];
if (ch->chan == chan) {
if (freq)
*freq = ch->freq;
return ch;
}
}
return NULL;
}
struct hostapd_channel_data *
hw_mode_get_channel(struct hostapd_hw_modes *mode, int freq, int *chan)
{
int i;
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *ch = &mode->channels[i];
if (ch->freq == freq) {
if (chan)
*chan = ch->chan;
return ch;
}
}
return NULL;
}
struct hostapd_channel_data *
hw_get_channel_freq(enum hostapd_hw_mode mode, int freq, int *chan,
struct hostapd_hw_modes *hw_features, int num_hw_features)
{
struct hostapd_channel_data *chan_data;
int i;
if (chan)
*chan = 0;
if (!hw_features)
return NULL;
for (i = 0; i < num_hw_features; i++) {
struct hostapd_hw_modes *curr_mode = &hw_features[i];
if (curr_mode->mode != mode)
continue;
chan_data = hw_mode_get_channel(curr_mode, freq, chan);
if (chan_data)
return chan_data;
}
return NULL;
}
int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
{
int freq;
hw_get_channel_chan(mode, chan, &freq);
return freq;
}
int hw_get_chan(enum hostapd_hw_mode mode, int freq,
struct hostapd_hw_modes *hw_features, int num_hw_features)
{
int chan;
hw_get_channel_freq(mode, freq, &chan, hw_features, num_hw_features);
return chan;
}
int allowed_ht40_channel_pair(enum hostapd_hw_mode mode,
struct hostapd_channel_data *p_chan,
struct hostapd_channel_data *s_chan)
{
int ok, first;
int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
- 149, 157, 165, 184, 192 };
+ 149, 157, 165, 173, 184, 192 };
size_t k;
int ht40_plus, pri_chan, sec_chan;
if (!p_chan || !s_chan)
return 0;
pri_chan = p_chan->chan;
sec_chan = s_chan->chan;
ht40_plus = pri_chan < sec_chan;
if (pri_chan == sec_chan || !sec_chan) {
if (chan_pri_allowed(p_chan))
return 1; /* HT40 not used */
wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary",
pri_chan);
return 0;
}
wpa_printf(MSG_DEBUG,
"HT40: control channel: %d (%d MHz), secondary channel: %d (%d MHz)",
pri_chan, p_chan->freq, sec_chan, s_chan->freq);
/* Verify that HT40 secondary channel is an allowed 20 MHz
* channel */
if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) ||
(ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) ||
(!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) {
wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
sec_chan);
return 0;
}
/*
* Verify that HT40 primary,secondary channel pair is allowed per
* IEEE 802.11n Annex J. This is only needed for 5 GHz band since
* 2.4 GHz rules allow all cases where the secondary channel fits into
* the list of allowed channels (already checked above).
*/
if (mode != HOSTAPD_MODE_IEEE80211A)
return 1;
first = pri_chan < sec_chan ? pri_chan : sec_chan;
ok = 0;
for (k = 0; k < ARRAY_SIZE(allowed); k++) {
if (first == allowed[k]) {
ok = 1;
break;
}
}
if (!ok) {
wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
pri_chan, sec_chan);
return 0;
}
return 1;
}
void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan)
{
struct ieee80211_ht_operation *oper;
struct ieee802_11_elems elems;
*pri_chan = *sec_chan = 0;
ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
if (elems.ht_operation) {
oper = (struct ieee80211_ht_operation *) elems.ht_operation;
*pri_chan = oper->primary_chan;
if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
int sec = oper->ht_param &
HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
*sec_chan = *pri_chan + 4;
else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
*sec_chan = *pri_chan - 4;
}
}
}
int check_40mhz_5g(struct wpa_scan_results *scan_res,
struct hostapd_channel_data *pri_chan,
struct hostapd_channel_data *sec_chan)
{
int pri_bss, sec_bss;
int bss_pri_chan, bss_sec_chan;
size_t i;
int match;
if (!scan_res || !pri_chan || !sec_chan ||
pri_chan->freq == sec_chan->freq)
return 0;
/*
* Switch PRI/SEC channels if Beacons were detected on selected SEC
* channel, but not on selected PRI channel.
*/
pri_bss = sec_bss = 0;
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
if (bss->freq == pri_chan->freq)
pri_bss++;
else if (bss->freq == sec_chan->freq)
sec_bss++;
}
if (sec_bss && !pri_bss) {
wpa_printf(MSG_INFO,
"Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes");
return 2;
}
/*
* Match PRI/SEC channel with any existing HT40 BSS on the same
* channels that we are about to use (if already mixed order in
* existing BSSes, use own preference).
*/
match = 0;
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
if (pri_chan->chan == bss_pri_chan &&
sec_chan->chan == bss_sec_chan) {
match = 1;
break;
}
}
if (!match) {
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
if (pri_chan->chan == bss_sec_chan &&
sec_chan->chan == bss_pri_chan) {
wpa_printf(MSG_INFO, "Switch own primary and "
"secondary channel due to BSS "
"overlap with " MACSTR,
MAC2STR(bss->bssid));
return 2;
}
}
}
return 1;
}
static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start,
int end)
{
struct ieee802_11_elems elems;
struct ieee80211_ht_operation *oper;
if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
return 0;
ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
if (!elems.ht_capabilities) {
wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
return 1;
}
if (elems.ht_operation) {
oper = (struct ieee80211_ht_operation *) elems.ht_operation;
if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
return 0;
wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
return 1;
}
return 0;
}
/*
* Returns:
* 0: no impact
* 1: overlapping BSS
* 2: overlapping BSS with 40 MHz intolerant advertisement
*/
int check_bss_coex_40mhz(struct wpa_scan_res *bss, int pri_freq, int sec_freq)
{
int affected_start, affected_end;
struct ieee802_11_elems elems;
int pri_chan, sec_chan;
int pri = bss->freq;
int sec = pri;
if (pri_freq == sec_freq)
return 1;
affected_start = (pri_freq + sec_freq) / 2 - 25;
affected_end = (pri_freq + sec_freq) / 2 + 25;
/* Check for overlapping 20 MHz BSS */
if (check_20mhz_bss(bss, pri_freq, affected_start, affected_end)) {
wpa_printf(MSG_DEBUG, "Overlapping 20 MHz BSS is found");
return 1;
}
get_pri_sec_chan(bss, &pri_chan, &sec_chan);
if (sec_chan) {
if (sec_chan < pri_chan)
sec = pri - 20;
else
sec = pri + 20;
}
if ((pri < affected_start || pri > affected_end) &&
(sec < affected_start || sec > affected_end))
return 0; /* not within affected channel range */
wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
" freq=%d pri=%d sec=%d",
MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
if (sec_chan) {
if (pri_freq != pri || sec_freq != sec) {
wpa_printf(MSG_DEBUG,
"40 MHz pri/sec mismatch with BSS "
MACSTR
" <%d,%d> (chan=%d%c) vs. <%d,%d>",
MAC2STR(bss->bssid),
pri, sec, pri_chan,
sec > pri ? '+' : '-',
pri_freq, sec_freq);
return 1;
}
}
ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
if (elems.ht_capabilities) {
struct ieee80211_ht_capabilities *ht_cap =
(struct ieee80211_ht_capabilities *)
elems.ht_capabilities;
if (le_to_host16(ht_cap->ht_capabilities_info) &
HT_CAP_INFO_40MHZ_INTOLERANT) {
wpa_printf(MSG_DEBUG,
"40 MHz Intolerant is set on channel %d in BSS "
MACSTR, pri, MAC2STR(bss->bssid));
return 2;
}
}
return 0;
}
int check_40mhz_2g4(struct hostapd_hw_modes *mode,
struct wpa_scan_results *scan_res, int pri_chan,
int sec_chan)
{
int pri_freq, sec_freq;
size_t i;
if (!mode || !scan_res || !pri_chan || !sec_chan ||
pri_chan == sec_chan)
return 0;
pri_freq = hw_get_freq(mode, pri_chan);
sec_freq = hw_get_freq(mode, sec_chan);
wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
(pri_freq + sec_freq) / 2 - 25,
(pri_freq + sec_freq) / 2 + 25);
for (i = 0; i < scan_res->num; i++) {
if (check_bss_coex_40mhz(scan_res->res[i], pri_freq, sec_freq))
return 0;
}
return 1;
}
int hostapd_set_freq_params(struct hostapd_freq_params *data,
enum hostapd_hw_mode mode,
int freq, int channel, int enable_edmg,
u8 edmg_channel, int ht_enabled,
int vht_enabled, int he_enabled,
int sec_channel_offset,
int oper_chwidth, int center_segment0,
int center_segment1, u32 vht_caps,
struct he_capabilities *he_cap)
{
- if (!he_cap)
+ if (!he_cap || !he_cap->he_supported)
he_enabled = 0;
os_memset(data, 0, sizeof(*data));
data->mode = mode;
data->freq = freq;
data->channel = channel;
data->ht_enabled = ht_enabled;
data->vht_enabled = vht_enabled;
data->he_enabled = he_enabled;
data->sec_channel_offset = sec_channel_offset;
data->center_freq1 = freq + sec_channel_offset * 10;
data->center_freq2 = 0;
- data->bandwidth = sec_channel_offset ? 40 : 20;
+ if (oper_chwidth == CHANWIDTH_80MHZ)
+ data->bandwidth = 80;
+ else if (oper_chwidth == CHANWIDTH_160MHZ ||
+ oper_chwidth == CHANWIDTH_80P80MHZ)
+ data->bandwidth = 160;
+ else if (sec_channel_offset)
+ data->bandwidth = 40;
+ else
+ data->bandwidth = 20;
+
hostapd_encode_edmg_chan(enable_edmg, edmg_channel, channel,
&data->edmg);
if (is_6ghz_freq(freq)) {
if (!data->he_enabled) {
wpa_printf(MSG_ERROR,
"Can't set 6 GHz mode - HE isn't enabled");
return -1;
}
if (center_idx_to_bw_6ghz(channel) < 0) {
wpa_printf(MSG_ERROR,
"Invalid control channel for 6 GHz band");
return -1;
}
if (!center_segment0) {
if (center_segment1) {
wpa_printf(MSG_ERROR,
"Segment 0 center frequency isn't set");
return -1;
}
-
- data->center_freq1 = data->freq;
- data->bandwidth = 20;
+ if (!sec_channel_offset)
+ data->center_freq1 = data->freq;
} else {
int freq1, freq2 = 0;
int bw = center_idx_to_bw_6ghz(center_segment0);
if (bw < 0) {
wpa_printf(MSG_ERROR,
"Invalid center frequency index for 6 GHz");
return -1;
}
freq1 = ieee80211_chan_to_freq(NULL, 131,
center_segment0);
if (freq1 < 0) {
wpa_printf(MSG_ERROR,
"Invalid segment 0 center frequency for 6 GHz");
return -1;
}
if (center_segment1) {
if (center_idx_to_bw_6ghz(center_segment1) != 2 ||
bw != 2) {
wpa_printf(MSG_ERROR,
"6 GHz 80+80 MHz configuration doesn't use valid 80 MHz channels");
return -1;
}
freq2 = ieee80211_chan_to_freq(NULL, 131,
center_segment1);
if (freq2 < 0) {
wpa_printf(MSG_ERROR,
"Invalid segment 1 center frequency for UHB");
return -1;
}
}
data->bandwidth = (1 << (u8) bw) * 20;
data->center_freq1 = freq1;
data->center_freq2 = freq2;
}
data->ht_enabled = 0;
data->vht_enabled = 0;
return 0;
}
if (data->he_enabled) switch (oper_chwidth) {
case CHANWIDTH_USE_HT:
- if (mode == HOSTAPD_MODE_IEEE80211G && sec_channel_offset) {
+ if (sec_channel_offset == 0)
+ break;
+
+ if (mode == HOSTAPD_MODE_IEEE80211G) {
if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G)) {
wpa_printf(MSG_ERROR,
"40 MHz channel width is not supported in 2.4 GHz");
return -1;
}
break;
}
/* fall through */
case CHANWIDTH_80MHZ:
if (mode == HOSTAPD_MODE_IEEE80211A) {
if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
wpa_printf(MSG_ERROR,
"40/80 MHz channel width is not supported in 5/6 GHz");
return -1;
}
}
break;
case CHANWIDTH_80P80MHZ:
if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) {
wpa_printf(MSG_ERROR,
"80+80 MHz channel width is not supported in 5/6 GHz");
return -1;
}
break;
case CHANWIDTH_160MHZ:
if (!(he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)) {
wpa_printf(MSG_ERROR,
"160 MHz channel width is not supported in 5 / 6GHz");
return -1;
}
break;
} else if (data->vht_enabled) switch (oper_chwidth) {
case CHANWIDTH_USE_HT:
break;
case CHANWIDTH_80P80MHZ:
if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
wpa_printf(MSG_ERROR,
"80+80 channel width is not supported!");
return -1;
}
/* fall through */
case CHANWIDTH_80MHZ:
break;
case CHANWIDTH_160MHZ:
if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
wpa_printf(MSG_ERROR,
"160 MHz channel width is not supported!");
return -1;
}
break;
}
if (data->he_enabled || data->vht_enabled) switch (oper_chwidth) {
case CHANWIDTH_USE_HT:
if (center_segment1 ||
(center_segment0 != 0 &&
5000 + center_segment0 * 5 != data->center_freq1 &&
2407 + center_segment0 * 5 != data->center_freq1)) {
wpa_printf(MSG_ERROR,
"20/40 MHz: center segment 0 (=%d) and center freq 1 (=%d) not in sync",
center_segment0, data->center_freq1);
return -1;
}
break;
case CHANWIDTH_80P80MHZ:
if (center_segment1 == center_segment0 + 4 ||
center_segment1 == center_segment0 - 4) {
wpa_printf(MSG_ERROR,
"80+80 MHz: center segment 1 only 20 MHz apart");
return -1;
}
data->center_freq2 = 5000 + center_segment1 * 5;
/* fall through */
case CHANWIDTH_80MHZ:
data->bandwidth = 80;
if (!sec_channel_offset) {
wpa_printf(MSG_ERROR,
"80/80+80 MHz: no second channel offset");
return -1;
}
if (oper_chwidth == CHANWIDTH_80MHZ && center_segment1) {
wpa_printf(MSG_ERROR,
"80 MHz: center segment 1 configured");
return -1;
}
if (oper_chwidth == CHANWIDTH_80P80MHZ && !center_segment1) {
wpa_printf(MSG_ERROR,
"80+80 MHz: center segment 1 not configured");
return -1;
}
if (!center_segment0) {
if (channel <= 48)
center_segment0 = 42;
else if (channel <= 64)
center_segment0 = 58;
else if (channel <= 112)
center_segment0 = 106;
else if (channel <= 128)
center_segment0 = 122;
else if (channel <= 144)
center_segment0 = 138;
else if (channel <= 161)
center_segment0 = 155;
else if (channel <= 177)
center_segment0 = 171;
data->center_freq1 = 5000 + center_segment0 * 5;
} else {
/*
* Note: HT/VHT config and params are coupled. Check if
* HT40 channel band is in VHT80 Pri channel band
* configuration.
*/
if (center_segment0 == channel + 6 ||
center_segment0 == channel + 2 ||
center_segment0 == channel - 2 ||
center_segment0 == channel - 6)
data->center_freq1 = 5000 + center_segment0 * 5;
else {
wpa_printf(MSG_ERROR,
"Wrong coupling between HT and VHT/HE channel setting");
return -1;
}
}
break;
case CHANWIDTH_160MHZ:
data->bandwidth = 160;
if (center_segment1) {
wpa_printf(MSG_ERROR,
"160 MHz: center segment 1 should not be set");
return -1;
}
if (!sec_channel_offset) {
wpa_printf(MSG_ERROR,
"160 MHz: second channel offset not set");
return -1;
}
/*
* Note: HT/VHT config and params are coupled. Check if
* HT40 channel band is in VHT160 channel band configuration.
*/
if (center_segment0 == channel + 14 ||
center_segment0 == channel + 10 ||
center_segment0 == channel + 6 ||
center_segment0 == channel + 2 ||
center_segment0 == channel - 2 ||
center_segment0 == channel - 6 ||
center_segment0 == channel - 10 ||
center_segment0 == channel - 14)
data->center_freq1 = 5000 + center_segment0 * 5;
else {
wpa_printf(MSG_ERROR,
"160 MHz: HT40 channel band is not in 160 MHz band");
return -1;
}
break;
}
return 0;
}
void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
int disabled)
{
/* Masking these out disables HT40 */
le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
HT_CAP_INFO_SHORT_GI40MHZ);
if (disabled)
htcaps->ht_capabilities_info &= ~msk;
else
htcaps->ht_capabilities_info |= msk;
}
#ifdef CONFIG_IEEE80211AC
static int _ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap,
const char *name)
{
u32 req_cap = conf & cap;
/*
* Make sure we support all requested capabilities.
* NOTE: We assume that 'cap' represents a capability mask,
* not a discrete value.
*/
if ((hw & req_cap) != req_cap) {
wpa_printf(MSG_ERROR,
"Driver does not support configured VHT capability [%s]",
name);
return 0;
}
return 1;
}
static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask,
unsigned int shift,
const char *name)
{
u32 hw_max = hw & mask;
u32 conf_val = conf & mask;
if (conf_val > hw_max) {
wpa_printf(MSG_ERROR,
"Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
name, conf_val >> shift, hw_max >> shift);
return 0;
}
return 1;
}
int ieee80211ac_cap_check(u32 hw, u32 conf)
{
#define VHT_CAP_CHECK(cap) \
do { \
if (!_ieee80211ac_cap_check(hw, conf, cap, #cap)) \
return 0; \
} while (0)
#define VHT_CAP_CHECK_MAX(cap) \
do { \
if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \
#cap)) \
return 0; \
} while (0)
VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
VHT_CAP_CHECK_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK);
VHT_CAP_CHECK(VHT_CAP_RXLDPC);
VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
VHT_CAP_CHECK(VHT_CAP_TXSTBC);
VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
#undef VHT_CAP_CHECK
#undef VHT_CAP_CHECK_MAX
return 1;
}
#endif /* CONFIG_IEEE80211AC */
u32 num_chan_to_bw(int num_chans)
{
switch (num_chans) {
case 2:
case 4:
case 8:
return num_chans * 20;
default:
return 20;
}
}
/* check if BW is applicable for channel */
int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
int ht40_plus, int pri)
{
u32 bw_mask;
switch (bw) {
case 20:
bw_mask = HOSTAPD_CHAN_WIDTH_20;
break;
case 40:
/* HT 40 MHz support declared only for primary channel,
* just skip 40 MHz secondary checking */
if (pri && ht40_plus)
bw_mask = HOSTAPD_CHAN_WIDTH_40P;
else if (pri && !ht40_plus)
bw_mask = HOSTAPD_CHAN_WIDTH_40M;
else
bw_mask = 0;
break;
case 80:
bw_mask = HOSTAPD_CHAN_WIDTH_80;
break;
case 160:
bw_mask = HOSTAPD_CHAN_WIDTH_160;
break;
default:
bw_mask = 0;
break;
}
return (chan->allowed_bw & bw_mask) == bw_mask;
}
/* check if channel is allowed to be used as primary */
int chan_pri_allowed(const struct hostapd_channel_data *chan)
{
return !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20);
}
diff --git a/contrib/wpa/src/common/ieee802_11_common.c b/contrib/wpa/src/common/ieee802_11_common.c
index 3e5cfb01d565..5b74ddcdf62b 100644
--- a/contrib/wpa/src/common/ieee802_11_common.c
+++ b/contrib/wpa/src/common/ieee802_11_common.c
@@ -1,2701 +1,2735 @@
/*
* IEEE 802.11 Common routines
* Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "defs.h"
#include "wpa_common.h"
#include "drivers/driver.h"
#include "qca-vendor.h"
#include "ieee802_11_defs.h"
#include "ieee802_11_common.h"
static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
struct ieee802_11_elems *elems,
int show_errors)
{
unsigned int oui;
/* first 3 bytes in vendor specific information element are the IEEE
* OUI of the vendor. The following byte is used a vendor specific
* sub-type. */
if (elen < 4) {
if (show_errors) {
wpa_printf(MSG_MSGDUMP, "short vendor specific "
"information element ignored (len=%lu)",
(unsigned long) elen);
}
return -1;
}
oui = WPA_GET_BE24(pos);
switch (oui) {
case OUI_MICROSOFT:
/* Microsoft/Wi-Fi information elements are further typed and
* subtyped */
switch (pos[3]) {
case 1:
/* Microsoft OUI (00:50:F2) with OUI Type 1:
* real WPA information element */
elems->wpa_ie = pos;
elems->wpa_ie_len = elen;
break;
case WMM_OUI_TYPE:
/* WMM information element */
if (elen < 5) {
wpa_printf(MSG_MSGDUMP, "short WMM "
"information element ignored "
"(len=%lu)",
(unsigned long) elen);
return -1;
}
switch (pos[4]) {
case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
/*
* Share same pointer since only one of these
* is used and they start with same data.
* Length field can be used to distinguish the
* IEs.
*/
elems->wmm = pos;
elems->wmm_len = elen;
break;
case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
elems->wmm_tspec = pos;
elems->wmm_tspec_len = elen;
break;
default:
wpa_printf(MSG_EXCESSIVE, "unknown WMM "
"information element ignored "
"(subtype=%d len=%lu)",
pos[4], (unsigned long) elen);
return -1;
}
break;
case 4:
/* Wi-Fi Protected Setup (WPS) IE */
elems->wps_ie = pos;
elems->wps_ie_len = elen;
break;
default:
wpa_printf(MSG_EXCESSIVE, "Unknown Microsoft "
"information element ignored "
"(type=%d len=%lu)",
pos[3], (unsigned long) elen);
return -1;
}
break;
case OUI_WFA:
switch (pos[3]) {
case P2P_OUI_TYPE:
/* Wi-Fi Alliance - P2P IE */
elems->p2p = pos;
elems->p2p_len = elen;
break;
case WFD_OUI_TYPE:
/* Wi-Fi Alliance - WFD IE */
elems->wfd = pos;
elems->wfd_len = elen;
break;
case HS20_INDICATION_OUI_TYPE:
/* Hotspot 2.0 */
elems->hs20 = pos;
elems->hs20_len = elen;
break;
case HS20_OSEN_OUI_TYPE:
/* Hotspot 2.0 OSEN */
elems->osen = pos;
elems->osen_len = elen;
break;
case MBO_OUI_TYPE:
/* MBO-OCE */
elems->mbo = pos;
elems->mbo_len = elen;
break;
case HS20_ROAMING_CONS_SEL_OUI_TYPE:
/* Hotspot 2.0 Roaming Consortium Selection */
elems->roaming_cons_sel = pos;
elems->roaming_cons_sel_len = elen;
break;
case MULTI_AP_OUI_TYPE:
elems->multi_ap = pos;
elems->multi_ap_len = elen;
break;
case OWE_OUI_TYPE:
/* OWE Transition Mode element */
break;
case DPP_CC_OUI_TYPE:
/* DPP Configurator Connectivity element */
break;
case SAE_PK_OUI_TYPE:
elems->sae_pk = pos + 4;
elems->sae_pk_len = elen - 4;
break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
"(type=%d len=%lu)",
pos[3], (unsigned long) elen);
return -1;
}
break;
case OUI_BROADCOM:
switch (pos[3]) {
case VENDOR_HT_CAPAB_OUI_TYPE:
elems->vendor_ht_cap = pos;
elems->vendor_ht_cap_len = elen;
break;
case VENDOR_VHT_TYPE:
if (elen > 4 &&
(pos[4] == VENDOR_VHT_SUBTYPE ||
pos[4] == VENDOR_VHT_SUBTYPE2)) {
elems->vendor_vht = pos;
elems->vendor_vht_len = elen;
} else
return -1;
break;
default:
wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
"information element ignored "
"(type=%d len=%lu)",
pos[3], (unsigned long) elen);
return -1;
}
break;
case OUI_QCA:
switch (pos[3]) {
case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
elems->pref_freq_list = pos;
elems->pref_freq_list_len = elen;
break;
default:
wpa_printf(MSG_EXCESSIVE,
"Unknown QCA information element ignored (type=%d len=%lu)",
pos[3], (unsigned long) elen);
return -1;
}
break;
default:
wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
"information element ignored (vendor OUI "
"%02x:%02x:%02x len=%lu)",
pos[0], pos[1], pos[2], (unsigned long) elen);
return -1;
}
return 0;
}
static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
struct ieee802_11_elems *elems,
int show_errors)
{
u8 ext_id;
if (elen < 1) {
if (show_errors) {
wpa_printf(MSG_MSGDUMP,
"short information element (Ext)");
}
return -1;
}
ext_id = *pos++;
elen--;
elems->frag_ies.last_eid_ext = 0;
switch (ext_id) {
case WLAN_EID_EXT_ASSOC_DELAY_INFO:
if (elen != 1)
break;
elems->assoc_delay_info = pos;
break;
case WLAN_EID_EXT_FILS_REQ_PARAMS:
if (elen < 3)
break;
elems->fils_req_params = pos;
elems->fils_req_params_len = elen;
break;
case WLAN_EID_EXT_FILS_KEY_CONFIRM:
elems->fils_key_confirm = pos;
elems->fils_key_confirm_len = elen;
break;
case WLAN_EID_EXT_FILS_SESSION:
if (elen != FILS_SESSION_LEN)
break;
elems->fils_session = pos;
break;
case WLAN_EID_EXT_FILS_HLP_CONTAINER:
if (elen < 2 * ETH_ALEN)
break;
elems->fils_hlp = pos;
elems->fils_hlp_len = elen;
break;
case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
if (elen < 1)
break;
elems->fils_ip_addr_assign = pos;
elems->fils_ip_addr_assign_len = elen;
break;
case WLAN_EID_EXT_KEY_DELIVERY:
if (elen < WPA_KEY_RSC_LEN)
break;
elems->key_delivery = pos;
elems->key_delivery_len = elen;
break;
case WLAN_EID_EXT_WRAPPED_DATA:
elems->wrapped_data = pos;
elems->wrapped_data_len = elen;
break;
case WLAN_EID_EXT_FILS_PUBLIC_KEY:
if (elen < 1)
break;
elems->fils_pk = pos;
elems->fils_pk_len = elen;
break;
case WLAN_EID_EXT_FILS_NONCE:
if (elen != FILS_NONCE_LEN)
break;
elems->fils_nonce = pos;
break;
case WLAN_EID_EXT_OWE_DH_PARAM:
if (elen < 2)
break;
elems->owe_dh = pos;
elems->owe_dh_len = elen;
break;
case WLAN_EID_EXT_PASSWORD_IDENTIFIER:
elems->password_id = pos;
elems->password_id_len = elen;
break;
case WLAN_EID_EXT_HE_CAPABILITIES:
elems->he_capabilities = pos;
elems->he_capabilities_len = elen;
break;
case WLAN_EID_EXT_HE_OPERATION:
elems->he_operation = pos;
elems->he_operation_len = elen;
break;
case WLAN_EID_EXT_OCV_OCI:
elems->oci = pos;
elems->oci_len = elen;
break;
case WLAN_EID_EXT_SHORT_SSID_LIST:
elems->short_ssid_list = pos;
elems->short_ssid_list_len = elen;
break;
case WLAN_EID_EXT_HE_6GHZ_BAND_CAP:
if (elen < sizeof(struct ieee80211_he_6ghz_band_cap))
break;
elems->he_6ghz_band_cap = pos;
break;
case WLAN_EID_EXT_PASN_PARAMS:
elems->pasn_params = pos;
elems->pasn_params_len = elen;
break;
default:
if (show_errors) {
wpa_printf(MSG_MSGDUMP,
"IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)",
ext_id, (unsigned int) elen);
}
return -1;
}
if (elen == 254)
elems->frag_ies.last_eid_ext = ext_id;
return 0;
}
static void ieee802_11_parse_fragment(struct frag_ies_info *frag_ies,
const u8 *pos, u8 elen)
{
if (frag_ies->n_frags >= MAX_NUM_FRAG_IES_SUPPORTED) {
wpa_printf(MSG_MSGDUMP, "Too many element fragments - skip");
return;
}
/*
* Note: while EID == 0 is a valid ID (SSID IE), it should not be
* fragmented.
*/
if (!frag_ies->last_eid) {
wpa_printf(MSG_MSGDUMP,
"Fragment without a valid last element - skip");
return;
}
frag_ies->frags[frag_ies->n_frags].ie = pos;
frag_ies->frags[frag_ies->n_frags].ie_len = elen;
frag_ies->frags[frag_ies->n_frags].eid = frag_ies->last_eid;
frag_ies->frags[frag_ies->n_frags].eid_ext = frag_ies->last_eid_ext;
frag_ies->n_frags++;
}
/**
* ieee802_11_parse_elems - Parse information elements in management frames
* @start: Pointer to the start of IEs
* @len: Length of IE buffer in octets
* @elems: Data structure for parsed elements
* @show_errors: Whether to show parsing errors in debug log
* Returns: Parsing result
*/
ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
struct ieee802_11_elems *elems,
int show_errors)
{
const struct element *elem;
int unknown = 0;
os_memset(elems, 0, sizeof(*elems));
if (!start)
return ParseOK;
for_each_element(elem, start, len) {
u8 id = elem->id, elen = elem->datalen;
const u8 *pos = elem->data;
switch (id) {
case WLAN_EID_SSID:
if (elen > SSID_MAX_LEN) {
wpa_printf(MSG_DEBUG,
"Ignored too long SSID element (elen=%u)",
elen);
break;
}
if (elems->ssid) {
wpa_printf(MSG_MSGDUMP,
"Ignored duplicated SSID element");
break;
}
elems->ssid = pos;
elems->ssid_len = elen;
break;
case WLAN_EID_SUPP_RATES:
elems->supp_rates = pos;
elems->supp_rates_len = elen;
break;
case WLAN_EID_DS_PARAMS:
if (elen < 1)
break;
elems->ds_params = pos;
break;
case WLAN_EID_CF_PARAMS:
case WLAN_EID_TIM:
break;
case WLAN_EID_CHALLENGE:
elems->challenge = pos;
elems->challenge_len = elen;
break;
case WLAN_EID_ERP_INFO:
if (elen < 1)
break;
elems->erp_info = pos;
break;
case WLAN_EID_EXT_SUPP_RATES:
elems->ext_supp_rates = pos;
elems->ext_supp_rates_len = elen;
break;
case WLAN_EID_VENDOR_SPECIFIC:
if (ieee802_11_parse_vendor_specific(pos, elen,
elems,
show_errors))
unknown++;
break;
case WLAN_EID_RSN:
elems->rsn_ie = pos;
elems->rsn_ie_len = elen;
break;
case WLAN_EID_RSNX:
elems->rsnxe = pos;
elems->rsnxe_len = elen;
break;
case WLAN_EID_PWR_CAPABILITY:
if (elen < 2)
break;
elems->power_capab = pos;
elems->power_capab_len = elen;
break;
case WLAN_EID_SUPPORTED_CHANNELS:
elems->supp_channels = pos;
elems->supp_channels_len = elen;
break;
case WLAN_EID_MOBILITY_DOMAIN:
if (elen < sizeof(struct rsn_mdie))
break;
elems->mdie = pos;
elems->mdie_len = elen;
break;
case WLAN_EID_FAST_BSS_TRANSITION:
if (elen < sizeof(struct rsn_ftie))
break;
elems->ftie = pos;
elems->ftie_len = elen;
break;
case WLAN_EID_TIMEOUT_INTERVAL:
if (elen != 5)
break;
elems->timeout_int = pos;
break;
case WLAN_EID_HT_CAP:
if (elen < sizeof(struct ieee80211_ht_capabilities))
break;
elems->ht_capabilities = pos;
break;
case WLAN_EID_HT_OPERATION:
if (elen < sizeof(struct ieee80211_ht_operation))
break;
elems->ht_operation = pos;
break;
case WLAN_EID_MESH_CONFIG:
elems->mesh_config = pos;
elems->mesh_config_len = elen;
break;
case WLAN_EID_MESH_ID:
elems->mesh_id = pos;
elems->mesh_id_len = elen;
break;
case WLAN_EID_PEER_MGMT:
elems->peer_mgmt = pos;
elems->peer_mgmt_len = elen;
break;
case WLAN_EID_VHT_CAP:
if (elen < sizeof(struct ieee80211_vht_capabilities))
break;
elems->vht_capabilities = pos;
break;
case WLAN_EID_VHT_OPERATION:
if (elen < sizeof(struct ieee80211_vht_operation))
break;
elems->vht_operation = pos;
break;
case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
if (elen != 1)
break;
elems->vht_opmode_notif = pos;
break;
case WLAN_EID_LINK_ID:
if (elen < 18)
break;
elems->link_id = pos;
break;
case WLAN_EID_INTERWORKING:
elems->interworking = pos;
elems->interworking_len = elen;
break;
case WLAN_EID_QOS_MAP_SET:
if (elen < 16)
break;
elems->qos_map_set = pos;
elems->qos_map_set_len = elen;
break;
case WLAN_EID_EXT_CAPAB:
elems->ext_capab = pos;
elems->ext_capab_len = elen;
break;
case WLAN_EID_BSS_MAX_IDLE_PERIOD:
if (elen < 3)
break;
elems->bss_max_idle_period = pos;
break;
case WLAN_EID_SSID_LIST:
elems->ssid_list = pos;
elems->ssid_list_len = elen;
break;
case WLAN_EID_AMPE:
elems->ampe = pos;
elems->ampe_len = elen;
break;
case WLAN_EID_MIC:
elems->mic = pos;
elems->mic_len = elen;
/* after mic everything is encrypted, so stop. */
goto done;
case WLAN_EID_MULTI_BAND:
if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
wpa_printf(MSG_MSGDUMP,
"IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
id, elen);
break;
}
elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
elems->mb_ies.nof_ies++;
break;
case WLAN_EID_SUPPORTED_OPERATING_CLASSES:
elems->supp_op_classes = pos;
elems->supp_op_classes_len = elen;
break;
case WLAN_EID_RRM_ENABLED_CAPABILITIES:
elems->rrm_enabled = pos;
elems->rrm_enabled_len = elen;
break;
case WLAN_EID_CAG_NUMBER:
elems->cag_number = pos;
elems->cag_number_len = elen;
break;
case WLAN_EID_AP_CSN:
if (elen < 1)
break;
elems->ap_csn = pos;
break;
case WLAN_EID_FILS_INDICATION:
if (elen < 2)
break;
elems->fils_indic = pos;
elems->fils_indic_len = elen;
break;
case WLAN_EID_DILS:
if (elen < 2)
break;
elems->dils = pos;
elems->dils_len = elen;
break;
case WLAN_EID_S1G_CAPABILITIES:
if (elen < 15)
break;
elems->s1g_capab = pos;
break;
case WLAN_EID_FRAGMENT:
ieee802_11_parse_fragment(&elems->frag_ies, pos, elen);
break;
case WLAN_EID_EXTENSION:
if (ieee802_11_parse_extension(pos, elen, elems,
show_errors))
unknown++;
break;
default:
unknown++;
if (!show_errors)
break;
wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
"ignored unknown element (id=%d elen=%d)",
id, elen);
break;
}
if (id != WLAN_EID_FRAGMENT && elen == 255)
elems->frag_ies.last_eid = id;
if (id == WLAN_EID_EXTENSION && !elems->frag_ies.last_eid_ext)
elems->frag_ies.last_eid = 0;
}
if (!for_each_element_completed(elem, start, len)) {
if (show_errors) {
wpa_printf(MSG_DEBUG,
"IEEE 802.11 element parse failed @%d",
(int) (start + len - (const u8 *) elem));
wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
}
return ParseFailed;
}
done:
return unknown ? ParseUnknown : ParseOK;
}
int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
{
const struct element *elem;
int count = 0;
if (ies == NULL)
return 0;
for_each_element(elem, ies, ies_len)
count++;
return count;
}
struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
u32 oui_type)
{
struct wpabuf *buf;
const struct element *elem, *found = NULL;
for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
if (elem->datalen >= 4 &&
WPA_GET_BE32(elem->data) == oui_type) {
found = elem;
break;
}
}
if (!found)
return NULL; /* No specified vendor IE found */
buf = wpabuf_alloc(ies_len);
if (buf == NULL)
return NULL;
/*
* There may be multiple vendor IEs in the message, so need to
* concatenate their data fields.
*/
for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type)
wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4);
}
return buf;
}
const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
{
u16 fc, type, stype;
/*
* PS-Poll frames are 16 bytes. All other frames are
* 24 bytes or longer.
*/
if (len < 16)
return NULL;
fc = le_to_host16(hdr->frame_control);
type = WLAN_FC_GET_TYPE(fc);
stype = WLAN_FC_GET_STYPE(fc);
switch (type) {
case WLAN_FC_TYPE_DATA:
if (len < 24)
return NULL;
switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
case WLAN_FC_FROMDS | WLAN_FC_TODS:
case WLAN_FC_TODS:
return hdr->addr1;
case WLAN_FC_FROMDS:
return hdr->addr2;
default:
return NULL;
}
case WLAN_FC_TYPE_CTRL:
if (stype != WLAN_FC_STYPE_PSPOLL)
return NULL;
return hdr->addr1;
case WLAN_FC_TYPE_MGMT:
return hdr->addr3;
default:
return NULL;
}
}
int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
const char *name, const char *val)
{
int num, v;
const char *pos;
struct hostapd_wmm_ac_params *ac;
/* skip 'wme_ac_' or 'wmm_ac_' prefix */
pos = name + 7;
if (os_strncmp(pos, "be_", 3) == 0) {
num = 0;
pos += 3;
} else if (os_strncmp(pos, "bk_", 3) == 0) {
num = 1;
pos += 3;
} else if (os_strncmp(pos, "vi_", 3) == 0) {
num = 2;
pos += 3;
} else if (os_strncmp(pos, "vo_", 3) == 0) {
num = 3;
pos += 3;
} else {
wpa_printf(MSG_ERROR, "Unknown WMM name '%s'", pos);
return -1;
}
ac = &wmm_ac_params[num];
if (os_strcmp(pos, "aifs") == 0) {
v = atoi(val);
if (v < 1 || v > 255) {
wpa_printf(MSG_ERROR, "Invalid AIFS value %d", v);
return -1;
}
ac->aifs = v;
} else if (os_strcmp(pos, "cwmin") == 0) {
v = atoi(val);
if (v < 0 || v > 15) {
wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
return -1;
}
ac->cwmin = v;
} else if (os_strcmp(pos, "cwmax") == 0) {
v = atoi(val);
if (v < 0 || v > 15) {
wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
return -1;
}
ac->cwmax = v;
} else if (os_strcmp(pos, "txop_limit") == 0) {
v = atoi(val);
if (v < 0 || v > 0xffff) {
wpa_printf(MSG_ERROR, "Invalid txop value %d", v);
return -1;
}
ac->txop_limit = v;
} else if (os_strcmp(pos, "acm") == 0) {
v = atoi(val);
if (v < 0 || v > 1) {
wpa_printf(MSG_ERROR, "Invalid acm value %d", v);
return -1;
}
ac->admission_control_mandatory = v;
} else {
wpa_printf(MSG_ERROR, "Unknown wmm_ac_ field '%s'", pos);
return -1;
}
return 0;
}
/* convert floats with one decimal place to value*10 int, i.e.,
* "1.5" will return 15
*/
static int hostapd_config_read_int10(const char *value)
{
int i, d;
char *pos;
i = atoi(value);
pos = os_strchr(value, '.');
d = 0;
if (pos) {
pos++;
if (*pos >= '0' && *pos <= '9')
d = *pos - '0';
}
return i * 10 + d;
}
static int valid_cw(int cw)
{
return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 ||
cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 ||
cw == 32767);
}
int hostapd_config_tx_queue(struct hostapd_tx_queue_params tx_queue[],
const char *name, const char *val)
{
int num;
const char *pos;
struct hostapd_tx_queue_params *queue;
/* skip 'tx_queue_' prefix */
pos = name + 9;
if (os_strncmp(pos, "data", 4) == 0 &&
pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
num = pos[4] - '0';
pos += 6;
} else if (os_strncmp(pos, "after_beacon_", 13) == 0 ||
os_strncmp(pos, "beacon_", 7) == 0) {
wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
return 0;
} else {
wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos);
return -1;
}
if (num >= NUM_TX_QUEUES) {
/* for backwards compatibility, do not trigger failure */
wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
return 0;
}
queue = &tx_queue[num];
if (os_strcmp(pos, "aifs") == 0) {
queue->aifs = atoi(val);
if (queue->aifs < 0 || queue->aifs > 255) {
wpa_printf(MSG_ERROR, "Invalid AIFS value %d",
queue->aifs);
return -1;
}
} else if (os_strcmp(pos, "cwmin") == 0) {
queue->cwmin = atoi(val);
if (!valid_cw(queue->cwmin)) {
wpa_printf(MSG_ERROR, "Invalid cwMin value %d",
queue->cwmin);
return -1;
}
} else if (os_strcmp(pos, "cwmax") == 0) {
queue->cwmax = atoi(val);
if (!valid_cw(queue->cwmax)) {
wpa_printf(MSG_ERROR, "Invalid cwMax value %d",
queue->cwmax);
return -1;
}
} else if (os_strcmp(pos, "burst") == 0) {
queue->burst = hostapd_config_read_int10(val);
} else {
wpa_printf(MSG_ERROR, "Unknown queue field '%s'", pos);
return -1;
}
return 0;
}
enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
{
u8 op_class;
return ieee80211_freq_to_channel_ext(freq, 0, CHANWIDTH_USE_HT,
&op_class, channel);
}
/**
* ieee80211_freq_to_channel_ext - Convert frequency into channel info
* for HT40, VHT, and HE. DFS channels are not covered.
* @freq: Frequency (MHz) to convert
* @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
* @chanwidth: VHT/EDMG channel width (CHANWIDTH_*)
* @op_class: Buffer for returning operating class
* @channel: Buffer for returning channel number
* Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
*/
enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
int sec_channel,
int chanwidth,
u8 *op_class, u8 *channel)
{
u8 vht_opclass;
/* TODO: more operating classes */
if (sec_channel > 1 || sec_channel < -1)
return NUM_HOSTAPD_MODES;
if (freq >= 2412 && freq <= 2472) {
if ((freq - 2407) % 5)
return NUM_HOSTAPD_MODES;
if (chanwidth)
return NUM_HOSTAPD_MODES;
/* 2.407 GHz, channels 1..13 */
if (sec_channel == 1)
*op_class = 83;
else if (sec_channel == -1)
*op_class = 84;
else
*op_class = 81;
*channel = (freq - 2407) / 5;
return HOSTAPD_MODE_IEEE80211G;
}
if (freq == 2484) {
if (sec_channel || chanwidth)
return NUM_HOSTAPD_MODES;
*op_class = 82; /* channel 14 */
*channel = 14;
return HOSTAPD_MODE_IEEE80211B;
}
if (freq >= 4900 && freq < 5000) {
if ((freq - 4000) % 5)
return NUM_HOSTAPD_MODES;
*channel = (freq - 4000) / 5;
*op_class = 0; /* TODO */
return HOSTAPD_MODE_IEEE80211A;
}
switch (chanwidth) {
case CHANWIDTH_80MHZ:
vht_opclass = 128;
break;
case CHANWIDTH_160MHZ:
vht_opclass = 129;
break;
case CHANWIDTH_80P80MHZ:
vht_opclass = 130;
break;
default:
vht_opclass = 0;
break;
}
/* 5 GHz, channels 36..48 */
if (freq >= 5180 && freq <= 5240) {
if ((freq - 5000) % 5)
return NUM_HOSTAPD_MODES;
if (vht_opclass)
*op_class = vht_opclass;
else if (sec_channel == 1)
*op_class = 116;
else if (sec_channel == -1)
*op_class = 117;
else
*op_class = 115;
*channel = (freq - 5000) / 5;
return HOSTAPD_MODE_IEEE80211A;
}
/* 5 GHz, channels 52..64 */
if (freq >= 5260 && freq <= 5320) {
if ((freq - 5000) % 5)
return NUM_HOSTAPD_MODES;
if (vht_opclass)
*op_class = vht_opclass;
else if (sec_channel == 1)
*op_class = 119;
else if (sec_channel == -1)
*op_class = 120;
else
*op_class = 118;
*channel = (freq - 5000) / 5;
return HOSTAPD_MODE_IEEE80211A;
}
/* 5 GHz, channels 149..177 */
if (freq >= 5745 && freq <= 5885) {
if ((freq - 5000) % 5)
return NUM_HOSTAPD_MODES;
if (vht_opclass)
*op_class = vht_opclass;
else if (sec_channel == 1)
*op_class = 126;
else if (sec_channel == -1)
*op_class = 127;
else if (freq <= 5805)
*op_class = 124;
else
*op_class = 125;
*channel = (freq - 5000) / 5;
return HOSTAPD_MODE_IEEE80211A;
}
- /* 5 GHz, channels 100..140 */
- if (freq >= 5000 && freq <= 5700) {
+ /* 5 GHz, channels 100..144 */
+ if (freq >= 5500 && freq <= 5720) {
if ((freq - 5000) % 5)
return NUM_HOSTAPD_MODES;
if (vht_opclass)
*op_class = vht_opclass;
else if (sec_channel == 1)
*op_class = 122;
else if (sec_channel == -1)
*op_class = 123;
else
*op_class = 121;
*channel = (freq - 5000) / 5;
return HOSTAPD_MODE_IEEE80211A;
}
if (freq >= 5000 && freq < 5900) {
if ((freq - 5000) % 5)
return NUM_HOSTAPD_MODES;
*channel = (freq - 5000) / 5;
*op_class = 0; /* TODO */
return HOSTAPD_MODE_IEEE80211A;
}
if (freq > 5950 && freq <= 7115) {
if ((freq - 5950) % 5)
return NUM_HOSTAPD_MODES;
switch (chanwidth) {
case CHANWIDTH_80MHZ:
*op_class = 133;
break;
case CHANWIDTH_160MHZ:
*op_class = 134;
break;
case CHANWIDTH_80P80MHZ:
*op_class = 135;
break;
default:
if (sec_channel)
*op_class = 132;
else
*op_class = 131;
break;
}
*channel = (freq - 5950) / 5;
return HOSTAPD_MODE_IEEE80211A;
}
if (freq == 5935) {
*op_class = 136;
*channel = (freq - 5925) / 5;
return HOSTAPD_MODE_IEEE80211A;
}
/* 56.16 GHz, channel 1..6 */
if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) {
if (sec_channel)
return NUM_HOSTAPD_MODES;
switch (chanwidth) {
case CHANWIDTH_USE_HT:
case CHANWIDTH_2160MHZ:
*channel = (freq - 56160) / 2160;
*op_class = 180;
break;
case CHANWIDTH_4320MHZ:
/* EDMG channels 9 - 13 */
if (freq > 56160 + 2160 * 5)
return NUM_HOSTAPD_MODES;
*channel = (freq - 56160) / 2160 + 8;
*op_class = 181;
break;
case CHANWIDTH_6480MHZ:
/* EDMG channels 17 - 20 */
if (freq > 56160 + 2160 * 4)
return NUM_HOSTAPD_MODES;
*channel = (freq - 56160) / 2160 + 16;
*op_class = 182;
break;
case CHANWIDTH_8640MHZ:
/* EDMG channels 25 - 27 */
if (freq > 56160 + 2160 * 3)
return NUM_HOSTAPD_MODES;
*channel = (freq - 56160) / 2160 + 24;
*op_class = 183;
break;
default:
return NUM_HOSTAPD_MODES;
}
return HOSTAPD_MODE_IEEE80211AD;
}
return NUM_HOSTAPD_MODES;
}
int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
int sec_channel, u8 *op_class, u8 *channel)
{
int cw = CHAN_WIDTH_UNKNOWN;
switch (chanwidth) {
case CHAN_WIDTH_UNKNOWN:
case CHAN_WIDTH_20_NOHT:
case CHAN_WIDTH_20:
case CHAN_WIDTH_40:
cw = CHANWIDTH_USE_HT;
break;
case CHAN_WIDTH_80:
cw = CHANWIDTH_80MHZ;
break;
case CHAN_WIDTH_80P80:
cw = CHANWIDTH_80P80MHZ;
break;
case CHAN_WIDTH_160:
cw = CHANWIDTH_160MHZ;
break;
case CHAN_WIDTH_2160:
cw = CHANWIDTH_2160MHZ;
break;
case CHAN_WIDTH_4320:
cw = CHANWIDTH_4320MHZ;
break;
case CHAN_WIDTH_6480:
cw = CHANWIDTH_6480MHZ;
break;
case CHAN_WIDTH_8640:
cw = CHANWIDTH_8640MHZ;
break;
}
if (ieee80211_freq_to_channel_ext(freq, sec_channel, cw, op_class,
channel) == NUM_HOSTAPD_MODES) {
wpa_printf(MSG_WARNING,
"Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)",
freq, chanwidth, sec_channel);
return -1;
}
return 0;
}
static const char *const us_op_class_cc[] = {
"US", "CA", NULL
};
static const char *const eu_op_class_cc[] = {
"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL
};
static const char *const jp_op_class_cc[] = {
"JP", NULL
};
static const char *const cn_op_class_cc[] = {
"CN", NULL
};
static int country_match(const char *const cc[], const char *const country)
{
int i;
if (country == NULL)
return 0;
for (i = 0; cc[i]; i++) {
if (cc[i][0] == country[0] && cc[i][1] == country[1])
return 1;
}
return 0;
}
static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
{
switch (op_class) {
case 12: /* channels 1..11 */
case 32: /* channels 1..7; 40 MHz */
case 33: /* channels 5..11; 40 MHz */
if (chan < 1 || chan > 11)
return -1;
return 2407 + 5 * chan;
case 1: /* channels 36,40,44,48 */
case 2: /* channels 52,56,60,64; dfs */
case 22: /* channels 36,44; 40 MHz */
case 23: /* channels 52,60; 40 MHz */
case 27: /* channels 40,48; 40 MHz */
case 28: /* channels 56,64; 40 MHz */
if (chan < 36 || chan > 64)
return -1;
return 5000 + 5 * chan;
case 4: /* channels 100-144 */
case 24: /* channels 100-140; 40 MHz */
if (chan < 100 || chan > 144)
return -1;
return 5000 + 5 * chan;
case 3: /* channels 149,153,157,161 */
case 25: /* channels 149,157; 40 MHz */
case 26: /* channels 149,157; 40 MHz */
case 30: /* channels 153,161; 40 MHz */
case 31: /* channels 153,161; 40 MHz */
if (chan < 149 || chan > 161)
return -1;
return 5000 + 5 * chan;
case 5: /* channels 149,153,157,161,165 */
if (chan < 149 || chan > 165)
return -1;
return 5000 + 5 * chan;
case 34: /* 60 GHz band, channels 1..8 */
if (chan < 1 || chan > 8)
return -1;
return 56160 + 2160 * chan;
case 37: /* 60 GHz band, EDMG CB2, channels 9..15 */
if (chan < 9 || chan > 15)
return -1;
return 56160 + 2160 * (chan - 8);
case 38: /* 60 GHz band, EDMG CB3, channels 17..22 */
if (chan < 17 || chan > 22)
return -1;
return 56160 + 2160 * (chan - 16);
case 39: /* 60 GHz band, EDMG CB4, channels 25..29 */
if (chan < 25 || chan > 29)
return -1;
return 56160 + 2160 * (chan - 24);
}
return -1;
}
static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
{
switch (op_class) {
case 4: /* channels 1..13 */
case 11: /* channels 1..9; 40 MHz */
case 12: /* channels 5..13; 40 MHz */
if (chan < 1 || chan > 13)
return -1;
return 2407 + 5 * chan;
case 1: /* channels 36,40,44,48 */
case 2: /* channels 52,56,60,64; dfs */
case 5: /* channels 36,44; 40 MHz */
case 6: /* channels 52,60; 40 MHz */
case 8: /* channels 40,48; 40 MHz */
case 9: /* channels 56,64; 40 MHz */
if (chan < 36 || chan > 64)
return -1;
return 5000 + 5 * chan;
case 3: /* channels 100-140 */
case 7: /* channels 100-132; 40 MHz */
case 10: /* channels 104-136; 40 MHz */
case 16: /* channels 100-140 */
if (chan < 100 || chan > 140)
return -1;
return 5000 + 5 * chan;
case 17: /* channels 149,153,157,161,165,169 */
if (chan < 149 || chan > 169)
return -1;
return 5000 + 5 * chan;
case 18: /* 60 GHz band, channels 1..6 */
if (chan < 1 || chan > 6)
return -1;
return 56160 + 2160 * chan;
case 21: /* 60 GHz band, EDMG CB2, channels 9..11 */
if (chan < 9 || chan > 11)
return -1;
return 56160 + 2160 * (chan - 8);
case 22: /* 60 GHz band, EDMG CB3, channels 17..18 */
if (chan < 17 || chan > 18)
return -1;
return 56160 + 2160 * (chan - 16);
case 23: /* 60 GHz band, EDMG CB4, channels 25 */
if (chan != 25)
return -1;
return 56160 + 2160 * (chan - 24);
}
return -1;
}
static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
{
switch (op_class) {
case 30: /* channels 1..13 */
case 56: /* channels 1..9; 40 MHz */
case 57: /* channels 5..13; 40 MHz */
if (chan < 1 || chan > 13)
return -1;
return 2407 + 5 * chan;
case 31: /* channel 14 */
if (chan != 14)
return -1;
return 2414 + 5 * chan;
case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
case 32: /* channels 52,56,60,64 */
case 33: /* channels 52,56,60,64 */
case 36: /* channels 36,44; 40 MHz */
case 37: /* channels 52,60; 40 MHz */
case 38: /* channels 52,60; 40 MHz */
case 41: /* channels 40,48; 40 MHz */
case 42: /* channels 56,64; 40 MHz */
case 43: /* channels 56,64; 40 MHz */
if (chan < 34 || chan > 64)
return -1;
return 5000 + 5 * chan;
case 34: /* channels 100-140 */
case 35: /* channels 100-140 */
case 39: /* channels 100-132; 40 MHz */
case 40: /* channels 100-132; 40 MHz */
case 44: /* channels 104-136; 40 MHz */
case 45: /* channels 104-136; 40 MHz */
case 58: /* channels 100-140 */
if (chan < 100 || chan > 140)
return -1;
return 5000 + 5 * chan;
case 59: /* 60 GHz band, channels 1..6 */
if (chan < 1 || chan > 6)
return -1;
return 56160 + 2160 * chan;
case 62: /* 60 GHz band, EDMG CB2, channels 9..11 */
if (chan < 9 || chan > 11)
return -1;
return 56160 + 2160 * (chan - 8);
case 63: /* 60 GHz band, EDMG CB3, channels 17..18 */
if (chan < 17 || chan > 18)
return -1;
return 56160 + 2160 * (chan - 16);
case 64: /* 60 GHz band, EDMG CB4, channel 25 */
if (chan != 25)
return -1;
return 56160 + 2160 * (chan - 24);
}
return -1;
}
static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
{
switch (op_class) {
case 7: /* channels 1..13 */
case 8: /* channels 1..9; 40 MHz */
case 9: /* channels 5..13; 40 MHz */
if (chan < 1 || chan > 13)
return -1;
return 2407 + 5 * chan;
case 1: /* channels 36,40,44,48 */
case 2: /* channels 52,56,60,64; dfs */
case 4: /* channels 36,44; 40 MHz */
case 5: /* channels 52,60; 40 MHz */
if (chan < 36 || chan > 64)
return -1;
return 5000 + 5 * chan;
case 3: /* channels 149,153,157,161,165 */
case 6: /* channels 149,157; 40 MHz */
if (chan < 149 || chan > 165)
return -1;
return 5000 + 5 * chan;
}
return -1;
}
static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
{
/* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
switch (op_class) {
case 81:
/* channels 1..13 */
if (chan < 1 || chan > 13)
return -1;
return 2407 + 5 * chan;
case 82:
/* channel 14 */
if (chan != 14)
return -1;
return 2414 + 5 * chan;
case 83: /* channels 1..9; 40 MHz */
case 84: /* channels 5..13; 40 MHz */
if (chan < 1 || chan > 13)
return -1;
return 2407 + 5 * chan;
case 115: /* channels 36,40,44,48; indoor only */
case 116: /* channels 36,44; 40 MHz; indoor only */
case 117: /* channels 40,48; 40 MHz; indoor only */
case 118: /* channels 52,56,60,64; dfs */
case 119: /* channels 52,60; 40 MHz; dfs */
case 120: /* channels 56,64; 40 MHz; dfs */
if (chan < 36 || chan > 64)
return -1;
return 5000 + 5 * chan;
case 121: /* channels 100-140 */
case 122: /* channels 100-142; 40 MHz */
case 123: /* channels 104-136; 40 MHz */
if (chan < 100 || chan > 140)
return -1;
return 5000 + 5 * chan;
case 124: /* channels 149,153,157,161 */
if (chan < 149 || chan > 161)
return -1;
return 5000 + 5 * chan;
case 125: /* channels 149,153,157,161,165,169,173,177 */
case 126: /* channels 149,157,165,173; 40 MHz */
case 127: /* channels 153,161,169,177; 40 MHz */
if (chan < 149 || chan > 177)
return -1;
return 5000 + 5 * chan;
case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */
case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */
if (chan < 36 || chan > 177)
return -1;
return 5000 + 5 * chan;
case 129: /* center freqs 50, 114, 163; 160 MHz */
if (chan < 36 || chan > 177)
return -1;
return 5000 + 5 * chan;
case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */
case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */
case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
if (chan < 1 || chan > 233)
return -1;
return 5950 + chan * 5;
case 136: /* UHB channels, 20 MHz: 2 */
if (chan == 2)
return 5935;
return -1;
case 180: /* 60 GHz band, channels 1..8 */
if (chan < 1 || chan > 8)
return -1;
return 56160 + 2160 * chan;
case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
if (chan < 9 || chan > 15)
return -1;
return 56160 + 2160 * (chan - 8);
case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
if (chan < 17 || chan > 22)
return -1;
return 56160 + 2160 * (chan - 16);
case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
if (chan < 25 || chan > 29)
return -1;
return 56160 + 2160 * (chan - 24);
}
return -1;
}
/**
* ieee80211_chan_to_freq - Convert channel info to frequency
* @country: Country code, if known; otherwise, global operating class is used
* @op_class: Operating class
* @chan: Channel number
* Returns: Frequency in MHz or -1 if the specified channel is unknown
*/
int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
{
int freq;
if (country_match(us_op_class_cc, country)) {
freq = ieee80211_chan_to_freq_us(op_class, chan);
if (freq > 0)
return freq;
}
if (country_match(eu_op_class_cc, country)) {
freq = ieee80211_chan_to_freq_eu(op_class, chan);
if (freq > 0)
return freq;
}
if (country_match(jp_op_class_cc, country)) {
freq = ieee80211_chan_to_freq_jp(op_class, chan);
if (freq > 0)
return freq;
}
if (country_match(cn_op_class_cc, country)) {
freq = ieee80211_chan_to_freq_cn(op_class, chan);
if (freq > 0)
return freq;
}
return ieee80211_chan_to_freq_global(op_class, chan);
}
int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
u16 num_modes)
{
int i, j;
if (!modes || !num_modes)
return (freq >= 5260 && freq <= 5320) ||
(freq >= 5500 && freq <= 5700);
for (i = 0; i < num_modes; i++) {
for (j = 0; j < modes[i].num_channels; j++) {
if (modes[i].channels[j].freq == freq &&
(modes[i].channels[j].flag & HOSTAPD_CHAN_RADAR))
return 1;
}
}
return 0;
}
+/*
+ * 802.11-2020: Table E-4 - Global operating classes
+ * DFS_50_100_Behavior: 118, 119, 120, 121, 122, 123
+ */
+int is_dfs_global_op_class(u8 op_class)
+{
+ return (op_class >= 118) && (op_class <= 123);
+}
+
+
static int is_11b(u8 rate)
{
return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
}
int supp_rates_11b_only(struct ieee802_11_elems *elems)
{
int num_11b = 0, num_others = 0;
int i;
if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
return 0;
for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
if (is_11b(elems->supp_rates[i]))
num_11b++;
else
num_others++;
}
for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
i++) {
if (is_11b(elems->ext_supp_rates[i]))
num_11b++;
else
num_others++;
}
return num_11b > 0 && num_others == 0;
}
const char * fc2str(u16 fc)
{
u16 stype = WLAN_FC_GET_STYPE(fc);
#define C2S(x) case x: return #x;
switch (WLAN_FC_GET_TYPE(fc)) {
case WLAN_FC_TYPE_MGMT:
switch (stype) {
C2S(WLAN_FC_STYPE_ASSOC_REQ)
C2S(WLAN_FC_STYPE_ASSOC_RESP)
C2S(WLAN_FC_STYPE_REASSOC_REQ)
C2S(WLAN_FC_STYPE_REASSOC_RESP)
C2S(WLAN_FC_STYPE_PROBE_REQ)
C2S(WLAN_FC_STYPE_PROBE_RESP)
C2S(WLAN_FC_STYPE_BEACON)
C2S(WLAN_FC_STYPE_ATIM)
C2S(WLAN_FC_STYPE_DISASSOC)
C2S(WLAN_FC_STYPE_AUTH)
C2S(WLAN_FC_STYPE_DEAUTH)
C2S(WLAN_FC_STYPE_ACTION)
}
break;
case WLAN_FC_TYPE_CTRL:
switch (stype) {
C2S(WLAN_FC_STYPE_PSPOLL)
C2S(WLAN_FC_STYPE_RTS)
C2S(WLAN_FC_STYPE_CTS)
C2S(WLAN_FC_STYPE_ACK)
C2S(WLAN_FC_STYPE_CFEND)
C2S(WLAN_FC_STYPE_CFENDACK)
}
break;
case WLAN_FC_TYPE_DATA:
switch (stype) {
C2S(WLAN_FC_STYPE_DATA)
C2S(WLAN_FC_STYPE_DATA_CFACK)
C2S(WLAN_FC_STYPE_DATA_CFPOLL)
C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
C2S(WLAN_FC_STYPE_NULLFUNC)
C2S(WLAN_FC_STYPE_CFACK)
C2S(WLAN_FC_STYPE_CFPOLL)
C2S(WLAN_FC_STYPE_CFACKPOLL)
C2S(WLAN_FC_STYPE_QOS_DATA)
C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
C2S(WLAN_FC_STYPE_QOS_NULL)
C2S(WLAN_FC_STYPE_QOS_CFPOLL)
C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
}
break;
}
return "WLAN_FC_TYPE_UNKNOWN";
#undef C2S
}
const char * reason2str(u16 reason)
{
#define R2S(r) case WLAN_REASON_ ## r: return #r;
switch (reason) {
R2S(UNSPECIFIED)
R2S(PREV_AUTH_NOT_VALID)
R2S(DEAUTH_LEAVING)
R2S(DISASSOC_DUE_TO_INACTIVITY)
R2S(DISASSOC_AP_BUSY)
R2S(CLASS2_FRAME_FROM_NONAUTH_STA)
R2S(CLASS3_FRAME_FROM_NONASSOC_STA)
R2S(DISASSOC_STA_HAS_LEFT)
R2S(STA_REQ_ASSOC_WITHOUT_AUTH)
R2S(PWR_CAPABILITY_NOT_VALID)
R2S(SUPPORTED_CHANNEL_NOT_VALID)
R2S(BSS_TRANSITION_DISASSOC)
R2S(INVALID_IE)
R2S(MICHAEL_MIC_FAILURE)
R2S(4WAY_HANDSHAKE_TIMEOUT)
R2S(GROUP_KEY_UPDATE_TIMEOUT)
R2S(IE_IN_4WAY_DIFFERS)
R2S(GROUP_CIPHER_NOT_VALID)
R2S(PAIRWISE_CIPHER_NOT_VALID)
R2S(AKMP_NOT_VALID)
R2S(UNSUPPORTED_RSN_IE_VERSION)
R2S(INVALID_RSN_IE_CAPAB)
R2S(IEEE_802_1X_AUTH_FAILED)
R2S(CIPHER_SUITE_REJECTED)
R2S(TDLS_TEARDOWN_UNREACHABLE)
R2S(TDLS_TEARDOWN_UNSPECIFIED)
R2S(SSP_REQUESTED_DISASSOC)
R2S(NO_SSP_ROAMING_AGREEMENT)
R2S(BAD_CIPHER_OR_AKM)
R2S(NOT_AUTHORIZED_THIS_LOCATION)
R2S(SERVICE_CHANGE_PRECLUDES_TS)
R2S(UNSPECIFIED_QOS_REASON)
R2S(NOT_ENOUGH_BANDWIDTH)
R2S(DISASSOC_LOW_ACK)
R2S(EXCEEDED_TXOP)
R2S(STA_LEAVING)
R2S(END_TS_BA_DLS)
R2S(UNKNOWN_TS_BA)
R2S(TIMEOUT)
R2S(PEERKEY_MISMATCH)
R2S(AUTHORIZED_ACCESS_LIMIT_REACHED)
R2S(EXTERNAL_SERVICE_REQUIREMENTS)
R2S(INVALID_FT_ACTION_FRAME_COUNT)
R2S(INVALID_PMKID)
R2S(INVALID_MDE)
R2S(INVALID_FTE)
R2S(MESH_PEERING_CANCELLED)
R2S(MESH_MAX_PEERS)
R2S(MESH_CONFIG_POLICY_VIOLATION)
R2S(MESH_CLOSE_RCVD)
R2S(MESH_MAX_RETRIES)
R2S(MESH_CONFIRM_TIMEOUT)
R2S(MESH_INVALID_GTK)
R2S(MESH_INCONSISTENT_PARAMS)
R2S(MESH_INVALID_SECURITY_CAP)
R2S(MESH_PATH_ERROR_NO_PROXY_INFO)
R2S(MESH_PATH_ERROR_NO_FORWARDING_INFO)
R2S(MESH_PATH_ERROR_DEST_UNREACHABLE)
R2S(MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS)
R2S(MESH_CHANNEL_SWITCH_REGULATORY_REQ)
R2S(MESH_CHANNEL_SWITCH_UNSPECIFIED)
}
return "UNKNOWN";
#undef R2S
}
const char * status2str(u16 status)
{
#define S2S(s) case WLAN_STATUS_ ## s: return #s;
switch (status) {
S2S(SUCCESS)
S2S(UNSPECIFIED_FAILURE)
S2S(TDLS_WAKEUP_ALTERNATE)
S2S(TDLS_WAKEUP_REJECT)
S2S(SECURITY_DISABLED)
S2S(UNACCEPTABLE_LIFETIME)
S2S(NOT_IN_SAME_BSS)
S2S(CAPS_UNSUPPORTED)
S2S(REASSOC_NO_ASSOC)
S2S(ASSOC_DENIED_UNSPEC)
S2S(NOT_SUPPORTED_AUTH_ALG)
S2S(UNKNOWN_AUTH_TRANSACTION)
S2S(CHALLENGE_FAIL)
S2S(AUTH_TIMEOUT)
S2S(AP_UNABLE_TO_HANDLE_NEW_STA)
S2S(ASSOC_DENIED_RATES)
S2S(ASSOC_DENIED_NOSHORT)
S2S(SPEC_MGMT_REQUIRED)
S2S(PWR_CAPABILITY_NOT_VALID)
S2S(SUPPORTED_CHANNEL_NOT_VALID)
S2S(ASSOC_DENIED_NO_SHORT_SLOT_TIME)
S2S(ASSOC_DENIED_NO_HT)
S2S(R0KH_UNREACHABLE)
S2S(ASSOC_DENIED_NO_PCO)
S2S(ASSOC_REJECTED_TEMPORARILY)
S2S(ROBUST_MGMT_FRAME_POLICY_VIOLATION)
S2S(UNSPECIFIED_QOS_FAILURE)
S2S(DENIED_INSUFFICIENT_BANDWIDTH)
S2S(DENIED_POOR_CHANNEL_CONDITIONS)
S2S(DENIED_QOS_NOT_SUPPORTED)
S2S(REQUEST_DECLINED)
S2S(INVALID_PARAMETERS)
S2S(REJECTED_WITH_SUGGESTED_CHANGES)
S2S(INVALID_IE)
S2S(GROUP_CIPHER_NOT_VALID)
S2S(PAIRWISE_CIPHER_NOT_VALID)
S2S(AKMP_NOT_VALID)
S2S(UNSUPPORTED_RSN_IE_VERSION)
S2S(INVALID_RSN_IE_CAPAB)
S2S(CIPHER_REJECTED_PER_POLICY)
S2S(TS_NOT_CREATED)
S2S(DIRECT_LINK_NOT_ALLOWED)
S2S(DEST_STA_NOT_PRESENT)
S2S(DEST_STA_NOT_QOS_STA)
S2S(ASSOC_DENIED_LISTEN_INT_TOO_LARGE)
S2S(INVALID_FT_ACTION_FRAME_COUNT)
S2S(INVALID_PMKID)
S2S(INVALID_MDIE)
S2S(INVALID_FTIE)
S2S(REQUESTED_TCLAS_NOT_SUPPORTED)
S2S(INSUFFICIENT_TCLAS_PROCESSING_RESOURCES)
S2S(TRY_ANOTHER_BSS)
S2S(GAS_ADV_PROTO_NOT_SUPPORTED)
S2S(NO_OUTSTANDING_GAS_REQ)
S2S(GAS_RESP_NOT_RECEIVED)
S2S(STA_TIMED_OUT_WAITING_FOR_GAS_RESP)
S2S(GAS_RESP_LARGER_THAN_LIMIT)
S2S(REQ_REFUSED_HOME)
S2S(ADV_SRV_UNREACHABLE)
S2S(REQ_REFUSED_SSPN)
S2S(REQ_REFUSED_UNAUTH_ACCESS)
S2S(INVALID_RSNIE)
S2S(U_APSD_COEX_NOT_SUPPORTED)
S2S(U_APSD_COEX_MODE_NOT_SUPPORTED)
S2S(BAD_INTERVAL_WITH_U_APSD_COEX)
S2S(ANTI_CLOGGING_TOKEN_REQ)
S2S(FINITE_CYCLIC_GROUP_NOT_SUPPORTED)
S2S(CANNOT_FIND_ALT_TBTT)
S2S(TRANSMISSION_FAILURE)
S2S(REQ_TCLAS_NOT_SUPPORTED)
S2S(TCLAS_RESOURCES_EXCHAUSTED)
S2S(REJECTED_WITH_SUGGESTED_BSS_TRANSITION)
S2S(REJECT_WITH_SCHEDULE)
S2S(REJECT_NO_WAKEUP_SPECIFIED)
S2S(SUCCESS_POWER_SAVE_MODE)
S2S(PENDING_ADMITTING_FST_SESSION)
S2S(PERFORMING_FST_NOW)
S2S(PENDING_GAP_IN_BA_WINDOW)
S2S(REJECT_U_PID_SETTING)
S2S(REFUSED_EXTERNAL_REASON)
S2S(REFUSED_AP_OUT_OF_MEMORY)
S2S(REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED)
S2S(QUERY_RESP_OUTSTANDING)
S2S(REJECT_DSE_BAND)
S2S(TCLAS_PROCESSING_TERMINATED)
S2S(TS_SCHEDULE_CONFLICT)
S2S(DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL)
S2S(MCCAOP_RESERVATION_CONFLICT)
S2S(MAF_LIMIT_EXCEEDED)
S2S(MCCA_TRACK_LIMIT_EXCEEDED)
S2S(DENIED_DUE_TO_SPECTRUM_MANAGEMENT)
S2S(ASSOC_DENIED_NO_VHT)
S2S(ENABLEMENT_DENIED)
S2S(RESTRICTION_FROM_AUTHORIZED_GDB)
S2S(AUTHORIZATION_DEENABLED)
S2S(FILS_AUTHENTICATION_FAILURE)
S2S(UNKNOWN_AUTHENTICATION_SERVER)
S2S(UNKNOWN_PASSWORD_IDENTIFIER)
S2S(DENIED_HE_NOT_SUPPORTED)
S2S(SAE_HASH_TO_ELEMENT)
S2S(SAE_PK)
}
return "UNKNOWN";
#undef S2S
}
int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
size_t ies_len)
{
const struct element *elem;
os_memset(info, 0, sizeof(*info));
if (!ies_buf)
return 0;
for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) {
if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED)
return 0;
wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
elem->datalen + 2);
info->ies[info->nof_ies].ie = elem->data;
info->ies[info->nof_ies].ie_len = elem->datalen;
info->nof_ies++;
}
if (!for_each_element_completed(elem, ies_buf, ies_len)) {
wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len);
return -1;
}
return 0;
}
struct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
{
struct wpabuf *mb_ies = NULL;
WPA_ASSERT(info != NULL);
if (info->nof_ies) {
u8 i;
size_t mb_ies_size = 0;
for (i = 0; i < info->nof_ies; i++)
mb_ies_size += 2 + info->ies[i].ie_len;
mb_ies = wpabuf_alloc(mb_ies_size);
if (mb_ies) {
for (i = 0; i < info->nof_ies; i++) {
wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
wpabuf_put_data(mb_ies,
info->ies[i].ie,
info->ies[i].ie_len);
}
}
}
return mb_ies;
}
const struct oper_class_map global_op_class[] = {
{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20, NO_P2P_SUPP },
/* Do not enable HT40 on 2.4 GHz for P2P use for now */
{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 121, 100, 140, 4, BW20, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 125, 149, 177, 4, BW20, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 126, 149, 173, 8, BW40PLUS, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 127, 153, 177, 8, BW40MINUS, P2P_SUPP },
/*
* IEEE P802.11ax/D8.0 Table E-4 actually talks about channel center
* frequency index 42, 58, 106, 122, 138, 155, 171 with channel spacing
* of 80 MHz, but currently use the following definition for simplicity
* (these center frequencies are not actual channels, which makes
* wpas_p2p_verify_channel() fail). wpas_p2p_verify_80mhz() should take
* care of removing invalid channels.
*/
{ HOSTAPD_MODE_IEEE80211A, 128, 36, 177, 4, BW80, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 129, 36, 177, 4, BW160, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 131, 1, 233, 4, BW20, P2P_SUPP },
- { HOSTAPD_MODE_IEEE80211A, 132, 1, 233, 8, BW40, NO_P2P_SUPP },
- { HOSTAPD_MODE_IEEE80211A, 133, 1, 233, 16, BW80, NO_P2P_SUPP },
- { HOSTAPD_MODE_IEEE80211A, 134, 1, 233, 32, BW160, NO_P2P_SUPP },
+ { HOSTAPD_MODE_IEEE80211A, 132, 1, 233, 8, BW40, P2P_SUPP },
+ { HOSTAPD_MODE_IEEE80211A, 133, 1, 233, 16, BW80, P2P_SUPP },
+ { HOSTAPD_MODE_IEEE80211A, 134, 1, 233, 32, BW160, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 135, 1, 233, 16, BW80P80, NO_P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211A, 136, 2, 2, 4, BW20, NO_P2P_SUPP },
/*
* IEEE Std 802.11ad-2012 and P802.ay/D5.0 60 GHz operating classes.
* Class 180 has the legacy channels 1-6. Classes 181-183 include
* channels which implement channel bonding features.
*/
{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 6, 1, BW2160, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211AD, 181, 9, 13, 1, BW4320, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211AD, 182, 17, 20, 1, BW6480, P2P_SUPP },
{ HOSTAPD_MODE_IEEE80211AD, 183, 25, 27, 1, BW8640, P2P_SUPP },
/* Keep the operating class 130 as the last entry as a workaround for
* the OneHundredAndThirty Delimiter value used in the Supported
* Operating Classes element to indicate the end of the Operating
* Classes field. */
{ HOSTAPD_MODE_IEEE80211A, 130, 36, 177, 4, BW80P80, P2P_SUPP },
{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
};
static enum phy_type ieee80211_phy_type_by_freq(int freq)
{
enum hostapd_hw_mode hw_mode;
u8 channel;
hw_mode = ieee80211_freq_to_chan(freq, &channel);
switch (hw_mode) {
case HOSTAPD_MODE_IEEE80211A:
return PHY_TYPE_OFDM;
case HOSTAPD_MODE_IEEE80211B:
return PHY_TYPE_HRDSSS;
case HOSTAPD_MODE_IEEE80211G:
return PHY_TYPE_ERP;
case HOSTAPD_MODE_IEEE80211AD:
return PHY_TYPE_DMG;
default:
return PHY_TYPE_UNSPECIFIED;
};
}
/* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */
enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht)
{
if (vht)
return PHY_TYPE_VHT;
if (ht)
return PHY_TYPE_HT;
return ieee80211_phy_type_by_freq(freq);
}
size_t global_op_class_size = ARRAY_SIZE(global_op_class);
/**
* get_ie - Fetch a specified information element from IEs buffer
* @ies: Information elements buffer
* @len: Information elements buffer length
* @eid: Information element identifier (WLAN_EID_*)
* Returns: Pointer to the information element (id field) or %NULL if not found
*
* This function returns the first matching information element in the IEs
* buffer or %NULL in case the element is not found.
*/
const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
{
const struct element *elem;
if (!ies)
return NULL;
for_each_element_id(elem, eid, ies, len)
return &elem->id;
return NULL;
}
/**
* get_ie_ext - Fetch a specified extended information element from IEs buffer
* @ies: Information elements buffer
* @len: Information elements buffer length
* @ext: Information element extension identifier (WLAN_EID_EXT_*)
* Returns: Pointer to the information element (id field) or %NULL if not found
*
* This function returns the first matching information element in the IEs
* buffer or %NULL in case the element is not found.
*/
const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext)
{
const struct element *elem;
if (!ies)
return NULL;
for_each_element_extid(elem, ext, ies, len)
return &elem->id;
return NULL;
}
const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
{
const struct element *elem;
for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
if (elem->datalen >= 4 &&
vendor_type == WPA_GET_BE32(elem->data))
return &elem->id;
}
return NULL;
}
size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
{
/*
* MBO IE requires 6 bytes without the attributes: EID (1), length (1),
* OUI (3), OUI type (1).
*/
if (len < 6 + attr_len) {
wpa_printf(MSG_DEBUG,
"MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
len, attr_len);
return 0;
}
*buf++ = WLAN_EID_VENDOR_SPECIFIC;
*buf++ = attr_len + 4;
WPA_PUT_BE24(buf, OUI_WFA);
buf += 3;
*buf++ = MBO_OUI_TYPE;
os_memcpy(buf, attr, attr_len);
return 6 + attr_len;
}
size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value)
{
u8 *pos = buf;
if (len < 9)
return 0;
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = 7; /* len */
WPA_PUT_BE24(pos, OUI_WFA);
pos += 3;
*pos++ = MULTI_AP_OUI_TYPE;
*pos++ = MULTI_AP_SUB_ELEM_TYPE;
*pos++ = 1; /* len */
*pos++ = value;
return pos - buf;
}
static const struct country_op_class us_op_class[] = {
{ 1, 115 },
{ 2, 118 },
{ 3, 124 },
{ 4, 121 },
{ 5, 125 },
{ 12, 81 },
{ 22, 116 },
{ 23, 119 },
{ 24, 122 },
{ 25, 126 },
{ 26, 126 },
{ 27, 117 },
{ 28, 120 },
{ 29, 123 },
{ 30, 127 },
{ 31, 127 },
{ 32, 83 },
{ 33, 84 },
{ 34, 180 },
};
static const struct country_op_class eu_op_class[] = {
{ 1, 115 },
{ 2, 118 },
{ 3, 121 },
{ 4, 81 },
{ 5, 116 },
{ 6, 119 },
{ 7, 122 },
{ 8, 117 },
{ 9, 120 },
{ 10, 123 },
{ 11, 83 },
{ 12, 84 },
{ 17, 125 },
{ 18, 180 },
};
static const struct country_op_class jp_op_class[] = {
{ 1, 115 },
{ 30, 81 },
{ 31, 82 },
{ 32, 118 },
{ 33, 118 },
{ 34, 121 },
{ 35, 121 },
{ 36, 116 },
{ 37, 119 },
{ 38, 119 },
{ 39, 122 },
{ 40, 122 },
{ 41, 117 },
{ 42, 120 },
{ 43, 120 },
{ 44, 123 },
{ 45, 123 },
{ 56, 83 },
{ 57, 84 },
{ 58, 121 },
{ 59, 180 },
};
static const struct country_op_class cn_op_class[] = {
{ 1, 115 },
{ 2, 118 },
{ 3, 125 },
{ 4, 116 },
{ 5, 119 },
{ 6, 126 },
{ 7, 81 },
{ 8, 83 },
{ 9, 84 },
};
static u8
global_op_class_from_country_array(u8 op_class, size_t array_size,
const struct country_op_class *country_array)
{
size_t i;
for (i = 0; i < array_size; i++) {
if (country_array[i].country_op_class == op_class)
return country_array[i].global_op_class;
}
return 0;
}
u8 country_to_global_op_class(const char *country, u8 op_class)
{
const struct country_op_class *country_array;
size_t size;
u8 g_op_class;
if (country_match(us_op_class_cc, country)) {
country_array = us_op_class;
size = ARRAY_SIZE(us_op_class);
} else if (country_match(eu_op_class_cc, country)) {
country_array = eu_op_class;
size = ARRAY_SIZE(eu_op_class);
} else if (country_match(jp_op_class_cc, country)) {
country_array = jp_op_class;
size = ARRAY_SIZE(jp_op_class);
} else if (country_match(cn_op_class_cc, country)) {
country_array = cn_op_class;
size = ARRAY_SIZE(cn_op_class);
} else {
/*
* Countries that do not match any of the above countries use
* global operating classes
*/
return op_class;
}
g_op_class = global_op_class_from_country_array(op_class, size,
country_array);
/*
* If the given operating class did not match any of the country's
* operating classes, assume that global operating class is used.
*/
return g_op_class ? g_op_class : op_class;
}
const struct oper_class_map * get_oper_class(const char *country, u8 op_class)
{
const struct oper_class_map *op;
if (country)
op_class = country_to_global_op_class(country, op_class);
op = &global_op_class[0];
while (op->op_class && op->op_class != op_class)
op++;
if (!op->op_class)
return NULL;
return op;
}
int oper_class_bw_to_int(const struct oper_class_map *map)
{
switch (map->bw) {
case BW20:
return 20;
case BW40:
case BW40PLUS:
case BW40MINUS:
return 40;
case BW80:
return 80;
case BW80P80:
case BW160:
return 160;
case BW2160:
return 2160;
default:
return 0;
}
}
int center_idx_to_bw_6ghz(u8 idx)
{
/* Channel: 2 */
if (idx == 2)
return 0; /* 20 MHz */
/* channels: 1, 5, 9, 13... */
if ((idx & 0x3) == 0x1)
return 0; /* 20 MHz */
/* channels 3, 11, 19... */
if ((idx & 0x7) == 0x3)
return 1; /* 40 MHz */
/* channels 7, 23, 39.. */
if ((idx & 0xf) == 0x7)
return 2; /* 80 MHz */
/* channels 15, 47, 79...*/
if ((idx & 0x1f) == 0xf)
return 3; /* 160 MHz */
return -1;
}
bool is_6ghz_freq(int freq)
{
if (freq < 5935 || freq > 7115)
return false;
if (freq == 5935)
return true;
if (center_idx_to_bw_6ghz((freq - 5950) / 5) < 0)
return false;
return true;
}
bool is_6ghz_op_class(u8 op_class)
{
return op_class >= 131 && op_class <= 136;
}
bool is_6ghz_psc_frequency(int freq)
{
int i;
if (!is_6ghz_freq(freq) || freq == 5935)
return false;
if ((((freq - 5950) / 5) & 0x3) != 0x1)
return false;
i = (freq - 5950 + 55) % 80;
if (i == 0)
i = (freq - 5950 + 55) / 80;
if (i >= 1 && i <= 15)
return true;
return false;
}
+/**
+ * get_6ghz_sec_channel - Get the relative position of the secondary channel
+ * to the primary channel in 6 GHz
+ * @channel: Primary channel to be checked for (in global op class 131)
+ * Returns: 1 = secondary channel above, -1 = secondary channel below
+ */
+
+int get_6ghz_sec_channel(int channel)
+{
+ /*
+ * In the 6 GHz band, primary channels are numbered as 1, 5, 9, 13.., so
+ * the 40 MHz channels are formed with the channel pairs as (1,5),
+ * (9,13), (17,21)..
+ * The secondary channel for a given primary channel is below the
+ * primary channel for the channels 5, 13, 21.. and it is above the
+ * primary channel for the channels 1, 9, 17..
+ */
+
+ if (((channel - 1) / 4) % 2)
+ return -1;
+ return 1;
+}
+
+
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len)
{
u8 *nei_pos = nei_rep;
const char *end;
/*
* BSS Transition Candidate List Entries - Neighbor Report elements
* neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
* <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
*/
while (pos) {
u8 *nei_start;
long int val;
char *endptr, *tmp;
pos = os_strstr(pos, " neighbor=");
if (!pos)
break;
if (nei_pos + 15 > nei_rep + nei_rep_len) {
wpa_printf(MSG_DEBUG,
"Not enough room for additional neighbor");
return -1;
}
pos += 10;
nei_start = nei_pos;
*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
nei_pos++; /* length to be filled in */
if (hwaddr_aton(pos, nei_pos)) {
wpa_printf(MSG_DEBUG, "Invalid BSSID");
return -1;
}
nei_pos += ETH_ALEN;
pos += 17;
if (*pos != ',') {
wpa_printf(MSG_DEBUG, "Missing BSSID Information");
return -1;
}
pos++;
val = strtol(pos, &endptr, 0);
WPA_PUT_LE32(nei_pos, val);
nei_pos += 4;
if (*endptr != ',') {
wpa_printf(MSG_DEBUG, "Missing Operating Class");
return -1;
}
pos = endptr + 1;
*nei_pos++ = atoi(pos); /* Operating Class */
pos = os_strchr(pos, ',');
if (pos == NULL) {
wpa_printf(MSG_DEBUG, "Missing Channel Number");
return -1;
}
pos++;
*nei_pos++ = atoi(pos); /* Channel Number */
pos = os_strchr(pos, ',');
if (pos == NULL) {
wpa_printf(MSG_DEBUG, "Missing PHY Type");
return -1;
}
pos++;
*nei_pos++ = atoi(pos); /* PHY Type */
end = os_strchr(pos, ' ');
tmp = os_strchr(pos, ',');
if (tmp && (!end || tmp < end)) {
/* Optional Subelements (hexdump) */
size_t len;
pos = tmp + 1;
end = os_strchr(pos, ' ');
if (end)
len = end - pos;
else
len = os_strlen(pos);
if (nei_pos + len / 2 > nei_rep + nei_rep_len) {
wpa_printf(MSG_DEBUG,
"Not enough room for neighbor subelements");
return -1;
}
if (len & 0x01 ||
hexstr2bin(pos, nei_pos, len / 2) < 0) {
wpa_printf(MSG_DEBUG,
"Invalid neighbor subelement info");
return -1;
}
nei_pos += len / 2;
pos = end;
}
nei_start[1] = nei_pos - nei_start - 2;
}
return nei_pos - nei_rep;
}
int ieee802_11_ext_capab(const u8 *ie, unsigned int capab)
{
if (!ie || ie[1] <= capab / 8)
return 0;
return !!(ie[2 + capab / 8] & BIT(capab % 8));
}
bool ieee802_11_rsnx_capab_len(const u8 *rsnxe, size_t rsnxe_len,
unsigned int capab)
{
const u8 *end;
size_t flen, i;
u32 capabs = 0;
if (!rsnxe || rsnxe_len == 0)
return false;
end = rsnxe + rsnxe_len;
flen = (rsnxe[0] & 0x0f) + 1;
if (rsnxe + flen > end)
return false;
if (flen > 4)
flen = 4;
for (i = 0; i < flen; i++)
capabs |= rsnxe[i] << (8 * i);
return capabs & BIT(capab);
}
bool ieee802_11_rsnx_capab(const u8 *rsnxe, unsigned int capab)
{
return ieee802_11_rsnx_capab_len(rsnxe ? rsnxe + 2 : NULL,
rsnxe ? rsnxe[1] : 0, capab);
}
void hostapd_encode_edmg_chan(int edmg_enable, u8 edmg_channel,
int primary_channel,
struct ieee80211_edmg_config *edmg)
{
if (!edmg_enable) {
edmg->channels = 0;
edmg->bw_config = 0;
return;
}
/* Only EDMG CB1 and EDMG CB2 contiguous channels supported for now */
switch (edmg_channel) {
case EDMG_CHANNEL_9:
edmg->channels = EDMG_CHANNEL_9_SUBCHANNELS;
edmg->bw_config = EDMG_BW_CONFIG_5;
return;
case EDMG_CHANNEL_10:
edmg->channels = EDMG_CHANNEL_10_SUBCHANNELS;
edmg->bw_config = EDMG_BW_CONFIG_5;
return;
case EDMG_CHANNEL_11:
edmg->channels = EDMG_CHANNEL_11_SUBCHANNELS;
edmg->bw_config = EDMG_BW_CONFIG_5;
return;
case EDMG_CHANNEL_12:
edmg->channels = EDMG_CHANNEL_12_SUBCHANNELS;
edmg->bw_config = EDMG_BW_CONFIG_5;
return;
case EDMG_CHANNEL_13:
edmg->channels = EDMG_CHANNEL_13_SUBCHANNELS;
edmg->bw_config = EDMG_BW_CONFIG_5;
return;
default:
if (primary_channel > 0 && primary_channel < 7) {
edmg->channels = BIT(primary_channel - 1);
edmg->bw_config = EDMG_BW_CONFIG_4;
} else {
edmg->channels = 0;
edmg->bw_config = 0;
}
break;
}
}
/* Check if the requested EDMG configuration is a subset of the allowed
* EDMG configuration. */
int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed,
struct ieee80211_edmg_config requested)
{
/*
* The validation check if the requested EDMG configuration
* is a subset of the allowed EDMG configuration:
* 1. Check that the requested channels are part (set) of the allowed
* channels.
* 2. P802.11ay defines the values of bw_config between 4 and 15.
* (bw config % 4) will give us 4 groups inside bw_config definition,
* inside each group we can check the subset just by comparing the
* bw_config value.
* Between this 4 groups, there is no subset relation - as a result of
* the P802.11ay definition.
* bw_config defined by IEEE P802.11ay/D4.0, 9.4.2.251, Table 13.
*/
if (((requested.channels & allowed.channels) != requested.channels) ||
((requested.bw_config % 4) > (allowed.bw_config % 4)) ||
requested.bw_config > allowed.bw_config)
return 0;
return 1;
}
int op_class_to_bandwidth(u8 op_class)
{
switch (op_class) {
case 81:
case 82:
return 20;
case 83: /* channels 1..9; 40 MHz */
case 84: /* channels 5..13; 40 MHz */
return 40;
case 115: /* channels 36,40,44,48; indoor only */
return 20;
case 116: /* channels 36,44; 40 MHz; indoor only */
case 117: /* channels 40,48; 40 MHz; indoor only */
return 40;
case 118: /* channels 52,56,60,64; dfs */
return 20;
case 119: /* channels 52,60; 40 MHz; dfs */
case 120: /* channels 56,64; 40 MHz; dfs */
return 40;
case 121: /* channels 100-140 */
return 20;
case 122: /* channels 100-142; 40 MHz */
case 123: /* channels 104-136; 40 MHz */
return 40;
case 124: /* channels 149,153,157,161 */
case 125: /* channels 149,153,157,161,165,169,173,177 */
return 20;
case 126: /* channels 149,157,161,165,169,173; 40 MHz */
case 127: /* channels 153..177; 40 MHz */
return 40;
case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */
return 80;
case 129: /* center freqs 50, 114, 163; 160 MHz */
return 160;
case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80+80 MHz */
return 80;
case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */
return 20;
case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */
return 40;
case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
return 80;
case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
return 160;
case 136: /* UHB channels, 20 MHz: 2 */
return 20;
case 180: /* 60 GHz band, channels 1..8 */
return 2160;
case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
return 4320;
case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
return 6480;
case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
return 8640;
}
return 20;
}
int op_class_to_ch_width(u8 op_class)
{
switch (op_class) {
case 81:
case 82:
return CHANWIDTH_USE_HT;
case 83: /* channels 1..9; 40 MHz */
case 84: /* channels 5..13; 40 MHz */
return CHANWIDTH_USE_HT;
case 115: /* channels 36,40,44,48; indoor only */
return CHANWIDTH_USE_HT;
case 116: /* channels 36,44; 40 MHz; indoor only */
case 117: /* channels 40,48; 40 MHz; indoor only */
return CHANWIDTH_USE_HT;
case 118: /* channels 52,56,60,64; dfs */
return CHANWIDTH_USE_HT;
case 119: /* channels 52,60; 40 MHz; dfs */
case 120: /* channels 56,64; 40 MHz; dfs */
return CHANWIDTH_USE_HT;
case 121: /* channels 100-140 */
return CHANWIDTH_USE_HT;
case 122: /* channels 100-142; 40 MHz */
case 123: /* channels 104-136; 40 MHz */
return CHANWIDTH_USE_HT;
case 124: /* channels 149,153,157,161 */
case 125: /* channels 149,153,157,161,165,169,171 */
return CHANWIDTH_USE_HT;
case 126: /* channels 149,157,165, 173; 40 MHz */
case 127: /* channels 153,161,169,177; 40 MHz */
return CHANWIDTH_USE_HT;
case 128: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80 MHz */
return CHANWIDTH_80MHZ;
case 129: /* center freqs 50, 114, 163; 160 MHz */
return CHANWIDTH_160MHZ;
case 130: /* center freqs 42, 58, 106, 122, 138, 155, 171; 80+80 MHz */
return CHANWIDTH_80P80MHZ;
case 131: /* UHB channels, 20 MHz: 1, 5, 9.. */
return CHANWIDTH_USE_HT;
case 132: /* UHB channels, 40 MHz: 3, 11, 19.. */
return CHANWIDTH_USE_HT;
case 133: /* UHB channels, 80 MHz: 7, 23, 39.. */
return CHANWIDTH_80MHZ;
case 134: /* UHB channels, 160 MHz: 15, 47, 79.. */
return CHANWIDTH_160MHZ;
case 135: /* UHB channels, 80+80 MHz: 7, 23, 39.. */
return CHANWIDTH_80P80MHZ;
case 136: /* UHB channels, 20 MHz: 2 */
return CHANWIDTH_USE_HT;
case 180: /* 60 GHz band, channels 1..8 */
return CHANWIDTH_2160MHZ;
case 181: /* 60 GHz band, EDMG CB2, channels 9..15 */
return CHANWIDTH_4320MHZ;
case 182: /* 60 GHz band, EDMG CB3, channels 17..22 */
return CHANWIDTH_6480MHZ;
case 183: /* 60 GHz band, EDMG CB4, channel 25..29 */
return CHANWIDTH_8640MHZ;
}
return CHANWIDTH_USE_HT;
}
struct wpabuf * ieee802_11_defrag_data(struct ieee802_11_elems *elems,
u8 eid, u8 eid_ext,
const u8 *data, u8 len)
{
struct frag_ies_info *frag_ies = &elems->frag_ies;
struct wpabuf *buf;
unsigned int i;
if (!elems || !data || !len)
return NULL;
buf = wpabuf_alloc_copy(data, len);
if (!buf)
return NULL;
for (i = 0; i < frag_ies->n_frags; i++) {
int ret;
if (frag_ies->frags[i].eid != eid ||
frag_ies->frags[i].eid_ext != eid_ext)
continue;
ret = wpabuf_resize(&buf, frag_ies->frags[i].ie_len);
if (ret < 0) {
wpabuf_free(buf);
return NULL;
}
/* Copy only the fragment data (without the EID and length) */
wpabuf_put_data(buf, frag_ies->frags[i].ie,
frag_ies->frags[i].ie_len);
}
return buf;
}
struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems,
u8 eid, u8 eid_ext)
{
const u8 *data;
u8 len;
/*
* TODO: Defragmentation mechanism can be supported for all IEs. For now
* handle only those that are used (or use ieee802_11_defrag_data()).
*/
switch (eid) {
case WLAN_EID_EXTENSION:
switch (eid_ext) {
case WLAN_EID_EXT_FILS_HLP_CONTAINER:
data = elems->fils_hlp;
len = elems->fils_hlp_len;
break;
case WLAN_EID_EXT_WRAPPED_DATA:
data = elems->wrapped_data;
len = elems->wrapped_data_len;
break;
default:
wpa_printf(MSG_DEBUG,
"Defragmentation not supported. eid_ext=%u",
eid_ext);
return NULL;
}
break;
default:
wpa_printf(MSG_DEBUG,
"Defragmentation not supported. eid=%u", eid);
return NULL;
}
return ieee802_11_defrag_data(elems, eid, eid_ext, data, len);
}
diff --git a/contrib/wpa/src/common/ieee802_11_common.h b/contrib/wpa/src/common/ieee802_11_common.h
index fe2b1bca601b..e4e4c613e9c6 100644
--- a/contrib/wpa/src/common/ieee802_11_common.h
+++ b/contrib/wpa/src/common/ieee802_11_common.h
@@ -1,340 +1,342 @@
/*
* IEEE 802.11 Common routines
* Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_11_COMMON_H
#define IEEE802_11_COMMON_H
#include "defs.h"
#include "ieee802_11_defs.h"
struct element {
u8 id;
u8 datalen;
u8 data[];
} STRUCT_PACKED;
struct hostapd_hw_modes;
#define MAX_NOF_MB_IES_SUPPORTED 5
#define MAX_NUM_FRAG_IES_SUPPORTED 3
struct mb_ies_info {
struct {
const u8 *ie;
u8 ie_len;
} ies[MAX_NOF_MB_IES_SUPPORTED];
u8 nof_ies;
};
struct frag_ies_info {
struct {
u8 eid;
u8 eid_ext;
const u8 *ie;
u8 ie_len;
} frags[MAX_NUM_FRAG_IES_SUPPORTED];
u8 n_frags;
/* the last parsed element ID and element extension ID */
u8 last_eid;
u8 last_eid_ext;
};
/* Parsed Information Elements */
struct ieee802_11_elems {
const u8 *ssid;
const u8 *supp_rates;
const u8 *ds_params;
const u8 *challenge;
const u8 *erp_info;
const u8 *ext_supp_rates;
const u8 *wpa_ie;
const u8 *rsn_ie;
const u8 *rsnxe;
const u8 *wmm; /* WMM Information or Parameter Element */
const u8 *wmm_tspec;
const u8 *wps_ie;
const u8 *supp_channels;
const u8 *mdie;
const u8 *ftie;
const u8 *timeout_int;
const u8 *ht_capabilities;
const u8 *ht_operation;
const u8 *mesh_config;
const u8 *mesh_id;
const u8 *peer_mgmt;
const u8 *vht_capabilities;
const u8 *vht_operation;
const u8 *vht_opmode_notif;
const u8 *vendor_ht_cap;
const u8 *vendor_vht;
const u8 *p2p;
const u8 *wfd;
const u8 *link_id;
const u8 *interworking;
const u8 *qos_map_set;
const u8 *hs20;
const u8 *ext_capab;
const u8 *bss_max_idle_period;
const u8 *ssid_list;
const u8 *osen;
const u8 *mbo;
const u8 *ampe;
const u8 *mic;
const u8 *pref_freq_list;
const u8 *supp_op_classes;
const u8 *rrm_enabled;
const u8 *cag_number;
const u8 *ap_csn;
const u8 *fils_indic;
const u8 *dils;
const u8 *assoc_delay_info;
const u8 *fils_req_params;
const u8 *fils_key_confirm;
const u8 *fils_session;
const u8 *fils_hlp;
const u8 *fils_ip_addr_assign;
const u8 *key_delivery;
const u8 *wrapped_data;
const u8 *fils_pk;
const u8 *fils_nonce;
const u8 *owe_dh;
const u8 *power_capab;
const u8 *roaming_cons_sel;
const u8 *password_id;
const u8 *oci;
const u8 *multi_ap;
const u8 *he_capabilities;
const u8 *he_operation;
const u8 *short_ssid_list;
const u8 *he_6ghz_band_cap;
const u8 *sae_pk;
const u8 *s1g_capab;
const u8 *pasn_params;
u8 ssid_len;
u8 supp_rates_len;
u8 challenge_len;
u8 ext_supp_rates_len;
u8 wpa_ie_len;
u8 rsn_ie_len;
u8 rsnxe_len;
u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */
u8 wmm_tspec_len;
u8 wps_ie_len;
u8 supp_channels_len;
u8 mdie_len;
u8 ftie_len;
u8 mesh_config_len;
u8 mesh_id_len;
u8 peer_mgmt_len;
u8 vendor_ht_cap_len;
u8 vendor_vht_len;
u8 p2p_len;
u8 wfd_len;
u8 interworking_len;
u8 qos_map_set_len;
u8 hs20_len;
u8 ext_capab_len;
u8 ssid_list_len;
u8 osen_len;
u8 mbo_len;
u8 ampe_len;
u8 mic_len;
u8 pref_freq_list_len;
u8 supp_op_classes_len;
u8 rrm_enabled_len;
u8 cag_number_len;
u8 fils_indic_len;
u8 dils_len;
u8 fils_req_params_len;
u8 fils_key_confirm_len;
u8 fils_hlp_len;
u8 fils_ip_addr_assign_len;
u8 key_delivery_len;
u8 wrapped_data_len;
u8 fils_pk_len;
u8 owe_dh_len;
u8 power_capab_len;
u8 roaming_cons_sel_len;
u8 password_id_len;
u8 oci_len;
u8 multi_ap_len;
u8 he_capabilities_len;
u8 he_operation_len;
u8 short_ssid_list_len;
u8 sae_pk_len;
u8 pasn_params_len;
struct mb_ies_info mb_ies;
struct frag_ies_info frag_ies;
};
typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
struct ieee802_11_elems *elems,
int show_errors);
int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
u32 oui_type);
struct ieee80211_hdr;
const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len);
struct hostapd_wmm_ac_params {
int cwmin;
int cwmax;
int aifs;
int txop_limit; /* in units of 32us */
int admission_control_mandatory;
};
int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
const char *name, const char *val);
struct hostapd_tx_queue_params {
int aifs;
int cwmin;
int cwmax;
int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */
};
#define NUM_TX_QUEUES 4
int hostapd_config_tx_queue(struct hostapd_tx_queue_params queue[],
const char *name, const char *val);
enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
int sec_channel, int vht,
u8 *op_class, u8 *channel);
int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
int sec_channel, u8 *op_class, u8 *channel);
int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
u16 num_modes);
+int is_dfs_global_op_class(u8 op_class);
enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht);
int supp_rates_11b_only(struct ieee802_11_elems *elems);
int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
size_t ies_len);
struct wpabuf * mb_ies_by_info(struct mb_ies_info *info);
const char * fc2str(u16 fc);
const char * reason2str(u16 reason);
const char * status2str(u16 status);
struct oper_class_map {
enum hostapd_hw_mode mode;
u8 op_class;
u8 min_chan;
u8 max_chan;
u8 inc;
enum { BW20, BW40PLUS, BW40MINUS, BW40, BW80, BW2160, BW160, BW80P80,
BW4320, BW6480, BW8640} bw;
enum { P2P_SUPP, NO_P2P_SUPP } p2p;
};
extern const struct oper_class_map global_op_class[];
extern size_t global_op_class_size;
const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext);
const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type);
size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value);
struct country_op_class {
u8 country_op_class;
u8 global_op_class;
};
u8 country_to_global_op_class(const char *country, u8 op_class);
const struct oper_class_map * get_oper_class(const char *country, u8 op_class);
int oper_class_bw_to_int(const struct oper_class_map *map);
int center_idx_to_bw_6ghz(u8 idx);
bool is_6ghz_freq(int freq);
bool is_6ghz_op_class(u8 op_class);
bool is_6ghz_psc_frequency(int freq);
+int get_6ghz_sec_channel(int channel);
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len);
int ieee802_11_ext_capab(const u8 *ie, unsigned int capab);
bool ieee802_11_rsnx_capab_len(const u8 *rsnxe, size_t rsnxe_len,
unsigned int capab);
bool ieee802_11_rsnx_capab(const u8 *rsnxe, unsigned int capab);
int op_class_to_bandwidth(u8 op_class);
int op_class_to_ch_width(u8 op_class);
/* element iteration helpers */
#define for_each_element(_elem, _data, _datalen) \
for (_elem = (const struct element *) (_data); \
(const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \
(int) sizeof(*_elem) && \
(const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \
(int) sizeof(*_elem) + _elem->datalen; \
_elem = (const struct element *) (_elem->data + _elem->datalen))
#define for_each_element_id(element, _id, data, datalen) \
for_each_element(element, data, datalen) \
if (element->id == (_id))
#define for_each_element_extid(element, extid, _data, _datalen) \
for_each_element(element, _data, _datalen) \
if (element->id == WLAN_EID_EXTENSION && \
element->datalen > 0 && \
element->data[0] == (extid))
#define for_each_subelement(sub, element) \
for_each_element(sub, (element)->data, (element)->datalen)
#define for_each_subelement_id(sub, id, element) \
for_each_element_id(sub, id, (element)->data, (element)->datalen)
#define for_each_subelement_extid(sub, extid, element) \
for_each_element_extid(sub, extid, (element)->data, (element)->datalen)
/**
* for_each_element_completed - Determine if element parsing consumed all data
* @element: Element pointer after for_each_element() or friends
* @data: Same data pointer as passed to for_each_element() or friends
* @datalen: Same data length as passed to for_each_element() or friends
*
* This function returns 1 if all the data was parsed or considered
* while walking the elements. Only use this if your for_each_element()
* loop cannot be broken out of, otherwise it always returns 0.
*
* If some data was malformed, this returns %false since the last parsed
* element will not fill the whole remaining data.
*/
static inline int for_each_element_completed(const struct element *element,
const void *data, size_t datalen)
{
return (const u8 *) element == (const u8 *) data + datalen;
}
struct ieee80211_edmg_config;
void hostapd_encode_edmg_chan(int edmg_enable, u8 edmg_channel,
int primary_channel,
struct ieee80211_edmg_config *edmg);
int ieee802_edmg_is_allowed(struct ieee80211_edmg_config allowed,
struct ieee80211_edmg_config requested);
struct wpabuf * ieee802_11_defrag_data(struct ieee802_11_elems *elems,
u8 eid, u8 eid_ext,
const u8 *data, u8 len);
struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems,
u8 eid, u8 eid_ext);
#endif /* IEEE802_11_COMMON_H */
diff --git a/contrib/wpa/src/common/ieee802_11_defs.h b/contrib/wpa/src/common/ieee802_11_defs.h
index 519a13b1d064..928b53500c5c 100644
--- a/contrib/wpa/src/common/ieee802_11_defs.h
+++ b/contrib/wpa/src/common/ieee802_11_defs.h
@@ -1,2448 +1,2504 @@
/*
* IEEE 802.11 Frame type definitions
* Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2008 Intel Corporation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_11_DEFS_H
#define IEEE802_11_DEFS_H
#include <utils/common.h>
/* IEEE 802.11 defines */
#define WLAN_FC_PVER 0x0003
#define WLAN_FC_TODS 0x0100
#define WLAN_FC_FROMDS 0x0200
#define WLAN_FC_MOREFRAG 0x0400
#define WLAN_FC_RETRY 0x0800
#define WLAN_FC_PWRMGT 0x1000
#define WLAN_FC_MOREDATA 0x2000
#define WLAN_FC_ISWEP 0x4000
#define WLAN_FC_HTC 0x8000
#define WLAN_FC_GET_TYPE(fc) (((fc) & 0x000c) >> 2)
#define WLAN_FC_GET_STYPE(fc) (((fc) & 0x00f0) >> 4)
#define WLAN_INVALID_MGMT_SEQ 0xFFFF
#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
#define WLAN_GET_SEQ_SEQ(seq) \
(((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
#define WLAN_FC_TYPE_MGMT 0
#define WLAN_FC_TYPE_CTRL 1
#define WLAN_FC_TYPE_DATA 2
/* management */
#define WLAN_FC_STYPE_ASSOC_REQ 0
#define WLAN_FC_STYPE_ASSOC_RESP 1
#define WLAN_FC_STYPE_REASSOC_REQ 2
#define WLAN_FC_STYPE_REASSOC_RESP 3
#define WLAN_FC_STYPE_PROBE_REQ 4
#define WLAN_FC_STYPE_PROBE_RESP 5
#define WLAN_FC_STYPE_BEACON 8
#define WLAN_FC_STYPE_ATIM 9
#define WLAN_FC_STYPE_DISASSOC 10
#define WLAN_FC_STYPE_AUTH 11
#define WLAN_FC_STYPE_DEAUTH 12
#define WLAN_FC_STYPE_ACTION 13
#define WLAN_FC_STYPE_ACTION_NO_ACK 14
/* control */
#define WLAN_FC_STYPE_PSPOLL 10
#define WLAN_FC_STYPE_RTS 11
#define WLAN_FC_STYPE_CTS 12
#define WLAN_FC_STYPE_ACK 13
#define WLAN_FC_STYPE_CFEND 14
#define WLAN_FC_STYPE_CFENDACK 15
/* data */
#define WLAN_FC_STYPE_DATA 0
#define WLAN_FC_STYPE_DATA_CFACK 1
#define WLAN_FC_STYPE_DATA_CFPOLL 2
#define WLAN_FC_STYPE_DATA_CFACKPOLL 3
#define WLAN_FC_STYPE_NULLFUNC 4
#define WLAN_FC_STYPE_CFACK 5
#define WLAN_FC_STYPE_CFPOLL 6
#define WLAN_FC_STYPE_CFACKPOLL 7
#define WLAN_FC_STYPE_QOS_DATA 8
#define WLAN_FC_STYPE_QOS_DATA_CFACK 9
#define WLAN_FC_STYPE_QOS_DATA_CFPOLL 10
#define WLAN_FC_STYPE_QOS_DATA_CFACKPOLL 11
#define WLAN_FC_STYPE_QOS_NULL 12
#define WLAN_FC_STYPE_QOS_CFPOLL 14
#define WLAN_FC_STYPE_QOS_CFACKPOLL 15
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
#define WLAN_AUTH_FT 2
#define WLAN_AUTH_SAE 3
#define WLAN_AUTH_FILS_SK 4
#define WLAN_AUTH_FILS_SK_PFS 5
#define WLAN_AUTH_FILS_PK 6
#define WLAN_AUTH_PASN 7
#define WLAN_AUTH_LEAP 128
#define WLAN_AUTH_CHALLENGE_LEN 128
#define WLAN_CAPABILITY_ESS BIT(0)
#define WLAN_CAPABILITY_IBSS BIT(1)
#define WLAN_CAPABILITY_CF_POLLABLE BIT(2)
#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3)
#define WLAN_CAPABILITY_PRIVACY BIT(4)
#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5)
#define WLAN_CAPABILITY_PBCC BIT(6)
#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7)
#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8)
#define WLAN_CAPABILITY_QOS BIT(9)
#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10)
#define WLAN_CAPABILITY_APSD BIT(11)
#define WLAN_CAPABILITY_RADIO_MEASUREMENT BIT(12)
#define WLAN_CAPABILITY_DSSS_OFDM BIT(13)
#define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14)
#define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15)
/* Status codes (IEEE Std 802.11-2016, 9.4.1.9, Table 9-46) */
#define WLAN_STATUS_SUCCESS 0
#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
#define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
#define WLAN_STATUS_TDLS_WAKEUP_REJECT 3
#define WLAN_STATUS_SECURITY_DISABLED 5
#define WLAN_STATUS_UNACCEPTABLE_LIFETIME 6
#define WLAN_STATUS_NOT_IN_SAME_BSS 7
#define WLAN_STATUS_CAPS_UNSUPPORTED 10
#define WLAN_STATUS_REASSOC_NO_ASSOC 11
#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
#define WLAN_STATUS_CHALLENGE_FAIL 15
#define WLAN_STATUS_AUTH_TIMEOUT 16
#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
#define WLAN_STATUS_ASSOC_DENIED_RATES 18
#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22
#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23
#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
#define WLAN_STATUS_ASSOC_DENIED_NO_HT 27
#define WLAN_STATUS_R0KH_UNREACHABLE 28
#define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29
#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32
#define WLAN_STATUS_DENIED_INSUFFICIENT_BANDWIDTH 33
#define WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS 34
#define WLAN_STATUS_DENIED_QOS_NOT_SUPPORTED 35
#define WLAN_STATUS_REQUEST_DECLINED 37
#define WLAN_STATUS_INVALID_PARAMETERS 38
#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES 39
#define WLAN_STATUS_INVALID_IE 40
#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
#define WLAN_STATUS_AKMP_NOT_VALID 43
#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44
#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45
#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46
#define WLAN_STATUS_TS_NOT_CREATED 47
#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48
#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50
#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
#define WLAN_STATUS_INVALID_PMKID 53
#define WLAN_STATUS_INVALID_MDIE 54
#define WLAN_STATUS_INVALID_FTIE 55
#define WLAN_STATUS_REQUESTED_TCLAS_NOT_SUPPORTED 56
#define WLAN_STATUS_INSUFFICIENT_TCLAS_PROCESSING_RESOURCES 57
#define WLAN_STATUS_TRY_ANOTHER_BSS 58
#define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59
#define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60
#define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61
#define WLAN_STATUS_STA_TIMED_OUT_WAITING_FOR_GAS_RESP 62
#define WLAN_STATUS_GAS_RESP_LARGER_THAN_LIMIT 63
#define WLAN_STATUS_REQ_REFUSED_HOME 64
#define WLAN_STATUS_ADV_SRV_UNREACHABLE 65
#define WLAN_STATUS_REQ_REFUSED_SSPN 67
#define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
#define WLAN_STATUS_INVALID_RSNIE 72
#define WLAN_STATUS_U_APSD_COEX_NOT_SUPPORTED 73
#define WLAN_STATUS_U_APSD_COEX_MODE_NOT_SUPPORTED 74
#define WLAN_STATUS_BAD_INTERVAL_WITH_U_APSD_COEX 75
#define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
#define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
#define WLAN_STATUS_CANNOT_FIND_ALT_TBTT 78
#define WLAN_STATUS_TRANSMISSION_FAILURE 79
#define WLAN_STATUS_REQ_TCLAS_NOT_SUPPORTED 80
#define WLAN_STATUS_TCLAS_RESOURCES_EXCHAUSTED 81
#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82
#define WLAN_STATUS_REJECT_WITH_SCHEDULE 83
#define WLAN_STATUS_REJECT_NO_WAKEUP_SPECIFIED 84
#define WLAN_STATUS_SUCCESS_POWER_SAVE_MODE 85
#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86
#define WLAN_STATUS_PERFORMING_FST_NOW 87
#define WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW 88
#define WLAN_STATUS_REJECT_U_PID_SETTING 89
#define WLAN_STATUS_REFUSED_EXTERNAL_REASON 92
#define WLAN_STATUS_REFUSED_AP_OUT_OF_MEMORY 93
#define WLAN_STATUS_REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED 94
#define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
#define WLAN_STATUS_REJECT_DSE_BAND 96
#define WLAN_STATUS_TCLAS_PROCESSING_TERMINATED 97
#define WLAN_STATUS_TS_SCHEDULE_CONFLICT 98
#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
#define WLAN_STATUS_MCCAOP_RESERVATION_CONFLICT 100
#define WLAN_STATUS_MAF_LIMIT_EXCEEDED 101
#define WLAN_STATUS_MCCA_TRACK_LIMIT_EXCEEDED 102
#define WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT 103
#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
#define WLAN_STATUS_ENABLEMENT_DENIED 105
#define WLAN_STATUS_RESTRICTION_FROM_AUTHORIZED_GDB 106
#define WLAN_STATUS_AUTHORIZATION_DEENABLED 107
#define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
#define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
#define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123
#define WLAN_STATUS_DENIED_HE_NOT_SUPPORTED 124
#define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126
#define WLAN_STATUS_SAE_PK 127
/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
#define WLAN_REASON_UNSPECIFIED 1
#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
#define WLAN_REASON_DEAUTH_LEAVING 3
#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
#define WLAN_REASON_DISASSOC_AP_BUSY 5
#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10
#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11
#define WLAN_REASON_BSS_TRANSITION_DISASSOC 12
#define WLAN_REASON_INVALID_IE 13
#define WLAN_REASON_MICHAEL_MIC_FAILURE 14
#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16
#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17
#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18
#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19
#define WLAN_REASON_AKMP_NOT_VALID 20
#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21
#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
#define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25
#define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26
#define WLAN_REASON_SSP_REQUESTED_DISASSOC 27
#define WLAN_REASON_NO_SSP_ROAMING_AGREEMENT 28
#define WLAN_REASON_BAD_CIPHER_OR_AKM 29
#define WLAN_REASON_NOT_AUTHORIZED_THIS_LOCATION 30
#define WLAN_REASON_SERVICE_CHANGE_PRECLUDES_TS 31
#define WLAN_REASON_UNSPECIFIED_QOS_REASON 32
#define WLAN_REASON_NOT_ENOUGH_BANDWIDTH 33
#define WLAN_REASON_DISASSOC_LOW_ACK 34
#define WLAN_REASON_EXCEEDED_TXOP 35
#define WLAN_REASON_STA_LEAVING 36
#define WLAN_REASON_END_TS_BA_DLS 37
#define WLAN_REASON_UNKNOWN_TS_BA 38
#define WLAN_REASON_TIMEOUT 39
#define WLAN_REASON_PEERKEY_MISMATCH 45
#define WLAN_REASON_AUTHORIZED_ACCESS_LIMIT_REACHED 46
#define WLAN_REASON_EXTERNAL_SERVICE_REQUIREMENTS 47
#define WLAN_REASON_INVALID_FT_ACTION_FRAME_COUNT 48
#define WLAN_REASON_INVALID_PMKID 49
#define WLAN_REASON_INVALID_MDE 50
#define WLAN_REASON_INVALID_FTE 51
#define WLAN_REASON_MESH_PEERING_CANCELLED 52
#define WLAN_REASON_MESH_MAX_PEERS 53
#define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54
#define WLAN_REASON_MESH_CLOSE_RCVD 55
#define WLAN_REASON_MESH_MAX_RETRIES 56
#define WLAN_REASON_MESH_CONFIRM_TIMEOUT 57
#define WLAN_REASON_MESH_INVALID_GTK 58
#define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59
#define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60
#define WLAN_REASON_MESH_PATH_ERROR_NO_PROXY_INFO 61
#define WLAN_REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO 62
#define WLAN_REASON_MESH_PATH_ERROR_DEST_UNREACHABLE 63
#define WLAN_REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS 64
#define WLAN_REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ 65
#define WLAN_REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED 66
/* Information Element IDs (IEEE Std 802.11-2016, 9.4.2.1, Table 9-77) */
#define WLAN_EID_SSID 0
#define WLAN_EID_SUPP_RATES 1
#define WLAN_EID_DS_PARAMS 3
#define WLAN_EID_CF_PARAMS 4
#define WLAN_EID_TIM 5
#define WLAN_EID_IBSS_PARAMS 6
#define WLAN_EID_COUNTRY 7
#define WLAN_EID_REQUEST 10
#define WLAN_EID_BSS_LOAD 11
#define WLAN_EID_EDCA_PARAM_SET 12
#define WLAN_EID_TSPEC 13
#define WLAN_EID_TCLAS 14
#define WLAN_EID_SCHEDULE 15
#define WLAN_EID_CHALLENGE 16
#define WLAN_EID_PWR_CONSTRAINT 32
#define WLAN_EID_PWR_CAPABILITY 33
#define WLAN_EID_TPC_REQUEST 34
#define WLAN_EID_TPC_REPORT 35
#define WLAN_EID_SUPPORTED_CHANNELS 36
#define WLAN_EID_CHANNEL_SWITCH 37
#define WLAN_EID_MEASURE_REQUEST 38
#define WLAN_EID_MEASURE_REPORT 39
#define WLAN_EID_QUIET 40
#define WLAN_EID_IBSS_DFS 41
#define WLAN_EID_ERP_INFO 42
#define WLAN_EID_TS_DELAY 43
#define WLAN_EID_TCLAS_PROCESSING 44
#define WLAN_EID_HT_CAP 45
#define WLAN_EID_QOS 46
#define WLAN_EID_RSN 48
#define WLAN_EID_EXT_SUPP_RATES 50
#define WLAN_EID_AP_CHANNEL_REPORT 51
#define WLAN_EID_NEIGHBOR_REPORT 52
#define WLAN_EID_RCPI 53
#define WLAN_EID_MOBILITY_DOMAIN 54
#define WLAN_EID_FAST_BSS_TRANSITION 55
#define WLAN_EID_TIMEOUT_INTERVAL 56
#define WLAN_EID_RIC_DATA 57
#define WLAN_EID_DSE_REGISTERED_LOCATION 58
#define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
#define WLAN_EID_EXT_CHANSWITCH_ANN 60
#define WLAN_EID_HT_OPERATION 61
#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
#define WLAN_EID_BSS_AVERAGE_ACCESS_DELAY 63
#define WLAN_EID_ANTENNA 64
#define WLAN_EID_RSNI 65
#define WLAN_EID_MEASUREMENT_PILOT_TRANSMISSION 66
#define WLAN_EID_BSS_AVAILABLE_ADM_CAPA 67
#define WLAN_EID_BSS_AC_ACCESS_DELAY 68 /* note: also used by WAPI */
#define WLAN_EID_TIME_ADVERTISEMENT 69
#define WLAN_EID_RRM_ENABLED_CAPABILITIES 70
#define WLAN_EID_MULTIPLE_BSSID 71
#define WLAN_EID_20_40_BSS_COEXISTENCE 72
#define WLAN_EID_20_40_BSS_INTOLERANT 73
#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
#define WLAN_EID_RIC_DESCRIPTOR 75
#define WLAN_EID_MMIE 76
#define WLAN_EID_EVENT_REQUEST 78
#define WLAN_EID_EVENT_REPORT 79
#define WLAN_EID_DIAGNOSTIC_REQUEST 80
#define WLAN_EID_DIAGNOSTIC_REPORT 81
#define WLAN_EID_LOCATION_PARAMETERS 82
#define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83
#define WLAN_EID_SSID_LIST 84
#define WLAN_EID_MULTIPLE_BSSID_INDEX 85
#define WLAN_EID_FMS_DESCRIPTOR 86
#define WLAN_EID_FMS_REQUEST 87
#define WLAN_EID_FMS_RESPONSE 88
#define WLAN_EID_QOS_TRAFFIC_CAPABILITY 89
#define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
#define WLAN_EID_TFS_REQ 91
#define WLAN_EID_TFS_RESP 92
#define WLAN_EID_WNMSLEEP 93
#define WLAN_EID_TIM_BROADCAST_REQUEST 94
#define WLAN_EID_TIM_BROADCAST_RESPONSE 95
#define WLAN_EID_COLLOCATED_INTERFERENCE_REPORT 96
#define WLAN_EID_CHANNEL_USAGE 97
#define WLAN_EID_TIME_ZONE 98
#define WLAN_EID_DMS_REQUEST 99
#define WLAN_EID_DMS_RESPONSE 100
#define WLAN_EID_LINK_ID 101
#define WLAN_EID_WAKEUP_SCHEDULE 102
#define WLAN_EID_CHANNEL_SWITCH_TIMING 104
#define WLAN_EID_PTI_CONTROL 105
#define WLAN_EID_TPU_BUFFER_STATUS 106
#define WLAN_EID_INTERWORKING 107
#define WLAN_EID_ADV_PROTO 108
#define WLAN_EID_EXPEDITED_BANDWIDTH_REQ 109
#define WLAN_EID_QOS_MAP_SET 110
#define WLAN_EID_ROAMING_CONSORTIUM 111
#define WLAN_EID_EMERGENCY_ALERT_ID 112
#define WLAN_EID_MESH_CONFIG 113
#define WLAN_EID_MESH_ID 114
#define WLAN_EID_MESH_LINK_METRIC_REPORT 115
#define WLAN_EID_CONGESTION_NOTIFICATION 116
#define WLAN_EID_PEER_MGMT 117
#define WLAN_EID_MESH_CHANNEL_SWITCH_PARAMETERS 118
#define WLAN_EID_MESH_AWAKE_WINDOW 119
#define WLAN_EID_BEACON_TIMING 120
#define WLAN_EID_MCCAOP_SETUP_REQUEST 121
#define WLAN_EID_MCCAOP_SETUP_REPLY 122
#define WLAN_EID_MCCAOP_ADVERTISEMENT 123
#define WLAN_EID_MCCAOP_TEARDOWN 124
#define WLAN_EID_GANN 125
#define WLAN_EID_RANN 126
#define WLAN_EID_EXT_CAPAB 127
#define WLAN_EID_PREQ 130
#define WLAN_EID_PREP 131
#define WLAN_EID_PERR 132
#define WLAN_EID_PXU 137
#define WLAN_EID_PXUC 138
#define WLAN_EID_AMPE 139
#define WLAN_EID_MIC 140
#define WLAN_EID_DESTINATION_URI 141
#define WLAN_EID_U_APSD_COEX 142
#define WLAN_EID_DMG_WAKEUP_SCHEDULE 143
#define WLAN_EID_EXTENDED_SCHEDULE 144
#define WLAN_EID_STA_AVAILABILITY 145
#define WLAN_EID_DMG_TSPEC 146
#define WLAN_EID_NEXT_DMG_ATI 147
#define WLAN_EID_DMG_CAPABILITIES 148
#define WLAN_EID_DMG_OPERATION 151
#define WLAN_EID_DMG_BSS_PARAMETER_CHANGE 152
#define WLAN_EID_DMG_BEAM_REFINEMENT 153
#define WLAN_EID_CHANNEL_MEASUREMENT_FEEDBACK 154
#define WLAN_EID_CCKM 156
#define WLAN_EID_AWAKE_WINDOW 157
#define WLAN_EID_MULTI_BAND 158
#define WLAN_EID_ADDBA_EXTENSION 159
#define WLAN_EID_NEXTPCP_LIST 160
#define WLAN_EID_PCP_HANDOVER 161
#define WLAN_EID_DMG_LINK_MARGIN 162
#define WLAN_EID_SWITCHING_STREAM 163
#define WLAN_EID_SESSION_TRANSITION 164
#define WLAN_EID_DYNAMIC_TONE_PAIRING_REPORT 165
#define WLAN_EID_CLUSTER_REPORT 166
#define WLAN_EID_REPLAY_CAPABILITIES 167
#define WLAN_EID_RELAY_TRANSFER_PARAM_SET 168
#define WLAN_EID_BEAMLINK_MAINTENANCE 169
#define WLAN_EID_MULTIPLE_MAC_SUBLAYERS 170
#define WLAN_EID_U_PID 171
#define WLAN_EID_DMG_LINK_ADAPTATION_ACK 172
#define WLAN_EID_MCCAOP_ADVERTISEMENT_OVERVIEW 174
#define WLAN_EID_QUIET_PERIOD_REQUEST 175
#define WLAN_EID_QUIET_PERIOD_RESPONSE 177
#define WLAN_EID_QMF_POLICY 181
#define WLAN_EID_ECAPC_POLICY 182
#define WLAN_EID_CLUSTER_TIME_OFFSET 183
#define WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY 184
#define WLAN_EID_SCS_DESCRIPTOR 185
#define WLAN_EID_QLOAD_REPORT 186
#define WLAN_EID_HCCA_TXOP_UPDATE_COUNT 187
#define WLAN_EID_HIGHER_LAYER_STREAM_ID 188
#define WLAN_EID_GCR_GROUP_ADDRESS 189
#define WLAN_EID_ANTENNA_SECTOR_ID_PATTERN 190
#define WLAN_EID_VHT_CAP 191
#define WLAN_EID_VHT_OPERATION 192
#define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
#define WLAN_EID_VHT_WIDE_BW_CHSWITCH 194
#define WLAN_EID_TRANSMIT_POWER_ENVELOPE 195
#define WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER 196
#define WLAN_EID_VHT_AID 197
#define WLAN_EID_VHT_QUIET_CHANNEL 198
#define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
#define WLAN_EID_UPSIM 200
#define WLAN_EID_REDUCED_NEIGHBOR_REPORT 201
#define WLAN_EID_TVHT_OPERATION 202
#define WLAN_EID_DEVICE_LOCATION 204
#define WLAN_EID_WHITE_SPACE_MAP 205
#define WLAN_EID_FTM_PARAMETERS 206
#define WLAN_EID_S1G_BCN_COMPAT 213
#define WLAN_EID_TWT 216
#define WLAN_EID_S1G_CAPABILITIES 217
#define WLAN_EID_VENDOR_SPECIFIC 221
#define WLAN_EID_S1G_OPERATION 232
#define WLAN_EID_CAG_NUMBER 237
#define WLAN_EID_AP_CSN 239
#define WLAN_EID_FILS_INDICATION 240
#define WLAN_EID_DILS 241
#define WLAN_EID_FRAGMENT 242
#define WLAN_EID_RSNX 244
#define WLAN_EID_EXTENSION 255
/* Element ID Extension (EID 255) values */
#define WLAN_EID_EXT_ASSOC_DELAY_INFO 1
#define WLAN_EID_EXT_FILS_REQ_PARAMS 2
#define WLAN_EID_EXT_FILS_KEY_CONFIRM 3
#define WLAN_EID_EXT_FILS_SESSION 4
#define WLAN_EID_EXT_FILS_HLP_CONTAINER 5
#define WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN 6
#define WLAN_EID_EXT_KEY_DELIVERY 7
#define WLAN_EID_EXT_WRAPPED_DATA 8
#define WLAN_EID_EXT_FTM_SYNC_INFO 9
#define WLAN_EID_EXT_EXTENDED_REQUEST 10
#define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11
#define WLAN_EID_EXT_FILS_PUBLIC_KEY 12
#define WLAN_EID_EXT_FILS_NONCE 13
#define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14
#define WLAN_EID_EXT_OWE_DH_PARAM 32
#define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33
#define WLAN_EID_EXT_HE_CAPABILITIES 35
#define WLAN_EID_EXT_HE_OPERATION 36
#define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38
#define WLAN_EID_EXT_SPATIAL_REUSE 39
#define WLAN_EID_EXT_OCV_OCI 54
#define WLAN_EID_EXT_SHORT_SSID_LIST 58
#define WLAN_EID_EXT_HE_6GHZ_BAND_CAP 59
#define WLAN_EID_EXT_EDMG_CAPABILITIES 61
#define WLAN_EID_EXT_EDMG_OPERATION 62
#define WLAN_EID_EXT_MSCS_DESCRIPTOR 88
#define WLAN_EID_EXT_TCLAS_MASK 89
#define WLAN_EID_EXT_REJECTED_GROUPS 92
#define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93
#define WLAN_EID_EXT_PASN_PARAMS 100
/* Extended Capabilities field */
#define WLAN_EXT_CAPAB_20_40_COEX 0
#define WLAN_EXT_CAPAB_GLK 1
#define WLAN_EXT_CAPAB_EXT_CHAN_SWITCH 2
#define WLAN_EXT_CAPAB_GLK_GCR 3
#define WLAN_EXT_CAPAB_PSMP 4
/* 5 - Reserved */
#define WLAN_EXT_CAPAB_S_PSMP 6
#define WLAN_EXT_CAPAB_EVENT 7
#define WLAN_EXT_CAPAB_DIAGNOSTICS 8
#define WLAN_EXT_CAPAB_MULTICAST_DIAGNOSTICS 9
#define WLAN_EXT_CAPAB_LOCATION_TRACKING 10
#define WLAN_EXT_CAPAB_FMS 11
#define WLAN_EXT_CAPAB_PROXY_ARP 12
#define WLAN_EXT_CAPAB_COLL_INTERF_REP 13
#define WLAN_EXT_CAPAB_CIVIC_LOCATION 14
#define WLAN_EXT_CAPAB_GEOSPATIAL_LOCATION 15
#define WLAN_EXT_CAPAB_TFS 16
#define WLAN_EXT_CAPAB_WNM_SLEEP_MODE 17
#define WLAN_EXT_CAPAB_TIM_BROADCAST 18
#define WLAN_EXT_CAPAB_BSS_TRANSITION 19
#define WLAN_EXT_CAPAB_QOS_TRAFFIC 20
#define WLAN_EXT_CAPAB_AC_STA_COUNT 21
#define WLAN_EXT_CAPAB_MULTIPLE_BSSID 22
#define WLAN_EXT_CAPAB_TIMING_MEASUREMENT 23
#define WLAN_EXT_CAPAB_CHANNEL_USAGE 24
#define WLAN_EXT_CAPAB_SSID_LIST 25
#define WLAN_EXT_CAPAB_DMS 26
#define WLAN_EXT_CAPAB_UTF_TSF_OFFSET 27
#define WLAN_EXT_CAPAB_TPU_BUFFER_STA 28
#define WLAN_EXT_CAPAB_TDLS_PEER_PSM 29
#define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH 30
#define WLAN_EXT_CAPAB_INTERWORKING 31
#define WLAN_EXT_CAPAB_QOS_MAP 32
#define WLAN_EXT_CAPAB_EBR 33
#define WLAN_EXT_CAPAB_SSPN_INTERFACE 34
/* 35 - Reserved */
#define WLAN_EXT_CAPAB_MSGCF 36
#define WLAN_EXT_CAPAB_TDLS 37
#define WLAN_EXT_CAPAB_TDLS_PROHIBITED 38
#define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH_PROHIBITED 39
#define WLAN_EXT_CAPAB_REJECT_UNADMITTED_FRAME 40
/* 41-43 - Service Interval Granularity */
#define WLAN_EXT_CAPAB_IDENTIFIER_LOCATION 44
#define WLAN_EXT_CAPAB_U_APSD_COEX 45
#define WLAN_EXT_CAPAB_WNM_NOTIFCATION 46
#define WLAN_EXT_CAPAB_QAB 47
#define WLAN_EXT_CAPAB_UTF_8_SSID 48
#define WLAN_EXT_CAPAB_QMF 49
#define WLAN_EXT_CAPAB_QMF_RECONFIG 50
#define WLAN_EXT_CAPAB_ROBUST_AV_STREAMING 51
#define WLAN_EXT_CAPAB_ADVANCED_GCR 52
#define WLAN_EXT_CAPAB_MESH_GCR 53
#define WLAN_EXT_CAPAB_SCS 54
#define WLAN_EXT_CAPAB_QLOAD_REPORT 55
#define WLAN_EXT_CAPAB_ALT_EDCA 56
#define WLAN_EXT_CAPAB_UNPROT_TXOP_NEG 57
#define WLAN_EXT_CAPAB_PROT_TXOP_NEG 58
/* 59 - Reserved */
#define WLAN_EXT_CAPAB_PROT_QLOAD_REPORT 60
#define WLAN_EXT_CAPAB_TDLS_WIDER_BW 61
#define WLAN_EXT_CAPAB_OPMODE_NOTIF 62
/* 63-64 - Max Number of MSDUs In A-MSDU */
#define WLAN_EXT_CAPAB_CHANNEL_SCHEDULE_MGMT 65
#define WLAN_EXT_CAPAB_GEODB_INBAND_ENABLING_SIGNAL 66
#define WLAN_EXT_CAPAB_NETWORK_CHANNEL_CTRL 67
#define WLAN_EXT_CAPAB_WHITE_SPACE_MAP 68
#define WLAN_EXT_CAPAB_CHANNEL_AVAIL_QUERY 69
#define WLAN_EXT_CAPAB_FTM_RESPONDER 70
#define WLAN_EXT_CAPAB_FTM_INITIATOR 71
#define WLAN_EXT_CAPAB_FILS 72
#define WLAN_EXT_CAPAB_EXT_SPECTRUM_MGMT 73
#define WLAN_EXT_CAPAB_FUTURE_CHANNEL_GUIDANCE 74
#define WLAN_EXT_CAPAB_PAD 75
/* 76-79 - Reserved */
#define WLAN_EXT_CAPAB_COMPLETE_NON_TX_BSSID_PROFILE 80
#define WLAN_EXT_CAPAB_SAE_PW_ID 81
#define WLAN_EXT_CAPAB_SAE_PW_ID_EXCLUSIVELY 82
#define WLAN_EXT_CAPAB_BEACON_PROTECTION 84
#define WLAN_EXT_CAPAB_MSCS 85
#define WLAN_EXT_CAPAB_SAE_PK_EXCLUSIVELY 88
/* Extended RSN Capabilities */
/* bits 0-3: Field length (n-1) */
#define WLAN_RSNX_CAPAB_PROTECTED_TWT 4
#define WLAN_RSNX_CAPAB_SAE_H2E 5
#define WLAN_RSNX_CAPAB_SAE_PK 6
#define WLAN_RSNX_CAPAB_SECURE_LTF 8
#define WLAN_RSNX_CAPAB_SECURE_RTT 9
#define WLAN_RSNX_CAPAB_PROT_RANGE_NEG 10
/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
#define WLAN_ACTION_SPECTRUM_MGMT 0
#define WLAN_ACTION_QOS 1
#define WLAN_ACTION_DLS 2
#define WLAN_ACTION_BLOCK_ACK 3
#define WLAN_ACTION_PUBLIC 4
#define WLAN_ACTION_RADIO_MEASUREMENT 5
#define WLAN_ACTION_FT 6
#define WLAN_ACTION_HT 7
#define WLAN_ACTION_SA_QUERY 8
#define WLAN_ACTION_PROTECTED_DUAL 9
#define WLAN_ACTION_WNM 10
#define WLAN_ACTION_UNPROTECTED_WNM 11
#define WLAN_ACTION_TDLS 12
#define WLAN_ACTION_MESH 13
#define WLAN_ACTION_MULTIHOP 14
#define WLAN_ACTION_SELF_PROTECTED 15
#define WLAN_ACTION_DMG 16
#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
#define WLAN_ACTION_FST 18
#define WLAN_ACTION_ROBUST_AV_STREAMING 19
#define WLAN_ACTION_UNPROTECTED_DMG 20
#define WLAN_ACTION_VHT 21
#define WLAN_ACTION_S1G 22
#define WLAN_ACTION_S1G_RELAY 23
#define WLAN_ACTION_FLOW_CONTROL 24
#define WLAN_ACTION_CTRL_RESP_MCS_NEG 25
#define WLAN_ACTION_FILS 26
#define WLAN_ACTION_PROTECTED_FTM 34
#define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126
#define WLAN_ACTION_VENDOR_SPECIFIC 127
/* Note: 128-255 used to report errors by setting category | 0x80 */
/* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */
#define WLAN_PA_20_40_BSS_COEX 0
#define WLAN_PA_DSE_ENABLEMENT 1
#define WLAN_PA_DSE_DEENABLEMENT 2
#define WLAN_PA_DSE_REG_LOCATION_ANNOUNCE 3
#define WLAN_PA_EXT_CHANNEL_SWITCH_ANNOUNCE 4
#define WLAN_PA_DSE_MEASUREMENT_REQ 5
#define WLAN_PA_DSE_MEASUREMENT_RESP 6
#define WLAN_PA_MEASUREMENT_PILOT 7
#define WLAN_PA_DSE_POWER_CONSTRAINT 8
#define WLAN_PA_VENDOR_SPECIFIC 9
#define WLAN_PA_GAS_INITIAL_REQ 10
#define WLAN_PA_GAS_INITIAL_RESP 11
#define WLAN_PA_GAS_COMEBACK_REQ 12
#define WLAN_PA_GAS_COMEBACK_RESP 13
#define WLAN_TDLS_DISCOVERY_RESPONSE 14
#define WLAN_PA_LOCATION_TRACK_NOTIFICATION 15
#define WLAN_PA_QAB_REQUEST_FRAME 16
#define WLAN_PA_QAB_RESPONSE_FRAME 17
#define WLAN_PA_QMF_POLICY 18
#define WLAN_PA_QMF_POLICY_CHANGE 19
#define WLAN_PA_QLOAD_REQUEST 20
#define WLAN_PA_QLOAD_REPORT 21
#define WLAN_PA_HCCA_TXOP_ADVERTISEMENT 22
#define WLAN_PA_HCCA_TXOP_RESPONSE 23
#define WLAN_PA_PUBLIC_KEY 24
#define WLAN_PA_CHANNEL_AVAILABILITY_QUERY 25
#define WLAN_PA_CHANNEL_SCHEDULE_MANAGEMENT 26
#define WLAN_PA_CONTACT_VERIFICATION_SIGNAL 27
#define WLAN_PA_GDD_ENABLEMENT_REQ 28
#define WLAN_PA_GDD_ENABLEMENT_RESP 29
#define WLAN_PA_NETWORK_CHANNEL_CONTROL 30
#define WLAN_PA_WHITE_SPACE_MAP_ANNOUNCEMENT 31
#define WLAN_PA_FTM_REQUEST 32
#define WLAN_PA_FTM 33
#define WLAN_PA_FILS_DISCOVERY 34
#define WLAN_PA_LOCATION_MEASUREMENT_REPORT 47
/* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11,
* Table 9-332) */
#define WLAN_PROT_DSE_ENABLEMENT 1
#define WLAN_PROT_DSE_DEENABLEMENT 2
#define WLAN_PROT_EXT_CSA 4
#define WLAN_PROT_MEASUREMENT_REQ 5
#define WLAN_PROT_MEASUREMENT_REPORT 6
#define WLAN_PROT_DSE_POWER_CONSTRAINT 8
#define WLAN_PROT_VENDOR_SPECIFIC 9
#define WLAN_PROT_GAS_INITIAL_REQ 10
#define WLAN_PROT_GAS_INITIAL_RESP 11
#define WLAN_PROT_GAS_COMEBACK_REQ 12
#define WLAN_PROT_GAS_COMEBACK_RESP 13
#define WLAN_PROT_QAB_REQUEST_FRAME 16
#define WLAN_PROT_QAB_RESPONSE_FRAME 17
#define WLAN_PROT_QMF_POLICY 18
#define WLAN_PROT_QMF_POLICY_CHANGE 19
#define WLAN_PROT_QLOAD_REQUEST 20
#define WLAN_PROT_QLOAD_REPORT 21
#define WLAN_PROT_HCCA_TXOP_ADVERTISEMENT 22
#define WLAN_PROT_HCCA_TXOP_RESPONSE 23
#define WLAN_PROT_CHANNEL_AVAILABILITY_QUERY 25
#define WLAN_PROT_CHANNEL_SCHEDULE_MANAGEMENT 26
#define WLAN_PROT_CONTACT_VERIFICATION_SIGNAL 27
#define WLAN_PROT_GDD_ENABLEMENT_REQ 28
#define WLAN_PROT_GDD_ENABLEMENT_RESP 29
#define WLAN_PROT_NETWORK_CHANNEL_CONTROL 30
#define WLAN_PROT_WHITE_SPACE_MAP_ANNOUNCEMENT 31
/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
#define WLAN_SA_QUERY_REQUEST 0
#define WLAN_SA_QUERY_RESPONSE 1
#define WLAN_SA_QUERY_TR_ID_LEN 2
/* TDLS action codes */
#define WLAN_TDLS_SETUP_REQUEST 0
#define WLAN_TDLS_SETUP_RESPONSE 1
#define WLAN_TDLS_SETUP_CONFIRM 2
#define WLAN_TDLS_TEARDOWN 3
#define WLAN_TDLS_PEER_TRAFFIC_INDICATION 4
#define WLAN_TDLS_CHANNEL_SWITCH_REQUEST 5
#define WLAN_TDLS_CHANNEL_SWITCH_RESPONSE 6
#define WLAN_TDLS_PEER_PSM_REQUEST 7
#define WLAN_TDLS_PEER_PSM_RESPONSE 8
#define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9
#define WLAN_TDLS_DISCOVERY_REQUEST 10
/* Radio Measurement Action codes */
#define WLAN_RRM_RADIO_MEASUREMENT_REQUEST 0
#define WLAN_RRM_RADIO_MEASUREMENT_REPORT 1
#define WLAN_RRM_LINK_MEASUREMENT_REQUEST 2
#define WLAN_RRM_LINK_MEASUREMENT_REPORT 3
#define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4
#define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5
/* Protected Fine Timing Frame Action Field value */
#define WLAN_PROT_FTM_REQUEST 1
#define WLAN_PROT_FTM 2
#define WLAN_PROT_FTM_REPORT 3
/* Radio Measurement capabilities (from RM Enabled Capabilities element)
* IEEE Std 802.11-2016, 9.4.2.45, Table 9-157 */
/* byte 1 (out of 5) */
#define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0)
#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
#define WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE BIT(4)
#define WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE BIT(5)
#define WLAN_RRM_CAPS_BEACON_REPORT_TABLE BIT(6)
/* byte 2 (out of 5) */
#define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4)
/* byte 5 (out of 5) */
#define WLAN_RRM_CAPS_FTM_RANGE_REPORT BIT(2)
/*
* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 (Fine Timing Measurement Range
* request) - Minimum AP count
*/
#define WLAN_RRM_RANGE_REQ_MAX_MIN_AP 15
/* Timeout Interval Type */
#define WLAN_TIMEOUT_REASSOC_DEADLINE 1
#define WLAN_TIMEOUT_KEY_LIFETIME 2
#define WLAN_TIMEOUT_ASSOC_COMEBACK 3
/* Interworking element (IEEE 802.11u) - Access Network Options */
#define INTERWORKING_ANO_ACCESS_NETWORK_MASK 0x0f
#define INTERWORKING_ANO_INTERNET 0x10
#define INTERWORKING_ANO_ASRA 0x20
#define INTERWORKING_ANO_ESR 0x40
#define INTERWORKING_ANO_UESA 0x80
#define INTERWORKING_ANT_PRIVATE 0
#define INTERWORKING_ANT_PRIVATE_WITH_GUEST 1
#define INTERWORKING_ANT_CHARGEABLE_PUBLIC 2
#define INTERWORKING_ANT_FREE_PUBLIC 3
#define INTERWORKING_ANT_PERSONAL_DEVICE 4
#define INTERWORKING_ANT_EMERGENCY_SERVICES 5
#define INTERWORKING_ANT_TEST 6
#define INTERWORKING_ANT_WILDCARD 15
/* Advertisement Protocol ID definitions (IEEE Std 802.11-2016, Table 9-215) */
enum adv_proto_id {
ACCESS_NETWORK_QUERY_PROTOCOL = 0,
MIH_INFO_SERVICE = 1,
MIH_CMD_AND_EVENT_DISCOVERY = 2,
EMERGENCY_ALERT_SYSTEM = 3,
REGISTERED_LOCATION_QUERY_PROTO = 4,
ADV_PROTO_VENDOR_SPECIFIC = 221
};
/* Access Network Query Protocol info ID definitions (IEEE Std 802.11-2016,
* Table 9-271; P802.11ai) */
enum anqp_info_id {
ANQP_QUERY_LIST = 256,
ANQP_CAPABILITY_LIST = 257,
ANQP_VENUE_NAME = 258,
ANQP_EMERGENCY_CALL_NUMBER = 259,
ANQP_NETWORK_AUTH_TYPE = 260,
ANQP_ROAMING_CONSORTIUM = 261,
ANQP_IP_ADDR_TYPE_AVAILABILITY = 262,
ANQP_NAI_REALM = 263,
ANQP_3GPP_CELLULAR_NETWORK = 264,
ANQP_AP_GEOSPATIAL_LOCATION = 265,
ANQP_AP_CIVIC_LOCATION = 266,
ANQP_AP_LOCATION_PUBLIC_URI = 267,
ANQP_DOMAIN_NAME = 268,
ANQP_EMERGENCY_ALERT_URI = 269,
ANQP_TDLS_CAPABILITY = 270,
ANQP_EMERGENCY_NAI = 271,
ANQP_NEIGHBOR_REPORT = 272,
ANQP_QUERY_AP_LIST = 273,
ANQP_AP_LIST_RESPONSE = 274,
ANQP_FILS_REALM_INFO = 275,
ANQP_CAG = 276,
ANQP_VENUE_URL = 277,
ANQP_ADVICE_OF_CHARGE = 278,
ANQP_LOCAL_CONTENT = 279,
ANQP_NETWORK_AUTH_TYPE_TIMESTAMP = 280,
ANQP_VENDOR_SPECIFIC = 56797
};
/* NAI Realm list - EAP Method subfield - Authentication Parameter ID */
enum nai_realm_eap_auth_param {
NAI_REALM_EAP_AUTH_EXPANDED_EAP_METHOD = 1,
NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH = 2,
NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD = 3,
NAI_REALM_EAP_AUTH_EXPANDED_INNER_EAP_METHOD = 4,
NAI_REALM_EAP_AUTH_CRED_TYPE = 5,
NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE = 6,
NAI_REALM_EAP_AUTH_VENDOR_SPECIFIC = 221
};
enum nai_realm_eap_auth_inner_non_eap {
NAI_REALM_INNER_NON_EAP_PAP = 1,
NAI_REALM_INNER_NON_EAP_CHAP = 2,
NAI_REALM_INNER_NON_EAP_MSCHAP = 3,
NAI_REALM_INNER_NON_EAP_MSCHAPV2 = 4
};
enum nai_realm_eap_cred_type {
NAI_REALM_CRED_TYPE_SIM = 1,
NAI_REALM_CRED_TYPE_USIM = 2,
NAI_REALM_CRED_TYPE_NFC_SECURE_ELEMENT = 3,
NAI_REALM_CRED_TYPE_HARDWARE_TOKEN = 4,
NAI_REALM_CRED_TYPE_SOFTOKEN = 5,
NAI_REALM_CRED_TYPE_CERTIFICATE = 6,
NAI_REALM_CRED_TYPE_USERNAME_PASSWORD = 7,
NAI_REALM_CRED_TYPE_NONE = 8,
NAI_REALM_CRED_TYPE_ANONYMOUS = 9,
NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10
};
/* Unprotected S1G Action field values for WLAN_ACTION_S1G */
#define S1G_ACT_AID_SWITCH_REQUEST 0
#define S1G_ACT_AID_SWITCH_RESPONSE 1
#define S1G_ACT_SYNC_CONTROL 2
#define S1G_ACT_STA_INFO_ANNOUNCE 3
#define S1G_ACT_EDCA_PARAM_SET 4
#define S1G_ACT_EL_OPERATION 5
#define S1G_ACT_TWT_SETUP 6
#define S1G_ACT_TWT_TEARDOWN 7
#define S1G_ACT_SECT_GROUP_ID_LIST 8
#define S1G_ACT_SECT_ID_FEEDBACK 9
#define S1G_ACT_TWT_INFORMATION 11
/*
* IEEE P802.11-REVmc/D5.0 Table 9-81 - Measurement type definitions for
* measurement requests
*/
enum measure_type {
MEASURE_TYPE_BASIC = 0,
MEASURE_TYPE_CCA = 1,
MEASURE_TYPE_RPI_HIST = 2,
MEASURE_TYPE_CHANNEL_LOAD = 3,
MEASURE_TYPE_NOISE_HIST = 4,
MEASURE_TYPE_BEACON = 5,
MEASURE_TYPE_FRAME = 6,
MEASURE_TYPE_STA_STATISTICS = 7,
MEASURE_TYPE_LCI = 8,
MEASURE_TYPE_TRANSMIT_STREAM = 9,
MEASURE_TYPE_MULTICAST_DIAG = 10,
MEASURE_TYPE_LOCATION_CIVIC = 11,
MEASURE_TYPE_LOCATION_ID = 12,
MEASURE_TYPE_DIRECTIONAL_CHAN_QUALITY = 13,
MEASURE_TYPE_DIRECTIONAL_MEASURE = 14,
MEASURE_TYPE_DIRECTIONAL_STATS = 15,
MEASURE_TYPE_FTM_RANGE = 16,
MEASURE_TYPE_MEASURE_PAUSE = 255,
};
/* IEEE Std 802.11-2012 Table 8-71 - Location subject definition */
enum location_subject {
LOCATION_SUBJECT_LOCAL = 0,
LOCATION_SUBJECT_REMOTE = 1,
LOCATION_SUBJECT_3RD_PARTY = 2,
};
/*
* IEEE P802.11-REVmc/D5.0 Table 9-94 - Optional subelement IDs for LCI request
*/
enum lci_req_subelem {
LCI_REQ_SUBELEM_AZIMUTH_REQ = 1,
LCI_REQ_SUBELEM_ORIGINATOR_MAC_ADDR = 2,
LCI_REQ_SUBELEM_TARGET_MAC_ADDR = 3,
LCI_REQ_SUBELEM_MAX_AGE = 4,
};
#define FILS_NONCE_LEN 16
#define FILS_SESSION_LEN 8
#define FILS_CACHE_ID_LEN 2
#define FILS_MAX_KEY_AUTH_LEN 48
#ifdef _MSC_VER
#pragma pack(push, 1)
#endif /* _MSC_VER */
struct ieee80211_hdr {
le16 frame_control;
le16 duration_id;
u8 addr1[6];
u8 addr2[6];
u8 addr3[6];
le16 seq_ctrl;
/* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame
*/
} STRUCT_PACKED;
#define IEEE80211_DA_FROMDS addr1
#define IEEE80211_BSSID_FROMDS addr2
#define IEEE80211_SA_FROMDS addr3
#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr))
#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
struct ieee80211_mgmt {
le16 frame_control;
le16 duration;
u8 da[6];
u8 sa[6];
u8 bssid[6];
le16 seq_ctrl;
union {
struct {
le16 auth_alg;
le16 auth_transaction;
le16 status_code;
/* possibly followed by Challenge text */
u8 variable[];
} STRUCT_PACKED auth;
struct {
le16 reason_code;
u8 variable[];
} STRUCT_PACKED deauth;
struct {
le16 capab_info;
le16 listen_interval;
/* followed by SSID and Supported rates */
u8 variable[];
} STRUCT_PACKED assoc_req;
struct {
le16 capab_info;
le16 status_code;
le16 aid;
/* followed by Supported rates */
u8 variable[];
} STRUCT_PACKED assoc_resp, reassoc_resp;
struct {
le16 capab_info;
le16 listen_interval;
u8 current_ap[6];
/* followed by SSID and Supported rates */
u8 variable[];
} STRUCT_PACKED reassoc_req;
struct {
le16 reason_code;
u8 variable[];
} STRUCT_PACKED disassoc;
struct {
u8 timestamp[8];
le16 beacon_int;
le16 capab_info;
/* followed by some of SSID, Supported rates,
* FH Params, DS Params, CF Params, IBSS Params, TIM */
u8 variable[];
} STRUCT_PACKED beacon;
/* probe_req: only variable items: SSID, Supported rates */
struct {
u8 timestamp[8];
le16 beacon_int;
le16 capab_info;
/* followed by some of SSID, Supported rates,
* FH Params, DS Params, CF Params, IBSS Params */
u8 variable[];
} STRUCT_PACKED probe_resp;
struct {
u8 category;
union {
struct {
u8 action_code;
u8 dialog_token;
u8 status_code;
u8 variable[];
} STRUCT_PACKED wmm_action;
struct{
u8 action_code;
u8 element_id;
u8 length;
u8 switch_mode;
u8 new_chan;
u8 switch_count;
} STRUCT_PACKED chan_switch;
struct {
u8 action;
u8 sta_addr[ETH_ALEN];
u8 target_ap_addr[ETH_ALEN];
u8 variable[]; /* FT Request */
} STRUCT_PACKED ft_action_req;
struct {
u8 action;
u8 sta_addr[ETH_ALEN];
u8 target_ap_addr[ETH_ALEN];
le16 status_code;
u8 variable[]; /* FT Request */
} STRUCT_PACKED ft_action_resp;
struct {
u8 action;
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
u8 variable[]; /* OCI element */
} STRUCT_PACKED sa_query_req;
struct {
u8 action; /* */
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
u8 variable[]; /* OCI element */
} STRUCT_PACKED sa_query_resp;
struct {
u8 action;
u8 dialogtoken;
u8 variable[];
} STRUCT_PACKED wnm_sleep_req;
struct {
u8 action;
u8 dialogtoken;
le16 keydata_len;
u8 variable[];
} STRUCT_PACKED wnm_sleep_resp;
struct {
u8 action;
u8 variable[];
} STRUCT_PACKED public_action;
struct {
u8 action; /* 9 */
u8 oui[3];
/* Vendor-specific content */
u8 variable[];
} STRUCT_PACKED vs_public_action;
struct {
u8 action; /* 7 */
u8 dialog_token;
u8 req_mode;
le16 disassoc_timer;
u8 validity_interval;
/* BSS Termination Duration (optional),
* Session Information URL (optional),
* BSS Transition Candidate List
* Entries */
u8 variable[];
} STRUCT_PACKED bss_tm_req;
struct {
u8 action; /* 8 */
u8 dialog_token;
u8 status_code;
u8 bss_termination_delay;
/* Target BSSID (optional),
* BSS Transition Candidate List
* Entries (optional) */
u8 variable[];
} STRUCT_PACKED bss_tm_resp;
struct {
u8 action; /* 6 */
u8 dialog_token;
u8 query_reason;
/* BSS Transition Candidate List
* Entries (optional) */
u8 variable[];
} STRUCT_PACKED bss_tm_query;
struct {
u8 action; /* 11 */
u8 dialog_token;
u8 req_info;
} STRUCT_PACKED coloc_intf_req;
struct {
u8 action; /* 12 */
u8 dialog_token;
u8 variable[];
} STRUCT_PACKED coloc_intf_report;
struct {
u8 action; /* 15 */
u8 variable[];
} STRUCT_PACKED slf_prot_action;
struct {
u8 action;
u8 variable[];
} STRUCT_PACKED fst_action;
struct {
u8 action;
u8 dialog_token;
u8 variable[];
} STRUCT_PACKED rrm;
} u;
} STRUCT_PACKED action;
} u;
} STRUCT_PACKED;
#define IEEE80211_MAX_MMPDU_SIZE 2304
/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
#define IEEE80211_HT_MCS_MASK_LEN 10
/* HT Capabilities element */
struct ieee80211_ht_capabilities {
le16 ht_capabilities_info;
u8 a_mpdu_params; /* Maximum A-MPDU Length Exponent B0..B1
* Minimum MPDU Start Spacing B2..B4
* Reserved B5..B7 */
u8 supported_mcs_set[16];
le16 ht_extended_capabilities;
le32 tx_bf_capability_info;
u8 asel_capabilities;
} STRUCT_PACKED;
/* HT Operation element */
struct ieee80211_ht_operation {
u8 primary_chan;
/* Five octets of HT Operation Information */
u8 ht_param; /* B0..B7 */
le16 operation_mode; /* B8..B23 */
le16 param; /* B24..B39 */
u8 basic_mcs_set[16];
} STRUCT_PACKED;
struct ieee80211_obss_scan_parameters {
le16 scan_passive_dwell;
le16 scan_active_dwell;
le16 width_trigger_scan_interval;
le16 scan_passive_total_per_channel;
le16 scan_active_total_per_channel;
le16 channel_transition_delay_factor;
le16 scan_activity_threshold;
} STRUCT_PACKED;
struct ieee80211_vht_capabilities {
le32 vht_capabilities_info;
struct {
le16 rx_map;
le16 rx_highest;
le16 tx_map;
le16 tx_highest;
} vht_supported_mcs_set;
} STRUCT_PACKED;
struct ieee80211_vht_operation {
u8 vht_op_info_chwidth;
u8 vht_op_info_chan_center_freq_seg0_idx;
u8 vht_op_info_chan_center_freq_seg1_idx;
le16 vht_basic_mcs_set;
} STRUCT_PACKED;
struct ieee80211_ampe_ie {
u8 selected_pairwise_suite[4];
u8 local_nonce[32];
u8 peer_nonce[32];
/* Followed by
* Key Replay Counter[8] (optional)
* (only in Mesh Group Key Inform/Acknowledge frames)
* GTKdata[variable] (optional)
* (MGTK[variable] || Key RSC[8] || GTKExpirationTime[4])
* IGTKdata[variable] (optional)
* (Key ID[2], IPN[6], IGTK[variable] in IGTK KDE format)
*/
} STRUCT_PACKED;
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
#define ERP_INFO_NON_ERP_PRESENT BIT(0)
#define ERP_INFO_USE_PROTECTION BIT(1)
#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
#define OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5
/* HT Capabilities Info field within HT Capabilities element */
#define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0))
#define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET ((u16) BIT(1))
#define HT_CAP_INFO_SMPS_MASK ((u16) (BIT(2) | BIT(3)))
#define HT_CAP_INFO_SMPS_STATIC ((u16) 0)
#define HT_CAP_INFO_SMPS_DYNAMIC ((u16) BIT(2))
#define HT_CAP_INFO_SMPS_DISABLED ((u16) (BIT(2) | BIT(3)))
#define HT_CAP_INFO_GREEN_FIELD ((u16) BIT(4))
#define HT_CAP_INFO_SHORT_GI20MHZ ((u16) BIT(5))
#define HT_CAP_INFO_SHORT_GI40MHZ ((u16) BIT(6))
#define HT_CAP_INFO_TX_STBC ((u16) BIT(7))
#define HT_CAP_INFO_RX_STBC_MASK ((u16) (BIT(8) | BIT(9)))
#define HT_CAP_INFO_RX_STBC_1 ((u16) BIT(8))
#define HT_CAP_INFO_RX_STBC_12 ((u16) BIT(9))
#define HT_CAP_INFO_RX_STBC_123 ((u16) (BIT(8) | BIT(9)))
#define HT_CAP_INFO_DELAYED_BA ((u16) BIT(10))
#define HT_CAP_INFO_MAX_AMSDU_SIZE ((u16) BIT(11))
#define HT_CAP_INFO_DSSS_CCK40MHZ ((u16) BIT(12))
/* B13 - Reserved (was PSMP support during P802.11n development) */
#define HT_CAP_INFO_40MHZ_INTOLERANT ((u16) BIT(14))
#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15))
/* HT Extended Capabilities field within HT Capabilities element */
#define EXT_HT_CAP_INFO_PCO ((u16) BIT(0))
#define EXT_HT_CAP_INFO_PCO_TRANS_TIME_MASK ((u16) (BIT(1) | BIT(2)))
#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1
/* B3..B7 - Reserved */
#define EXT_HT_CAP_INFO_MCS_FEEDBACK_MASK ((u16) (BIT(8) | BIT(9)))
#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8
#define EXT_HT_CAP_INFO_HTC_SUPPORT ((u16) BIT(10))
#define EXT_HT_CAP_INFO_RD_RESPONDER ((u16) BIT(11))
/* B12..B15 - Reserved */
/* Transmit Beanforming Capabilities within HT Capabilities element */
#define TX_BF_CAP_IMPLICIT_TXBF_RX_CAP ((u32) BIT(0))
#define TX_BF_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
#define TX_BF_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
#define TX_BF_CAP_RX_NDP_CAP ((u32) BIT(3))
#define TX_BF_CAP_TX_NDP_CAP ((u32) BIT(4))
#define TX_BF_CAP_IMPLICIT_TX_BF_CAP ((u32) BIT(5))
#define TX_BF_CAP_CALIBRATION_MASK ((u32) (BIT(6) | BIT(7))
#define TX_BF_CAP_CALIB_OFFSET 6
#define TX_BF_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
#define TX_BF_CAP_EXPLICIT_NONCOMPR_STEERING_CAP ((u32) BIT(9))
#define TX_BF_CAP_EXPLICIT_COMPR_STEERING_CAP ((u32) BIT(10))
#define TX_BF_CAP_EXPLICIT_TX_BF_CSI_FEEDBACK_MASK ((u32) (BIT(10) | BIT(11)))
#define TX_BF_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
#define TX_BF_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
#define TX_BF_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
#define TX_BF_CAP_MINIMAL_GROUPING_OFFSET 17
#define TX_BF_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
#define TX_BF_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
#define TX_BF_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
#define TX_BF_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_MASK ((u32) (BIT(27) | BIT(28)))
#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_OFFSET 27
/* B29..B31 - Reserved */
/* ASEL Capability field within HT Capabilities element */
#define ASEL_CAP_ASEL_CAPABLE ((u8) BIT(0))
#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
#define ASEL_CAP_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
#define ASEL_CAP_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
#define ASEL_CAP_RX_AS_CAP ((u8) BIT(5))
#define ASEL_CAP_TX_SOUNDING_PPDUS_CAP ((u8) BIT(6))
/* B7 - Reserved */
/* First octet of HT Operation Information within HT Operation element */
#define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8) BIT(0) | BIT(1))
#define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8) BIT(0))
#define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8) BIT(0) | BIT(1))
#define HT_INFO_HT_PARAM_STA_CHNL_WIDTH ((u8) BIT(2))
#define HT_INFO_HT_PARAM_RIFS_MODE ((u8) BIT(3))
/* B4..B7 - Reserved */
/* HT Protection (B8..B9 of HT Operation Information) */
#define HT_PROT_NO_PROTECTION 0
#define HT_PROT_NONMEMBER_PROTECTION 1
#define HT_PROT_20MHZ_PROTECTION 2
#define HT_PROT_NON_HT_MIXED 3
/* Bits within ieee80211_ht_operation::operation_mode (BIT(0) maps to B8 in
* HT Operation Information) */
#define HT_OPER_OP_MODE_HT_PROT_MASK ((u16) (BIT(0) | BIT(1))) /* B8..B9 */
#define HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT ((u16) BIT(2)) /* B10 */
/* BIT(3), i.e., B11 in HT Operation Information field - Reserved */
#define HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT ((u16) BIT(4)) /* B12 */
/* BIT(5)..BIT(15), i.e., B13..B23 - Reserved */
/* Last two octets of HT Operation Information (BIT(0) = B24) */
/* B24..B29 - Reserved */
#define HT_OPER_PARAM_DUAL_BEACON ((u16) BIT(6))
#define HT_OPER_PARAM_DUAL_CTS_PROTECTION ((u16) BIT(7))
#define HT_OPER_PARAM_STBC_BEACON ((u16) BIT(8))
#define HT_OPER_PARAM_LSIG_TXOP_PROT_FULL_SUPP ((u16) BIT(9))
#define HT_OPER_PARAM_PCO_ACTIVE ((u16) BIT(10))
#define HT_OPER_PARAM_PCO_PHASE ((u16) BIT(11))
/* B36..B39 - Reserved */
#define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY 123
/* VHT Defines */
#define VHT_CAP_MAX_MPDU_LENGTH_7991 ((u32) BIT(0))
#define VHT_CAP_MAX_MPDU_LENGTH_11454 ((u32) BIT(1))
#define VHT_CAP_MAX_MPDU_LENGTH_MASK ((u32) BIT(0) | BIT(1))
#define VHT_CAP_MAX_MPDU_LENGTH_MASK_SHIFT 0
#define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2))
#define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3))
#define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3))
#define VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT 2
#define VHT_CAP_RXLDPC ((u32) BIT(4))
#define VHT_CAP_SHORT_GI_80 ((u32) BIT(5))
#define VHT_CAP_SHORT_GI_160 ((u32) BIT(6))
#define VHT_CAP_TXSTBC ((u32) BIT(7))
#define VHT_CAP_RXSTBC_1 ((u32) BIT(8))
#define VHT_CAP_RXSTBC_2 ((u32) BIT(9))
#define VHT_CAP_RXSTBC_3 ((u32) BIT(8) | BIT(9))
#define VHT_CAP_RXSTBC_4 ((u32) BIT(10))
#define VHT_CAP_RXSTBC_MASK ((u32) BIT(8) | BIT(9) | \
BIT(10))
#define VHT_CAP_RXSTBC_MASK_SHIFT 8
#define VHT_CAP_SU_BEAMFORMER_CAPABLE ((u32) BIT(11))
#define VHT_CAP_SU_BEAMFORMEE_CAPABLE ((u32) BIT(12))
#define VHT_CAP_BEAMFORMEE_STS_MAX ((u32) BIT(13) | \
BIT(14) | BIT(15))
#define VHT_CAP_BEAMFORMEE_STS_MAX_SHIFT 13
#define VHT_CAP_BEAMFORMEE_STS_OFFSET 13
#define VHT_CAP_SOUNDING_DIMENSION_MAX ((u32) BIT(16) | \
BIT(17) | BIT(18))
#define VHT_CAP_SOUNDING_DIMENSION_MAX_SHIFT 16
#define VHT_CAP_SOUNDING_DIMENSION_OFFSET 16
#define VHT_CAP_MU_BEAMFORMER_CAPABLE ((u32) BIT(19))
#define VHT_CAP_MU_BEAMFORMEE_CAPABLE ((u32) BIT(20))
#define VHT_CAP_VHT_TXOP_PS ((u32) BIT(21))
#define VHT_CAP_HTC_VHT ((u32) BIT(22))
#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1 ((u32) BIT(23))
#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2 ((u32) BIT(24))
#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3 ((u32) BIT(23) | BIT(24))
#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4 ((u32) BIT(25))
#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5 ((u32) BIT(23) | BIT(25))
#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6 ((u32) BIT(24) | BIT(25))
#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX ((u32) BIT(23) | \
BIT(24) | BIT(25))
#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT 23
#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB ((u32) BIT(27))
#define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB ((u32) BIT(26) | BIT(27))
#define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28))
#define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29))
#define VHT_OPMODE_CHANNEL_WIDTH_MASK ((u8) BIT(0) | BIT(1))
#define VHT_OPMODE_CHANNEL_RxNSS_MASK ((u8) BIT(4) | BIT(5) | \
BIT(6))
#define VHT_OPMODE_NOTIF_RX_NSS_SHIFT 4
#define VHT_RX_NSS_MAX_STREAMS 8
/* VHT/EDMG channel widths */
#define CHANWIDTH_USE_HT 0
#define CHANWIDTH_80MHZ 1
#define CHANWIDTH_160MHZ 2
#define CHANWIDTH_80P80MHZ 3
#define CHANWIDTH_2160MHZ 4
#define CHANWIDTH_4320MHZ 5
#define CHANWIDTH_6480MHZ 6
#define CHANWIDTH_8640MHZ 7
+#define CHANWIDTH_40MHZ_6GHZ 8
#define HE_NSS_MAX_STREAMS 8
#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
* 00:50:F2 */
#define WPA_IE_VENDOR_TYPE 0x0050f201
#define WMM_IE_VENDOR_TYPE 0x0050f202
#define WPS_IE_VENDOR_TYPE 0x0050f204
#define OUI_WFA 0x506f9a
#define P2P_IE_VENDOR_TYPE 0x506f9a09
#define WFD_IE_VENDOR_TYPE 0x506f9a0a
#define WFD_OUI_TYPE 10
#define HS20_IE_VENDOR_TYPE 0x506f9a10
#define OSEN_IE_VENDOR_TYPE 0x506f9a12
#define MBO_IE_VENDOR_TYPE 0x506f9a16
#define MBO_OUI_TYPE 22
#define OWE_IE_VENDOR_TYPE 0x506f9a1c
#define OWE_OUI_TYPE 28
#define MULTI_AP_OUI_TYPE 0x1B
#define DPP_CC_IE_VENDOR_TYPE 0x506f9a1e
#define DPP_CC_OUI_TYPE 0x1e
#define SAE_PK_IE_VENDOR_TYPE 0x506f9a1f
#define SAE_PK_OUI_TYPE 0x1f
+#define QM_IE_VENDOR_TYPE 0x506f9a22
+#define QM_IE_OUI_TYPE 0x22
+#define WFA_CAPA_IE_VENDOR_TYPE 0x506f9a23
+#define WFA_CAPA_OUI_TYPE 0x23
#define MULTI_AP_SUB_ELEM_TYPE 0x06
#define MULTI_AP_TEAR_DOWN BIT(4)
#define MULTI_AP_FRONTHAUL_BSS BIT(5)
#define MULTI_AP_BACKHAUL_BSS BIT(6)
#define MULTI_AP_BACKHAUL_STA BIT(7)
#define WMM_OUI_TYPE 2
#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1
#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2
#define WMM_VERSION 1
#define WMM_ACTION_CODE_ADDTS_REQ 0
#define WMM_ACTION_CODE_ADDTS_RESP 1
#define WMM_ACTION_CODE_DELTS 2
#define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0
#define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1
/* 2 - Reserved */
#define WMM_ADDTS_STATUS_REFUSED 3
/* 4-255 - Reserved */
/* WMM TSPEC Direction Field Values */
#define WMM_TSPEC_DIRECTION_UPLINK 0
#define WMM_TSPEC_DIRECTION_DOWNLINK 1
/* 2 - Reserved */
#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3
/*
* WMM Information Element (used in (Re)Association Request frames; may also be
* used in Beacon frames)
*/
struct wmm_information_element {
/* Element ID: 221 (0xdd); Length: 7 */
/* required fields for WMM version 1 */
u8 oui[3]; /* 00:50:f2 */
u8 oui_type; /* 2 */
u8 oui_subtype; /* 0 */
u8 version; /* 1 for WMM version 1.0 */
u8 qos_info; /* AP/STA specific QoS info */
} STRUCT_PACKED;
#define WMM_QOSINFO_AP_UAPSD 0x80
#define WMM_QOSINFO_STA_AC_MASK 0x0f
#define WMM_QOSINFO_STA_SP_MASK 0x03
#define WMM_QOSINFO_STA_SP_SHIFT 5
#define WMM_AC_AIFSN_MASK 0x0f
#define WMM_AC_AIFNS_SHIFT 0
#define WMM_AC_ACM 0x10
#define WMM_AC_ACI_MASK 0x60
#define WMM_AC_ACI_SHIFT 5
#define WMM_AC_ECWMIN_MASK 0x0f
#define WMM_AC_ECWMIN_SHIFT 0
#define WMM_AC_ECWMAX_MASK 0xf0
#define WMM_AC_ECWMAX_SHIFT 4
struct wmm_ac_parameter {
u8 aci_aifsn; /* AIFSN, ACM, ACI */
u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
le16 txop_limit;
} STRUCT_PACKED;
/*
* WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association
* Response frmaes)
*/
struct wmm_parameter_element {
/* Element ID: 221 (0xdd); Length: 24 */
/* required fields for WMM version 1 */
u8 oui[3]; /* 00:50:f2 */
u8 oui_type; /* 2 */
u8 oui_subtype; /* 1 */
u8 version; /* 1 for WMM version 1.0 */
u8 qos_info; /* AP/STA specific QoS info */
u8 reserved; /* 0 */
struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
} STRUCT_PACKED;
/* WMM TSPEC Element */
struct wmm_tspec_element {
u8 eid; /* 221 = 0xdd */
u8 length; /* 6 + 55 = 61 */
u8 oui[3]; /* 00:50:f2 */
u8 oui_type; /* 2 */
u8 oui_subtype; /* 2 */
u8 version; /* 1 */
/* WMM TSPEC body (55 octets): */
u8 ts_info[3];
le16 nominal_msdu_size;
le16 maximum_msdu_size;
le32 minimum_service_interval;
le32 maximum_service_interval;
le32 inactivity_interval;
le32 suspension_interval;
le32 service_start_time;
le32 minimum_data_rate;
le32 mean_data_rate;
le32 peak_data_rate;
le32 maximum_burst_size;
le32 delay_bound;
le32 minimum_phy_rate;
le16 surplus_bandwidth_allowance;
le16 medium_time;
} STRUCT_PACKED;
/* Access Categories / ACI to AC coding */
enum wmm_ac {
WMM_AC_BE = 0 /* Best Effort */,
WMM_AC_BK = 1 /* Background */,
WMM_AC_VI = 2 /* Video */,
WMM_AC_VO = 3 /* Voice */,
WMM_AC_NUM = 4
};
#define HS20_INDICATION_OUI_TYPE 16
#define HS20_ANQP_OUI_TYPE 17
#define HS20_OSEN_OUI_TYPE 18
#define HS20_ROAMING_CONS_SEL_OUI_TYPE 29
#define HS20_STYPE_QUERY_LIST 1
#define HS20_STYPE_CAPABILITY_LIST 2
#define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
#define HS20_STYPE_WAN_METRICS 4
#define HS20_STYPE_CONNECTION_CAPABILITY 5
#define HS20_STYPE_NAI_HOME_REALM_QUERY 6
#define HS20_STYPE_OPERATING_CLASS 7
#define HS20_STYPE_OSU_PROVIDERS_LIST 8
#define HS20_STYPE_ICON_REQUEST 10
#define HS20_STYPE_ICON_BINARY_FILE 11
#define HS20_STYPE_OPERATOR_ICON_METADATA 12
#define HS20_STYPE_OSU_PROVIDERS_NAI_LIST 13
#define HS20_DGAF_DISABLED 0x01
#define HS20_PPS_MO_ID_PRESENT 0x02
#define HS20_ANQP_DOMAIN_ID_PRESENT 0x04
#ifndef HS20_VERSION
#define HS20_VERSION 0x20 /* Release 3 */
#endif /* HS20_VERSION */
/* WNM-Notification WFA vendors specific subtypes */
#define HS20_WNM_SUB_REM_NEEDED 0
#define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
#define WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT 2
#define WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA 3
#define HS20_WNM_T_C_ACCEPTANCE 4
#define HS20_DEAUTH_REASON_CODE_BSS 0
#define HS20_DEAUTH_REASON_CODE_ESS 1
/* MBO v0.0_r19, 4.2: MBO Attributes */
/* Table 4-5: MBO Attributes */
/* OCE v0.0.10, Table 4-3: OCE Attributes */
enum mbo_attr_id {
MBO_ATTR_ID_AP_CAPA_IND = 1,
MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2,
MBO_ATTR_ID_CELL_DATA_CAPA = 3,
MBO_ATTR_ID_ASSOC_DISALLOW = 4,
MBO_ATTR_ID_CELL_DATA_PREF = 5,
MBO_ATTR_ID_TRANSITION_REASON = 6,
MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7,
MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8,
OCE_ATTR_ID_CAPA_IND = 101,
OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT = 102,
OCE_ATTR_ID_REDUCED_WAN_METRICS = 103,
OCE_ATTR_ID_RNR_COMPLETENESS = 104,
};
/* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */
/* Table 4-7: MBO AP Capability Indication Field Values */
#define MBO_AP_CAPA_CELL_AWARE BIT(6)
/* MBO v0.0_r19, 4.2.2: Non-preferred Channel Report Attribute */
/* Table 4-10: Reason Code Field Values */
enum mbo_non_pref_chan_reason {
MBO_NON_PREF_CHAN_REASON_UNSPECIFIED = 0,
MBO_NON_PREF_CHAN_REASON_RSSI = 1,
MBO_NON_PREF_CHAN_REASON_EXT_INTERFERENCE = 2,
MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE = 3,
};
/* MBO v0.0_r19, 4.2.3: Cellular Data Capabilities Attribute */
/* Table 4-13: Cellular Data Connectivity Field */
enum mbo_cellular_capa {
MBO_CELL_CAPA_AVAILABLE = 1,
MBO_CELL_CAPA_NOT_AVAILABLE = 2,
MBO_CELL_CAPA_NOT_SUPPORTED = 3,
};
/* MBO v0.0_r19, 4.2.4: Association Disallowed Attribute */
/* Table 4-15: Reason Code Field Values */
enum mbo_assoc_disallow_reason {
MBO_ASSOC_DISALLOW_REASON_UNSPECIFIED = 1,
MBO_ASSOC_DISALLOW_REASON_MAX_STA = 2,
MBO_ASSOC_DISALLOW_REASON_AIR_INTERFERENCE = 3,
MBO_ASSOC_DISALLOW_REASON_AUTH_SERVER_OVERLOAD = 4,
MBO_ASSOC_DISALLOW_REASON_LOW_RSSI = 5,
};
/* MBO v0.0_r19, 4.2.5: Cellular Data Connection Preference Attribute */
/* Table 4-17: Cellular Preference Field Values */
enum mbo_cell_pref {
MBO_CELL_PREF_EXCLUDED = 0,
MBO_CELL_PREF_NO_USE = 1,
MBO_CELL_PREF_USE = 255
};
/* MBO v0.0_r19, 4.2.6: Transition Reason Code Attribute */
/* Table 4-19: Transition Reason Code Field Values */
enum mbo_transition_reason {
MBO_TRANSITION_REASON_UNSPECIFIED = 0,
MBO_TRANSITION_REASON_FRAME_LOSS = 1,
MBO_TRANSITION_REASON_DELAY = 2,
MBO_TRANSITION_REASON_BANDWIDTH = 3,
MBO_TRANSITION_REASON_LOAD_BALANCE = 4,
MBO_TRANSITION_REASON_RSSI = 5,
MBO_TRANSITION_REASON_RETRANSMISSIONS = 6,
MBO_TRANSITION_REASON_INTERFERENCE = 7,
MBO_TRANSITION_REASON_GRAY_ZONE = 8,
MBO_TRANSITION_REASON_PREMIUM_AP = 9,
};
/* MBO v0.0_r19, 4.2.7: Transition Rejection Reason Code Attribute */
/* Table 4-21: Transition Rejection Reason Code Field Values */
enum mbo_transition_reject_reason {
MBO_TRANSITION_REJECT_REASON_UNSPECIFIED = 0,
MBO_TRANSITION_REJECT_REASON_FRAME_LOSS = 1,
MBO_TRANSITION_REJECT_REASON_DELAY = 2,
MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY = 3,
MBO_TRANSITION_REJECT_REASON_RSSI = 4,
MBO_TRANSITION_REJECT_REASON_INTERFERENCE = 5,
MBO_TRANSITION_REJECT_REASON_SERVICES = 6,
};
/* MBO v0.0_r27, 4.3: MBO ANQP-elements */
#define MBO_ANQP_OUI_TYPE 0x12
#define MBO_ANQP_SUBTYPE_QUERY_LIST 1
#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 2
#define MAX_MBO_ANQP_SUBTYPE MBO_ANQP_SUBTYPE_CELL_CONN_PREF
/* OCE v0.0.10, 4.2.1: OCE Capability Indication Attribute */
#define OCE_RELEASE 1
#define OCE_RELEASE_MASK (BIT(0) | BIT(1) | BIT(2))
#define OCE_IS_STA_CFON BIT(3)
#define OCE_IS_NON_OCE_AP_PRESENT BIT(4)
/* Wi-Fi Direct (P2P) */
#define P2P_OUI_TYPE 9
enum p2p_attr_id {
P2P_ATTR_STATUS = 0,
P2P_ATTR_MINOR_REASON_CODE = 1,
P2P_ATTR_CAPABILITY = 2,
P2P_ATTR_DEVICE_ID = 3,
P2P_ATTR_GROUP_OWNER_INTENT = 4,
P2P_ATTR_CONFIGURATION_TIMEOUT = 5,
P2P_ATTR_LISTEN_CHANNEL = 6,
P2P_ATTR_GROUP_BSSID = 7,
P2P_ATTR_EXT_LISTEN_TIMING = 8,
P2P_ATTR_INTENDED_INTERFACE_ADDR = 9,
P2P_ATTR_MANAGEABILITY = 10,
P2P_ATTR_CHANNEL_LIST = 11,
P2P_ATTR_NOTICE_OF_ABSENCE = 12,
P2P_ATTR_DEVICE_INFO = 13,
P2P_ATTR_GROUP_INFO = 14,
P2P_ATTR_GROUP_ID = 15,
P2P_ATTR_INTERFACE = 16,
P2P_ATTR_OPERATING_CHANNEL = 17,
P2P_ATTR_INVITATION_FLAGS = 18,
P2P_ATTR_OOB_GO_NEG_CHANNEL = 19,
P2P_ATTR_SERVICE_HASH = 21,
P2P_ATTR_SESSION_INFORMATION_DATA = 22,
P2P_ATTR_CONNECTION_CAPABILITY = 23,
P2P_ATTR_ADVERTISEMENT_ID = 24,
P2P_ATTR_ADVERTISED_SERVICE = 25,
P2P_ATTR_SESSION_ID = 26,
P2P_ATTR_FEATURE_CAPABILITY = 27,
P2P_ATTR_PERSISTENT_GROUP = 28,
P2P_ATTR_VENDOR_SPECIFIC = 221
};
#define P2P_MAX_GO_INTENT 15
/* P2P Capability - Device Capability bitmap */
#define P2P_DEV_CAPAB_SERVICE_DISCOVERY BIT(0)
#define P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY BIT(1)
#define P2P_DEV_CAPAB_CONCURRENT_OPER BIT(2)
#define P2P_DEV_CAPAB_INFRA_MANAGED BIT(3)
#define P2P_DEV_CAPAB_DEVICE_LIMIT BIT(4)
#define P2P_DEV_CAPAB_INVITATION_PROCEDURE BIT(5)
#define P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE BIT(6)
/* P2P Capability - Group Capability bitmap */
#define P2P_GROUP_CAPAB_GROUP_OWNER BIT(0)
#define P2P_GROUP_CAPAB_PERSISTENT_GROUP BIT(1)
#define P2P_GROUP_CAPAB_GROUP_LIMIT BIT(2)
#define P2P_GROUP_CAPAB_INTRA_BSS_DIST BIT(3)
#define P2P_GROUP_CAPAB_CROSS_CONN BIT(4)
#define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5)
#define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7)
/* P2PS Coordination Protocol Transport Bitmap */
#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0)
#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1)
struct p2ps_feature_capab {
u8 cpt;
u8 reserved;
} STRUCT_PACKED;
/* Invitation Flags */
#define P2P_INVITATION_FLAGS_TYPE BIT(0)
/* P2P Manageability */
#define P2P_MAN_DEVICE_MANAGEMENT BIT(0)
#define P2P_MAN_CROSS_CONNECTION_PERMITTED BIT(1)
#define P2P_MAN_COEXISTENCE_OPTIONAL BIT(2)
enum p2p_status_code {
P2P_SC_SUCCESS = 0,
P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1,
P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2,
P2P_SC_FAIL_LIMIT_REACHED = 3,
P2P_SC_FAIL_INVALID_PARAMS = 4,
P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5,
P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6,
P2P_SC_FAIL_NO_COMMON_CHANNELS = 7,
P2P_SC_FAIL_UNKNOWN_GROUP = 8,
P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9,
P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10,
P2P_SC_FAIL_REJECTED_BY_USER = 11,
P2P_SC_SUCCESS_DEFERRED = 12,
};
enum p2p_role_indication {
P2P_DEVICE_NOT_IN_GROUP = 0x00,
P2P_CLIENT_IN_A_GROUP = 0x01,
P2P_GO_IN_A_GROUP = 0x02,
};
#define P2P_WILDCARD_SSID "DIRECT-"
#define P2P_WILDCARD_SSID_LEN 7
/* P2P action frames */
enum p2p_act_frame_type {
P2P_NOA = 0,
P2P_PRESENCE_REQ = 1,
P2P_PRESENCE_RESP = 2,
P2P_GO_DISC_REQ = 3
};
/* P2P public action frames */
enum p2p_action_frame_type {
P2P_GO_NEG_REQ = 0,
P2P_GO_NEG_RESP = 1,
P2P_GO_NEG_CONF = 2,
P2P_INVITATION_REQ = 3,
P2P_INVITATION_RESP = 4,
P2P_DEV_DISC_REQ = 5,
P2P_DEV_DISC_RESP = 6,
P2P_PROV_DISC_REQ = 7,
P2P_PROV_DISC_RESP = 8
};
enum p2p_service_protocol_type {
P2P_SERV_ALL_SERVICES = 0,
P2P_SERV_BONJOUR = 1,
P2P_SERV_UPNP = 2,
P2P_SERV_WS_DISCOVERY = 3,
P2P_SERV_WIFI_DISPLAY = 4,
P2P_SERV_P2PS = 11,
P2P_SERV_VENDOR_SPECIFIC = 255
};
enum p2p_sd_status {
P2P_SD_SUCCESS = 0,
P2P_SD_PROTO_NOT_AVAILABLE = 1,
P2P_SD_REQUESTED_INFO_NOT_AVAILABLE = 2,
P2P_SD_BAD_REQUEST = 3
};
enum wifi_display_subelem {
WFD_SUBELEM_DEVICE_INFO = 0,
WFD_SUBELEM_ASSOCIATED_BSSID = 1,
WFD_SUBELEM_AUDIO_FORMATS = 2,
WFD_SUBELEM_VIDEO_FORMATS = 3,
WFD_SUBELEM_3D_VIDEO_FORMATS = 4,
WFD_SUBELEM_CONTENT_PROTECTION = 5,
WFD_SUBELEM_COUPLED_SINK = 6,
WFD_SUBELEM_EXT_CAPAB = 7,
WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
WFD_SUBELEM_SESSION_INFO = 9,
WFD_SUBELEM_MAC_INFO = 10,
WFD_SUBELEM_R2_DEVICE_INFO = 11,
};
/* 802.11s */
#define MESH_SYNC_METHOD_NEIGHBOR_OFFSET 1
#define MESH_SYNC_METHOD_VENDOR 255
#define MESH_PATH_PROTOCOL_HWMP 1
#define MESH_PATH_PROTOCOL_VENDOR 255
#define MESH_PATH_METRIC_AIRTIME 1
#define MESH_PATH_METRIC_VENDOR 255
/* IEEE 802.11s - Mesh Capability */
#define MESH_CAP_ACCEPT_ADDITIONAL_PEER BIT(0)
#define MESH_CAP_MCCA_SUPPORTED BIT(1)
#define MESH_CAP_MCCA_ENABLED BIT(2)
#define MESH_CAP_FORWARDING BIT(3)
#define MESH_CAP_MBCA_ENABLED BIT(4)
#define MESH_CAP_TBTT_ADJUSTING BIT(5)
#define MESH_CAP_MESH_PS_LEVEL BIT(6)
enum plink_action_field {
PLINK_OPEN = 1,
PLINK_CONFIRM,
PLINK_CLOSE
};
#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
#define VENDOR_VHT_TYPE 0x04
#define VENDOR_VHT_SUBTYPE 0x08
#define VENDOR_VHT_SUBTYPE2 0x00
#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
/* IEEE 802.11v - WNM Action field values */
enum wnm_action {
WNM_EVENT_REQ = 0,
WNM_EVENT_REPORT = 1,
WNM_DIAGNOSTIC_REQ = 2,
WNM_DIAGNOSTIC_REPORT = 3,
WNM_LOCATION_CFG_REQ = 4,
WNM_LOCATION_CFG_RESP = 5,
WNM_BSS_TRANS_MGMT_QUERY = 6,
WNM_BSS_TRANS_MGMT_REQ = 7,
WNM_BSS_TRANS_MGMT_RESP = 8,
WNM_FMS_REQ = 9,
WNM_FMS_RESP = 10,
WNM_COLLOCATED_INTERFERENCE_REQ = 11,
WNM_COLLOCATED_INTERFERENCE_REPORT = 12,
WNM_TFS_REQ = 13,
WNM_TFS_RESP = 14,
WNM_TFS_NOTIFY = 15,
WNM_SLEEP_MODE_REQ = 16,
WNM_SLEEP_MODE_RESP = 17,
WNM_TIM_BROADCAST_REQ = 18,
WNM_TIM_BROADCAST_RESP = 19,
WNM_QOS_TRAFFIC_CAPAB_UPDATE = 20,
WNM_CHANNEL_USAGE_REQ = 21,
WNM_CHANNEL_USAGE_RESP = 22,
WNM_DMS_REQ = 23,
WNM_DMS_RESP = 24,
WNM_TIMING_MEASUREMENT_REQ = 25,
WNM_NOTIFICATION_REQ = 26,
WNM_NOTIFICATION_RESP = 27
};
/* IEEE 802.11v - BSS Transition Management Request - Request Mode */
#define WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED BIT(0)
#define WNM_BSS_TM_REQ_ABRIDGED BIT(1)
#define WNM_BSS_TM_REQ_DISASSOC_IMMINENT BIT(2)
#define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
#define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
/* IEEE Std 802.11-2012 - Table 8-253 */
enum bss_trans_mgmt_status_code {
WNM_BSS_TM_ACCEPT = 0,
WNM_BSS_TM_REJECT_UNSPECIFIED = 1,
WNM_BSS_TM_REJECT_INSUFFICIENT_BEACON = 2,
WNM_BSS_TM_REJECT_INSUFFICIENT_CAPABITY = 3,
WNM_BSS_TM_REJECT_UNDESIRED = 4,
WNM_BSS_TM_REJECT_DELAY_REQUEST = 5,
WNM_BSS_TM_REJECT_STA_CANDIDATE_LIST_PROVIDED = 6,
WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES = 7,
WNM_BSS_TM_REJECT_LEAVING_ESS = 8
};
/*
* IEEE P802.11-REVmc/D5.0 Table 9-150 - Optional subelement IDs for
* neighbor report
*/
#define WNM_NEIGHBOR_TSF 1
#define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING 2
#define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE 3
#define WNM_NEIGHBOR_BSS_TERMINATION_DURATION 4
#define WNM_NEIGHBOR_BEARING 5
#define WNM_NEIGHBOR_WIDE_BW_CHAN 6
#define WNM_NEIGHBOR_MEASUREMENT_REPORT 39
#define WNM_NEIGHBOR_HT_CAPAB 45
#define WNM_NEIGHBOR_HT_OPER 61
#define WNM_NEIGHBOR_SEC_CHAN_OFFSET 62
#define WNM_NEIGHBOR_MEASUREMENT_PILOT 66
#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES 70
#define WNM_NEIGHBOR_MULTIPLE_BSSID 71
#define WNM_NEIGHBOR_VHT_CAPAB 191
#define WNM_NEIGHBOR_VHT_OPER 192
/* QoS action */
enum qos_action {
QOS_ADDTS_REQ = 0,
QOS_ADDTS_RESP = 1,
QOS_DELTS = 2,
QOS_SCHEDULE = 3,
QOS_QOS_MAP_CONFIG = 4,
};
/* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
#define WLAN_20_40_BSS_COEX_INFO_REQ BIT(0)
#define WLAN_20_40_BSS_COEX_40MHZ_INTOL BIT(1)
#define WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ BIT(2)
#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_REQ BIT(3)
#define WLAN_20_40_BSS_COEX_OBSS_EXEMPT_GRNT BIT(4)
struct ieee80211_2040_bss_coex_ie {
u8 element_id;
u8 length;
u8 coex_param;
} STRUCT_PACKED;
struct ieee80211_2040_intol_chan_report {
u8 element_id;
u8 length;
u8 op_class;
u8 variable[0]; /* Channel List */
} STRUCT_PACKED;
/* IEEE 802.11v - WNM-Sleep Mode element */
struct wnm_sleep_element {
u8 eid; /* WLAN_EID_WNMSLEEP */
u8 len;
u8 action_type; /* WNM_SLEEP_ENTER/WNM_SLEEP_MODE_EXIT */
u8 status;
le16 intval;
} STRUCT_PACKED;
#define WNM_SLEEP_MODE_ENTER 0
#define WNM_SLEEP_MODE_EXIT 1
enum wnm_sleep_mode_response_status {
WNM_STATUS_SLEEP_ACCEPT = 0,
WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1,
WNM_STATUS_DENIED_ACTION = 2,
WNM_STATUS_DENIED_TMP = 3,
WNM_STATUS_DENIED_KEY = 4,
WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5
};
/* WNM-Sleep Mode subelement IDs */
enum wnm_sleep_mode_subelement_id {
WNM_SLEEP_SUBELEM_GTK = 0,
WNM_SLEEP_SUBELEM_IGTK = 1,
WNM_SLEEP_SUBELEM_BIGTK = 2,
};
/* WNM notification type (IEEE P802.11-REVmd/D3.0, Table 9-430) */
enum wnm_notification_Type {
WNM_NOTIF_TYPE_FIRMWARE_UPDATE = 0,
WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE = 2,
WNM_NOTIF_TYPE_VENDOR_SPECIFIC = 221,
};
/* Channel Switch modes (802.11h) */
#define CHAN_SWITCH_MODE_ALLOW_TX 0
#define CHAN_SWITCH_MODE_BLOCK_TX 1
struct tpc_report {
u8 eid;
u8 len;
u8 tx_power;
u8 link_margin;
} STRUCT_PACKED;
#define RRM_CAPABILITIES_IE_LEN 5
/* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */
struct rrm_link_measurement_request {
u8 dialog_token;
s8 tx_power;
s8 max_tp;
u8 variable[0];
} STRUCT_PACKED;
/* IEEE Std 802.11-2012, 8.5.7.5 - Link Measurement Report frame format */
struct rrm_link_measurement_report {
u8 dialog_token;
struct tpc_report tpc;
u8 rx_ant_id;
u8 tx_ant_id;
u8 rcpi;
u8 rsni;
u8 variable[0];
} STRUCT_PACKED;
/* IEEE Std 802.11-2016, 9.4.2.21 - Measurement Request element */
struct rrm_measurement_request_element {
u8 eid; /* Element ID */
u8 len; /* Length */
u8 token; /* Measurement Token */
u8 mode; /* Measurement Request Mode */
u8 type; /* Measurement Type */
u8 variable[0]; /* Measurement Request */
} STRUCT_PACKED;
/* IEEE Std 802.11-2016, Figure 9-148 - Measurement Request Mode field */
#define MEASUREMENT_REQUEST_MODE_PARALLEL BIT(0)
#define MEASUREMENT_REQUEST_MODE_ENABLE BIT(1)
#define MEASUREMENT_REQUEST_MODE_REQUEST BIT(2)
#define MEASUREMENT_REQUEST_MODE_REPORT BIT(3)
#define MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY BIT(4)
/* IEEE Std 802.11-2016, 9.4.2.21.7 - Beacon request */
struct rrm_measurement_beacon_request {
u8 oper_class; /* Operating Class */
u8 channel; /* Channel Number */
le16 rand_interval; /* Randomization Interval (in TUs) */
le16 duration; /* Measurement Duration (in TUs) */
u8 mode; /* Measurement Mode */
u8 bssid[ETH_ALEN]; /* BSSID */
u8 variable[0]; /* Optional Subelements */
} STRUCT_PACKED;
/*
* IEEE Std 802.11-2016, Table 9-87 - Measurement Mode definitions for Beacon
* request
*/
enum beacon_report_mode {
BEACON_REPORT_MODE_PASSIVE = 0,
BEACON_REPORT_MODE_ACTIVE = 1,
BEACON_REPORT_MODE_TABLE = 2,
};
/* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */
/* IEEE P802.11-REVmd/D2.0, Table 9-106 - Optional subelement IDs for
* Beacon request */
#define WLAN_BEACON_REQUEST_SUBELEM_SSID 0
#define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */
#define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */
#define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10
#define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */
#define WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION 164
#define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221
/*
* IEEE Std 802.11-2016, Table 9-90 - Reporting Detail values
*/
enum beacon_report_detail {
/* No fixed-length fields or elements */
BEACON_REPORT_DETAIL_NONE = 0,
/* All fixed-length fields and any requested elements in the Request
* element if present */
BEACON_REPORT_DETAIL_REQUESTED_ONLY = 1,
/* All fixed-length fields and elements (default, used when Reporting
* Detail subelement is not included in a Beacon request) */
BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS = 2,
};
/* IEEE Std 802.11-2016, 9.4.2.22 - Measurement Report element */
struct rrm_measurement_report_element {
u8 eid; /* Element ID */
u8 len; /* Length */
u8 token; /* Measurement Token */
u8 mode; /* Measurement Report Mode */
u8 type; /* Measurement Type */
u8 variable[0]; /* Measurement Report */
} STRUCT_PACKED;
/* IEEE Std 802.11-2016, Figure 9-192 - Measurement Report Mode field */
#define MEASUREMENT_REPORT_MODE_ACCEPT 0
#define MEASUREMENT_REPORT_MODE_REJECT_LATE BIT(0)
#define MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE BIT(1)
#define MEASUREMENT_REPORT_MODE_REJECT_REFUSED BIT(2)
/* IEEE Std 802.11-2016, 9.4.2.22.7 - Beacon report */
struct rrm_measurement_beacon_report {
u8 op_class; /* Operating Class */
u8 channel; /* Channel Number */
le64 start_time; /* Actual Measurement Start Time
* (in TSF of the BSS requesting the measurement) */
le16 duration; /* in TUs */
u8 report_info; /* Reported Frame Information */
u8 rcpi; /* RCPI */
u8 rsni; /* RSNI */
u8 bssid[ETH_ALEN]; /* BSSID */
u8 antenna_id; /* Antenna ID */
le32 parent_tsf; /* Parent TSF */
u8 variable[0]; /* Optional Subelements */
} STRUCT_PACKED;
/* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */
/* IEEE P802.11-REVmd/D2.0, Table 9-130 - Optional subelement IDs for
* Beacon report */
#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1
#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID 2
#define WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION 164
#define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221
/* IEEE P802.11-REVmd/D2.0, Table 9-232 - Data field format of the
* Reported Frame Body Fragment ID subelement */
#define REPORTED_FRAME_BODY_SUBELEM_LEN 4
#define REPORTED_FRAME_BODY_MORE_FRAGMENTS BIT(7)
/* IEEE P802.11-REVmd/D2.0, 9.4.2.21.7 - Beacon report */
#define BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN 3
/* IEEE Std 802.11ad-2012 - Multi-band element */
struct multi_band_ie {
u8 eid; /* WLAN_EID_MULTI_BAND */
u8 len;
u8 mb_ctrl;
u8 band_id;
u8 op_class;
u8 chan;
u8 bssid[ETH_ALEN];
le16 beacon_int;
u8 tsf_offs[8];
u8 mb_connection_capability;
u8 fst_session_tmout;
/* Optional:
* STA MAC Address
* Pairwise Cipher Suite Count
* Pairwise Cipher Suite List
*/
u8 variable[0];
} STRUCT_PACKED;
enum mb_ctrl_sta_role {
MB_STA_ROLE_AP = 0,
MB_STA_ROLE_TDLS_STA = 1,
MB_STA_ROLE_IBSS_STA = 2,
MB_STA_ROLE_PCP = 3,
MB_STA_ROLE_NON_PCP_NON_AP = 4
};
#define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2))
#define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK))
#define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3)))
#define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4)))
enum mb_band_id {
MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */
MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */
MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */
};
#define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0)))
#define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1)))
#define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2)))
#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3)))
#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4)))
/* IEEE Std 802.11ad-2014 - FST Action field */
enum fst_action {
FST_ACTION_SETUP_REQUEST = 0,
FST_ACTION_SETUP_RESPONSE = 1,
FST_ACTION_TEAR_DOWN = 2,
FST_ACTION_ACK_REQUEST = 3,
FST_ACTION_ACK_RESPONSE = 4,
FST_ACTION_ON_CHANNEL_TUNNEL = 5,
};
/* IEEE Std 802.11ac-2013, Annex C - dot11PHYType */
enum phy_type {
PHY_TYPE_UNSPECIFIED = 0,
PHY_TYPE_FHSS = 1,
PHY_TYPE_DSSS = 2,
PHY_TYPE_IRBASEBAND = 3,
PHY_TYPE_OFDM = 4,
PHY_TYPE_HRDSSS = 5,
PHY_TYPE_ERP = 6,
PHY_TYPE_HT = 7,
PHY_TYPE_DMG = 8,
PHY_TYPE_VHT = 9,
};
/* IEEE P802.11-REVmd/D3.0, 9.4.2.36 - Neighbor Report element */
/* BSSID Information Field */
#define NEI_REP_BSSID_INFO_AP_NOT_REACH BIT(0)
#define NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH BIT(1)
#define NEI_REP_BSSID_INFO_AP_REACHABLE (BIT(0) | BIT(1))
#define NEI_REP_BSSID_INFO_SECURITY BIT(2)
#define NEI_REP_BSSID_INFO_KEY_SCOPE BIT(3)
#define NEI_REP_BSSID_INFO_SPECTRUM_MGMT BIT(4)
#define NEI_REP_BSSID_INFO_QOS BIT(5)
#define NEI_REP_BSSID_INFO_APSD BIT(6)
#define NEI_REP_BSSID_INFO_RM BIT(7)
#define NEI_REP_BSSID_INFO_DELAYED_BA BIT(8)
#define NEI_REP_BSSID_INFO_IMM_BA BIT(9)
#define NEI_REP_BSSID_INFO_MOBILITY_DOMAIN BIT(10)
#define NEI_REP_BSSID_INFO_HT BIT(11)
#define NEI_REP_BSSID_INFO_VHT BIT(12)
#define NEI_REP_BSSID_INFO_FTM BIT(13)
#define NEI_REP_BSSID_INFO_HE BIT(14)
/*
* IEEE P802.11-REVmc/D5.0 Table 9-152 - HT/VHT Operation Information
* subfields.
* Note: These definitions are not the same as other CHANWIDTH_*.
*/
enum nr_chan_width {
NR_CHAN_WIDTH_20 = 0,
NR_CHAN_WIDTH_40 = 1,
NR_CHAN_WIDTH_80 = 2,
NR_CHAN_WIDTH_160 = 3,
NR_CHAN_WIDTH_80P80 = 4,
};
struct ieee80211_he_capabilities {
u8 he_mac_capab_info[6];
u8 he_phy_capab_info[11];
/* Followed by 4, 8, or 12 octets of Supported HE-MCS And NSS Set field
* and optional variable length PPE Thresholds field. */
u8 optional[37];
} STRUCT_PACKED;
#define IEEE80211_HE_CAPAB_MIN_LEN (6 + 11)
struct ieee80211_he_operation {
le32 he_oper_params; /* HE Operation Parameters[3] and
* BSS Color Information[1] */
le16 he_mcs_nss_set;
/* Followed by conditional VHT Operation Information (3 octets),
* Max Co-Hosted BSSID Indicator subfield (1 octet), and/or 6 GHz
* Operation Information subfield (5 octets). */
} STRUCT_PACKED;
/* IEEE P802.11ax/D6.0, Figure 9-787k - 6 GHz Operation Information field */
struct ieee80211_he_6ghz_oper_info {
u8 primary_chan;
u8 control;
u8 chan_center_freq_seg0;
u8 chan_center_freq_seg1;
u8 min_rate;
} STRUCT_PACKED;
#define HE_6GHZ_OPER_INFO_CTRL_CHAN_WIDTH_MASK (BIT(0) | BIT(1))
#define HE_6GHZ_OPER_INFO_CTRL_DUP_BEACON BIT(2)
/* IEEE P802.11ax/D6.0, 9.4.2.261 HE 6 GHz Band Capabilities element */
struct ieee80211_he_6ghz_band_cap {
/* Minimum MPDU Start Spacing B0..B2
* Maximum A-MPDU Length Exponent B3..B5
* Maximum MPDU Length B6..B7 */
le16 capab;
} STRUCT_PACKED;
#define HE_6GHZ_BAND_CAP_MIN_MPDU_START (BIT(0) | BIT(1) | BIT(2))
#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_16K BIT(3)
#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_32K BIT(4)
#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_64K (BIT(3) | BIT(4))
#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_128K BIT(5)
#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_256K (BIT(3) | BIT(5))
#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_512K (BIT(4) | BIT(5))
#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_1024K (BIT(3) | BIT(4) | BIT(5))
#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK (BIT(3) | BIT(4) | BIT(5))
#define HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT 3
#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_7991 BIT(6)
#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_11454 BIT(7)
#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK (BIT(6) | BIT(7))
#define HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT 6
#define HE_6GHZ_BAND_CAP_SMPS_MASK (BIT(9) | BIT(10))
#define HE_6GHZ_BAND_CAP_SMPS_STATIC 0
#define HE_6GHZ_BAND_CAP_SMPS_DYNAMIC BIT(9)
#define HE_6GHZ_BAND_CAP_SMPS_DISABLED (BIT(9) | BIT(10))
#define HE_6GHZ_BAND_CAP_RD_RESPONDER BIT(11)
#define HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS BIT(12)
#define HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS BIT(13)
/*
* IEEE P802.11ax/D4.0, 9.4.2.246 Spatial Reuse Parameter Set element
*/
struct ieee80211_spatial_reuse {
u8 sr_ctrl; /* SR Control */
/* Up to 19 octets of parameters:
* Non-SRG OBSS PD Max Offset[0 or 1]
* SRG OBSS PD Min Offset[0 or 1]
* SRG OBSS PD Max Offset[0 or 1]
* SRG BSS Color Bitmap[0 or 8]
* SRG Partial BSSID Bitmap[0 or 8]
*/
u8 params[19];
} STRUCT_PACKED;
/* HE Capabilities Information defines */
#define HE_MACCAP_TWT_RESPONDER ((u8) BIT(2))
#define HE_PHYCAP_CHANNEL_WIDTH_SET_IDX 0
#define HE_PHYCAP_CHANNEL_WIDTH_MASK ((u8) (BIT(1) | BIT(2) | \
BIT(3) | BIT(4)))
#define HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G ((u8) BIT(1))
#define HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G ((u8) BIT(2))
#define HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G ((u8) BIT(3))
#define HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G ((u8) BIT(4))
#define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3
#define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7))
#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4
#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB ((u8) BIT(0))
#define HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX 4
#define HE_PHYCAP_MU_BEAMFORMER_CAPAB ((u8) BIT(1))
#define HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX 6
#define HE_PHYCAP_PPE_THRESHOLD_PRESENT ((u8) BIT(7))
/* HE PPE Threshold define */
#define HE_PPE_THRES_RU_INDEX_BITMASK_MASK 0xf
#define HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT 3
#define HE_PPE_THRES_NSS_MASK 0x7
/* HE Operation defines */
/* HE Operation Parameters and BSS Color Information fields */
#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(0) | BIT(1) | \
BIT(2)))
#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 0
#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(3))
#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(4) | BIT(5) | \
BIT(6) | BIT(7) | \
BIT(8) | BIT(9) | \
BIT(10) | BIT(11) | \
BIT(12) | BIT(13)))
#define HE_OPERATION_RTS_THRESHOLD_OFFSET 4
#define HE_OPERATION_VHT_OPER_INFO ((u32) BIT(14))
#define HE_OPERATION_COHOSTED_BSS ((u32) BIT(15))
#define HE_OPERATION_ER_SU_DISABLE ((u32) BIT(16))
#define HE_OPERATION_6GHZ_OPER_INFO ((u32) BIT(17))
#define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(24) | BIT(25) | \
BIT(26) | BIT(27) | \
BIT(28) | BIT(29)))
#define HE_OPERATION_BSS_COLOR_PARTIAL ((u32) BIT(30))
#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(31))
#define HE_OPERATION_BSS_COLOR_OFFSET 24
/* Spatial Reuse defines */
#define SPATIAL_REUSE_SRP_DISALLOWED BIT(0)
#define SPATIAL_REUSE_NON_SRG_OBSS_PD_SR_DISALLOWED BIT(1)
#define SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT BIT(2)
#define SPATIAL_REUSE_SRG_INFORMATION_PRESENT BIT(3)
#define SPATIAL_REUSE_HESIGA_SR_VAL15_ALLOWED BIT(4)
struct ieee80211_he_mu_edca_parameter_set {
u8 he_qos_info;
u8 he_mu_ac_be_param[3];
u8 he_mu_ac_bk_param[3];
u8 he_mu_ac_vi_param[3];
u8 he_mu_ac_vo_param[3];
} STRUCT_PACKED;
/* HE MU AC parameter record field format */
/* ACI/AIFSN */
#define HE_MU_AC_PARAM_ACI_IDX 0
#define HE_MU_AC_PARAM_AIFSN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3)))
#define HE_MU_AC_PARAM_ACM ((u8) BIT(4))
#define HE_MU_AC_PARAM_ACI ((u8) (BIT(5) | BIT(6)))
/* B7: Reserved */
/* ECWmin/ECWmax */
#define HE_MU_AC_PARAM_ECW_IDX 1
#define HE_MU_AC_PARAM_ECWMIN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3)))
#define HE_MU_AC_PARAM_ECWMAX ((u8) (BIT(4) | BIT(5) | BIT(6) | BIT(7)))
/* MU EDCA Timer */
#define HE_MU_AC_PARAM_TIMER_IDX 2
/* HE QoS Info field */
#define HE_QOS_INFO_EDCA_PARAM_SET_COUNT ((u8) (BIT(0) | BIT(1) | \
BIT(2) | BIT(3)))
#define HE_QOS_INFO_Q_ACK ((u8) (BIT(4)))
#define HE_QOS_INFO_QUEUE_REQUEST ((u8) (BIT(5)))
#define HE_QOS_INFO_TXOP_REQUEST ((u8) (BIT(6)))
/* B7: Reserved if sent by an AP; More Data Ack if sent by a non-AP STA */
#define HE_QOS_INFO_MORE_DATA_ACK ((u8) (BIT(7)))
+/*
+ * IEEE Std 802.11-2020 and IEEE Std 802.11ax-2021
+ * 9.4.2.170 Reduced Neighbor Report element
+ */
+#define RNR_HEADER_LEN 2
+#define RNR_TBTT_HEADER_LEN 4
+#define RNR_TBTT_INFO_COUNT(x) (((x) & 0xf) << 4)
+#define RNR_TBTT_INFO_COUNT_MAX 16
+#define RNR_TBTT_INFO_LEN 13
+#define RNR_NEIGHBOR_AP_OFFSET_UNKNOWN 255
+/* Figure 9-632a - BSS Parameters subfield format */
+#define RNR_BSS_PARAM_OCT_RECOMMENDED BIT(0)
+#define RNR_BSS_PARAM_SAME_SSID BIT(1)
+#define RNR_BSS_PARAM_MULTIPLE_BSSID BIT(2)
+#define RNR_BSS_PARAM_TRANSMITTED_BSSID BIT(3)
+#define RNR_BSS_PARAM_MEMBER_CO_LOCATED_ESS BIT(4)
+#define RNR_BSS_PARAM_UNSOLIC_PROBE_RESP_ACTIVE BIT(5)
+#define RNR_BSS_PARAM_CO_LOCATED BIT(6)
+#define RNR_20_MHZ_PSD_MAX_TXPOWER 255 /* dBm */
+
/* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */
#define EDMG_BSS_OPERATING_CHANNELS_OFFSET 6
#define EDMG_OPERATING_CHANNEL_WIDTH_OFFSET 7
/* IEEE P802.11ay/D4.0, 29.3.4 - Channelization */
enum edmg_channel {
EDMG_CHANNEL_9 = 9,
EDMG_CHANNEL_10 = 10,
EDMG_CHANNEL_11 = 11,
EDMG_CHANNEL_12 = 12,
EDMG_CHANNEL_13 = 13,
};
/* Represent CB2 contiguous channels */
#define EDMG_CHANNEL_9_SUBCHANNELS (BIT(0) | BIT(1)) /* channels 1 and 2 */
#define EDMG_CHANNEL_10_SUBCHANNELS (BIT(1) | BIT(2)) /* channels 2 and 3 */
#define EDMG_CHANNEL_11_SUBCHANNELS (BIT(2) | BIT(3)) /* channels 3 and 4 */
#define EDMG_CHANNEL_12_SUBCHANNELS (BIT(3) | BIT(4)) /* channels 4 and 5 */
#define EDMG_CHANNEL_13_SUBCHANNELS (BIT(4) | BIT(5)) /* channels 5 and 6 */
/**
* enum edmg_bw_config - Allowed channel bandwidth configurations
* @EDMG_BW_CONFIG_4: 2.16 GHz
* @EDMG_BW_CONFIG_5: 2.16 GHz and 4.32 GHz
*
* IEEE P802.11ay/D4.0, 9.4.2.251 (EDMG Operation element),
* Table 13 (Channel BW Configuration subfield definition)
*/
enum edmg_bw_config {
EDMG_BW_CONFIG_4 = 4,
EDMG_BW_CONFIG_5 = 5,
};
/* DPP Public Action frame identifiers - OUI_WFA */
#define DPP_OUI_TYPE 0x1A
/* Robust AV streaming Action field values */
enum robust_av_streaming_action {
ROBUST_AV_SCS_REQ = 0,
ROBUST_AV_SCS_RESP = 1,
ROBUST_AV_GROUP_MEMBERSHIP_REQ = 2,
ROBUST_AV_GROUP_MEMBERSHIP_RESP = 3,
ROBUST_AV_MSCS_REQ = 4,
ROBUST_AV_MSCS_RESP = 5,
};
enum scs_request_type {
SCS_REQ_ADD = 0,
SCS_REQ_REMOVE = 1,
SCS_REQ_CHANGE = 2,
};
/* Optional subelement IDs for MSCS Descriptor element */
enum mscs_description_subelem {
MCSC_SUBELEM_STATUS = 1,
};
/*
* IEEE Std 802.11ai-2016, 9.6.8.36 FILS Discovery frame format,
* Figure 9-687b - FILS Discovery Frame Control subfield format
*/
#define FD_FRAME_CTL_CAP_PRESENT ((u16) BIT(5))
#define FD_FRAME_CTL_SHORT_SSID_PRESENT ((u16) BIT(6))
#define FD_FRAME_CTL_AP_CSN_PRESENT ((u16) BIT(7))
#define FD_FRAME_CTL_ANO_PRESENT ((u16) BIT(8))
#define FD_FRAME_CTL_FREQ_SEG1_PRESENT ((u16) BIT(9))
#define FD_FRAME_CTL_PRI_CHAN_PRESENT ((u16) BIT(10))
#define FD_FRAME_CTL_RSN_INFO_PRESENT ((u16) BIT(11))
#define FD_FRAME_CTL_LENGTH_PRESENT ((u16) BIT(12))
#define FD_FRAME_CTL_MD_PRESENT ((u16) BIT(13))
/*
* IEEE Std 802.11ai-2016, 9.6.8.36 FILS Discovery frame format,
* Figure 9-687c - FD Capability subfield format
*/
#define FD_CAP_ESS BIT(0)
#define FD_CAP_PRIVACY BIT(1)
#define FD_CAP_MULTI_BSSID_PRESENT BIT(9)
#define FD_CAP_BSS_CHWIDTH_20 0
#define FD_CAP_BSS_CHWIDTH_40 1
#define FD_CAP_BSS_CHWIDTH_80 2
#define FD_CAP_BSS_CHWIDTH_160_80_80 3
#define FD_CAP_BSS_CHWIDTH_SHIFT 2
#define FD_CAP_NSS_1 0
#define FD_CAP_NSS_2 1
#define FD_CAP_NSS_3 2
#define FD_CAP_NSS_4 3
#define FD_CAP_NSS_5_8 4
#define FD_CAP_NSS_SHIFT 5
#define FD_CAP_PHY_INDEX_HR_DSSS 0
#define FD_CAP_PHY_INDEX_ERP_OFDM 1
#define FD_CAP_PHY_INDEX_HT 2
#define FD_CAP_PHY_INDEX_VHT 3
#define FD_CAP_PHY_INDEX_HE 4 /* P802.11ax */
#define FD_CAP_PHY_INDEX_SHIFT 10
/*
* IEEE P802.11ax/D8.0 26.17.2.3.2, AP behavior for fast passive scanning
*/
#define FD_MAX_INTERVAL_6GHZ 20 /* TUs */
+/* Protected Vendor-specific QoS Management Action frame identifiers - WFA */
+#define QM_ACTION_VENDOR_TYPE 0x506f9a1a
+#define QM_ACTION_OUI_TYPE 0x1a
+
+/* QoS Management Action frame OUI subtypes */
+#define QM_DSCP_POLICY_QUERY 0
+#define QM_DSCP_POLICY_REQ 1
+#define QM_DSCP_POLICY_RESP 2
+
+/* QoS Management attributes */
+enum qm_attr_id {
+ QM_ATTR_PORT_RANGE = 1,
+ QM_ATTR_DSCP_POLICY = 2,
+ QM_ATTR_TCLAS = 3,
+ QM_ATTR_DOMAIN_NAME = 4,
+};
+
+/* DSCP Policy attribute - Request Type */
+enum dscp_policy_request_type {
+ DSCP_POLICY_REQ_ADD = 0, /* ADD/UPDATE */
+ DSCP_POLICY_REQ_REMOVE = 1,
+};
+
+/* Request/Response Control field of DSCP Policy Request/Response frame */
+#define DSCP_POLICY_CTRL_MORE BIT(0)
+#define DSCP_POLICY_CTRL_RESET BIT(1)
+
+/* Wi-Fi Alliance Capabilities element - Capabilities field */
+#define WFA_CAPA_QM_DSCP_POLICY BIT(0)
+#define WFA_CAPA_QM_UNSOLIC_DSCP BIT(1)
+
#endif /* IEEE802_11_DEFS_H */
diff --git a/contrib/wpa/src/common/ptksa_cache.c b/contrib/wpa/src/common/ptksa_cache.c
index 6a053d65019d..8fcb135077f2 100644
--- a/contrib/wpa/src/common/ptksa_cache.c
+++ b/contrib/wpa/src/common/ptksa_cache.c
@@ -1,321 +1,321 @@
/*
* RSN PTKSA cache implementation
*
* Copyright (C) 2019 Intel Corporation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "utils/common.h"
#include "eloop.h"
#include "common/ptksa_cache.h"
#define PTKSA_CACHE_MAX_ENTRIES 16
struct ptksa_cache {
struct dl_list ptksa;
unsigned int n_ptksa;
};
static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa);
static void ptksa_cache_free_entry(struct ptksa_cache *ptksa,
struct ptksa_cache_entry *entry)
{
ptksa->n_ptksa--;
dl_list_del(&entry->list);
bin_clear_free(entry, sizeof(*entry));
}
static void ptksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
{
struct ptksa_cache *ptksa = eloop_ctx;
struct ptksa_cache_entry *e, *next;
struct os_reltime now;
if (!ptksa)
return;
os_get_reltime(&now);
dl_list_for_each_safe(e, next, &ptksa->ptksa,
struct ptksa_cache_entry, list) {
if (e->expiration > now.sec)
continue;
wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR,
MAC2STR(e->addr));
ptksa_cache_free_entry(ptksa, e);
}
ptksa_cache_set_expiration(ptksa);
}
static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa)
{
struct ptksa_cache_entry *e;
int sec;
struct os_reltime now;
eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL);
if (!ptksa || !ptksa->n_ptksa)
return;
e = dl_list_first(&ptksa->ptksa, struct ptksa_cache_entry, list);
if (!e)
return;
os_get_reltime(&now);
sec = e->expiration - now.sec;
if (sec < 0)
sec = 0;
eloop_register_timeout(sec + 1, 0, ptksa_cache_expire, ptksa, NULL);
}
/*
* ptksa_cache_init - Initialize PTKSA cache
*
* Returns: Pointer to PTKSA cache data or %NULL on failure
*/
struct ptksa_cache * ptksa_cache_init(void)
{
struct ptksa_cache *ptksa = os_zalloc(sizeof(struct ptksa_cache));
wpa_printf(MSG_DEBUG, "PTKSA: Initializing");
if (ptksa)
dl_list_init(&ptksa->ptksa);
return ptksa;
}
/*
* ptksa_cache_deinit - Free all entries in PTKSA cache
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
*/
void ptksa_cache_deinit(struct ptksa_cache *ptksa)
{
struct ptksa_cache_entry *e, *next;
if (!ptksa)
return;
wpa_printf(MSG_DEBUG, "PTKSA: Deinit. n_ptksa=%u", ptksa->n_ptksa);
dl_list_for_each_safe(e, next, &ptksa->ptksa,
struct ptksa_cache_entry, list)
ptksa_cache_free_entry(ptksa, e);
eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL);
os_free(ptksa);
}
/*
* ptksa_cache_get - Fetch a PTKSA cache entry
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
* @addr: Peer address or %NULL to match any
* @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any
* Returns: Pointer to PTKSA cache entry or %NULL if no match was found
*/
struct ptksa_cache_entry * ptksa_cache_get(struct ptksa_cache *ptksa,
const u8 *addr, u32 cipher)
{
struct ptksa_cache_entry *e;
if (!ptksa)
return NULL;
dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) {
if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) &&
(cipher == WPA_CIPHER_NONE || cipher == e->cipher))
return e;
}
return NULL;
}
/*
* ptksa_cache_list - Dump text list of entries in PTKSA cache
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
* @buf: Buffer for the list
* @len: Length of the buffer
* Returns: Number of bytes written to buffer
*
* This function is used to generate a text format representation of the
* current PTKSA cache contents for the ctrl_iface PTKSA command.
*/
int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len)
{
struct ptksa_cache_entry *e;
int i = 0, ret;
char *pos = buf;
struct os_reltime now;
if (!ptksa)
return 0;
os_get_reltime(&now);
ret = os_snprintf(pos, buf + len - pos,
"Index / ADDR / Cipher / expiration (secs) / TK / KDK\n");
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) {
ret = os_snprintf(pos, buf + len - pos, "%u " MACSTR,
i, MAC2STR(e->addr));
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
ret = os_snprintf(pos, buf + len - pos, " %s %lu ",
wpa_cipher_txt(e->cipher),
e->expiration - now.sec);
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.tk,
e->ptk.tk_len);
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
ret = os_snprintf(pos, buf + len - pos, " ");
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.kdk,
e->ptk.kdk_len);
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
ret = os_snprintf(pos, buf + len - pos, "\n");
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
i++;
}
return pos - buf;
}
/*
* ptksa_cache_flush - Flush PTKSA cache entries
*
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
* @addr: Peer address or %NULL to match any
* @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any
*/
void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
{
struct ptksa_cache_entry *e, *next;
bool removed = false;
if (!ptksa)
return;
dl_list_for_each_safe(e, next, &ptksa->ptksa, struct ptksa_cache_entry,
list) {
if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) &&
(cipher == WPA_CIPHER_NONE || cipher == e->cipher)) {
wpa_printf(MSG_DEBUG,
"Flush PTKSA cache entry for " MACSTR,
MAC2STR(e->addr));
ptksa_cache_free_entry(ptksa, e);
removed = true;
}
}
if (removed)
ptksa_cache_set_expiration(ptksa);
}
/*
* ptksa_cache_add - Add a PTKSA cache entry
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
* @addr: Peer address
* @cipher: The cipher used
* @life_time: The PTK life time in seconds
* @ptk: The PTK
* Returns: Pointer to the added PTKSA cache entry or %NULL on error
*
* This function creates a PTKSA entry and adds it to the PTKSA cache.
* If an old entry is already in the cache for the same peer and cipher
* this entry will be replaced with the new entry.
*/
struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
const u8 *addr, u32 cipher,
u32 life_time,
const struct wpa_ptk *ptk)
{
- struct ptksa_cache_entry *entry, *tmp;
+ struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL;
struct os_reltime now;
if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE)
return NULL;
/* remove a previous entry if present */
ptksa_cache_flush(ptksa, addr, cipher);
/* no place to add another entry */
if (ptksa->n_ptksa >= PTKSA_CACHE_MAX_ENTRIES)
return NULL;
entry = os_zalloc(sizeof(*entry));
if (!entry)
return NULL;
dl_list_init(&entry->list);
os_memcpy(entry->addr, addr, ETH_ALEN);
entry->cipher = cipher;
os_memcpy(&entry->ptk, ptk, sizeof(entry->ptk));
os_get_reltime(&now);
entry->expiration = now.sec + life_time;
dl_list_for_each(tmp, &ptksa->ptksa, struct ptksa_cache_entry, list) {
- if (tmp->expiration > entry->expiration)
+ if (tmp->expiration > entry->expiration) {
+ tmp2 = tmp;
break;
+ }
}
/*
- * If the list was empty add to the head; otherwise if the expiration is
- * later then all other entries, add it to the end of the list;
+ * If the expiration is later then all other or the list is empty
+ * entries, add it to the end of the list;
* otherwise add it before the relevant entry.
*/
- if (!tmp)
- dl_list_add(&ptksa->ptksa, &entry->list);
- else if (tmp->expiration < entry->expiration)
- dl_list_add(&tmp->list, &entry->list);
+ if (tmp2)
+ dl_list_add(&tmp2->list, &entry->list);
else
- dl_list_add_tail(&tmp->list, &entry->list);
+ dl_list_add_tail(&ptksa->ptksa, &entry->list);
ptksa->n_ptksa++;
wpa_printf(MSG_DEBUG,
"Added PTKSA cache entry addr=" MACSTR " cipher=%u",
MAC2STR(addr), cipher);
return entry;
}
diff --git a/contrib/wpa/src/common/qca-vendor.h b/contrib/wpa/src/common/qca-vendor.h
index 47666f04ae7c..4dac10ef9724 100644
--- a/contrib/wpa/src/common/qca-vendor.h
+++ b/contrib/wpa/src/common/qca-vendor.h
@@ -1,11132 +1,11787 @@
/*
* Qualcomm Atheros OUI and vendor specific assignments
* Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef QCA_VENDOR_H
#define QCA_VENDOR_H
/*
* This file is a registry of identifier assignments from the Qualcomm Atheros
* OUI 00:13:74 for purposes other than MAC address assignment. New identifiers
* can be assigned through normal review process for changes to the upstream
* hostap.git repository.
*/
#define OUI_QCA 0x001374
#ifndef BIT
#define BIT(x) (1U << (x))
#endif
/**
* enum qca_radiotap_vendor_ids - QCA radiotap vendor namespace IDs
*/
enum qca_radiotap_vendor_ids {
QCA_RADIOTAP_VID_WLANTEST = 0,
};
/**
* enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers
*
* @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0
*
* @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event
*
* @QCA_NL80211_VENDOR_SUBCMD_ROAMING: Set roaming policy for drivers that use
* internal BSS-selection. This command uses
* @QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY to specify the new roaming policy
* for the current connection (i.e., changes policy set by the nl80211
* Connect command). @QCA_WLAN_VENDOR_ATTR_MAC_ADDR may optionally be
* included to indicate which BSS to use in case roaming is disabled.
*
* @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency
* ranges to avoid to reduce issues due to interference or internal
* co-existence information in the driver. These frequencies aim to
* minimize the traffic but not to totally avoid the traffic. That said
* for a P2P use case, these frequencies are allowed for the P2P
* discovery/negotiation but avoid the group to get formed on these
* frequencies. The event data structure is defined in
* struct qca_avoid_freq_list.
*
* @QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: Command to check driver support
* for DFS offloading.
*
* @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
* NAN Request/Response and NAN Indication messages. These messages are
* interpreted between the framework and the firmware component. While
* sending the command from userspace to the driver, payload is not
* encapsulated inside any attribute. Attribute QCA_WLAN_VENDOR_ATTR_NAN
* is used when receiving vendor events in userspace from the driver.
*
* @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
* used to configure PMK to the driver even when not connected. This can
* be used to request offloading of key management operations. Only used
* if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
*
* @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of
* NL80211_CMD_ROAM event with optional attributes including information
* from offloaded key management operation. Uses
* enum qca_wlan_vendor_attr_roam_auth attributes. Only used
* if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
*
* @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to
* invoke the ACS function in device and pass selected channels to
* hostapd. Uses enum qca_wlan_vendor_attr_acs_offload attributes.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features
* supported by the driver. enum qca_wlan_vendor_features defines
* the possible features.
*
* @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: Event used by driver,
* which supports DFS offloading, to indicate a channel availability check
* start.
*
* @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: Event used by driver,
* which supports DFS offloading, to indicate a channel availability check
* completion.
*
* @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: Event used by driver,
* which supports DFS offloading, to indicate that the channel availability
* check aborted, no change to the channel status.
*
* @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: Event used by
* driver, which supports DFS offloading, to indicate that the
* Non-Occupancy Period for this channel is over, channel becomes usable.
*
* @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver,
* which supports DFS offloading, to indicate a radar pattern has been
* detected. The channel is now unusable.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO: Get information from the driver.
* Attributes defined in enum qca_wlan_vendor_attr_get_wifi_info.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: Get the feature bitmap
* based on enum wifi_logger_supported_features. Attributes defined in
* enum qca_wlan_vendor_attr_get_logger_features.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA: Get the ring data from a particular
* logger ring, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID is passed as the
* attribute for this command. Attributes defined in
* enum qca_wlan_vendor_attr_wifi_logger_start.
*
* @QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES: Get the supported TDLS
* capabilities of the driver, parameters includes the attributes defined
* in enum qca_wlan_vendor_attr_tdls_get_capabilities.
*
* @QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS: Vendor command used to offload
* sending of certain periodic IP packet to firmware, attributes defined in
* enum qca_wlan_vendor_attr_offloaded_packets.
*
* @QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI: Command used to configure RSSI
* monitoring, defines min and max RSSI which are configured for RSSI
* monitoring. Also used to notify the RSSI breach and provides the BSSID
* and RSSI value that was breached. Attributes defined in
* enum qca_wlan_vendor_attr_rssi_monitoring.
*
* @QCA_NL80211_VENDOR_SUBCMD_NDP: Command used for performing various NAN
* Data Path (NDP) related operations, attributes defined in
* enum qca_wlan_vendor_attr_ndp_params.
*
* @QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD: Command used to enable/disable
* Neighbour Discovery offload, attributes defined in
* enum qca_wlan_vendor_attr_nd_offload.
*
* @QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER: Used to set/get the various
* configuration parameter for BPF packet filter, attributes defined in
* enum qca_wlan_vendor_attr_packet_filter.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE: Gets the driver-firmware
* maximum supported size, attributes defined in
* enum qca_wlan_vendor_drv_info.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS: Command to get various
* data about wake reasons and datapath IP statistics, attributes defined
* in enum qca_wlan_vendor_attr_wake_stats.
*
* @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG: Command used to set configuration
* for IEEE 802.11 communicating outside the context of a basic service
* set, called OCB command. Uses the attributes defines in
* enum qca_wlan_vendor_attr_ocb_set_config.
*
* @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME: Command used to set OCB
* UTC time. Use the attributes defines in
* enum qca_wlan_vendor_attr_ocb_set_utc_time.
*
* @QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT: Command used to start
* sending OCB timing advert frames. Uses the attributes defines in
* enum qca_wlan_vendor_attr_ocb_start_timing_advert.
*
* @QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT: Command used to stop
* OCB timing advert. Uses the attributes defines in
* enum qca_wlan_vendor_attr_ocb_stop_timing_advert.
*
* @QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER: Command used to get TSF
* timer value. Uses the attributes defines in
* enum qca_wlan_vendor_attr_ocb_get_tsf_resp.
*
* @QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES: Command/event to update the
* link properties of the respective interface. As an event, is used
* to notify the connected station's status. The attributes for this
* command are defined in enum qca_wlan_vendor_attr_link_properties.
*
* @QCA_NL80211_VENDOR_SUBCMD_SETBAND: Command to configure the enabled band(s)
* to the driver. This command sets the band(s) through either the
* attribute QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE or
* QCA_WLAN_VENDOR_ATTR_SETBAND_MASK (or both).
* QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE refers enum qca_set_band as unsigned
* integer values and QCA_WLAN_VENDOR_ATTR_SETBAND_MASK refers it as 32
* bit unsigned bitmask values. The allowed values for
* QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE are limited to QCA_SETBAND_AUTO,
* QCA_SETBAND_5G, and QCA_SETBAND_2G. Other values/bitmasks are valid for
* QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. The attribute
* QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE is deprecated and the recommendation
* is to use the QCA_WLAN_VENDOR_ATTR_SETBAND_MASK. If the both attributes
* are included for backwards compatibility, the configurations through
* QCA_WLAN_VENDOR_ATTR_SETBAND_MASK will take the precedence with drivers
* that support both attributes.
*
* @QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY: This command is used to configure
* DFS policy and channel hint for ACS operation. This command uses the
* attributes defined in enum qca_wlan_vendor_attr_acs_config and
* enum qca_acs_dfs_mode.
*
* @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to
* start the P2P Listen offload function in device and pass the listen
* channel, period, interval, count, device types, and vendor specific
* information elements to the device driver and firmware.
* Uses the attributes defines in
* enum qca_wlan_vendor_attr_p2p_listen_offload.
*
* @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: Command/event used to
* indicate stop request/response of the P2P Listen offload function in
* device. As an event, it indicates either the feature stopped after it
* was already running or feature has actually failed to start. Uses the
* attributes defines in enum qca_wlan_vendor_attr_p2p_listen_offload.
*
* @QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH: After AP starts
* beaconing, this sub command provides the driver, the frequencies on the
* 5 GHz band to check for any radar activity. Driver selects one channel
* from this priority list provided through
* @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST and starts
* to check for radar activity on it. If no radar activity is detected
* during the channel availability check period, driver internally switches
* to the selected frequency of operation. If the frequency is zero, driver
* internally selects a channel. The status of this conditional switch is
* indicated through an event using the same sub command through
* @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS. Attributes are
* listed in qca_wlan_vendor_attr_sap_conditional_chan_switch.
*
* @QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND: Set GPIO pins. This uses the
* attributes defined in enum qca_wlan_gpio_attr.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY: Fetch hardware capabilities.
* This uses @QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY to indicate which
* capabilities are to be fetched and other
* enum qca_wlan_vendor_attr_get_hw_capability attributes to return the
* requested capabilities.
*
* @QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT: Link layer statistics extension.
* enum qca_wlan_vendor_attr_ll_stats_ext attributes are used with this
* command and event.
*
* @QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA: Get capabilities for
* indoor location features. Capabilities are reported in
* QCA_WLAN_VENDOR_ATTR_LOC_CAPA.
*
* @QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION: Start an FTM
* (fine timing measurement) session with one or more peers.
* Specify Session cookie in QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE and
* peer information in QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS.
* On success, 0 or more QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT
* events will be reported, followed by
* QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE event to indicate
* end of session.
* Refer to IEEE P802.11-REVmc/D7.0, 11.24.6
*
* @QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION: Abort a running session.
* A QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE will be reported with
* status code indicating session was aborted.
*
* @QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT: Event with measurement
* results for one peer. Results are reported in
* QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS.
*
* @QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE: Event triggered when
* FTM session is finished, either successfully or aborted by
* request.
*
* @QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER: Configure FTM responder
* mode. QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE specifies whether
* to enable or disable the responder. LCI/LCR reports can be
* configured with QCA_WLAN_VENDOR_ATTR_FTM_LCI and
* QCA_WLAN_VENDOR_ATTR_FTM_LCR. Can be called multiple
* times to update the LCI/LCR reports.
*
* @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS: Perform a standalone AOA (angle of
* arrival) measurement with a single peer. Specify peer MAC address in
* QCA_WLAN_VENDOR_ATTR_MAC_ADDR and optionally frequency (MHz) in
* QCA_WLAN_VENDOR_ATTR_FREQ (if not specified, locate peer in kernel
* scan results cache and use the frequency from there).
* Also specify measurement type in QCA_WLAN_VENDOR_ATTR_AOA_TYPE.
* Measurement result is reported in
* QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT event.
*
* @QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS: Abort an AOA measurement. Specify
* peer MAC address in QCA_WLAN_VENDOR_ATTR_MAC_ADDR.
*
* @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT: Event that reports
* the AOA measurement result.
* Peer MAC address reported in QCA_WLAN_VENDOR_ATTR_MAC_ADDR.
* success/failure status is reported in
* QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS.
* Measurement data is reported in QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT.
* The antenna array(s) used in the measurement are reported in
* QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK.
*
* @QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST: Encrypt/decrypt the given
* data as per the given parameters.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI: Get antenna RSSI value for a
* specific chain.
*
* @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG: Get low level
* configuration for a DMG RF sector. Specify sector index in
* QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in
* QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and RF modules
* to return sector information for in
* QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK. Returns sector configuration
* in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. Also return the
* exact time where information was captured in
* QCA_WLAN_VENDOR_ATTR_TSF.
*
* @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG: Set low level
* configuration for a DMG RF sector. Specify sector index in
* QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in
* QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and sector configuration
* for one or more DMG RF modules in
* QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG.
*
* @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR: Get selected
* DMG RF sector for a station. This is the sector that the HW
* will use to communicate with the station. Specify the MAC address
* of associated station/AP/PCP in QCA_WLAN_VENDOR_ATTR_MAC_ADDR (not
* needed for unassociated station). Specify sector type to return in
* QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE. Returns the selected
* sector index in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX.
* Also return the exact time where the information was captured
* in QCA_WLAN_VENDOR_ATTR_TSF.
*
* @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR: Set the
* selected DMG RF sector for a station. This is the sector that
* the HW will use to communicate with the station.
* Specify the MAC address of associated station/AP/PCP in
* QCA_WLAN_VENDOR_ATTR_MAC_ADDR, the sector type to select in
* QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and the sector index
* in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX.
* The selected sector will be locked such that it will not be
* modified like it normally does (for example when station
* moves around). To unlock the selected sector for a station
* pass the special value 0xFFFF in the sector index. To unlock
* all connected stations also pass a broadcast MAC address.
*
* @QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS: Configure the TDLS behavior
* in the host driver. The different TDLS configurations are defined
* by the attributes in enum qca_wlan_vendor_attr_tdls_configuration.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: Query device IEEE 802.11ax HE
* capabilities. The response uses the attributes defined in
* enum qca_wlan_vendor_attr_get_he_capabilities.
*
* @QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN: Abort an ongoing vendor scan that was
* started with QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN. This command
* carries the scan cookie of the corresponding scan request. The scan
* cookie is represented by QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE.
*
* @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS: Set the Specific
* Absorption Rate (SAR) power limits. A critical regulation for
* FCC compliance, OEMs require methods to set SAR limits on TX
* power of WLAN/WWAN. enum qca_vendor_attr_sar_limits
* attributes are used with this command.
*
* @QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS: This command/event is used by the
* host driver for offloading the implementation of Auto Channel Selection
* (ACS) to an external user space entity. This interface is used as the
* event from the host driver to the user space entity and also as the
* request from the user space entity to the host driver. The event from
* the host driver is used by the user space entity as an indication to
* start the ACS functionality. The attributes used by this event are
* represented by the enum qca_wlan_vendor_attr_external_acs_event.
* User space entity uses the same interface to inform the host driver with
* selected channels after the ACS operation using the attributes defined
* by enum qca_wlan_vendor_attr_external_acs_channels.
*
* @QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE: Vendor event carrying the
* requisite information leading to a power save failure. The information
* carried as part of this event is represented by the
* enum qca_attr_chip_power_save_failure attributes.
*
* @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET: Start/Stop the NUD statistics
* collection. Uses attributes defined in enum qca_attr_nud_stats_set.
*
* @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET: Get the NUD statistics. These
* statistics are represented by the enum qca_attr_nud_stats_get
* attributes.
*
* @QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: Sub-command to fetch
* the BSS transition status, whether accept or reject, for a list of
* candidate BSSIDs provided by the userspace. This uses the vendor
* attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and
* QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. The userspace shall specify
* the attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and an
* array of QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID nested in
* QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO in the request. In the response
* the driver shall specify array of
* QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID and
* QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS pairs nested in
* QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO.
*
* @QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL: Set the trace level for a
* specific QCA module. The trace levels are represented by
* enum qca_attr_trace_level attributes.
*
* @QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT: Set the Beam Refinement
* Protocol antenna limit in different modes. See enum
* qca_wlan_vendor_attr_brp_ant_limit_mode.
*
* @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START: Start spectral scan. The scan
* parameters are specified by enum qca_wlan_vendor_attr_spectral_scan.
* This returns a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE)
* identifying the operation in success case. In failure cases an
* error code (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE)
* describing the reason for the failure is returned.
*
* @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP: Stop spectral scan. This uses
* a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) from
* @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START to identify the scan to
* be stopped.
*
* @QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS: Set the active Type Of Service on the
* specific interface. This can be used to modify some of the low level
* scan parameters (off channel dwell time, home channel time) in the
* driver/firmware. These parameters are maintained within the host driver.
* This command is valid only when the interface is in the connected state.
* These scan parameters shall be reset by the driver/firmware once
* disconnected. The attributes used with this command are defined in
* enum qca_wlan_vendor_attr_active_tos.
*
* @QCA_NL80211_VENDOR_SUBCMD_HANG: Event indicating to the user space that the
* driver has detected an internal failure. This event carries the
* information indicating the reason that triggered this detection. The
* attributes for this command are defined in
* enum qca_wlan_vendor_attr_hang.
*
* @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG: Get the current values
* of spectral parameters used. The spectral scan parameters are specified
* by enum qca_wlan_vendor_attr_spectral_scan.
*
* @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS: Get the debug stats
* for spectral scan functionality. The debug stats are specified by
* enum qca_wlan_vendor_attr_spectral_diag_stats.
*
* @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO: Get spectral
* scan system capabilities. The capabilities are specified
* by enum qca_wlan_vendor_attr_spectral_cap.
*
* @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS: Get the current
* status of spectral scan. The status values are specified
* by enum qca_wlan_vendor_attr_spectral_scan_status.
*
* @QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING: Sub-command to flush
* peer pending packets. Specify the peer MAC address in
* QCA_WLAN_VENDOR_ATTR_PEER_ADDR and the access category of the packets
* in QCA_WLAN_VENDOR_ATTR_AC. The attributes are listed
* in enum qca_wlan_vendor_attr_flush_pending.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO: Get vendor specific Representative
* RF Operating Parameter (RROP) information. The attributes for this
* information are defined in enum qca_wlan_vendor_attr_rrop_info. This is
* intended for use by external Auto Channel Selection applications.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS: Get the Specific Absorption Rate
* (SAR) power limits. This is a companion to the command
* @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS and is used to retrieve the
* settings currently in use. The attributes returned by this command are
* defined by enum qca_vendor_attr_sar_limits.
*
* @QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO: Provides the current behavior of
* the WLAN hardware MAC. Also, provides the WLAN netdev interface
* information attached to the respective MAC.
* This works both as a query (user space asks the current mode) or event
* interface (driver advertising the current mode to the user space).
* Driver does not trigger this event for temporary hardware mode changes.
* Mode changes w.r.t Wi-Fi connection update (VIZ creation / deletion,
* channel change, etc.) are updated with this event. Attributes for this
* interface are defined in enum qca_wlan_vendor_attr_mac.
*
* @QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH: Set MSDU queue depth threshold
* per peer per TID. Attributes for this command are define in
* enum qca_wlan_set_qdepth_thresh_attr.
* @QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD: Provides the thermal shutdown action
* guide for WLAN driver. Request to suspend of driver and FW if the
* temperature is higher than the suspend threshold; resume action is
* requested to driver if the temperature is lower than the resume
* threshold. In user poll mode, request temperature data by user. For test
* purpose, getting thermal shutdown configuration parameters is needed.
* Attributes for this interface are defined in
* enum qca_wlan_vendor_attr_thermal_cmd.
* @QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT: Thermal events reported from
* driver. Thermal temperature and indication of resume completion are
* reported as thermal events. The attributes for this command are defined
* in enum qca_wlan_vendor_attr_thermal_event.
*
* @QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION: Sub command to set WiFi
* test configuration. Attributes for this command are defined in
* enum qca_wlan_vendor_attr_wifi_test_config.
*
* @QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER: This command is used to configure an
* RX filter to receive frames from stations that are active on the
* operating channel, but not associated with the local device (e.g., STAs
* associated with other APs). Filtering is done based on a list of BSSIDs
* and STA MAC addresses added by the user. This command is also used to
* fetch the statistics of unassociated stations. The attributes used with
* this command are defined in enum qca_wlan_vendor_attr_bss_filter.
*
* @QCA_NL80211_VENDOR_SUBCMD_NAN_EXT: An extendable version of NAN vendor
* command. The earlier command for NAN, QCA_NL80211_VENDOR_SUBCMD_NAN,
* carried a payload which was a binary blob of data. The command was not
* extendable to send more information. The newer version carries the
* legacy blob encapsulated within an attribute and can be extended with
* additional vendor attributes that can enhance the NAN command interface.
* @QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT: Event to indicate scan triggered
* or stopped within driver/firmware in order to initiate roaming. The
* attributes used with this event are defined in enum
* qca_wlan_vendor_attr_roam_scan. Some drivers may not send these events
* in few cases, e.g., if the host processor is sleeping when this event
* is generated in firmware.
*
* @QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG: This command is used to
* configure parameters per peer to capture Channel Frequency Response
* (CFR) and enable Periodic CFR capture. The attributes for this command
* are defined in enum qca_wlan_vendor_peer_cfr_capture_attr. This command
* can also be used to send CFR data from the driver to userspace when
* netlink events are used to send CFR data.
*
* @QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT: Event to indicate changes
* in throughput dynamically. The driver estimates the throughput based on
* number of packets being transmitted/received per second and indicates
* the changes in throughput to user space. Userspace tools can use this
* information to configure kernel's TCP parameters in order to achieve
* peak throughput. Optionally, the driver will also send guidance on
* modifications to kernel's TCP parameters which can be referred by
* userspace tools. The attributes used with this event are defined in enum
* qca_wlan_vendor_attr_throughput_change.
*
* @QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG: This command is used to set
* priorities among different types of traffic during coex scenarios.
* Current supported prioritization is among WLAN/BT/ZIGBEE with different
* profiles mentioned in enum qca_coex_config_profiles. The associated
* attributes used with this command are defined in enum
* qca_vendor_attr_coex_config.
*
* Based on the config provided, FW will boost the weight and prioritize
* the traffic for that subsystem (WLAN/BT/Zigbee).
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: This command is used to query
* the supported AKM suite selectorss from the driver. It returns the list
* of supported AKMs in the attribute NL80211_ATTR_AKM_SUITES.
* @QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE: This command is used to get firmware
* state from the driver. It returns the firmware state in the attribute
* QCA_WLAN_VENDOR_ATTR_FW_STATE.
* @QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH: This vendor subcommand
* is used by the driver to flush per-peer cached statistics to user space
* application. This interface is used as an event from the driver to
* user space application. Attributes for this event are specified in
* enum qca_wlan_vendor_attr_peer_stats_cache_params.
* QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA attribute is expected to be
* sent in the event.
* @QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG: This sub command is used to
* improve the success rate of Zigbee joining network.
* Due to PTA master limitation, Zigbee joining network success rate is
* low while WLAN is working. The WLAN driver needs to configure some
* parameters including Zigbee state and specific WLAN periods to enhance
* PTA master. All these parameters are delivered by the attributes
* defined in enum qca_mpta_helper_vendor_attr.
* @QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING: This sub command is used to
* implement Beacon frame reporting feature.
*
* Userspace can request the driver/firmware to periodically report
* received Beacon frames whose BSSID is same as the current connected
* BSS's MAC address.
*
* In case the STA seamlessly (without sending disconnect indication to
* userspace) roams to a different BSS, Beacon frame reporting will be
* automatically enabled for the Beacon frames whose BSSID is same as the
* MAC address of the new BSS. Beacon reporting will be stopped when the
* STA is disconnected (when the disconnect indication is sent to
* userspace) and need to be explicitly enabled by userspace for next
* connection.
*
* When a Beacon frame matching configured conditions is received, and if
* userspace has requested to send asynchronous beacon reports, the
* driver/firmware will encapsulate the details of the Beacon frame in an
* event and send it to userspace along with updating the BSS information
* in cfg80211 scan cache, otherwise driver will only update the cfg80211
* scan cache with the information from the received Beacon frame but will
* not send any active report to userspace.
*
* The userspace can request the driver/firmware to stop reporting Beacon
* frames. If the driver/firmware is not able to receive Beacon frames due
* to other Wi-Fi operations such as off-channel activities, etc., the
* driver/firmware will send a pause event to userspace and stop reporting
* Beacon frames. Whether the beacon reporting will be automatically
* resumed or not by the driver/firmware later will be reported to
* userspace using the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES
* flag. The beacon reporting shall be resumed for all the cases except
* either when userspace sets
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME flag in the command
* which triggered the current beacon reporting or during any disconnection
* case as indicated by setting
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON to
* QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED by the
* driver.
*
* After QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_PAUSE event is received
* by userspace with QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES
* flag not set, the next first
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO event from the driver
* shall be considered as un-pause event.
*
* All the attributes used with this command are defined in
* enum qca_wlan_vendor_attr_beacon_reporting_params.
* @QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP: In practice, some APs have
* interop issues with the DUT. This sub command is used to transfer the
* AP info between the driver and user space. This works both as a command
* and an event. As a command, it configures the stored list of APs from
* user space to firmware; as an event, it indicates the AP info detected
* by the firmware to user space for persistent storage. The attributes
* defined in enum qca_vendor_attr_interop_issues_ap are used to deliver
* the parameters.
* @QCA_NL80211_VENDOR_SUBCMD_OEM_DATA: This command/event is used to
* send/receive OEM data binary blobs to/from application/service to/from
* firmware. The attributes defined in enum
* qca_wlan_vendor_attr_oem_data_params are used to deliver the
* parameters.
* @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_EXT: This command/event is used
* to send/receive avoid frequency data using
* enum qca_wlan_vendor_attr_avoid_frequency_ext.
* This new command is alternative to existing command
* QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY since existing command/event
* is using stream of bytes instead of structured data using vendor
* attributes.
*
* @QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE: This vendor subcommand is used to
* add the STA node details in driver/firmware. Attributes for this event
* are specified in enum qca_wlan_vendor_attr_add_sta_node_params.
* @QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE: This command is used to set BT
* coex chain mode from application/service.
* The attributes defined in enum qca_vendor_attr_btc_chain_mode are used
* to deliver the parameters.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO: This vendor subcommand is used to
* get information of a station from driver to userspace. This command can
* be used in both STA and AP modes. For STA mode, it provides information
* of the current association when in connected state or the last
* association when in disconnected state. For AP mode, only information
* of the currently connected stations is available. This command uses
* attributes defined in enum qca_wlan_vendor_attr_get_sta_info.
*
* @QCA_NL80211_VENDOR_SUBCMD_REQUEST_SAR_LIMITS_EVENT: This acts as an event.
* Host drivers can request the user space entity to set the SAR power
* limits with this event. Accordingly, the user space entity is expected
* to set the SAR power limits. Host drivers can retry this event to the
* user space for the SAR power limits configuration from user space. If
* the driver does not get the SAR power limits from user space for all
* the retried attempts, it can configure a default SAR power limit.
*
* @QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO: This acts as a vendor event and
* is used to update the information about the station from the driver to
* userspace. Uses attributes from enum
* qca_wlan_vendor_attr_update_sta_info.
*
* @QCA_NL80211_VENDOR_SUBCMD_DRIVER_DISCONNECT_REASON: This acts as an event.
* The host driver initiates the disconnection for scenarios such as beacon
* miss, NUD failure, peer kick out, etc. The disconnection indication
* through cfg80211_disconnected() expects the reason codes from enum
* ieee80211_reasoncode which does not signify these various reasons why
* the driver has triggered the disconnection. This event will be used to
* send the driver specific reason codes by the host driver to userspace.
* Host drivers should trigger this event and pass the respective reason
* code immediately prior to triggering cfg80211_disconnected(). The
* attributes used with this event are defined in enum
* qca_wlan_vendor_attr_driver_disconnect_reason.
*
* @QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC: This vendor subcommand is used to
* add/delete TSPEC for each AC. One command is for one specific AC only.
* This command can only be used in STA mode and the STA must be
* associated with an AP when the command is issued. Uses attributes
* defined in enum qca_wlan_vendor_attr_config_tspec.
*
* @QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT: Vendor subcommand to configure TWT.
* Uses attributes defined in enum qca_wlan_vendor_attr_config_twt.
*
* @QCA_NL80211_VENDOR_SUBCMD_GETBAND: Command to get the enabled band(s) from
* the driver. The band configurations obtained are referred through
* QCA_WLAN_VENDOR_ATTR_SETBAND_MASK.
*
* @QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS: Vendor subcommand/event for medium
* assessment.
* Uses attributes defined in enum qca_wlan_vendor_attr_medium_assess.
*
* @QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID: This acts as a vendor event and is
* used to update SSID information in hostapd when it is updated in the
* driver. Uses the attribute NL80211_ATTR_SSID.
*
* @QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS: This vendor subcommand is used by
* the driver to send opaque data from the firmware to userspace. The
* driver sends an event to userspace whenever such data is received from
* the firmware.
*
* QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA is used as the attribute to
* send this opaque data for this event.
*
* The format of the opaque data is specific to the particular firmware
* version and there is no guarantee of the format remaining same.
*
* @QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS: This acts as an event.
* The host driver selects Tx VDEV, and notifies user. The attributes
* used with this event are defined in enum
* qca_wlan_vendor_attr_mbssid_tx_vdev_status.
+ * This event contains Tx VDEV group information, other VDEVs
+ * interface index, and status information.
*
* @QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY: Vendor command to
* configure the concurrent session policies when multiple STA interfaces
* are (getting) active. The attributes used by this command are defined
* in enum qca_wlan_vendor_attr_concurrent_sta_policy.
*
* @QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS: Userspace can use this command
* to query usable channels for different interface types such as STA,
* AP, P2P GO, P2P Client, NAN, etc. The driver shall report all usable
* channels in the response based on country code, different static
* configurations, concurrency combinations, etc. The attributes used
* with this command are defined in
* enum qca_wlan_vendor_attr_usable_channels.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_RADAR_HISTORY: This vendor subcommand is used
* to get DFS radar history from the driver to userspace. The driver
* returns QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES attribute with an
* array of nested entries.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_MDNS_OFFLOAD: Userspace can use this command to
+ * enable/disable mDNS offload to the firmware. The attributes used with
+ * this command are defined in enum qca_wlan_vendor_attr_mdns_offload.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_MONITOR_MODE: This vendor subcommand is used
+ * to set packet monitor mode that aims to send the specified set of TX and
+ * RX frames on the current client interface to an active monitor
+ * interface. If this monitor mode is set, the driver will send the
+ * configured frames, from the interface on which the command is issued, to
+ * an active monitor interface. The attributes used with this command are
+ * defined in enum qca_wlan_vendor_attr_set_monitor_mode.
+ *
+ * Though the monitor mode is configured for the respective
+ * Data/Management/Control frames, it is up to the respective WLAN
+ * driver/firmware/hardware designs to consider the possibility of sending
+ * these frames over the monitor interface. For example, the Control frames
+ * are handled within the hardware and thus passing such frames over the
+ * monitor interface is left to the respective designs.
+ *
+ * Also, this monitor mode is governed to behave accordingly in
+ * suspend/resume states. If the firmware handles any of such frames in
+ * suspend state without waking up the host and if the monitor mode is
+ * configured to notify all such frames, the firmware is expected to resume
+ * the host and forward the respective frames to the monitor interface.
+ * Please note that such a request to get the frames over the monitor
+ * interface will have a definite power implication.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS: This vendor subcommand is used both
+ * as a request to set the driver/firmware with the parameters to trigger
+ * the roaming events, and also used by the driver/firmware to pass on the
+ * various roam events to userspace.
+ * Applicable only for the STA mode. The attributes used with this command
+ * are defined in enum qca_wlan_vendor_attr_roam_events.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
QCA_NL80211_VENDOR_SUBCMD_TEST = 1,
/* subcmds 2..8 not yet allocated */
QCA_NL80211_VENDOR_SUBCMD_ROAMING = 9,
QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11,
QCA_NL80211_VENDOR_SUBCMD_NAN = 12,
QCA_NL80211_VENDOR_SUBCMD_STATS_EXT = 13,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS = 17,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS = 18,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS = 19,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_START = 20,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_STOP = 21,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS = 22,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES = 23,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS = 24,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE = 25,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT = 26,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT = 27,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND = 28,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_BSSID_HOTLIST = 29,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_BSSID_HOTLIST = 30,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE = 31,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SIGNIFICANT_CHANGE = 32,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SIGNIFICANT_CHANGE = 33,
QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE = 34,
QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE = 35,
QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS = 36,
QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE = 37,
QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES = 38,
QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI = 39,
QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG = 40,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_LOST = 41,
QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX = 42,
/* 43..49 - reserved for QCA */
QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50,
QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51,
QCA_NL80211_VENDOR_SUBCMD_APFIND = 52,
/* 53 - reserved - was used by QCA, but not in use anymore */
QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54,
QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55,
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED = 56,
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED = 57,
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58,
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59,
QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60,
QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO = 61,
QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START = 62,
QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP = 63,
QCA_NL80211_VENDOR_SUBCMD_ROAM = 64,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST = 65,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SSID_HOTLIST = 66,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_FOUND = 67,
QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_LOST = 68,
QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST = 69,
QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST = 70,
QCA_NL80211_VENDOR_SUBCMD_PNO_RESET_PASSPOINT_LIST = 71,
QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND = 72,
QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND = 73,
/* Wi-Fi configuration subcommands */
QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74,
QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION = 75,
QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET = 76,
QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA = 77,
QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES = 78,
QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS = 79,
QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI = 80,
QCA_NL80211_VENDOR_SUBCMD_NDP = 81,
QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD = 82,
QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER = 83,
QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE = 84,
QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS = 85,
/* 86-90 - reserved for QCA */
QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91,
QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92,
QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93,
QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT = 94,
QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT = 95,
QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER = 96,
QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS = 97,
QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS = 98,
QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99,
QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100,
QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101,
QCA_NL80211_VENDOR_SUBCMD_GW_PARAM_CONFIG = 102,
QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103,
QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104,
QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN = 106,
QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE = 107,
QCA_NL80211_VENDOR_SUBCMD_OTA_TEST = 108,
QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE = 109,
/* 110..114 - reserved for QCA */
QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB = 115,
QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY = 116,
/* 117 - reserved for QCA */
QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG = 118,
QCA_NL80211_VENDOR_SUBCMD_TSF = 119,
QCA_NL80211_VENDOR_SUBCMD_WISA = 120,
/* 121 - reserved for QCA */
QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START = 122,
QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP = 123,
QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH = 124,
QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND = 125,
QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY = 126,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT = 127,
/* FTM/indoor location subcommands */
QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128,
QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129,
QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION = 130,
QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT = 131,
QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE = 132,
QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER = 133,
QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134,
QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135,
QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136,
QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST = 137,
QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI = 138,
/* DMG low level RF sector operations */
QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS = 143,
QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES = 144,
QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN = 145,
QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS = 146,
QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS = 147,
QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE = 148,
QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET = 149,
QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET = 150,
QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS = 151,
QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL = 152,
QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT = 153,
QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START = 154,
QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP = 155,
QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS = 156,
QCA_NL80211_VENDOR_SUBCMD_HANG = 157,
QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG = 158,
QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS = 159,
QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO = 160,
QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS = 161,
/* Flush peer pending data */
QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING = 162,
QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO = 163,
QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS = 164,
QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO = 165,
QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH = 166,
/* Thermal shutdown commands to protect wifi chip */
QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD = 167,
QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT = 168,
/* Wi-Fi test configuration subcommand */
QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION = 169,
/* Frame filter operations for other BSSs/unassociated STAs */
QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER = 170,
QCA_NL80211_VENDOR_SUBCMD_NAN_EXT = 171,
QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT = 172,
QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG = 173,
QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174,
QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175,
QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS = 176,
QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177,
QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178,
QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179,
QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING = 180,
QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP = 181,
QCA_NL80211_VENDOR_SUBCMD_OEM_DATA = 182,
QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_EXT = 183,
QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE = 184,
QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE = 185,
QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO = 186,
QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS_EVENT = 187,
QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO = 188,
QCA_NL80211_VENDOR_SUBCMD_DRIVER_DISCONNECT_REASON = 189,
QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC = 190,
QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT = 191,
QCA_NL80211_VENDOR_SUBCMD_GETBAND = 192,
QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS = 193,
QCA_NL80211_VENDOR_SUBCMD_UPDATE_SSID = 194,
QCA_NL80211_VENDOR_SUBCMD_WIFI_FW_STATS = 195,
QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS = 196,
QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY = 197,
QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS = 198,
QCA_NL80211_VENDOR_SUBCMD_GET_RADAR_HISTORY = 199,
+ QCA_NL80211_VENDOR_SUBCMD_MDNS_OFFLOAD = 200,
+ /* 201 - reserved for QCA */
+ QCA_NL80211_VENDOR_SUBCMD_SET_MONITOR_MODE = 202,
+ QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS = 203,
};
enum qca_wlan_vendor_attr {
QCA_WLAN_VENDOR_ATTR_INVALID = 0,
/* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */
QCA_WLAN_VENDOR_ATTR_DFS = 1,
/* Used only when driver sends vendor events to the userspace under the
* command QCA_NL80211_VENDOR_SUBCMD_NAN. Not used when userspace sends
* commands to the driver.
*/
QCA_WLAN_VENDOR_ATTR_NAN = 2,
/* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3,
/* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
QCA_WLAN_VENDOR_ATTR_IFINDEX = 4,
/* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined
* by enum qca_roaming_policy.
*/
QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5,
QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7,
QCA_WLAN_VENDOR_ATTR_TEST = 8,
/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
/* Unsigned 32-bit value. */
QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA = 9,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND = 10,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
/* Unsigned 32-bit value from enum qca_set_band. The allowed values for
* this attribute are limited to QCA_SETBAND_AUTO, QCA_SETBAND_5G, and
* QCA_SETBAND_2G. This attribute is deprecated. Recommendation is to
* use QCA_WLAN_VENDOR_ATTR_SETBAND_MASK instead.
*/
QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
/* Dummy (NOP) attribute for 64 bit padding */
QCA_WLAN_VENDOR_ATTR_PAD = 13,
/* Unique FTM session cookie (Unsigned 64 bit). Specified in
* QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION. Reported in
* the session in QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT and
* QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE.
*/
QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE = 14,
/* Indoor location capabilities, returned by
* QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA.
* see enum qca_wlan_vendor_attr_loc_capa.
*/
QCA_WLAN_VENDOR_ATTR_LOC_CAPA = 15,
/* Array of nested attributes containing information about each peer
* in FTM measurement session. See enum qca_wlan_vendor_attr_peer_info
* for supported attributes for each peer.
*/
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS = 16,
/* Array of nested attributes containing measurement results for
* one or more peers, reported by the
* QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT event.
* See enum qca_wlan_vendor_attr_peer_result for list of supported
* attributes.
*/
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS = 17,
/* Flag attribute for enabling or disabling responder functionality. */
QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE = 18,
/* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER
* command to specify the LCI report that will be sent by
* the responder during a measurement exchange. The format is
* defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.10.
*/
QCA_WLAN_VENDOR_ATTR_FTM_LCI = 19,
/* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER
* command to specify the location civic report that will
* be sent by the responder during a measurement exchange.
* The format is defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.13.
*/
QCA_WLAN_VENDOR_ATTR_FTM_LCR = 20,
/* Session/measurement completion status code,
* reported in QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE and
* QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT
* see enum qca_vendor_attr_loc_session_status.
*/
QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS = 21,
/* Initial dialog token used by responder (0 if not specified),
* unsigned 8 bit value.
*/
QCA_WLAN_VENDOR_ATTR_FTM_INITIAL_TOKEN = 22,
/* AOA measurement type. Requested in QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS
* and optionally in QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION if
* AOA measurements are needed as part of an FTM session.
* Reported by QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT. See
* enum qca_wlan_vendor_attr_aoa_type.
*/
QCA_WLAN_VENDOR_ATTR_AOA_TYPE = 23,
/* A bit mask (unsigned 32 bit value) of antenna arrays used
* by indoor location measurements. Refers to the antenna
* arrays described by QCA_VENDOR_ATTR_LOC_CAPA_ANTENNA_ARRAYS.
*/
QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK = 24,
/* AOA measurement data. Its contents depends on the AOA measurement
* type and antenna array mask:
* QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: array of U16 values,
* phase of the strongest CIR path for each antenna in the measured
* array(s).
* QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: array of 2 U16
* values, phase and amplitude of the strongest CIR path for each
* antenna in the measured array(s).
*/
QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25,
/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
* to specify the chain number (unsigned 32 bit value) to inquire
* the corresponding antenna RSSI value
*/
QCA_WLAN_VENDOR_ATTR_CHAIN_INDEX = 26,
/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
* to report the specific antenna RSSI value (unsigned 32 bit value)
*/
QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27,
/* Frequency in MHz, various uses. Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_FREQ = 28,
/* TSF timer value, unsigned 64 bit value.
* May be returned by various commands.
*/
QCA_WLAN_VENDOR_ATTR_TSF = 29,
/* DMG RF sector index, unsigned 16 bit number. Valid values are
* 0..127 for sector indices or 65535 as special value used to
* unlock sector selection in
* QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR.
*/
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX = 30,
/* DMG RF sector type, unsigned 8 bit value. One of the values
* in enum qca_wlan_vendor_attr_dmg_rf_sector_type.
*/
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE = 31,
/* Bitmask of DMG RF modules for which information is requested. Each
* bit corresponds to an RF module with the same index as the bit
* number. Unsigned 32 bit number but only low 8 bits can be set since
* all DMG chips currently have up to 8 RF modules.
*/
QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK = 32,
/* Array of nested attributes where each entry is DMG RF sector
* configuration for a single RF module.
* Attributes for each entry are taken from enum
* qca_wlan_vendor_attr_dmg_rf_sector_cfg.
* Specified in QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG
* and returned by QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG.
*/
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG = 33,
/* Used in QCA_NL80211_VENDOR_SUBCMD_STATS_EXT command
* to report frame aggregation statistics to userspace.
*/
QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM = 34,
QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO = 35,
/* Unsigned 8-bit value representing MBO transition reason code as
* provided by the AP used by subcommand
* QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS. This is
* specified by the userspace in the request to the driver.
*/
QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON = 36,
/* Array of nested attributes, BSSID and status code, used by subcommand
* QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS, where each
* entry is taken from enum qca_wlan_vendor_attr_btm_candidate_info.
* The userspace space specifies the list/array of candidate BSSIDs in
* the order of preference in the request. The driver specifies the
* status code, for each BSSID in the list, in the response. The
* acceptable candidates are listed in the order preferred by the
* driver.
*/
QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO = 37,
/* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command
* See enum qca_wlan_vendor_attr_brp_ant_limit_mode.
*/
QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE = 38,
/* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command
* to define the number of antennas to use for BRP.
* different purpose in each ANT_LIMIT_MODE:
* DISABLE - ignored
* EFFECTIVE - upper limit to number of antennas to be used
* FORCE - exact number of antennas to be used
* unsigned 8 bit value
*/
QCA_WLAN_VENDOR_ATTR_BRP_ANT_NUM_LIMIT = 39,
/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
* to report the corresponding antenna index to the chain RSSI value
*/
QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40,
/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report
* the specific antenna EVM value (unsigned 32 bit value). With a
* determinate group of antennas, the driver specifies the EVM value
* for each antenna ID, and application extract them in user space.
*/
QCA_WLAN_VENDOR_ATTR_CHAIN_EVM = 41,
/*
* Used in QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE command to report
* wlan firmware current state. FW state is an unsigned 8 bit value,
* one of the values in enum qca_wlan_vendor_attr_fw_state.
*/
QCA_WLAN_VENDOR_ATTR_FW_STATE = 42,
/* Unsigned 32-bitmask value from enum qca_set_band. Substitutes the
* attribute QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE for which only a subset
* of single values from enum qca_set_band are valid. This attribute
* uses bitmask combinations to define the respective allowed band
* combinations and this attributes takes precedence over
* QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE if both attributes are included.
*/
QCA_WLAN_VENDOR_ATTR_SETBAND_MASK = 43,
/* keep last */
QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
};
enum qca_roaming_policy {
QCA_ROAMING_NOT_ALLOWED,
QCA_ROAMING_ALLOWED_WITHIN_ESS,
};
/**
* enum qca_roam_reason - Represents the reason codes for roaming. Used by
* QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON.
*
* @QCA_ROAM_REASON_UNKNOWN: Any reason that do not classify under the below
* reasons.
*
* @QCA_ROAM_REASON_PER: Roam triggered when packet error rates (PER) breached
* the configured threshold.
*
* @QCA_ROAM_REASON_BEACON_MISS: Roam triggered due to the continuous configured
* beacon misses from the then connected AP.
*
* @QCA_ROAM_REASON_POOR_RSSI: Roam triggered due to the poor RSSI reported
* by the connected AP.
*
* @QCA_ROAM_REASON_BETTER_RSSI: Roam triggered for finding a BSS with a better
* RSSI than the connected BSS. Here the RSSI of the current BSS is not poor.
*
* @QCA_ROAM_REASON_CONGESTION: Roam triggered considering the connected channel
* or environment being very noisy or congested.
*
- * @QCA_ROAM_REASON_EXPLICIT_REQUEST: Roam triggered due to an explicit request
+ * @QCA_ROAM_REASON_USER_TRIGGER: Roam triggered due to an explicit request
* from the user (user space).
*
* @QCA_ROAM_REASON_BTM: Roam triggered due to BTM Request frame received from
* the connected AP.
*
* @QCA_ROAM_REASON_BSS_LOAD: Roam triggered due to the channel utilization
* breaching out the configured threshold.
+ *
+ * @QCA_ROAM_REASON_WTC: Roam triggered due to Wireless to Cellular BSS
+ * transition request.
+ *
+ * @QCA_ROAM_REASON_IDLE: Roam triggered when device is suspended, there is no
+ * data activity with the AP and the current RSSI falls below a certain
+ * threshold.
+ *
+ * @QCA_ROAM_REASON_DISCONNECTION: Roam triggered due to Deauthentication or
+ * Disassociation frames received from the connected AP.
+ *
+ * @QCA_ROAM_REASON_PERIODIC_TIMER: Roam triggered as part of the periodic scan
+ * that happens when there is no candidate AP found during the poor RSSI scan
+ * trigger.
+ *
+ * @QCA_ROAM_REASON_BACKGROUND_SCAN: Roam triggered based on the scan results
+ * obtained from an external scan (not aimed at roaming).
+ *
+ * @QCA_ROAM_REASON_BT_ACTIVITY: Roam triggered due to Bluetooth connection is
+ * established when the station is connected in the 2.4 GHz band.
*/
enum qca_roam_reason {
QCA_ROAM_REASON_UNKNOWN,
QCA_ROAM_REASON_PER,
QCA_ROAM_REASON_BEACON_MISS,
QCA_ROAM_REASON_POOR_RSSI,
QCA_ROAM_REASON_BETTER_RSSI,
QCA_ROAM_REASON_CONGESTION,
QCA_ROAM_REASON_USER_TRIGGER,
QCA_ROAM_REASON_BTM,
QCA_ROAM_REASON_BSS_LOAD,
+ QCA_ROAM_REASON_WTC,
+ QCA_ROAM_REASON_IDLE,
+ QCA_ROAM_REASON_DISCONNECTION,
+ QCA_ROAM_REASON_PERIODIC_TIMER,
+ QCA_ROAM_REASON_BACKGROUND_SCAN,
+ QCA_ROAM_REASON_BT_ACTIVITY,
};
enum qca_wlan_vendor_attr_roam_auth {
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS,
/* Indicates the status of re-association requested by user space for
* the BSSID specified by QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID.
* Type u16.
* Represents the status code from AP. Use
* %WLAN_STATUS_UNSPECIFIED_FAILURE if the device cannot give you the
* real status code for failures.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS,
/* This attribute indicates that the old association was maintained when
* a re-association is requested by user space and that re-association
* attempt fails (i.e., cannot connect to the requested BSS, but can
* remain associated with the BSS with which the association was in
* place when being requested to roam). Used along with
* WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS to indicate the current
* re-association status. Type flag.
* This attribute is applicable only for re-association failure cases.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RETAIN_CONNECTION,
/* This attribute specifies the PMK if one was newly generated during
* FILS roaming. This is added to the PMKSA cache and is used in
* subsequent connections with PMKSA caching.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK = 11,
/* This attribute specifies the PMKID used/generated for the current
* FILS roam. This is used in subsequent connections with PMKSA caching.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID = 12,
/* A 16-bit unsigned value specifying the next sequence number to use
* in ERP message in the currently associated realm. This is used in
* doing subsequent ERP based connections in the same realm.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13,
/* A 16-bit unsigned value representing the reasons for the roaming.
* Defined by enum qca_roam_reason.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1
};
enum qca_wlan_vendor_attr_p2p_listen_offload {
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INVALID = 0,
/* A 32-bit unsigned value; the P2P listen frequency (MHz); must be one
* of the social channels.
*/
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL,
/* A 32-bit unsigned value; the P2P listen offload period (ms).
*/
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD,
/* A 32-bit unsigned value; the P2P listen interval duration (ms).
*/
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL,
/* A 32-bit unsigned value; number of interval times the firmware needs
* to run the offloaded P2P listen operation before it stops.
*/
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT,
/* An array of arbitrary binary data with one or more 8-byte values.
* The device types include both primary and secondary device types.
*/
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES,
/* An array of unsigned 8-bit characters; vendor information elements.
*/
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE,
/* A 32-bit unsigned value; a control flag to indicate whether listen
* results need to be flushed to wpa_supplicant.
*/
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG,
/* A 8-bit unsigned value; reason code for P2P listen offload stop
* event.
*/
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON,
/* keep last */
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX =
QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_attr_acs_offload - Defines attributes to be used with
* vendor command/event QCA_NL80211_VENDOR_SUBCMD_DO_ACS.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL: Required (u8).
* Used with event to notify the primary channel number selected in ACS
* operation.
* Note: If both the driver and user-space application supports the 6 GHz band,
* QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL is deprecated; use
* QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY instead.
* To maintain backward compatibility, QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL
* is still used if either of the driver or user space application doesn't
* support the 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL: Required (u8).
* Used with event to notify the secondary channel number selected in ACS
* operation.
* Note: If both the driver and user-space application supports the 6 GHz band,
* QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL is deprecated; use
* QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY instead.
* To maintain backward compatibility,
* QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL is still used if either of
* the driver or user space application doesn't support 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE: Required (u8).
* (a) Used with command to configure hw_mode from
* enum qca_wlan_vendor_acs_hw_mode for ACS operation.
* (b) Also used with event to notify the hw_mode of selected primary channel
* in ACS operation.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED: Flag attribute.
* Used with command to configure ACS operation for HT mode.
* Disable (flag attribute not present) - HT disabled and
* Enable (flag attribute present) - HT enabled.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED: Flag attribute.
* Used with command to configure ACS operation for HT40 mode.
* Disable (flag attribute not present) - HT40 disabled and
* Enable (flag attribute present) - HT40 enabled.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED: Flag attribute.
* Used with command to configure ACS operation for VHT mode.
* Disable (flag attribute not present) - VHT disabled and
* Enable (flag attribute present) - VHT enabled.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH: Optional (u16) with command and
* mandatory with event.
* If specified in command path, ACS operation is configured with the given
* channel width (in MHz).
* In event path, specifies the channel width of the primary channel selected.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST: Required and type is NLA_UNSPEC.
* Used with command to configure channel list using an array of
* channel numbers (u8).
* Note: If both the driver and user-space application supports the 6 GHz band,
* the driver mandates use of QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST whereas
* QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST is optional.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL: Required (u8).
* Used with event to notify the VHT segment 0 center channel number selected in
* ACS operation. The value is the index of the channel center frequency for
* 20 MHz, 40 MHz, and 80 MHz channels. The value is the center frequency index
* of the primary 80 MHz segment for 160 MHz and 80+80 MHz channels.
* Note: If both the driver and user-space application supports the 6 GHz band,
* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL is deprecated; use
* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY instead.
* To maintain backward compatibility,
* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL is still used if either of
* the driver or user space application doesn't support the 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL: Required (u8).
* Used with event to notify the VHT segment 1 center channel number selected in
* ACS operation. The value is zero for 20 MHz, 40 MHz, and 80 MHz channels.
* The value is the index of the channel center frequency for 160 MHz channels
* and the center frequency index of the secondary 80 MHz segment for 80+80 MHz
* channels.
* Note: If both the driver and user-space application supports the 6 GHz band,
* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL is deprecated; use
* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY instead.
* To maintain backward compatibility,
* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL is still used if either of
* the driver or user space application doesn't support the 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST: Required and type is NLA_UNSPEC.
* Used with command to configure the channel list using an array of channel
* center frequencies in MHz (u32).
* Note: If both the driver and user-space application supports the 6 GHz band,
* the driver first parses the frequency list and if it fails to get a frequency
* list, parses the channel list specified using
* QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST (considers only 2 GHz and 5 GHz channels in
* QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST).
*
* @QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY: Required (u32).
* Used with event to notify the primary channel center frequency (MHz) selected
* in ACS operation.
* Note: If the driver supports the 6 GHz band, the event sent from the driver
* includes this attribute along with QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY: Required (u32).
* Used with event to notify the secondary channel center frequency (MHz)
* selected in ACS operation.
* Note: If the driver supports the 6 GHz band, the event sent from the driver
* includes this attribute along with
* QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY: Required (u32).
* Used with event to notify the VHT segment 0 center channel frequency (MHz)
* selected in ACS operation.
* Note: If the driver supports the 6 GHz band, the event sent from the driver
* includes this attribute along with
* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY: Required (u32).
* Used with event to notify the VHT segment 1 center channel frequency (MHz)
* selected in ACS operation.
* Note: If the driver supports the 6 GHz band, the event sent from the driver
* includes this attribute along with
* QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED: Flag attribute.
* Used with command to notify the driver of EDMG request for ACS
* operation.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL: Optional (u8).
* Used with event to notify the EDMG channel number selected in ACS
* operation.
* EDMG primary channel is indicated by QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP: Optional (u16).
+ * Used with event to notify the puncture pattern selected in ACS operation.
+ * Encoding for this attribute will follow the convention used in the Disabled
+ * Subchannel Bitmap field of the EHT Operation IE.
*/
enum qca_wlan_vendor_attr_acs_offload {
QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL = 1,
QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL = 2,
QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE = 3,
QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED = 4,
QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED = 5,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED = 6,
QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH = 7,
QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST = 8,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL = 9,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL = 10,
QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST = 11,
QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_FREQUENCY = 12,
QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_FREQUENCY = 13,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_FREQUENCY = 14,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_FREQUENCY = 15,
QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED = 16,
QCA_WLAN_VENDOR_ATTR_ACS_EDMG_CHANNEL = 17,
+ QCA_WLAN_VENDOR_ATTR_ACS_PUNCTURE_BITMAP = 18,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ACS_MAX =
QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_acs_hw_mode - Defines HW mode to be used with the
* vendor command/event QCA_NL80211_VENDOR_SUBCMD_DO_ACS.
*
* @QCA_ACS_MODE_IEEE80211B: 802.11b mode
* @QCA_ACS_MODE_IEEE80211G: 802.11g mode
* @QCA_ACS_MODE_IEEE80211A: 802.11a mode
* @QCA_ACS_MODE_IEEE80211AD: 802.11ad mode
* @QCA_ACS_MODE_IEEE80211ANY: all modes
* @QCA_ACS_MODE_IEEE80211AX: 802.11ax mode
*/
enum qca_wlan_vendor_acs_hw_mode {
QCA_ACS_MODE_IEEE80211B,
QCA_ACS_MODE_IEEE80211G,
QCA_ACS_MODE_IEEE80211A,
QCA_ACS_MODE_IEEE80211AD,
QCA_ACS_MODE_IEEE80211ANY,
QCA_ACS_MODE_IEEE80211AX,
};
/**
* enum qca_wlan_vendor_features - Vendor device/driver feature flags
*
* @QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD: Device supports key
* management offload, a mechanism where the station's firmware
* does the exchange with the AP to establish the temporal keys
* after roaming, rather than having the user space wpa_supplicant do it.
* @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
* band selection based on channel selection results.
* @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports
* simultaneous off-channel operations.
* @QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P
* Listen offload; a mechanism where the station's firmware takes care of
* responding to incoming Probe Request frames received from other P2P
* Devices whilst in Listen state, rather than having the user space
* wpa_supplicant do it. Information from received P2P requests are
* forwarded from firmware to host whenever the host processor wakes up.
* @QCA_WLAN_VENDOR_FEATURE_OCE_STA: Device supports all OCE non-AP STA
* specific features.
* @QCA_WLAN_VENDOR_FEATURE_OCE_AP: Device supports all OCE AP specific
* features.
* @QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON: Device supports OCE STA-CFON
* specific features only. If a Device sets this bit but not the
* %QCA_WLAN_VENDOR_FEATURE_OCE_AP, the userspace shall assume that
* this Device may not support all OCE AP functionalities but can support
* only OCE STA-CFON functionalities.
* @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self
* managed regulatory.
* @QCA_WLAN_VENDOR_FEATURE_TWT: Device supports TWT (Target Wake Time).
* @QCA_WLAN_VENDOR_FEATURE_11AX: Device supports 802.11ax (HE)
* @QCA_WLAN_VENDOR_FEATURE_6GHZ_SUPPORT: Device supports 6 GHz band operation
* @QCA_WLAN_VENDOR_FEATURE_THERMAL_CONFIG: Device is capable of receiving
* and applying thermal configuration through
* %QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL and
* %QCA_WLAN_VENDOR_ATTR_THERMAL_COMPLETION_WINDOW attributes from
* userspace.
* @QCA_WLAN_VENDOR_FEATURE_ADAPTIVE_11R: Device supports Adaptive 11r.
* With Adaptive 11r feature, access points advertise the vendor
* specific IEs and MDE but do not include FT AKM in the RSNE.
* The Adaptive 11r supported stations are expected to identify
* such vendor specific IEs and connect to the AP in FT mode though
* the profile is configured in non-FT mode.
* The driver-based SME cases also need to have this support for
* Adaptive 11r to handle the connection and roaming scenarios.
* This flag indicates the support for the same to the user space.
* @QCA_WLAN_VENDOR_FEATURE_CONCURRENT_BAND_SESSIONS: Device supports
* concurrent network sessions on different Wi-Fi bands. This feature
* capability is attributed to the hardware's capability to support
* the same (e.g., DBS).
* @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT: Flag indicating whether the
* responses for the respective TWT operations are asynchronous (separate
* event message) from the driver. If not specified, the responses are
* synchronous (in vendor command reply) to the request. Each TWT
* operation is specifically mentioned (against its respective
* documentation) to support either of these or both modes.
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0,
QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1,
QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2,
QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD = 3,
QCA_WLAN_VENDOR_FEATURE_OCE_STA = 4,
QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5,
QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6,
QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7,
QCA_WLAN_VENDOR_FEATURE_TWT = 8,
QCA_WLAN_VENDOR_FEATURE_11AX = 9,
QCA_WLAN_VENDOR_FEATURE_6GHZ_SUPPORT = 10,
QCA_WLAN_VENDOR_FEATURE_THERMAL_CONFIG = 11,
QCA_WLAN_VENDOR_FEATURE_ADAPTIVE_11R = 12,
QCA_WLAN_VENDOR_FEATURE_CONCURRENT_BAND_SESSIONS = 13,
QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT = 14,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
/**
* enum qca_wlan_vendor_attr_data_offload_ind - Vendor Data Offload Indication
*
* @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION: Session corresponding to
* the offloaded data.
* @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL: Protocol of the offloaded
* data.
* @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT: Event type for the data offload
* indication.
*/
enum qca_wlan_vendor_attr_data_offload_ind {
QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION,
QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL,
QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT,
/* keep last */
QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX =
QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_attr_ocb_set_config - Vendor subcmd attributes to set
* OCB config
*
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT: Number of channels in the
* configuration
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE: Size of the schedule
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY: Array of channels
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY: Array of channels to be
* scheduled
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY: Array of NDL channel
* information
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY: Array of NDL
* active state configuration
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS: Configuration flags such as
* OCB_CONFIG_FLAG_80211_FRAME_MODE
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM: Default TX parameters to
* use in the case that a packet is sent without a TX control header
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION: Max duration after the
* last TA received that the local time set by TA is synchronous to other
* communicating OCB STAs.
*/
enum qca_wlan_vendor_attr_ocb_set_config {
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT = 1,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE = 2,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY = 3,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY = 4,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY = 5,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY = 6,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS = 7,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM = 8,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION = 9,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX =
QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_attr_ocb_set_utc_time - Vendor subcmd attributes to set
* UTC time
*
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE: The UTC time as an array of
* 10 bytes
* @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR: The time error as an array of
* 5 bytes
*/
enum qca_wlan_vendor_attr_ocb_set_utc_time {
QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE = 1,
QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR = 2,
QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX =
QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_attr_ocb_start_timing_advert - Vendor subcmd attributes
* to start sending timing advert frames
*
* @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ: Cannel frequency
* on which to send the frames
* @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE: Number of times
* the frame is sent in 5 seconds
*/
enum qca_wlan_vendor_attr_ocb_start_timing_advert {
QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ = 1,
QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE = 2,
QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX =
QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_attr_ocb_stop_timing_advert - Vendor subcmd attributes
* to stop timing advert
*
* @QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ: The channel
* frequency on which to stop the timing advert
*/
enum qca_wlan_vendor_attr_ocb_stop_timing_advert {
QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ = 1,
QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX =
QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_attr_ocb_get_tsf_response - Vendor subcmd attributes to
* get TSF timer value
*
* @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH: Higher 32 bits of the
* timer
* @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW: Lower 32 bits of the timer
*/
enum qca_wlan_vendor_attr_ocb_get_tsf_resp {
QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH = 1,
QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW = 2,
QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_MAX =
QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST - 1
};
enum qca_vendor_attr_get_preferred_freq_list {
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID,
/* A 32-unsigned value; the interface type/mode for which the preferred
* frequency list is requested (see enum qca_iface_type for possible
* values); used in GET_PREFERRED_FREQ_LIST command from user-space to
* kernel and in the kernel response back to user-space.
*/
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
/* An array of 32-unsigned values; values are frequency (MHz); sent
* from kernel space to user space.
*/
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST,
/* An array of nested values as per enum qca_wlan_vendor_attr_pcl
* attribute. Each element contains frequency (MHz), weight, and flag
* bit mask indicating how the frequency should be used in P2P
* negotiation; sent from kernel space to user space.
*/
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX =
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST - 1
};
enum qca_vendor_attr_probable_oper_channel {
QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_INVALID,
/* 32-bit unsigned value; indicates the connection/iface type likely to
* come on this channel (see enum qca_iface_type).
*/
QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
/* 32-bit unsigned value; the frequency (MHz) of the probable channel */
QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
/* keep last */
QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_MAX =
QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_AFTER_LAST - 1
};
enum qca_iface_type {
QCA_IFACE_TYPE_STA,
QCA_IFACE_TYPE_AP,
QCA_IFACE_TYPE_P2P_CLIENT,
QCA_IFACE_TYPE_P2P_GO,
QCA_IFACE_TYPE_IBSS,
QCA_IFACE_TYPE_TDLS,
};
enum qca_set_band {
QCA_SETBAND_AUTO = 0,
QCA_SETBAND_5G = BIT(0),
QCA_SETBAND_2G = BIT(1),
QCA_SETBAND_6G = BIT(2),
};
/**
* enum qca_access_policy - Access control policy
*
* Access control policy is applied on the configured IE
* (QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE).
* To be set with QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY.
*
* @QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED: Deny Wi-Fi connections which match
* the specific configuration (IE) set, i.e., allow all the
* connections which do not match the configuration.
* @QCA_ACCESS_POLICY_DENY_UNLESS_LISTED: Accept Wi-Fi connections which match
* the specific configuration (IE) set, i.e., deny all the
* connections which do not match the configuration.
*/
enum qca_access_policy {
QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED,
QCA_ACCESS_POLICY_DENY_UNLESS_LISTED,
};
/**
* enum qca_vendor_attr_get_tsf: Vendor attributes for TSF capture
* @QCA_WLAN_VENDOR_ATTR_TSF_CMD: enum qca_tsf_operation (u32)
* @QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE: Unsigned 64 bit TSF timer value
* @QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE: Unsigned 64 bit Synchronized
* SOC timer value at TSF capture
*/
enum qca_vendor_attr_tsf_cmd {
QCA_WLAN_VENDOR_ATTR_TSF_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TSF_CMD,
QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE,
QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE,
QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TSF_MAX =
QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST - 1
};
/**
* enum qca_tsf_operation: TSF driver commands
* @QCA_TSF_CAPTURE: Initiate TSF Capture
* @QCA_TSF_GET: Get TSF capture value
* @QCA_TSF_SYNC_GET: Initiate TSF capture and return with captured value
* @QCA_TSF_AUTO_REPORT_ENABLE: Used in STA mode only. Once set, the target
* will automatically send TSF report to the host. To query
* QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY, this operation needs to be
* initiated first.
* @QCA_TSF_AUTO_REPORT_DISABLE: Used in STA mode only. Once set, the target
* will not automatically send TSF report to the host. If
* QCA_TSF_AUTO_REPORT_ENABLE is initiated and
* QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY is not queried anymore, this
* operation needs to be initiated.
*/
enum qca_tsf_cmd {
QCA_TSF_CAPTURE,
QCA_TSF_GET,
QCA_TSF_SYNC_GET,
QCA_TSF_AUTO_REPORT_ENABLE,
QCA_TSF_AUTO_REPORT_DISABLE,
};
/**
* enum qca_vendor_attr_wisa_cmd
* @QCA_WLAN_VENDOR_ATTR_WISA_MODE: WISA mode value (u32)
* WISA setup vendor commands
*/
enum qca_vendor_attr_wisa_cmd {
QCA_WLAN_VENDOR_ATTR_WISA_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_WISA_MODE,
QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WISA_MAX =
QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST - 1
};
/* IEEE 802.11 Vendor Specific elements */
/**
* enum qca_vendor_element_id - QCA Vendor Specific element types
*
* These values are used to identify QCA Vendor Specific elements. The
* payload of the element starts with the three octet OUI (OUI_QCA) and
* is followed by a single octet type which is defined by this enum.
*
* @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list.
* This element can be used to specify preference order for supported
* channels. The channels in this list are in preference order (the first
* one has the highest preference) and are described as a pair of
* (global) Operating Class and Channel Number (each one octet) fields.
*
* This extends the standard P2P functionality by providing option to have
* more than one preferred operating channel. When this element is present,
* it replaces the preference indicated in the Operating Channel attribute.
* For supporting other implementations, the Operating Channel attribute is
* expected to be used with the highest preference channel. Similarly, all
* the channels included in this Preferred channel list element are
* expected to be included in the Channel List attribute.
*
* This vendor element may be included in GO Negotiation Request, P2P
* Invitation Request, and Provision Discovery Request frames.
*
* @QCA_VENDOR_ELEM_HE_CAPAB: HE Capabilities element.
* This element can be used for pre-standard publication testing of HE
* before P802.11ax draft assigns the element ID. The payload of this
* vendor specific element is defined by the latest P802.11ax draft.
* Please note that the draft is still work in progress and this element
* payload is subject to change.
*
* @QCA_VENDOR_ELEM_HE_OPER: HE Operation element.
* This element can be used for pre-standard publication testing of HE
* before P802.11ax draft assigns the element ID. The payload of this
* vendor specific element is defined by the latest P802.11ax draft.
* Please note that the draft is still work in progress and this element
* payload is subject to change.
*
* @QCA_VENDOR_ELEM_RAPS: RAPS element (OFDMA-based Random Access Parameter Set
* element).
* This element can be used for pre-standard publication testing of HE
* before P802.11ax draft assigns the element ID extension. The payload of
* this vendor specific element is defined by the latest P802.11ax draft
* (not including the Element ID Extension field). Please note that the
* draft is still work in progress and this element payload is subject to
* change.
*
* @QCA_VENDOR_ELEM_MU_EDCA_PARAMS: MU EDCA Parameter Set element.
* This element can be used for pre-standard publication testing of HE
* before P802.11ax draft assigns the element ID extension. The payload of
* this vendor specific element is defined by the latest P802.11ax draft
* (not including the Element ID Extension field). Please note that the
* draft is still work in progress and this element payload is subject to
* change.
*
* @QCA_VENDOR_ELEM_BSS_COLOR_CHANGE: BSS Color Change Announcement element.
* This element can be used for pre-standard publication testing of HE
* before P802.11ax draft assigns the element ID extension. The payload of
* this vendor specific element is defined by the latest P802.11ax draft
* (not including the Element ID Extension field). Please note that the
* draft is still work in progress and this element payload is subject to
* change.
*
* @QCA_VENDOR_ELEM_ALLPLAY: Allplay element
*/
enum qca_vendor_element_id {
QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
QCA_VENDOR_ELEM_HE_CAPAB = 1,
QCA_VENDOR_ELEM_HE_OPER = 2,
QCA_VENDOR_ELEM_RAPS = 3,
QCA_VENDOR_ELEM_MU_EDCA_PARAMS = 4,
QCA_VENDOR_ELEM_BSS_COLOR_CHANGE = 5,
QCA_VENDOR_ELEM_ALLPLAY = 6,
};
+/**
+ * enum qca_wlan_vendor_scan_priority - Specifies the valid values that the
+ * vendor scan attribute QCA_WLAN_VENDOR_ATTR_SCAN_PRIORITY can take.
+ * @QCA_WLAN_VENDOR_SCAN_PRIORITY_VERY_LOW: Very low priority
+ * @QCA_WLAN_VENDOR_SCAN_PRIORITY_LOW: Low priority
+ * @QCA_WLAN_VENDOR_SCAN_PRIORITY_MEDIUM: Medium priority
+ * @QCA_WLAN_VENDOR_SCAN_PRIORITY_HIGH: High priority
+ * @QCA_WLAN_VENDOR_SCAN_PRIORITY_VERY_HIGH: Very high priority
+ */
+enum qca_wlan_vendor_scan_priority {
+ QCA_WLAN_VENDOR_SCAN_PRIORITY_VERY_LOW = 0,
+ QCA_WLAN_VENDOR_SCAN_PRIORITY_LOW = 1,
+ QCA_WLAN_VENDOR_SCAN_PRIORITY_MEDIUM = 2,
+ QCA_WLAN_VENDOR_SCAN_PRIORITY_HIGH = 3,
+ QCA_WLAN_VENDOR_SCAN_PRIORITY_VERY_HIGH = 4,
+};
+
/**
* enum qca_wlan_vendor_attr_scan - Specifies vendor scan attributes
*
* @QCA_WLAN_VENDOR_ATTR_SCAN_IE: IEs that should be included as part of scan
* @QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES: Nested unsigned 32-bit attributes
* with frequencies to be scanned (in MHz)
* @QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS: Nested attribute with SSIDs to be scanned
* @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported
* rates to be included
* @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests
* at non CCK rate in 2GHz band
* @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags
* @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the
* driver for the specific scan request
* @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan
* request decoded as in enum scan_status
* @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation
* scan flag is set
* @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with
* randomisation
* @QCA_WLAN_VENDOR_ATTR_SCAN_BSSID: 6-byte MAC address representing the
* specific BSSID to scan for.
* @QCA_WLAN_VENDOR_ATTR_SCAN_DWELL_TIME: Unsigned 64-bit dwell time in
* microseconds. This is a common value which applies across all
* frequencies specified by QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES.
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_PRIORITY: Priority of vendor scan relative to
+ * other scan requests. It is a u32 attribute and takes values from enum
+ * qca_wlan_vendor_scan_priority. This is an optional attribute.
+ * If this attribute is not configured, the driver shall use
+ * QCA_WLAN_VENDOR_SCAN_PRIORITY_HIGH as the priority of vendor scan.
*/
enum qca_wlan_vendor_attr_scan {
QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
QCA_WLAN_VENDOR_ATTR_SCAN_IE = 1,
QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES = 2,
QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS = 3,
QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES = 4,
QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE = 5,
QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS = 6,
QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE = 7,
QCA_WLAN_VENDOR_ATTR_SCAN_STATUS = 8,
QCA_WLAN_VENDOR_ATTR_SCAN_MAC = 9,
QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK = 10,
QCA_WLAN_VENDOR_ATTR_SCAN_BSSID = 11,
QCA_WLAN_VENDOR_ATTR_SCAN_DWELL_TIME = 12,
+ QCA_WLAN_VENDOR_ATTR_SCAN_PRIORITY = 13,
QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
};
/**
* enum scan_status - Specifies the valid values the vendor scan attribute
* QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take
*
* @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with
* new scan results
* @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between
*/
enum scan_status {
VENDOR_SCAN_STATUS_NEW_RESULTS,
VENDOR_SCAN_STATUS_ABORTED,
VENDOR_SCAN_STATUS_MAX,
};
/**
* enum qca_vendor_attr_ota_test - Specifies the values for vendor
* command QCA_NL80211_VENDOR_SUBCMD_OTA_TEST
* @QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE: enable ota test
*/
enum qca_vendor_attr_ota_test {
QCA_WLAN_VENDOR_ATTR_OTA_TEST_INVALID,
/* 8-bit unsigned value to indicate if OTA test is enabled */
QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE,
/* keep last */
QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX =
QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST - 1
};
/**
* enum qca_vendor_attr_txpower_scale - vendor sub commands index
*
* @QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE: scaling value
*/
enum qca_vendor_attr_txpower_scale {
QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_INVALID,
/* 8-bit unsigned value to indicate the scaling of tx power */
QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX =
QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST - 1
};
/**
* enum qca_vendor_attr_txpower_decr_db - Attributes for TX power decrease
*
* These attributes are used with QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB.
*/
enum qca_vendor_attr_txpower_decr_db {
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID,
/* 8-bit unsigned value to indicate the reduction of TX power in dB for
* a virtual interface.
*/
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_MAX =
QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST - 1
};
/* Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION and
* QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION subcommands.
*/
enum qca_wlan_vendor_attr_config {
QCA_WLAN_VENDOR_ATTR_CONFIG_INVALID = 0,
/* Unsigned 32-bit value to set the DTIM period.
* Whether the wifi chipset wakes at every dtim beacon or a multiple of
* the DTIM period. If DTIM is set to 3, the STA shall wake up every 3
* DTIM beacons.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_DTIM = 1,
/* Unsigned 32-bit value to set the wifi_iface stats averaging factor
* used to calculate statistics like average the TSF offset or average
* number of frame leaked.
* For instance, upon Beacon frame reception:
* current_avg = ((beacon_TSF - TBTT) * factor + previous_avg * (0x10000 - factor) ) / 0x10000
* For instance, when evaluating leaky APs:
* current_avg = ((num frame received within guard time) * factor + previous_avg * (0x10000 - factor)) / 0x10000
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR = 2,
/* Unsigned 32-bit value to configure guard time, i.e., when
* implementing IEEE power management based on frame control PM bit, how
* long the driver waits before shutting down the radio and after
* receiving an ACK frame for a Data frame with PM bit set.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME = 3,
/* Unsigned 32-bit value to change the FTM capability dynamically */
QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT = 4,
/* Unsigned 16-bit value to configure maximum TX rate dynamically */
QCA_WLAN_VENDOR_ATTR_CONF_TX_RATE = 5,
/* Unsigned 32-bit value to configure the number of continuous
* Beacon Miss which shall be used by the firmware to penalize
* the RSSI.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS = 6,
/* Unsigned 8-bit value to configure the channel avoidance indication
* behavior. Firmware to send only one indication and ignore duplicate
* indications when set to avoid multiple Apps wakeups.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7,
/* 8-bit unsigned value to configure the maximum TX MPDU for
* aggregation.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8,
/* 8-bit unsigned value to configure the maximum RX MPDU for
* aggregation.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9,
/* 8-bit unsigned value to configure the Non aggregrate/11g sw
* retry threshold (0 disable, 31 max).
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10,
/* 8-bit unsigned value to configure the aggregrate sw
* retry threshold (0 disable, 31 max).
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11,
/* 8-bit unsigned value to configure the MGMT frame
* retry threshold (0 disable, 31 max).
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12,
/* 8-bit unsigned value to configure the CTRL frame
* retry threshold (0 disable, 31 max).
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13,
/* 8-bit unsigned value to configure the propagation delay for
* 2G/5G band (0~63, units in us)
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14,
/* Unsigned 32-bit value to configure the number of unicast TX fail
* packet count. The peer is disconnected once this threshold is
* reached.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15,
/* Attribute used to set scan default IEs to the driver.
*
* These IEs can be used by scan operations that will be initiated by
* the driver/firmware.
*
* For further scan requests coming to the driver, these IEs should be
* merged with the IEs received along with scan request coming to the
* driver. If a particular IE is present in the scan default IEs but not
* present in the scan request, then that IE should be added to the IEs
* sent in the Probe Request frames for that scan request.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16,
/* Unsigned 32-bit attribute for generic commands */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17,
/* Unsigned 32-bit value attribute for generic commands */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_VALUE = 18,
/* Unsigned 32-bit data attribute for generic command response */
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19,
/* Unsigned 32-bit length attribute for
* QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20,
/* Unsigned 32-bit flags attribute for
* QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21,
/* Unsigned 32-bit, defining the access policy.
* See enum qca_access_policy. Used with
* QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22,
/* Sets the list of full set of IEs for which a specific access policy
* has to be applied. Used along with
* QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access.
* Zero length payload can be used to clear this access constraint.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23,
/* Unsigned 32-bit, specifies the interface index (netdev) for which the
* corresponding configurations are applied. If the interface index is
* not specified, the configurations are attributed to the respective
* wiphy.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24,
/* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */
QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25,
/* 8-bit unsigned value to configure the driver and below layers to
* ignore the assoc disallowed set by APs while connecting
* 1-Ignore, 0-Don't ignore
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26,
/* 32-bit unsigned value to trigger antenna diversity features:
* 1-Enable, 0-Disable
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27,
/* 32-bit unsigned value to configure specific chain antenna */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28,
/* 32-bit unsigned value to trigger cycle selftest
* 1-Enable, 0-Disable
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29,
/* 32-bit unsigned to configure the cycle time of selftest
* the unit is micro-second
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30,
/* 32-bit unsigned value to set reorder timeout for AC_VO */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE = 31,
/* 32-bit unsigned value to set reorder timeout for AC_VI */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO = 32,
/* 32-bit unsigned value to set reorder timeout for AC_BE */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT = 33,
/* 32-bit unsigned value to set reorder timeout for AC_BK */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND = 34,
/* 6-byte MAC address to point out the specific peer */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC = 35,
/* 32-bit unsigned value to set window size for specific peer */
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT = 36,
/* 8-bit unsigned value to set the beacon miss threshold in 2.4 GHz */
QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24 = 37,
/* 8-bit unsigned value to set the beacon miss threshold in 5 GHz */
QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5 = 38,
/* 32-bit unsigned value to configure 5 or 10 MHz channel width for
* station device while in disconnect state. The attribute use the
* value of enum nl80211_chan_width: NL80211_CHAN_WIDTH_5 means 5 MHz,
* NL80211_CHAN_WIDTH_10 means 10 MHz. If set, the device work in 5 or
* 10 MHz channel width, the station will not connect to a BSS using 20
* MHz or higher bandwidth. Set to NL80211_CHAN_WIDTH_20_NOHT to
* clear this constraint.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH = 39,
/* 32-bit unsigned value to configure the propagation absolute delay
* for 2G/5G band (units in us)
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY = 40,
/* 32-bit unsigned value to set probe period */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD = 41,
/* 32-bit unsigned value to set stay period */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD = 42,
/* 32-bit unsigned value to set snr diff */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF = 43,
/* 32-bit unsigned value to set probe dwell time */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME = 44,
/* 32-bit unsigned value to set mgmt snr weight */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT = 45,
/* 32-bit unsigned value to set data snr weight */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT = 46,
/* 32-bit unsigned value to set ack snr weight */
QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT = 47,
/* 32-bit unsigned value to configure the listen interval.
* This is in units of beacon intervals. This configuration alters
* the negotiated listen interval with the AP during the connection.
* It is highly recommended to configure a value less than or equal to
* the one negotiated during the association. Configuring any greater
* value can have adverse effects (frame loss, AP disassociating STA,
* etc.).
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL = 48,
/*
* 8 bit unsigned value that is set on an AP/GO virtual interface to
* disable operations that would cause the AP/GO to leave its operating
* channel.
*
* This will restrict the scans to the AP/GO operating channel and the
* channels of the other band, if DBS is supported.A STA/CLI interface
* brought up after this setting is enabled, will be restricted to
* connecting to devices only on the AP/GO interface's operating channel
* or on the other band in DBS case. P2P supported channel list is
* modified, to only include AP interface's operating-channel and the
* channels of the other band if DBS is supported.
*
* These restrictions are only applicable as long as the AP/GO interface
* is alive. If the AP/GO interface is brought down then this
* setting/restriction is forgotten.
*
* If this variable is set on an AP/GO interface while a multi-channel
* concurrent session is active, it has no effect on the operation of
* the current interfaces, other than restricting the scan to the AP/GO
* operating channel and the other band channels if DBS is supported.
* However, if the STA is brought down and restarted then the new STA
* connection will either be formed on the AP/GO channel or on the
* other band in a DBS case. This is because of the scan being
* restricted on these channels as mentioned above.
*
* 1-Restrict / 0-Don't restrict offchannel operations.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL = 49,
/*
* 8 bit unsigned value to enable/disable LRO (Large Receive Offload)
* on an interface.
* 1 - Enable, 0 - Disable.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_LRO = 50,
/*
* 8 bit unsigned value to globally enable/disable scan
* 1 - Enable, 0 - Disable.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE = 51,
/* 8-bit unsigned value to set the total beacon miss count
* This parameter will set the total beacon miss count.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT = 52,
/* Unsigned 32-bit value to configure the number of continuous
* Beacon Miss which shall be used by the firmware to penalize
* the RSSI for BTC.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS_BTC = 53,
/* 8-bit unsigned value to configure the driver and below layers to
* enable/disable all FILS features.
* 0-enable, 1-disable
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS = 54,
/* 16-bit unsigned value to configure the level of WLAN latency
* module. See enum qca_wlan_vendor_attr_config_latency_level.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL = 55,
/* 8-bit unsigned value indicating the driver to use the RSNE as-is from
* the connect interface. Exclusively used for the scenarios where the
* device is used as a test bed device with special functionality and
* not recommended for production. This helps driver to not validate the
* RSNE passed from user space and thus allow arbitrary IE data to be
* used for testing purposes.
* 1-enable, 0-disable.
* Applications set/reset this configuration. If not reset, this
* parameter remains in use until the driver is unloaded.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE = 56,
/* 8-bit unsigned value to trigger green Tx power saving.
* 1-Enable, 0-Disable
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_GTX = 57,
/* Attribute to configure disconnect IEs to the driver.
* This carries an array of unsigned 8-bit characters.
*
* If this is configured, driver shall fill the IEs in disassoc/deauth
* frame.
* These IEs are expected to be considered only for the next
* immediate disconnection (disassoc/deauth frame) originated by
* the DUT, irrespective of the entity (user space/driver/firmware)
* triggering the disconnection.
* The host drivers are not expected to use the IEs set through
* this interface for further disconnections after the first immediate
* disconnection initiated post the configuration.
* If the IEs are also updated through cfg80211 interface (after the
* enhancement to cfg80211_disconnect), host driver is expected to
* take the union of IEs from both of these interfaces and send in
* further disassoc/deauth frames.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_DISCONNECT_IES = 58,
/* 8-bit unsigned value for ELNA bypass.
* 1-Enable, 0-Disable
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_ELNA_BYPASS = 59,
/* 8-bit unsigned value. This attribute enables/disables the host driver
* to send the Beacon Report Response with failure reason for the
* scenarios where STA cannot honor the Beacon Report Request from AP.
* 1-Enable, 0-Disable.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL = 60,
/* 8-bit unsigned value. This attribute enables/disables the host driver
* to send roam reason information in the Reassociation Request frame to
* the target AP when roaming within the same ESS.
* 1-Enable, 0-Disable.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_ROAM_REASON = 61,
/* 32-bit unsigned value to configure different PHY modes to the
* driver/firmware. The possible values are defined in
* enum qca_wlan_vendor_phy_mode. The configuration will be reset to
* default value, i.e., QCA_WLAN_VENDOR_PHY_MODE_AUTO upon restarting
* the driver.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE = 62,
/* 8-bit unsigned value to configure the maximum supported channel width
* for STA mode. If this value is configured when STA is in connected
* state, it should not exceed the negotiated channel width. If it is
* configured when STA is in disconnected state, the configured value
* will take effect for the next immediate connection.
* Possible values are:
* NL80211_CHAN_WIDTH_20
* NL80211_CHAN_WIDTH_40
* NL80211_CHAN_WIDTH_80
* NL80211_CHAN_WIDTH_80P80
* NL80211_CHAN_WIDTH_160
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_WIDTH = 63,
/* 8-bit unsigned value to enable/disable dynamic bandwidth adjustment.
* This attribute is only applicable for STA mode. When dynamic
* bandwidth adjustment is disabled, STA will use static channel width
* the value of which is negotiated during connection.
* 1-enable (default), 0-disable
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_BW = 64,
/* 8-bit unsigned value to configure the maximum number of subframes of
* TX MSDU for aggregation. Possible values are 0-31. When set to 0,
* it is decided by the hardware.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MSDU_AGGREGATION = 65,
/* 8-bit unsigned value to configure the maximum number of subframes of
* RX MSDU for aggregation. Possible values are 0-31. When set to 0,
* it is decided by the hardware.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MSDU_AGGREGATION = 66,
/* 8-bit unsigned value. This attribute is used to dynamically
* enable/disable the LDPC capability of the device. When configured in
* the disconnected state, the updated configuration will be considered
* for the immediately following connection attempt. If this
* configuration is modified while the device is in the connected state,
* the LDPC TX will be updated with this configuration immediately,
* while the LDPC RX configuration update will take place starting from
* the subsequent association attempt.
* 1-Enable, 0-Disable.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_LDPC = 67,
/* 8-bit unsigned value. This attribute is used to dynamically
* enable/disable the TX STBC capability of the device. When configured
* in the disconnected state, the updated configuration will be
* considered for the immediately following connection attempt. If the
* connection is formed with TX STBC enabled and if this configuration
* is disabled during that association, the TX will be impacted
* immediately. Further connection attempts will disable TX STBC.
* However, enabling the TX STBC for a connected session with disabled
* capability is not allowed and will fail.
* 1-Enable, 0-Disable.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_TX_STBC = 68,
/* 8-bit unsigned value. This attribute is used to dynamically
* enable/disable the RX STBC capability of the device. When configured
* in the disconnected state, the updated configuration will be
* considered for the immediately following connection attempt. If the
* configuration is modified in the connected state, there will be no
* impact for the current association, but further connection attempts
* will use the updated configuration.
* 1-Enable, 0-Disable.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_STBC = 69,
/* 8-bit unsigned value. This attribute is used to dynamically configure
* the number of spatial streams. When configured in the disconnected
* state, the updated configuration will be considered for the
* immediately following connection attempt. If the NSS is updated after
* the connection, the updated NSS value is notified to the peer using
* the Operating Mode Notification/Spatial Multiplexing Power Save
* frame. The updated NSS value after the connection shall not be
* greater than the one negotiated during the connection. Any such
* higher value configuration shall be returned with a failure.
* Only symmetric NSS configuration (such as 2X2 or 1X1) can be done
* using this attribute. QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS and
* QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attributes shall be used to
* configure the asymmetric NSS configuration (such as 1X2).
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_NSS = 70,
/* 8-bit unsigned value to trigger Optimized Power Management:
* 1-Enable, 0-Disable
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT = 71,
/* 8-bit unsigned value. This attribute takes the QoS/access category
* value represented by the enum qca_wlan_ac_type and expects the driver
* to upgrade the UDP frames to this access category. The value of
* QCA_WLAN_AC_ALL is invalid for this attribute. This will override the
* DSCP value configured in the frame with the intention to only upgrade
* the access category. That said, it is not intended to downgrade the
* access category for the frames.
* Set the value to QCA_WLAN_AC_BK if the QoS upgrade needs to be
* disabled, as BK is of the lowest priority and an upgrade to it does
* not result in any changes for the frames.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_UDP_QOS_UPGRADE = 72,
/* 8-bit unsigned value. This attribute is used to dynamically configure
* the number of chains to be used for transmitting data. This
* configuration is allowed only when in connected state and will be
* effective until disconnected. The driver rejects this configuration
* if the number of spatial streams being used in the current connection
* cannot be supported by this configuration.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_TX_CHAINS = 73,
/* 8-bit unsigned value. This attribute is used to dynamically configure
* the number of chains to be used for receiving data. This
* configuration is allowed only when in connected state and will be
* effective until disconnected. The driver rejects this configuration
* if the number of spatial streams being used in the current connection
* cannot be supported by this configuration.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_NUM_RX_CHAINS = 74,
/* 8-bit unsigned value to configure ANI setting type.
* See &enum qca_wlan_ani_setting for possible values.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_SETTING = 75,
/* 32-bit signed value to configure ANI level. This is used when
* ANI settings type is &QCA_WLAN_ANI_SETTING_FIXED.
* The set and get of ANI level with &QCA_WLAN_ANI_SETTING_AUTO
* is invalid, the driver will return a failure.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_ANI_LEVEL = 76,
/* 8-bit unsigned value. This attribute is used to dynamically configure
* the number of spatial streams used for transmitting the data. When
* configured in the disconnected state, the configured value will
* be considered for the following connection attempt.
* If the NSS is updated after the connection, the updated NSS value
* is notified to the peer using the Operating Mode Notification/Spatial
* Multiplexing Power Save frame.
* The TX NSS value configured after the connection shall not be greater
* than the value negotiated during the connection. Any such higher
* value configuration shall be treated as invalid configuration by
* the driver. This attribute shall be configured along with
* QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attribute to define the symmetric
* configuration (such as 2X2 or 1X1) or the asymmetric
* configuration (such as 1X2).
* If QCA_WLAN_VENDOR_ATTR_CONFIG_NSS attribute is also provided along
* with this QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS attribute the driver
* will update the TX NSS based on QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS = 77,
/* 8-bit unsigned value. This attribute is used to dynamically configure
* the number of spatial streams used for receiving the data. When
* configured in the disconnected state, the configured value will
* be considered for the following connection attempt.
* If the NSS is updated after the connection, the updated NSS value
* is notified to the peer using the Operating Mode Notification/Spatial
* Multiplexing Power Save frame.
* The RX NSS value configured after the connection shall not be greater
* than the value negotiated during the connection. Any such higher
* value configuration shall be treated as invalid configuration by
* the driver. This attribute shall be configured along with
* QCA_WLAN_VENDOR_ATTR_CONFIG_TX_NSS attribute to define the symmetric
* configuration (such as 2X2 or 1X1) or the asymmetric
* configuration (such as 1X2).
* If QCA_WLAN_VENDOR_ATTR_CONFIG_NSS attribute is also provided along
* with this QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS attribute the driver
* will update the RX NSS based on QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_RX_NSS = 78,
/*
* 8-bit unsigned value. This attribute, when set, indicates whether the
* specified interface is the primary STA interface when there are more
* than one STA interfaces concurrently active.
*
* This configuration helps the firmware/hardware to support certain
* features (e.g., roaming) on this primary interface, if the same
* cannot be supported on the concurrent STA interfaces simultaneously.
*
* This configuration is only applicable for a single STA interface on
* a device and gives the priority for it only over other concurrent STA
* interfaces.
*
* If the device is a multi wiphy/soc, this configuration applies to a
* single STA interface across the wiphys.
*
* 1-Enable (is the primary STA), 0-Disable (is not the primary STA)
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_CONCURRENT_STA_PRIMARY = 79,
/*
* 8-bit unsigned value. This attribute can be used to configure the
* driver to enable/disable FT-over-DS feature. Possible values for
* this attribute are 1-Enable and 0-Disable.
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_FT_OVER_DS = 80,
+ /*
+ * 8-bit unsigned value. This attribute can be used to configure the
+ * firmware to enable/disable ARP/NS offload feature. Possible values
+ * for this attribute are 0-Disable and 1-Enable.
+ *
+ * This attribute is only applicable for STA/P2P-Client interface,
+ * and is optional, default behavior is ARP/NS offload enabled.
+ *
+ * This attribute can be set in disconnected and connected state, and
+ * will restore to the default behavior if the interface is closed.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_ARP_NS_OFFLOAD = 81,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST - 1,
};
/* Compatibility defines for previously used incorrect enum
* qca_wlan_vendor_attr_config names. These values should not be used in any
* new implementation. */
#define QCA_WLAN_VENDOR_ATTR_DISCONNECT_IES \
QCA_WLAN_VENDOR_ATTR_CONFIG_DISCONNECT_IES
#define QCA_WLAN_VENDOR_ATTR_BEACON_REPORT_FAIL \
QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL
/**
* enum qca_wlan_ani_setting - ANI setting type
* @QCA_WLAN_ANI_SETTING_AUTO: Automatically determine ANI level
* @QCA_WLAN_ANI_SETTING_FIXED: Fix ANI level to the dBm parameter
*/
enum qca_wlan_ani_setting {
QCA_WLAN_ANI_SETTING_AUTO = 0,
QCA_WLAN_ANI_SETTING_FIXED = 1,
};
/**
* enum qca_wlan_vendor_attr_sap_config - Parameters for AP configuration
*
* @QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL: Optional (u8)
* Channel number on which Access Point should restart.
* Note: If both the driver and user space application supports the 6 GHz band,
* this attribute is deprecated and QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY
* should be used.
* To maintain backward compatibility, QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL
* is still used if either of the driver or user space application doesn't
* support the 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY: Optional (u32)
* Channel center frequency (MHz) on which the access point should restart.
*/
enum qca_wlan_vendor_attr_sap_config {
QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_CHANNEL = 1,
/* List of frequencies on which AP is expected to operate.
* This is irrespective of ACS configuration. This list is a priority
* based one and is looked for before the AP is created to ensure the
* best concurrency sessions (avoid MCC and use DBS/SCC) co-exist in
* the system.
*/
QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST = 2,
QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_FREQUENCY = 3,
QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX =
QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_sap_conditional_chan_switch - Parameters for AP
* conditional channel switch
*/
enum qca_wlan_vendor_attr_sap_conditional_chan_switch {
QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0,
/* Priority based frequency list (an array of u32 values in host byte
* order)
*/
QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1,
/* Status of the conditional switch (u32).
* 0: Success, Non-zero: Failure
*/
QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS = 2,
QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX =
QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST - 1,
};
/**
* enum qca_wlan_gpio_attr - Parameters for GPIO configuration
*
* @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND: Required (u32)
* value to specify the GPIO command. Please refer to enum qca_gpio_cmd_type
* for the available values.
*
* @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM: Required (u32)
* value to specify the GPIO number.
* This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
* %QCA_WLAN_VENDOR_GPIO_CONFIG or %QCA_WLAN_VENDOR_GPIO_OUTPUT.
*
* @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE: Required (u32)
* value to specify the GPIO output level. Please refer to enum qca_gpio_value
* for the available values.
* This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
* %QCA_WLAN_VENDOR_GPIO_OUTPUT.
*
* @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE: Optional (u32)
* value to specify the GPIO pull type. Please refer to enum qca_gpio_pull_type
* for the available values.
* This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
* %QCA_WLAN_VENDOR_GPIO_CONFIG and
* %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG attribute is not present.
* Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG
* attribute is present.
*
* @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE: Optional (u32)
* value to specify the GPIO interrupt mode. Please refer to enum
* qca_gpio_interrupt_mode for the available values.
* This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
* %QCA_WLAN_VENDOR_GPIO_CONFIG and
* %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG attribute is not present.
* Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG
* attribute is present.
*
* @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR: Optional (u32)
* value to specify the GPIO direction. Please refer to enum qca_gpio_direction
* for the available values.
* This is required, when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
* %QCA_WLAN_VENDOR_GPIO_CONFIG and
* %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG attribute is not present.
* Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG
* attribute is present.
*
* @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MUX_CONFIG: Optional (u32)
* Value to specify the mux config. Meaning of a given value is dependent
* on the target chipset and GPIO pin. Must be of the range 0-15.
* Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
* %QCA_WLAN_VENDOR_GPIO_CONFIG. Defaults to 0.
*
* @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DRIVE: Optional (u32)
* Value to specify the drive, refer to enum qca_gpio_drive.
* Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
* %QCA_WLAN_VENDOR_GPIO_CONFIG. Defaults to QCA_WLAN_GPIO_DRIVE_2MA(0).
*
* @QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG: Optional (flag)
* Optional when %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND is
* %QCA_WLAN_VENDOR_GPIO_CONFIG. When present this attribute signals that all
* other parameters for the given GPIO will be obtained from internal
* configuration. Only %QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM must be
* specified to indicate the GPIO pin being configured.
*/
enum qca_wlan_gpio_attr {
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INVALID = 0,
/* Unsigned 32-bit attribute for GPIO command */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND = 1,
/* Unsigned 32-bit attribute for GPIO PIN number to configure */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM = 2,
/* Unsigned 32-bit attribute for GPIO value to configure */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE = 3,
/* Unsigned 32-bit attribute for GPIO pull type */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE = 4,
/* Unsigned 32-bit attribute for GPIO interrupt mode */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE = 5,
/* Unsigned 32-bit attribute for GPIO direction to configure */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DIR = 6,
/* Unsigned 32-bit attribute for GPIO mux config */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MUX_CONFIG = 7,
/* Unsigned 32-bit attribute for GPIO drive */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_DRIVE = 8,
/* Flag attribute for using internal GPIO configuration */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTERNAL_CONFIG = 9,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST,
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MAX =
QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST - 1
};
/**
* enum gpio_cmd_type - GPIO configuration command type
* @QCA_WLAN_VENDOR_GPIO_CONFIG: Set GPIO configuration info
* @QCA_WLAN_VENDOR_GPIO_OUTPUT: Set GPIO output level
*/
enum qca_gpio_cmd_type {
QCA_WLAN_VENDOR_GPIO_CONFIG = 0,
QCA_WLAN_VENDOR_GPIO_OUTPUT = 1,
};
/**
* enum qca_gpio_pull_type - GPIO pull type
* @QCA_WLAN_GPIO_PULL_NONE: Set GPIO pull type to none
* @QCA_WLAN_GPIO_PULL_UP: Set GPIO pull up
* @QCA_WLAN_GPIO_PULL_DOWN: Set GPIO pull down
*/
enum qca_gpio_pull_type {
QCA_WLAN_GPIO_PULL_NONE = 0,
QCA_WLAN_GPIO_PULL_UP = 1,
QCA_WLAN_GPIO_PULL_DOWN = 2,
QCA_WLAN_GPIO_PULL_MAX,
};
/**
* enum qca_gpio_direction - GPIO direction
* @QCA_WLAN_GPIO_INPUT: Set GPIO as input mode
* @QCA_WLAN_GPIO_OUTPUT: Set GPIO as output mode
* @QCA_WLAN_GPIO_VALUE_MAX: Invalid value
*/
enum qca_gpio_direction {
QCA_WLAN_GPIO_INPUT = 0,
QCA_WLAN_GPIO_OUTPUT = 1,
QCA_WLAN_GPIO_DIR_MAX,
};
/**
* enum qca_gpio_value - GPIO Value
* @QCA_WLAN_GPIO_LEVEL_LOW: set gpio output level to low
* @QCA_WLAN_GPIO_LEVEL_HIGH: set gpio output level to high
* @QCA_WLAN_GPIO_LEVEL_MAX: Invalid value
*/
enum qca_gpio_value {
QCA_WLAN_GPIO_LEVEL_LOW = 0,
QCA_WLAN_GPIO_LEVEL_HIGH = 1,
QCA_WLAN_GPIO_LEVEL_MAX,
};
/**
* enum gpio_interrupt_mode - GPIO interrupt mode
* @QCA_WLAN_GPIO_INTMODE_DISABLE: Disable interrupt trigger
* @QCA_WLAN_GPIO_INTMODE_RISING_EDGE: Interrupt with GPIO rising edge trigger
* @QCA_WLAN_GPIO_INTMODE_FALLING_EDGE: Interrupt with GPIO falling edge trigger
* @QCA_WLAN_GPIO_INTMODE_BOTH_EDGE: Interrupt with GPIO both edge trigger
* @QCA_WLAN_GPIO_INTMODE_LEVEL_LOW: Interrupt with GPIO level low trigger
* @QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH: Interrupt with GPIO level high trigger
* @QCA_WLAN_GPIO_INTMODE_MAX: Invalid value
*/
enum qca_gpio_interrupt_mode {
QCA_WLAN_GPIO_INTMODE_DISABLE = 0,
QCA_WLAN_GPIO_INTMODE_RISING_EDGE = 1,
QCA_WLAN_GPIO_INTMODE_FALLING_EDGE = 2,
QCA_WLAN_GPIO_INTMODE_BOTH_EDGE = 3,
QCA_WLAN_GPIO_INTMODE_LEVEL_LOW = 4,
QCA_WLAN_GPIO_INTMODE_LEVEL_HIGH = 5,
QCA_WLAN_GPIO_INTMODE_MAX,
};
/**
* enum qca_gpio_drive - GPIO drive
* @QCA_WLAN_GPIO_DRIVE_2MA: drive 2MA
* @QCA_WLAN_GPIO_DRIVE_4MA: drive 4MA
* @QCA_WLAN_GPIO_DRIVE_6MA: drive 6MA
* @QCA_WLAN_GPIO_DRIVE_8MA: drive 8MA
* @QCA_WLAN_GPIO_DRIVE_10MA: drive 10MA
* @QCA_WLAN_GPIO_DRIVE_12MA: drive 12MA
* @QCA_WLAN_GPIO_DRIVE_14MA: drive 14MA
* @QCA_WLAN_GPIO_DRIVE_16MA: drive 16MA
* @QCA_WLAN_GPIO_DRIVE_MAX: invalid GPIO drive
*/
enum qca_gpio_drive {
QCA_WLAN_GPIO_DRIVE_2MA = 0,
QCA_WLAN_GPIO_DRIVE_4MA = 1,
QCA_WLAN_GPIO_DRIVE_6MA = 2,
QCA_WLAN_GPIO_DRIVE_8MA = 3,
QCA_WLAN_GPIO_DRIVE_10MA = 4,
QCA_WLAN_GPIO_DRIVE_12MA = 5,
QCA_WLAN_GPIO_DRIVE_14MA = 6,
QCA_WLAN_GPIO_DRIVE_16MA = 7,
QCA_WLAN_GPIO_DRIVE_MAX,
};
/**
* qca_wlan_set_qdepth_thresh_attr - Parameters for setting
* MSDUQ depth threshold per peer per tid in the target
*
* Associated Vendor Command:
* QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH
*/
enum qca_wlan_set_qdepth_thresh_attr {
QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_INVALID = 0,
/* 6-byte MAC address */
QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAC_ADDR,
/* Unsigned 32-bit attribute for holding the TID */
QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_TID,
/* Unsigned 32-bit attribute for holding the update mask
* bit 0 - Update high priority msdu qdepth threshold
* bit 1 - Update low priority msdu qdepth threshold
* bit 2 - Update UDP msdu qdepth threshold
* bit 3 - Update Non UDP msdu qdepth threshold
* rest of bits are reserved
*/
QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_UPDATE_MASK,
/* Unsigned 32-bit attribute for holding the threshold value */
QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_VALUE,
/* keep last */
QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST,
QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAX =
QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST - 1,
};
/**
* enum qca_acs_dfs_mode - Defines different types of DFS channel
* configurations for ACS operation.
*
* @QCA_ACS_DFS_MODE_NONE: Refer to invalid DFS mode
* @QCA_ACS_DFS_MODE_ENABLE: Consider DFS channels in ACS operation
* @QCA_ACS_DFS_MODE_DISABLE: Do not consider DFS channels in ACS operation
* @QCA_ACS_DFS_MODE_DEPRIORITIZE: Deprioritize DFS channels in ACS operation
*/
enum qca_acs_dfs_mode {
QCA_ACS_DFS_MODE_NONE = 0,
QCA_ACS_DFS_MODE_ENABLE = 1,
QCA_ACS_DFS_MODE_DISABLE = 2,
QCA_ACS_DFS_MODE_DEPRIORITIZE = 3,
};
/**
* enum qca_wlan_vendor_attr_acs_config - Defines Configuration attributes
* used by the vendor command QCA_NL80211_VENDOR_SUBCMD_ACS_POLICY.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE: Required (u8)
* DFS mode for ACS operation from enum qca_acs_dfs_mode.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT: Required (u8)
* channel number hint for ACS operation, if valid channel is specified then
* ACS operation gives priority to this channel.
* Note: If both the driver and user space application supports the 6 GHz band,
* this attribute is deprecated and QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT
* should be used.
* To maintain backward compatibility, QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT
* is still used if either of the driver or user space application doesn't
* support the 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT: Required (u32).
* Channel center frequency (MHz) hint for ACS operation, if a valid center
* frequency is specified, ACS operation gives priority to this channel.
*/
enum qca_wlan_vendor_attr_acs_config {
QCA_WLAN_VENDOR_ATTR_ACS_MODE_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_ACS_DFS_MODE = 1,
QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_HINT = 2,
QCA_WLAN_VENDOR_ATTR_ACS_FREQUENCY_HINT = 3,
QCA_WLAN_VENDOR_ATTR_ACS_DFS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ACS_DFS_MAX =
QCA_WLAN_VENDOR_ATTR_ACS_DFS_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability
*/
enum qca_wlan_vendor_attr_get_hw_capability {
QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_INVALID,
/* Antenna isolation
* An attribute used in the response.
* The content of this attribute is encoded in a byte array. Each byte
* value is an antenna isolation value. The array length is the number
* of antennas.
*/
QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION,
/* Request HW capability
* An attribute used in the request.
* The content of this attribute is a u32 array for one or more of
* hardware capabilities (attribute IDs) that are being requested. Each
* u32 value has a value from this
* enum qca_wlan_vendor_attr_get_hw_capability
* identifying which capabilities are requested.
*/
QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY,
/* keep last */
QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_MAX =
QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_ll_stats_ext - Attributes for MAC layer monitoring
* offload which is an extension for LL_STATS.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD: Monitoring period. Unit in ms.
* If MAC counters do not exceed the threshold, FW will report monitored
* link layer counters periodically as this setting. The first report is
* always triggered by this timer.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD: It is a percentage (1-99).
* For each MAC layer counter, FW holds two copies. One is the current value.
* The other is the last report. Once a current counter's increment is larger
* than the threshold, FW will indicate that counter to host even if the
* monitoring timer does not expire.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG: Peer STA power state change
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID: TID of MSDU
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU: Count of MSDU with the same
* failure code.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS: TX failure code
* 1: TX packet discarded
* 2: No ACK
* 3: Postpone
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS: peer MAC address
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE: Peer STA current state
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL: Global threshold.
* Threshold for all monitored parameters. If per counter dedicated threshold
* is not enabled, this threshold will take effect.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE: Indicate what triggers this
* event, PERORID_TIMEOUT == 1, THRESH_EXCEED == 0.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID: interface ID
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID: peer ID
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP: bitmap for TX counters
* Bit0: TX counter unit in MSDU
* Bit1: TX counter unit in MPDU
* Bit2: TX counter unit in PPDU
* Bit3: TX counter unit in byte
* Bit4: Dropped MSDUs
* Bit5: Dropped Bytes
* Bit6: MPDU retry counter
* Bit7: MPDU failure counter
* Bit8: PPDU failure counter
* Bit9: MPDU aggregation counter
* Bit10: MCS counter for ACKed MPDUs
* Bit11: MCS counter for Failed MPDUs
* Bit12: TX Delay counter
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP: bitmap for RX counters
* Bit0: MAC RX counter unit in MPDU
* Bit1: MAC RX counter unit in byte
* Bit2: PHY RX counter unit in PPDU
* Bit3: PHY RX counter unit in byte
* Bit4: Disorder counter
* Bit5: Retry counter
* Bit6: Duplication counter
* Bit7: Discard counter
* Bit8: MPDU aggregation size counter
* Bit9: MCS counter
* Bit10: Peer STA power state change (wake to sleep) counter
* Bit11: Peer STA power save counter, total time in PS mode
* Bit12: Probe request counter
* Bit13: Other management frames counter
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP: bitmap for CCA
* Bit0: Idle time
* Bit1: TX time
* Bit2: time RX in current bss
* Bit3: Out of current bss time
* Bit4: Wireless medium busy time
* Bit5: RX in bad condition time
* Bit6: TX in bad condition time
* Bit7: time wlan card not available
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP: bitmap for signal
* Bit0: Per channel SNR counter
* Bit1: Per channel noise floor counter
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM: number of peers
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM: number of channels
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_AC_RX_NUM: number of RX stats
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS: per channel BSS CCA stats
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER: container for per PEER stats
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU: Number of total TX MSDUs
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU: Number of total TX MPDUs
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU: Number of total TX PPDUs
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES: bytes of TX data
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP: Number of dropped TX packets
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES: Bytes dropped
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY: waiting time without an ACK
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK: number of MPDU not-ACKed
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK: number of PPDU not-ACKed
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM:
* aggregation stats buffer length
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM: length of mcs stats
* buffer for ACKed MPDUs.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM: length of mcs stats
* buffer for failed MPDUs.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE:
* length of delay stats array.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR: TX aggregation stats
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS: MCS stats for ACKed MPDUs
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS: MCS stats for failed MPDUs
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY: tx delay stats
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU: MPDUs received
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES: bytes received
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU: PPDU received
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES: PPDU bytes received
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST: packets lost
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY: number of RX packets
* flagged as retransmissions
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP: number of RX packets
* flagged as duplicated
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD: number of RX
* packets discarded
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM: length of RX aggregation
* stats buffer.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM: length of RX mcs
* stats buffer.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS: RX mcs stats buffer
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR: aggregation stats buffer
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES: times STAs go to sleep
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION: STAs' total sleep time
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ: number of probe
* requests received
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT: number of other mgmt
* frames received
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME: Percentage of idle time
* there is no TX, nor RX, nor interference.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME: percentage of time
* transmitting packets.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME: percentage of time
* for receiving.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY: percentage of time
* interference detected.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD: percentage of time
* receiving packets with errors.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD: percentage of time
* TX no-ACK.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL: percentage of time
* the chip is unable to work in normal conditions.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME: percentage of time
* receiving packets in current BSS.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME: percentage of time
* receiving packets not in current BSS.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM: number of antennas
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL:
* This is a container for per antenna signal stats.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR: per antenna SNR value
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF: per antenna NF value
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON: RSSI of beacon
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON: SNR of beacon
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME: u64
* Absolute timestamp from 1970/1/1, unit in ms. After receiving the
* message, user layer APP could call gettimeofday to get another
* timestamp and calculate transfer delay for the message.
* @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME: u32
* Real period for this measurement, unit in us.
*/
enum qca_wlan_vendor_attr_ll_stats_ext {
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0,
/* Attributes for configurations */
QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD,
/* Peer STA power state change */
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG,
/* TX failure event */
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS,
/* MAC counters */
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER,
/* Sub-attributes for PEER_AC_TX */
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY,
/* Sub-attributes for PEER_AC_RX */
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT,
/* Sub-attributes for CCA_BSS */
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL,
/* sub-attribute for BSS_RX_TIME */
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME,
/* Sub-attributes for PEER_SIGNAL */
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF,
/* Sub-attributes for IFACE_BSS */
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX =
QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1
};
/* Attributes for FTM commands and events */
/**
* enum qca_wlan_vendor_attr_loc_capa - Indoor location capabilities
*
* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS: Various flags. See
* enum qca_wlan_vendor_attr_loc_capa_flags.
* @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS: Maximum number
* of measurement sessions that can run concurrently.
* Default is one session (no session concurrency).
* @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS: The total number of unique
* peers that are supported in running sessions. For example,
* if the value is 8 and maximum number of sessions is 2, you can
* have one session with 8 unique peers, or 2 sessions with 4 unique
* peers each, and so on.
* @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP: Maximum number
* of bursts per peer, as an exponent (2^value). Default is 0,
* meaning no multi-burst support.
* @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST: Maximum number
* of measurement exchanges allowed in a single burst.
* @QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES: Supported AOA measurement
* types. A bit mask (unsigned 32 bit value), each bit corresponds
* to an AOA type as defined by enum qca_vendor_attr_aoa_type.
*/
enum qca_wlan_vendor_attr_loc_capa {
QCA_WLAN_VENDOR_ATTR_LOC_CAPA_INVALID,
QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS,
QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS,
QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS,
QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP,
QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST,
QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES,
/* keep last */
QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LOC_CAPA_MAX =
QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_loc_capa_flags: Indoor location capability flags
*
* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER: Set if driver
* can be configured as an FTM responder (for example, an AP that
* services FTM requests). QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER
* will be supported if set.
* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver
* can run FTM sessions. QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION
* will be supported if set.
* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder
* supports immediate (ASAP) response.
* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone
* AOA measurement using QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS.
* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM: Set if driver supports
* requesting AOA measurements as part of an FTM session.
*/
enum qca_wlan_vendor_attr_loc_capa_flags {
QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER = 1 << 0,
QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR = 1 << 1,
QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP = 1 << 2,
QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA = 1 << 3,
QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM = 1 << 4,
};
/**
* enum qca_wlan_vendor_attr_ftm_peer_info: Information about
* a single peer in a measurement session.
*
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR: The MAC address of the peer.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS: Various flags related
* to measurement. See enum qca_wlan_vendor_attr_ftm_peer_meas_flags.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS: Nested attribute of
* FTM measurement parameters, as specified by IEEE P802.11-REVmc/D7.0
* 9.4.2.167. See enum qca_wlan_vendor_attr_ftm_meas_param for
* list of supported attributes.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID: Initial token ID for
* secure measurement.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA
* measurement every <value> bursts. If 0 or not specified,
* AOA measurements will be disabled for this peer.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ: Frequency in MHz where
* the measurement frames are exchanged. Optional; if not
* specified, try to locate the peer in the kernel scan
* results cache and use frequency from there.
*/
enum qca_wlan_vendor_attr_ftm_peer_info {
QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ,
/* keep last */
QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX =
QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_ftm_peer_meas_flags: Measurement request flags,
* per-peer
*
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP: If set, request
* immediate (ASAP) response from peer.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI: If set, request
* LCI report from peer. The LCI report includes the absolute
* location of the peer in "official" coordinates (similar to GPS).
* See IEEE P802.11-REVmc/D7.0, 11.24.6.7 for more information.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR: If set, request
* Location civic report from peer. The LCR includes the location
* of the peer in free-form format. See IEEE P802.11-REVmc/D7.0,
* 11.24.6.7 for more information.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE: If set,
* request a secure measurement.
* QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID must also be provided.
*/
enum qca_wlan_vendor_attr_ftm_peer_meas_flags {
QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP = 1 << 0,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI = 1 << 1,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR = 1 << 2,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE = 1 << 3,
};
/**
* enum qca_wlan_vendor_attr_ftm_meas_param: Measurement parameters
*
* @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST: Number of measurements
* to perform in a single burst.
* @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP: Number of bursts to
* perform, specified as an exponent (2^value).
* @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION: Duration of burst
* instance, as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167.
* @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD: Time between bursts,
* as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167. Must
* be larger than QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION.
*/
enum qca_wlan_vendor_attr_ftm_meas_param {
QCA_WLAN_VENDOR_ATTR_FTM_PARAM_INVALID,
QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST,
QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP,
QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION,
QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD,
/* keep last */
QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MAX =
QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_ftm_peer_result: Per-peer results
*
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR: MAC address of the reported
* peer.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS: Status of measurement
* request for this peer.
* See enum qca_wlan_vendor_attr_ftm_peer_result_status.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS: Various flags related
* to measurement results for this peer.
* See enum qca_wlan_vendor_attr_ftm_peer_result_flags.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS: Specified when
* request failed and peer requested not to send an additional request
* for this number of seconds.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI: LCI report when received
* from peer. In the format specified by IEEE P802.11-REVmc/D7.0,
* 9.4.2.22.10.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR: Location civic report when
* received from peer. In the format specified by IEEE P802.11-REVmc/D7.0,
* 9.4.2.22.13.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS: Reported when peer
* overridden some measurement request parameters. See
* enum qca_wlan_vendor_attr_ftm_meas_param.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS: AOA measurement
* for this peer. Same contents as @QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS: Array of measurement
* results. Each entry is a nested attribute defined
* by enum qca_wlan_vendor_attr_ftm_meas.
*/
enum qca_wlan_vendor_attr_ftm_peer_result {
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_INVALID,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS,
/* keep last */
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAX =
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_ftm_peer_result_status
*
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK: Request sent ok and results
* will be provided. Peer may have overridden some measurement parameters,
* in which case overridden parameters will be report by
* QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAM attribute.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE: Peer is incapable
* of performing the measurement request. No more results will be sent
* for this peer in this session.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED: Peer reported request
* failed, and requested not to send an additional request for number
* of seconds specified by QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS
* attribute.
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID: Request validation
* failed. Request was not sent over the air.
*/
enum qca_wlan_vendor_attr_ftm_peer_result_status {
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED,
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID,
};
/**
* enum qca_wlan_vendor_attr_ftm_peer_result_flags: Various flags
* for measurement result, per-peer
*
* @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE: If set,
* measurement completed for this peer. No more results will be reported
* for this peer in this session.
*/
enum qca_wlan_vendor_attr_ftm_peer_result_flags {
QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE = 1 << 0,
};
/**
* enum qca_vendor_attr_loc_session_status: Session completion status code
*
* @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK: Session completed
* successfully.
* @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED: Session aborted
* by request.
* @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID: Session request
* was invalid and was not started.
* @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED: Session had an error
* and did not complete normally (for example out of resources).
*/
enum qca_vendor_attr_loc_session_status {
QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK,
QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED,
QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID,
QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED,
};
/**
* enum qca_wlan_vendor_attr_ftm_meas: Single measurement data
*
* @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1: Time of departure (TOD) of FTM packet as
* recorded by responder, in picoseconds.
* See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
* @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2: Time of arrival (TOA) of FTM packet at
* initiator, in picoseconds.
* See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
* @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3: TOD of ACK packet as recorded by
* initiator, in picoseconds.
* See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
* @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4: TOA of ACK packet at
* responder, in picoseconds.
* See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
* @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI: RSSI (signal level) as recorded
* during this measurement exchange. Optional and will be provided if
* the hardware can measure it.
* @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR: TOD error reported by
* responder. Not always provided.
* See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
* @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR: TOA error reported by
* responder. Not always provided.
* See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
* @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR: TOD error measured by
* initiator. Not always provided.
* See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
* @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR: TOA error measured by
* initiator. Not always provided.
* See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
* @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD: Dummy attribute for padding.
*/
enum qca_wlan_vendor_attr_ftm_meas {
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INVALID,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD,
/* keep last */
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_MAX =
QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_aoa_type - AOA measurement type
*
* @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: Phase of the strongest
* CIR (channel impulse response) path for each antenna.
* @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: Phase and amplitude
* of the strongest CIR path for each antenna.
*/
enum qca_wlan_vendor_attr_aoa_type {
QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE,
QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP,
QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX
};
/**
* enum qca_wlan_vendor_attr_encryption_test - Attributes to
* validate encryption engine
*
* @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION: Flag attribute.
* This will be included if the request is for decryption; if not included,
* the request is treated as a request for encryption by default.
* @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER: Unsigned 32-bit value
* indicating the key cipher suite. Takes same values as
* NL80211_ATTR_KEY_CIPHER.
* @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID: Unsigned 8-bit value
* Key Id to be used for encryption
* @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK: Array of 8-bit values.
* Key (TK) to be used for encryption/decryption
* @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN: Array of 8-bit values.
* Packet number to be specified for encryption/decryption
* 6 bytes for TKIP/CCMP/GCMP.
* @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA: Array of 8-bit values
* representing the 802.11 packet (header + payload + FCS) that
* needs to be encrypted/decrypted.
* Encrypted/decrypted response from the driver will also be sent
* to userspace with the same attribute.
*/
enum qca_wlan_vendor_attr_encryption_test {
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION,
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER,
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID,
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK,
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN,
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX =
QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_attr_dmg_rf_sector_type - Type of
* sector for DMG RF sector operations.
*
* @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX: RX sector
* @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX: TX sector
*/
enum qca_wlan_vendor_attr_dmg_rf_sector_type {
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX,
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX,
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_MAX
};
/**
* enum qca_wlan_vendor_attr_fw_state - State of firmware
*
* @QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR: FW is in bad state
* @QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE: FW is active
*/
enum qca_wlan_vendor_attr_fw_state {
QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR,
QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE,
QCA_WLAN_VENDOR_ATTR_FW_STATE_MAX
};
/**
* BRP antenna limit mode
*
* @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force
* antenna limit, BRP will be performed as usual.
* @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE: Define maximal
* antennas limit. the hardware may use less antennas than the
* maximum limit.
* @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE: The hardware will
* use exactly the specified number of antennas for BRP.
*/
enum qca_wlan_vendor_attr_brp_ant_limit_mode {
QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE,
QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE,
QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE,
QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_MAX
};
/**
* enum qca_wlan_vendor_attr_dmg_rf_sector_cfg - Attributes for
* DMG RF sector configuration for a single RF module.
* The values are defined in a compact way which closely matches
* the way it is stored in HW registers.
* The configuration provides values for 32 antennas and 8 distribution
* amplifiers, and together describes the characteristics of the RF
* sector - such as a beam in some direction with some gain.
*
* @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX: Index
* of RF module for this configuration.
* @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0: Bit 0 of edge
* amplifier gain index. Unsigned 32 bit number containing
* bits for all 32 antennas.
* @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1: Bit 1 of edge
* amplifier gain index. Unsigned 32 bit number containing
* bits for all 32 antennas.
* @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2: Bit 2 of edge
* amplifier gain index. Unsigned 32 bit number containing
* bits for all 32 antennas.
* @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI: Phase values
* for first 16 antennas, 2 bits per antenna.
* @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO: Phase values
* for last 16 antennas, 2 bits per antenna.
* @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16: Contains
* DTYPE values (3 bits) for each distribution amplifier, followed
* by X16 switch bits for each distribution amplifier. There are
* total of 8 distribution amplifiers.
*/
enum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX = 1,
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0 = 2,
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1 = 3,
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2 = 4,
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI = 5,
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO = 6,
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16 = 7,
/* keep last */
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MAX =
QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
};
enum qca_wlan_vendor_attr_ll_stats_set {
QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_INVALID = 0,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD = 1,
QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX =
QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST - 1,
};
enum qca_wlan_vendor_attr_ll_stats_clr {
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_INVALID = 0,
/* Unsigned 32bit bitmap for clearing statistics
* All radio statistics 0x00000001
* cca_busy_time (within radio statistics) 0x00000002
* All channel stats (within radio statistics) 0x00000004
* All scan statistics (within radio statistics) 0x00000008
* All interface statistics 0x00000010
* All tx rate statistics (within interface statistics) 0x00000020
* All ac statistics (with in interface statistics) 0x00000040
* All contention (min, max, avg) statistics (within ac statisctics)
* 0x00000080.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK = 1,
/* Unsigned 8 bit value: Request to stop statistics collection */
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ = 2,
/* Unsigned 32 bit bitmap: Response from the driver
* for the cleared statistics
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK = 3,
/* Unsigned 8 bit value: Response from driver/firmware
* for the stop request
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX =
QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST - 1,
};
enum qca_wlan_vendor_attr_ll_stats_get {
QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_INVALID = 0,
/* Unsigned 32 bit value provided by the caller issuing the GET stats
* command. When reporting the stats results, the driver uses the same
* value to indicate which GET request the results correspond to.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID = 1,
/* Unsigned 32 bit value - bit mask to identify what statistics are
* requested for retrieval.
* Radio Statistics 0x00000001
* Interface Statistics 0x00000020
* All Peer Statistics 0x00000040
* Peer Statistics 0x00000080
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX =
QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST - 1,
};
enum qca_wlan_vendor_attr_ll_stats_results {
QCA_WLAN_VENDOR_ATTR_LL_STATS_INVALID = 0,
/* Unsigned 32bit value. Used by the driver; must match the request id
* provided with the QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET command.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_REQ_ID = 1,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX = 2,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX = 3,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX = 4,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX = 5,
/* Signed 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT = 6,
/* Signed 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA = 7,
/* Signed 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK = 8,
/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_* are
* nested within the interface stats.
*/
/* Interface mode, e.g., STA, SOFTAP, IBSS, etc.
* Type = enum wifi_interface_mode.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE = 9,
/* Interface MAC address. An array of 6 Unsigned int8 */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR = 10,
/* Type = enum wifi_connection_state, e.g., DISCONNECTED,
* AUTHENTICATING, etc. valid for STA, CLI only.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE = 11,
/* Type = enum wifi_roam_state. Roaming state, e.g., IDLE or ACTIVE
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING = 12,
/* Unsigned 32 bit value. WIFI_CAPABILITY_XXX */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES = 13,
/* NULL terminated SSID. An array of 33 Unsigned 8bit values */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID = 14,
/* BSSID. An array of 6 unsigned 8 bit values */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID = 15,
/* Country string advertised by AP. An array of 3 unsigned 8 bit
* values.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR = 16,
/* Country string for this association. An array of 3 unsigned 8 bit
* values.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR = 17,
/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* could
* be nested within the interface stats.
*/
/* Type = enum wifi_traffic_ac, e.g., V0, VI, BE and BK */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC = 18,
/* Unsigned int 32 value corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU = 19,
/* Unsigned int 32 value corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU = 20,
/* Unsigned int 32 value corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST = 21,
/* Unsigned int 32 value corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST = 22,
/* Unsigned int 32 value corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU = 23,
/* Unsigned int 32 value corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU = 24,
/* Unsigned int 32 value corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST = 25,
/* Unsigned int 32 value corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES = 26,
/* Unsigned int 32 value corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT = 27,
/* Unsigned int 32 values corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG = 28,
/* Unsigned int 32 values corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN = 29,
/* Unsigned int 32 values corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX = 30,
/* Unsigned int 32 values corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG = 31,
/* Unsigned int 32 values corresponding to respective AC */
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES = 32,
/* Unsigned 32 bit value. Number of peers */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS = 33,
/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* are
* nested within the interface stats.
*/
/* Type = enum wifi_peer_type. Peer type, e.g., STA, AP, P2P GO etc. */
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE = 34,
/* MAC addr corresponding to respective peer. An array of 6 unsigned
* 8 bit values.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS = 35,
/* Unsigned int 32 bit value representing capabilities corresponding
* to respective peer.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES = 36,
/* Unsigned 32 bit value. Number of rates */
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES = 37,
/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_*
* are nested within the rate stat.
*/
/* Wi-Fi Rate - separate attributes defined for individual fields */
/* Unsigned int 8 bit value; 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE = 38,
/* Unsigned int 8 bit value; 0:1x1, 1:2x2, 3:3x3, 4:4x4 */
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS = 39,
/* Unsigned int 8 bit value; 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz */
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW = 40,
/* Unsigned int 8 bit value; OFDM/CCK rate code would be as per IEEE Std
* in the units of 0.5 Mbps HT/VHT it would be MCS index
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX = 41,
/* Unsigned 32 bit value. Bit rate in units of 100 kbps */
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE = 42,
/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_STAT_* could be
* nested within the peer info stats.
*/
/* Unsigned int 32 bit value. Number of successfully transmitted data
* packets, i.e., with ACK received corresponding to the respective
* rate.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU = 43,
/* Unsigned int 32 bit value. Number of received data packets
* corresponding to the respective rate.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU = 44,
/* Unsigned int 32 bit value. Number of data packet losses, i.e., no ACK
* received corresponding to the respective rate.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST = 45,
/* Unsigned int 32 bit value. Total number of data packet retries for
* the respective rate.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES = 46,
/* Unsigned int 32 bit value. Total number of short data packet retries
* for the respective rate.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT = 47,
/* Unsigned int 32 bit value. Total number of long data packet retries
* for the respective rate.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG = 48,
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID = 49,
/* Unsigned 32 bit value. Total number of msecs the radio is awake
* accruing over time.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME = 50,
/* Unsigned 32 bit value. Total number of msecs the radio is
* transmitting accruing over time.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME = 51,
/* Unsigned 32 bit value. Total number of msecs the radio is in active
* receive accruing over time.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME = 52,
/* Unsigned 32 bit value. Total number of msecs the radio is awake due
* to all scan accruing over time.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN = 53,
/* Unsigned 32 bit value. Total number of msecs the radio is awake due
* to NAN accruing over time.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD = 54,
/* Unsigned 32 bit value. Total number of msecs the radio is awake due
* to GSCAN accruing over time.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN = 55,
/* Unsigned 32 bit value. Total number of msecs the radio is awake due
* to roam scan accruing over time.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN = 56,
/* Unsigned 32 bit value. Total number of msecs the radio is awake due
* to PNO scan accruing over time.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN = 57,
/* Unsigned 32 bit value. Total number of msecs the radio is awake due
* to Hotspot 2.0 scans and GAS exchange accruing over time.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20 = 58,
/* Unsigned 32 bit value. Number of channels. */
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS = 59,
/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_* could
* be nested within the channel stats.
*/
/* Type = enum wifi_channel_width. Channel width, e.g., 20, 40, 80 */
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH = 60,
/* Unsigned 32 bit value. Primary 20 MHz channel. */
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ = 61,
/* Unsigned 32 bit value. Center frequency (MHz) first segment. */
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0 = 62,
/* Unsigned 32 bit value. Center frequency (MHz) second segment. */
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1 = 63,
/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_* could be
* nested within the radio stats.
*/
/* Unsigned int 32 bit value representing total number of msecs the
* radio is awake on that channel accruing over time, corresponding to
* the respective channel.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME = 64,
/* Unsigned int 32 bit value representing total number of msecs the CCA
* register is busy accruing over time corresponding to the respective
* channel.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME = 65,
QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66,
/* Signifies the nested list of channel attributes
* QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_*
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67,
/* Signifies the nested list of peer info attributes
* QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_*
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO = 68,
/* Signifies the nested list of rate info attributes
* QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_*
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO = 69,
/* Signifies the nested list of wmm info attributes
* QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_*
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO = 70,
/* Unsigned 8 bit value. Used by the driver; if set to 1, it indicates
* that more stats, e.g., peers or radio, are to follow in the next
* QCA_NL80211_VENDOR_SUBCMD_LL_STATS_*_RESULTS event.
* Otherwise, it is set to 0.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA = 71,
/* Unsigned 64 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET = 72,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED = 73,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED = 74,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME = 75,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE = 76,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS = 77,
/* Number of msecs the radio spent in transmitting for each power level
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL = 78,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT = 79,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT = 80,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT = 81,
/* Unsigned 32 bit value */
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT = 82,
/* Unsigned int 32 value.
* Pending MSDUs corresponding to respective AC.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU = 83,
/* u32 value representing total time in milliseconds for which the radio
* is transmitting on this channel. This attribute will be nested
* within QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_TX_TIME = 84,
/* u32 value representing total time in milliseconds for which the radio
* is receiving all 802.11 frames intended for this device on this
* channel. This attribute will be nested within
* QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_RX_TIME = 85,
/* u8 value representing the channel load percentage. Possible values
* are 0-100.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_LOAD_PERCENTAGE = 86,
+ /* u8 value representing the time slicing duty cycle percentage.
+ * Possible values are 0-100.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_TS_DUTY_CYCLE = 87,
/* keep last */
QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST - 1,
};
enum qca_wlan_vendor_attr_ll_stats_type {
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_INVALID = 0,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_RADIO = 1,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_IFACE = 2,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_PEERS = 3,
/* keep last */
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST,
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_MAX =
QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_tdls_configuration - Attributes for
* TDLS configuration to the host driver.
*
* @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE: Configure the TDLS trigger
* mode in the host driver. enum qca_wlan_vendor_tdls_trigger_mode
* represents the different TDLS trigger modes.
* @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD: Duration (u32) within
* which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD number
* of packets shall meet the criteria for implicit TDLS setup.
* @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD: Number (u32) of Tx/Rx packets
* within a duration QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD
* to initiate a TDLS setup.
* @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD: Time (u32) to initiate
* a TDLS Discovery to the peer.
* @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT: Max number (u32) of
* discovery attempts to know the TDLS capability of the peer. A peer is
* marked as TDLS not capable if there is no response for all the attempts.
* @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT: Represents a duration (u32)
* within which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD
* number of TX / RX frames meet the criteria for TDLS teardown.
* @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD: Minimum number (u32)
* of Tx/Rx packets within a duration
* QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT to tear down a TDLS link.
* @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD: Threshold
* corresponding to the RSSI of the peer below which a TDLS setup is
* triggered.
* @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD: Threshold
* corresponding to the RSSI of the peer above which a TDLS teardown is
* triggered.
*/
enum qca_wlan_vendor_attr_tdls_configuration {
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE = 1,
/* Attributes configuring the TDLS Implicit Trigger */
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD = 2,
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD = 3,
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD = 4,
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT = 5,
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT = 6,
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD = 7,
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD = 8,
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD = 9,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX =
QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_tdls_trigger_mode: Represents the TDLS trigger mode in
* the driver
*
* The following are the different values for
* QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE.
*
* @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: The trigger to initiate/teardown
* the TDLS connection to a respective peer comes from the user space.
* wpa_supplicant provides the commands TDLS_SETUP, TDLS_TEARDOWN,
* TDLS_DISCOVER to do this.
* @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: Host driver triggers this TDLS
* setup/teardown to the eligible peer once the configured criteria
* (such as TX/RX threshold, RSSI) is met. The attributes
* in QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IMPLICIT_PARAMS correspond to
* the different configuration criteria for the TDLS trigger from the
* host driver.
* @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: Enables the driver to trigger
* the TDLS setup / teardown through the implicit mode only to the
* configured MAC addresses (wpa_supplicant, with tdls_external_control=1,
* configures the MAC address through TDLS_SETUP / TDLS_TEARDOWN commands).
* External mode works on top of the implicit mode. Thus the host driver
* is expected to configure in TDLS Implicit mode too to operate in
* External mode.
* Configuring External mode alone without Implicit mode is invalid.
*
* All the above implementations work as expected only when the host driver
* advertises the capability WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP - representing
* that the TDLS message exchange is not internal to the host driver, but
* depends on wpa_supplicant to do the message exchange.
*/
enum qca_wlan_vendor_tdls_trigger_mode {
QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = 1 << 0,
QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = 1 << 1,
QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = 1 << 2,
};
/**
* enum qca_vendor_attr_sar_limits_selections - Source of SAR power limits
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0: Select SAR profile #0
* that is hard-coded in the Board Data File (BDF).
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1: Select SAR profile #1
* that is hard-coded in the Board Data File (BDF).
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2: Select SAR profile #2
* that is hard-coded in the Board Data File (BDF).
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3: Select SAR profile #3
* that is hard-coded in the Board Data File (BDF).
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4: Select SAR profile #4
* that is hard-coded in the Board Data File (BDF).
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE: Do not select any
* source of SAR power limits, thereby disabling the SAR power
* limit feature.
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER: Select the SAR power
* limits configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0: Select the SAR power
* limits version 2.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
*
* This enumerates the valid set of values that may be supplied for
* attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT in an instance of
* the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command or in
* the response to an instance of the
* %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command.
*/
enum qca_vendor_attr_sar_limits_selections {
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 = 0,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1 = 1,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2 = 2,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3 = 3,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4 = 4,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE = 5,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER = 6,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 = 7,
};
/**
* enum qca_vendor_attr_sar_limits_spec_modulations -
* SAR limits specification modulation
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK -
* CCK modulation
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM -
* OFDM modulation
*
* This enumerates the valid set of values that may be supplied for
* attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION in an
* instance of attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC in an
* instance of the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor
* command or in the response to an instance of the
* %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command.
*/
enum qca_vendor_attr_sar_limits_spec_modulations {
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK = 0,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM = 1,
};
/**
* enum qca_vendor_attr_sar_limits - Attributes for SAR power limits
*
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE: Optional (u32) value to
* select which SAR power limit table should be used. Valid
* values are enumerated in enum
* %qca_vendor_attr_sar_limits_selections. The existing SAR
* power limit selection is unchanged if this attribute is not
* present.
*
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS: Optional (u32) value
* which specifies the number of SAR power limit specifications
* which will follow.
*
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC: Nested array of SAR power
* limit specifications. The number of specifications is
* specified by @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS. Each
* specification contains a set of
* QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_* attributes. A
* specification is uniquely identified by the attributes
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND,
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN, and
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION and always
* contains as a payload the attribute
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT,
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX.
* Either %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT or
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX is
* needed based upon the value of
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE.
*
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND: Optional (u32) value to
* indicate for which band this specification applies. Valid
* values are enumerated in enum %nl80211_band (although not all
* bands may be supported by a given device). If the attribute is
* not supplied then the specification will be applied to all
* supported bands.
*
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN: Optional (u32) value
* to indicate for which antenna chain this specification
* applies, i.e. 1 for chain 1, 2 for chain 2, etc. If the
* attribute is not supplied then the specification will be
* applied to all chains.
*
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION: Optional (u32)
* value to indicate for which modulation scheme this
* specification applies. Valid values are enumerated in enum
* %qca_vendor_attr_sar_limits_spec_modulations. If the attribute
* is not supplied then the specification will be applied to all
* modulation schemes.
*
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT: Required (u32)
* value to specify the actual power limit value in units of 0.5
* dBm (i.e., a value of 11 represents 5.5 dBm).
* This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER.
*
* @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX: Required (u32)
* value to indicate SAR V2 indices (0 - 11) to select SAR V2 profiles.
* This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is
* %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0.
*
* These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS
* and %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS.
*/
enum qca_vendor_attr_sar_limits {
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE = 1,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS = 2,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC = 3,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND = 4,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN = 5,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION = 6,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT = 7,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX = 8,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX =
QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_attr_get_wifi_info: Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO sub command.
*
* @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION: In a request this attribute
* should be set to any U8 value to indicate that the driver version
* should be returned. When enabled in this manner, in a response this
* attribute will contain a string representation of the driver version.
*
* @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION: In a request this attribute
* should be set to any U8 value to indicate that the firmware version
* should be returned. When enabled in this manner, in a response this
* attribute will contain a string representation of the firmware version.
*
* @QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX: In a request this attribute
* should be set to any U32 value to indicate that the current radio
* index should be returned. When enabled in this manner, in a response
* this attribute will contain a U32 radio index value.
*
*/
enum qca_wlan_vendor_attr_get_wifi_info {
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION = 1,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION = 2,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_RADIO_INDEX = 3,
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX =
QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST - 1,
};
/*
* enum qca_wlan_vendor_attr_wifi_logger_start: Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START sub command.
*/
enum qca_wlan_vendor_attr_wifi_logger_start {
QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID = 1,
QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL = 2,
QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS = 3,
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_GET_MAX =
QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST - 1,
};
enum qca_wlan_vendor_attr_logger_results {
QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_INVALID = 0,
/* Unsigned 32-bit value; must match the request Id supplied by
* Wi-Fi HAL in the corresponding subcmd NL msg.
*/
QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_REQUEST_ID = 1,
/* Unsigned 32-bit value; used to indicate the size of memory
* dump to be allocated.
*/
QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MEMDUMP_SIZE = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MAX =
QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST - 1,
};
/**
* enum qca_scan_freq_list_type: Frequency list types
*
* @QCA_PREFERRED_SCAN_FREQ_LIST: The driver shall use the scan frequency list
* specified with attribute QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST as
* a preferred frequency list for roaming.
*
* @QCA_SPECIFIC_SCAN_FREQ_LIST: The driver shall use the frequency list
* specified with attribute QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST as
* a specific frequency list for roaming.
*/
enum qca_scan_freq_list_type {
QCA_PREFERRED_SCAN_FREQ_LIST = 1,
QCA_SPECIFIC_SCAN_FREQ_LIST = 2,
};
/**
* enum qca_vendor_attr_scan_freq_list_scheme: Frequency list scheme
*
* @QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST: Nested attribute of u32 values
* List of frequencies in MHz to be considered for a roam scan.
*
* @QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_TYPE: Unsigned 32-bit value.
* Type of frequency list scheme being configured/gotten as defined by the
* enum qca_scan_freq_list_type.
*/
enum qca_vendor_attr_scan_freq_list_scheme {
QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST = 1,
QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_TYPE = 2,
/* keep last */
QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_AFTER_LAST,
QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_MAX =
QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST_SCHEME_AFTER_LAST - 1,
};
/**
* enum qca_roam_scan_scheme: Scan scheme
*
* @QCA_ROAM_SCAN_SCHEME_NO_SCAN: No frequencies specified to scan.
* Indicates the driver to not scan on a Roam Trigger scenario, but
* disconnect. E.g., on a BTM request from the AP the driver/firmware shall
* disconnect from the current connected AP by notifying a failure
* code in the BTM response.
*
* @QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN: Indicates the driver/firmware to
* trigger partial frequency scans. These frequencies are the ones learned
* or maintained by the driver based on the probability of finding the
* BSSIDs in the ESS for which the roaming is triggered.
*
* @QCA_ROAM_SCAN_SCHEME_FULL_SCAN: Indicates the driver/firmware to
* trigger the scan on all the valid frequencies to find better
* candidates to roam.
*/
enum qca_roam_scan_scheme {
QCA_ROAM_SCAN_SCHEME_NO_SCAN = 0,
QCA_ROAM_SCAN_SCHEME_PARTIAL_SCAN = 1,
QCA_ROAM_SCAN_SCHEME_FULL_SCAN = 2,
};
/*
* enum qca_vendor_roam_triggers: Bitmap of roaming triggers
*
* @QCA_ROAM_TRIGGER_REASON_PER: Set if the roam has to be triggered based on
* a bad packet error rates (PER).
* @QCA_ROAM_TRIGGER_REASON_BEACON_MISS: Set if the roam has to be triggered
* based on beacon misses from the connected AP.
* @QCA_ROAM_TRIGGER_REASON_POOR_RSSI: Set if the roam has to be triggered
* due to poor RSSI of the connected AP.
* @QCA_ROAM_TRIGGER_REASON_BETTER_RSSI: Set if the roam has to be triggered
* upon finding a BSSID with a better RSSI than the connected BSSID.
* Here the RSSI of the current BSSID need not be poor.
* @QCA_ROAM_TRIGGER_REASON_PERIODIC: Set if the roam has to be triggered
* by triggering a periodic scan to find a better AP to roam.
* @QCA_ROAM_TRIGGER_REASON_DENSE: Set if the roam has to be triggered
* when the connected channel environment is too noisy/congested.
* @QCA_ROAM_TRIGGER_REASON_BTM: Set if the roam has to be triggered
* when BTM Request frame is received from the connected AP.
* @QCA_ROAM_TRIGGER_REASON_BSS_LOAD: Set if the roam has to be triggered
* when the channel utilization is goes above the configured threshold.
* @QCA_ROAM_TRIGGER_REASON_USER_TRIGGER: Set if the roam has to be triggered
* based on the request from the user (space).
* @QCA_ROAM_TRIGGER_REASON_DEAUTH: Set if the roam has to be triggered when
* device receives Deauthentication/Disassociation frame from connected AP.
* @QCA_ROAM_TRIGGER_REASON_IDLE: Set if the roam has to be triggered when the
* device is in idle state (no TX/RX) and suspend mode, if the current RSSI
* is determined to be a poor one.
* @QCA_ROAM_TRIGGER_REASON_TX_FAILURES: Set if the roam has to be triggered
* based on continuous TX Data frame failures to the connected AP.
* @QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN: Set if the roam has to be triggered
* based on the scan results obtained from an external scan (not triggered
* to aim roaming).
*
* Set the corresponding roam trigger reason bit to consider it for roam
* trigger.
* Userspace can set multiple bits and send to the driver. The driver shall
* consider all of them to trigger/initiate a roam scan.
*/
enum qca_vendor_roam_triggers {
QCA_ROAM_TRIGGER_REASON_PER = 1 << 0,
QCA_ROAM_TRIGGER_REASON_BEACON_MISS = 1 << 1,
QCA_ROAM_TRIGGER_REASON_POOR_RSSI = 1 << 2,
QCA_ROAM_TRIGGER_REASON_BETTER_RSSI = 1 << 3,
QCA_ROAM_TRIGGER_REASON_PERIODIC = 1 << 4,
QCA_ROAM_TRIGGER_REASON_DENSE = 1 << 5,
QCA_ROAM_TRIGGER_REASON_BTM = 1 << 6,
QCA_ROAM_TRIGGER_REASON_BSS_LOAD = 1 << 7,
QCA_ROAM_TRIGGER_REASON_USER_TRIGGER = 1 << 8,
QCA_ROAM_TRIGGER_REASON_DEAUTH = 1 << 9,
QCA_ROAM_TRIGGER_REASON_IDLE = 1 << 10,
QCA_ROAM_TRIGGER_REASON_TX_FAILURES = 1 << 11,
QCA_ROAM_TRIGGER_REASON_EXTERNAL_SCAN = 1 << 12,
};
/*
* enum qca_vendor_roam_fail_reasons: Defines the various roam
* fail reasons. This enum value is used in
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON attribute.
*
* @QCA_ROAM_FAIL_REASON_SCAN_NOT_ALLOWED: Roam module in the firmware is not
* able to trigger the scan.
* @QCA_ROAM_FAIL_REASON_NO_AP_FOUND: No roamable APs found during roam scan.
* @QCA_ROAM_FAIL_REASON_NO_CAND_AP_FOUND: No candidate APs found during roam
* scan.
* @QCA_ROAM_FAIL_REASON_HOST: Roam fail due to disconnect issued from host.
* @QCA_ROAM_FAIL_REASON_AUTH_SEND: Unable to send Authentication frame.
* @QCA_ROAM_FAIL_REASON_AUTH_RECV: Received Authentication frame with error
* status code.
* @QCA_ROAM_FAIL_REASON_NO_AUTH_RESP: Authentication frame not received.
* @QCA_ROAM_FAIL_REASON_REASSOC_SEND: Unable to send Reassociation Request
* frame.
* @QCA_ROAM_FAIL_REASON_REASSOC_RECV: Received Reassociation Response frame
* with error status code.
* @QCA_ROAM_FAIL_REASON_NO_REASSOC_RESP: Reassociation Response frame not
* received.
* @QCA_ROAM_FAIL_REASON_SCAN_FAIL: Scan module not able to start scan.
* @QCA_ROAM_FAIL_REASON_AUTH_NO_ACK: No ACK is received for Authentication
* frame.
* @QCA_ROAM_FAIL_REASON_AUTH_INTERNAL_DROP: Authentication frame is dropped
* internally before transmission.
* @QCA_ROAM_FAIL_REASON_REASSOC_NO_ACK: No ACK is received for Reassociation
* Request frame.
* @QCA_ROAM_FAIL_REASON_REASSOC_INTERNAL_DROP: Reassociation Request frame is
* dropped internally.
* @QCA_ROAM_FAIL_REASON_EAPOL_M1_TIMEOUT: EAPOL-Key M1 is not received and
* times out.
* @QCA_ROAM_FAIL_REASON_EAPOL_M2_SEND: Unable to send EAPOL-Key M2 frame.
* @QCA_ROAM_FAIL_REASON_EAPOL_M2_INTERNAL_DROP: EAPOL-Key M2 frame dropped
* internally.
* @QCA_ROAM_FAIL_REASON_EAPOL_M2_NO_ACK: No ACK is received for EAPOL-Key
* M2 frame.
* @QCA_ROAM_FAIL_REASON_EAPOL_M3_TIMEOUT: EAPOL-Key M3 frame is not received.
* @QCA_ROAM_FAIL_REASON_EAPOL_M4_SEND: Unable to send EAPOL-Key M4 frame.
* @QCA_ROAM_FAIL_REASON_EAPOL_M4_INTERNAL_DROP: EAPOL-Key M4 frame dropped
* internally.
* @QCA_ROAM_FAIL_REASON_EAPOL_M4_NO_ACK: No ACK is received for EAPOL-Key M4
* frame.
* @QCA_ROAM_FAIL_REASON_NO_SCAN_FOR_FINAL_BEACON_MISS: Roam scan is not
* started for final beacon miss case.
* @QCA_ROAM_FAIL_REASON_DISCONNECT: Deauthentication or Disassociation frame
* received from the AP during roaming handoff.
* @QCA_ROAM_FAIL_REASON_RESUME_ABORT: Firmware roams to the AP when the Apps
* or host is suspended and gives the indication of the last roamed AP only
* when the Apps is resumed. If the Apps is resumed while the roaming is in
* progress, this ongoing roaming is aborted and the last roamed AP is
* indicated to host.
* @QCA_ROAM_FAIL_REASON_SAE_INVALID_PMKID: WPA3-SAE invalid PMKID.
* @QCA_ROAM_FAIL_REASON_SAE_PREAUTH_TIMEOUT: WPA3-SAE pre-authentication times
* out.
* @QCA_ROAM_FAIL_REASON_SAE_PREAUTH_FAIL: WPA3-SAE pre-authentication fails.
*/
enum qca_vendor_roam_fail_reasons {
QCA_ROAM_FAIL_REASON_NONE = 0,
QCA_ROAM_FAIL_REASON_SCAN_NOT_ALLOWED = 1,
QCA_ROAM_FAIL_REASON_NO_AP_FOUND = 2,
QCA_ROAM_FAIL_REASON_NO_CAND_AP_FOUND = 3,
QCA_ROAM_FAIL_REASON_HOST = 4,
QCA_ROAM_FAIL_REASON_AUTH_SEND = 5,
QCA_ROAM_FAIL_REASON_AUTH_RECV = 6,
QCA_ROAM_FAIL_REASON_NO_AUTH_RESP = 7,
QCA_ROAM_FAIL_REASON_REASSOC_SEND = 8,
QCA_ROAM_FAIL_REASON_REASSOC_RECV = 9,
QCA_ROAM_FAIL_REASON_NO_REASSOC_RESP = 10,
QCA_ROAM_FAIL_REASON_SCAN_FAIL = 11,
QCA_ROAM_FAIL_REASON_AUTH_NO_ACK = 12,
QCA_ROAM_FAIL_REASON_AUTH_INTERNAL_DROP = 13,
QCA_ROAM_FAIL_REASON_REASSOC_NO_ACK = 14,
QCA_ROAM_FAIL_REASON_REASSOC_INTERNAL_DROP = 15,
QCA_ROAM_FAIL_REASON_EAPOL_M1_TIMEOUT = 16,
QCA_ROAM_FAIL_REASON_EAPOL_M2_SEND = 17,
QCA_ROAM_FAIL_REASON_EAPOL_M2_INTERNAL_DROP = 18,
QCA_ROAM_FAIL_REASON_EAPOL_M2_NO_ACK = 19,
QCA_ROAM_FAIL_REASON_EAPOL_M3_TIMEOUT = 20,
QCA_ROAM_FAIL_REASON_EAPOL_M4_SEND = 21,
QCA_ROAM_FAIL_REASON_EAPOL_M4_INTERNAL_DROP = 22,
QCA_ROAM_FAIL_REASON_EAPOL_M4_NO_ACK = 23,
QCA_ROAM_FAIL_REASON_NO_SCAN_FOR_FINAL_BEACON_MISS = 24,
QCA_ROAM_FAIL_REASON_DISCONNECT = 25,
QCA_ROAM_FAIL_REASON_RESUME_ABORT = 26,
QCA_ROAM_FAIL_REASON_SAE_INVALID_PMKID = 27,
QCA_ROAM_FAIL_REASON_SAE_PREAUTH_TIMEOUT = 28,
QCA_ROAM_FAIL_REASON_SAE_PREAUTH_FAIL = 29,
};
/*
* enum qca_vendor_roam_invoke_fail_reasons: Defines the various roam
* invoke fail reasons. This enum value is used in
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON attribute.
*
* @QCA_ROAM_INVOKE_STATUS_IFACE_INVALID: Invalid interface ID is passed
* in roam invoke command.
* @QCA_ROAM_INVOKE_STATUS_OFFLOAD_DISABLE: Roam offload in firmware is not
* enabled.
* @QCA_ROAM_INVOKE_STATUS_AP_SSID_LENGTH_INVALID: Connected AP profile SSID
* length is invalid.
* @QCA_ROAM_INVOKE_STATUS_ROAM_DISALLOW: Firmware internal roaming is already
* in progress.
* @QCA_ROAM_INVOKE_STATUS_NON_ROAMABLE_AP: Host sends the Beacon/Probe Response
* of the AP in the roam invoke command to firmware. This reason is sent by the
* firmware when the given AP is configured to be ignored or SSID/security
* does not match.
* @QCA_ROAM_INVOKE_STATUS_ROAM_INTERNAL_FAIL: Roam handoff failed because of
* firmware internal reasons.
* @QCA_ROAM_INVOKE_STATUS_DISALLOW: Roam invoke trigger is not enabled.
* @QCA_ROAM_INVOKE_STATUS_SCAN_FAIL: Scan start fail for roam invoke.
* @QCA_ROAM_INVOKE_STATUS_START_ROAM_FAIL: Roam handoff start fail.
* @QCA_ROAM_INVOKE_STATUS_INVALID_PARAMS: Roam invoke parameters are invalid.
* @QCA_ROAM_INVOKE_STATUS_NO_CAND_AP: No candidate AP found to roam to.
* @QCA_ROAM_INVOKE_STATUS_ROAM_FAIL: Roam handoff failed.
*/
enum qca_vendor_roam_invoke_fail_reasons {
QCA_ROAM_INVOKE_STATUS_NONE = 0,
QCA_ROAM_INVOKE_STATUS_IFACE_INVALID = 1,
QCA_ROAM_INVOKE_STATUS_OFFLOAD_DISABLE = 2,
QCA_ROAM_INVOKE_STATUS_AP_SSID_LENGTH_INVALID = 3,
QCA_ROAM_INVOKE_STATUS_ROAM_DISALLOW = 4,
QCA_ROAM_INVOKE_STATUS_NON_ROAMABLE_AP = 5,
QCA_ROAM_INVOKE_STATUS_ROAM_INTERNAL_FAIL = 6,
QCA_ROAM_INVOKE_STATUS_DISALLOW = 7,
QCA_ROAM_INVOKE_STATUS_SCAN_FAIL = 8,
QCA_ROAM_INVOKE_STATUS_START_ROAM_FAIL = 9,
QCA_ROAM_INVOKE_STATUS_INVALID_PARAMS = 10,
QCA_ROAM_INVOKE_STATUS_NO_CAND_AP = 11,
QCA_ROAM_INVOKE_STATUS_ROAM_FAIL = 12,
};
/**
* enum qca_vendor_attr_roam_candidate_selection_criteria:
*
* Each attribute carries a weightage in percentage (%).
*
* @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_RSSI: Unsigned 8-bit value.
* Represents the weightage to be given for the RSSI selection
* criteria among other parameters.
*
* @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE: Unsigned 8-bit value.
* Represents the weightage to be given for the rate selection
* criteria among other parameters.
*
* @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BW: Unsigned 8-bit value.
* Represents the weightage to be given for the band width selection
* criteria among other parameters.
*
* @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BAND: Unsigned 8-bit value.
* Represents the weightage to be given for the band selection
* criteria among other parameters.
*
* @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_NSS: Unsigned 8-bit value.
* Represents the weightage to be given for the NSS selection
* criteria among other parameters.
*
* @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_CHAN_CONGESTION: Unsigned 8-bit value.
* Represents the weightage to be given for the channel congestion
* selection criteria among other parameters.
*
* @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BEAMFORMING: Unsigned 8-bit value.
* Represents the weightage to be given for the beamforming selection
* criteria among other parameters.
*
* @QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_OCE_WAN: Unsigned 8-bit value.
* Represents the weightage to be given for the OCE selection
* criteria among other parameters.
*/
enum qca_vendor_attr_roam_candidate_selection_criteria {
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_RSSI = 1,
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE = 2,
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BW = 3,
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BAND = 4,
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_NSS = 5,
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_CHAN_CONGESTION = 6,
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_BEAMFORMING = 7,
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_SCORE_OCE_WAN = 8,
/* keep last */
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_AFTER_LAST,
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_MAX =
QCA_ATTR_ROAM_CAND_SEL_CRITERIA_RATE_AFTER_LAST - 1,
};
/**
* enum qca_vendor_attr_roam_control - Attributes to carry roam configuration
* The following attributes are used to set/get/clear the respective
* configurations to/from the driver.
* For the get, the attribute for the configuration to be queried shall
* carry any of its acceptable values to the driver. In return, the driver
* shall send the configured values within the same attribute to the user
* space.
*
* @QCA_ATTR_ROAM_CONTROL_ENABLE: Unsigned 8-bit value.
* Signifies to enable/disable roam control in driver.
* 1-enable, 0-disable
* Enable: Mandates the driver to do the further roams using the
* configuration parameters set through
* QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET.
* Disable: Disables the driver/firmware roaming triggered through
* QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET. Further roaming is
* expected to continue with the default configurations.
*
* @QCA_ATTR_ROAM_CONTROL_STATUS: Unsigned 8-bit value.
* This is used along with QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET.
* Roam control status is obtained through this attribute.
*
* @QCA_ATTR_ROAM_CONTROL_CLEAR_ALL: Flag attribute to indicate the
* complete config set through QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET
* is to be cleared in the driver.
* This is used along with QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR
* and shall be ignored if used with other sub commands.
* If this attribute is specified along with subcmd
* QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR, the driver shall ignore
* all other attributes, if there are any.
* If this attribute is not specified when the subcmd
* QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR is sent, the driver shall
* clear the data corresponding to the attributes specified.
*
* @QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME: Nested attribute to carry the
* list of frequencies and its type, represented by
* enum qca_vendor_attr_scan_freq_list_scheme.
* Frequency list and its type are mandatory for this attribute to set
* the frequencies.
* Frequency type is mandatory for this attribute to get the frequencies
* and the frequency list is obtained through
* QCA_ATTR_ROAM_CONTROL_SCAN_FREQ_LIST.
* Frequency list type is mandatory for this attribute to clear the
* frequencies.
*
* @QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD: Unsigned 32-bit value.
* Carries the value of scan period in seconds to set.
* The value of scan period is obtained with the same attribute for get.
* Clears the scan period in the driver when specified with clear command.
* Scan period is the idle time in seconds between each subsequent
* channel scans.
*
* @QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD: Unsigned 32-bit value.
* Carries the value of full scan period in seconds to set.
* The value of full scan period is obtained with the same attribute for
* get.
* Clears the full scan period in the driver when specified with clear
* command. Full scan period is the idle period in seconds between two
* successive full channel roam scans.
*
* @QCA_ATTR_ROAM_CONTROL_TRIGGERS: Unsigned 32-bit value.
* Carries a bitmap of the roam triggers specified in
* enum qca_vendor_roam_triggers.
* The driver shall enable roaming by enabling corresponding roam triggers
* based on the trigger bits sent with this attribute.
* If this attribute is not configured, the driver shall proceed with
* default behavior.
* The bitmap configured is obtained with the same attribute for get.
* Clears the bitmap configured in driver when specified with clear
* command.
*
* @QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA: Nested attribute signifying the
* weightage in percentage (%) to be given for each selection criteria.
* Different roam candidate selection criteria are represented by
* enum qca_vendor_attr_roam_candidate_selection_criteria.
* The driver shall select the roam candidate based on corresponding
* candidate selection scores sent.
*
* An empty nested attribute is used to indicate that no specific
* preference score/criteria is configured (i.e., to disable this mechanism
* in the set case and to show that the mechanism is disabled in the get
* case).
*
* Userspace can send multiple attributes out of this enum to the driver.
* Since this attribute represents the weight/percentage of preference for
* the respective selection criteria, it is preferred to configure 100%
* total weightage. The value in each attribute or cumulative weight of the
* values in all the nested attributes should not exceed 100%. The driver
* shall reject such configuration.
*
* If the weights configured through this attribute are less than 100%,
* the driver shall honor the weights (x%) passed for the corresponding
* selection criteria and choose/distribute rest of the weight (100-x)%
* for the other selection criteria, based on its internal logic.
*
* The selection criteria configured is obtained with the same
* attribute for get.
*
* Clears the selection criteria configured in the driver when specified
* with clear command.
*
* @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME: Unsigned 32-bit value.
* Represents value of the scan frequency scheme from enum
* qca_roam_scan_scheme.
* It's an optional attribute. If this attribute is not configured, the
* driver shall proceed with default behavior.
*
* @QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD: Signed 32-bit value in dBm,
* signifying the RSSI threshold of the current connected AP, indicating
* the driver to trigger roam only when the current connected AP's RSSI
* is less than this threshold.
*
* @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD: Signed 32-bit value in dBm,
* signifying the RSSI threshold of the candidate AP, indicating
* the driver to trigger roam only to the candidate AP with RSSI
* better than this threshold. If RSSI thresholds for candidate APs found
* in the 2.4 GHz, 5 GHz, and 6 GHz bands are configured separately using
* QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_2P4GHZ,
* QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_5GHZ, and/or
* QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_6GHZ, those values will
* take precedence over the value configured using the
* QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD attribute.
*
* @QCA_ATTR_ROAM_CONTROL_USER_REASON: Unsigned 32-bit value. Represents the
* user defined reason code to be sent to the AP in response to AP's
* request to trigger the roam if the roaming cannot be triggered.
* Applies to all the scenarios of AP assisted roaming (e.g., BTM).
*
* @QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS: Unsigned 32-bit value.
* Carries a bitmap of the roam triggers specified in
* enum qca_vendor_roam_triggers.
* Represents the roam triggers for which the specific scan scheme from
* enum qca_roam_scan_scheme has to be applied.
* It's an optional attribute. If this attribute is not configured, but
* QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME is specified, the scan scheme
* specified through QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME is applicable for
* all the roams.
* If both QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME and
* QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS are not specified, the
* driver shall proceed with the default behavior.
*
* @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_2P4GHZ: Signed 32-bit value
* in dBm, signifying the RSSI threshold of the candidate AP found in the
* 2.4 GHz band. The driver/firmware shall trigger roaming to the candidate
* AP found in the 2.4 GHz band only if its RSSI value is better than this
* threshold. Optional attribute. If this attribute is not included, the
* threshold value specified by the
* QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD attribute shall be used.
*
* @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_5GHZ: Signed 32-bit value in
* dBm, signifying the RSSI threshold of the candidate AP found in the 5
* GHz band. The driver/firmware shall trigger roaming to the candidate AP
* found in the 5 GHz band only if its RSSI value is better than this
* threshold. Optional attribute. If this attribute is not included, the
* threshold value specified by tge
* QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD attribute shall be used.
*
* @QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_6GHZ: Signed 32-bit value in
* dBm, signifying the RSSI threshold of the candidate AP found in the 6
* GHz band. The driver/firmware shall trigger roaming to the candidate AP
* found in the 6 GHz band only if its RSSI value is better than this
* threshold. Optional attribute. If this attribute is not included, the
* threshold value specified by the
* QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD attribute shall be used.
*
+ * @QCA_ATTR_ROAM_CONTROL_BAND_MASK: Unsigned 32-bit value.
+ * Carries bitmask value of bits from &enum qca_set_band and represents
+ * all the bands in which roaming is allowed. The configuration is valid
+ * until next disconnection. If this attribute is not present, the
+ * existing configuration shall be used. By default, roaming is allowed on
+ * all bands supported by the local device. When the value is set to
+ * %QCA_SETBAND_AUTO, all supported bands shall be enabled.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_ACTIVE_CH_DWELL_TIME: u16 value in milliseconds.
+ * Optional parameter. Scan dwell time for active channels in the 2.4/5 GHz
+ * bands. If this attribute is not configured, the driver shall proceed
+ * with default behavior.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_PASSIVE_CH_DWELL_TIME: u16 value in milliseconds.
+ * Optional parameter. Scan dwell time for passive channels in the 5 GHz
+ * band. If this attribute is not configured, the driver shall proceed with
+ * default behavior.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_HOME_CHANNEL_TIME: u16 value in milliseconds.
+ * Optional parameter. The minimum duration to stay on the connected AP
+ * channel during the channel scanning. If this attribute is not
+ * configured, the driver shall proceed with default behavior.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_MAXIMUM_AWAY_TIME: u16 value in milliseconds.
+ * Optional parameter. The maximum duration for which the radio can scan
+ * foreign channels consecutively without coming back to home channel. If
+ * this attribute is not configured, the driver shall proceed with default
+ * behavior.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_6G_PSC_DWELL_TIME: u16 value in milliseconds.
+ * Optional parameter. Scan dwell time for 6G Preferred Scanning Channels.
+ * If this attribute is not configured, the driver shall proceed with
+ * default behavior.
+ *
+ * @QCA_ATTR_ROAM_CONTROL_SCAN_6G_NON_PSC_DWELL_TIME: u16 value in milliseconds.
+ * Optional parameter. Scan dwell time for 6G Non Preferred Scanning
+ * Channels. If this attribute is not configured, the driver shall proceed
+ * with default behavior.
*/
enum qca_vendor_attr_roam_control {
QCA_ATTR_ROAM_CONTROL_ENABLE = 1,
QCA_ATTR_ROAM_CONTROL_STATUS = 2,
QCA_ATTR_ROAM_CONTROL_CLEAR_ALL = 3,
QCA_ATTR_ROAM_CONTROL_FREQ_LIST_SCHEME= 4,
QCA_ATTR_ROAM_CONTROL_SCAN_PERIOD = 5,
QCA_ATTR_ROAM_CONTROL_FULL_SCAN_PERIOD = 6,
QCA_ATTR_ROAM_CONTROL_TRIGGERS = 7,
QCA_ATTR_ROAM_CONTROL_SELECTION_CRITERIA = 8,
QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME = 9,
QCA_ATTR_ROAM_CONTROL_CONNECTED_RSSI_THRESHOLD = 10,
QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD = 11,
QCA_ATTR_ROAM_CONTROL_USER_REASON = 12,
QCA_ATTR_ROAM_CONTROL_SCAN_SCHEME_TRIGGERS = 13,
QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_2P4GHZ = 14,
QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_5GHZ = 15,
QCA_ATTR_ROAM_CONTROL_CANDIDATE_RSSI_THRESHOLD_6GHZ = 16,
+ QCA_ATTR_ROAM_CONTROL_BAND_MASK = 17,
+ QCA_ATTR_ROAM_CONTROL_ACTIVE_CH_DWELL_TIME = 18,
+ QCA_ATTR_ROAM_CONTROL_PASSIVE_CH_DWELL_TIME = 19,
+ QCA_ATTR_ROAM_CONTROL_HOME_CHANNEL_TIME = 20,
+ QCA_ATTR_ROAM_CONTROL_MAXIMUM_AWAY_TIME = 21,
+ QCA_ATTR_ROAM_CONTROL_SCAN_6G_PSC_DWELL_TIME = 22,
+ QCA_ATTR_ROAM_CONTROL_SCAN_6G_NON_PSC_DWELL_TIME = 23,
/* keep last */
QCA_ATTR_ROAM_CONTROL_AFTER_LAST,
QCA_ATTR_ROAM_CONTROL_MAX =
QCA_ATTR_ROAM_CONTROL_AFTER_LAST - 1,
};
/*
* enum qca_wlan_vendor_attr_roaming_config_params: Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_ROAM sub command.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD: Unsigned 32-bit value.
* Represents the different roam sub commands referred by
* enum qca_wlan_vendor_roaming_subcmd.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID: Unsigned 32-bit value.
* Represents the Request ID for the specific set of commands.
* This also helps to map specific set of commands to the respective
* ID / client. e.g., helps to identify the user entity configuring the
* ignored BSSIDs and accordingly clear the respective ones with the
* matching ID.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS: Unsigned
* 32-bit value.Represents the number of whitelist SSIDs configured.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST: Nested attribute
* to carry the list of Whitelist SSIDs.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID: SSID (binary attribute,
* 0..32 octets). Represents the white list SSID. Whitelist SSIDs
* represent the list of SSIDs to which the firmware/driver can consider
* to roam to.
*
* The following PARAM_A_BAND_XX attributes are applied to 5GHz BSSIDs when
* comparing with a 2.4GHz BSSID. They are not applied when comparing two
* 5GHz BSSIDs.The following attributes are set through the Roaming SUBCMD -
* QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_GSCAN_ROAM_PARAMS.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD: Signed 32-bit
* value, RSSI threshold above which 5GHz RSSI is favored.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD: Signed 32-bit
* value, RSSI threshold below which 5GHz RSSI is penalized.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR: Unsigned 32-bit
* value, factor by which 5GHz RSSI is boosted.
* boost=(RSSI_measured-5GHz_boost_threshold)*5GHz_boost_factor
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR: Unsigned 32-bit
* value, factor by which 5GHz RSSI is penalized.
* penalty=(5GHz_penalty_threshold-RSSI_measured)*5GHz_penalty_factor
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST: Unsigned 32-bit
* value, maximum boost that can be applied to a 5GHz RSSI.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS: Unsigned 32-bit
* value, boost applied to current BSSID to ensure the currently
* associated BSSID is favored so as to prevent ping-pong situations.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER: Signed 32-bit
* value, RSSI below which "Alert" roam is enabled.
* "Alert" mode roaming - firmware is "urgently" hunting for another BSSID
* because the RSSI is low, or because many successive beacons have been
* lost or other bad link conditions.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE: Unsigned 32-bit
* value. 1-Enable, 0-Disable. Represents "Lazy" mode, where
* firmware is hunting for a better BSSID or white listed SSID even though
* the RSSI of the link is good. The parameters enabling the roaming are
* configured through the PARAM_A_BAND_XX attrbutes.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS: Nested attribute,
* represents the BSSIDs preferred over others while evaluating them
* for the roaming.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID: Unsigned
* 32-bit value. Represents the number of preferred BSSIDs set.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID: 6-byte MAC
* address representing the BSSID to be preferred.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER: Signed
* 32-bit value, representing the modifier to be applied to the RSSI of
* the BSSID for the purpose of comparing it with other roam candidate.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS: Nested attribute,
* represents the BSSIDs to get ignored for roaming.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID: Unsigned
* 32-bit value, represents the number of ignored BSSIDs.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID: 6-byte MAC
* address representing the ignored BSSID.
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT: Flag attribute,
* indicates this request to ignore the BSSID as a hint to the driver. The
* driver can select this BSSID in the worst case (when no other BSSIDs are
* better).
*
* @QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL: Nested attribute to
* set/get/clear the roam control config as
* defined @enum qca_vendor_attr_roam_control.
*/
enum qca_wlan_vendor_attr_roaming_config_params {
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD = 1,
QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID = 2,
/* Attributes for wifi_set_ssid_white_list */
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS = 3,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST = 4,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID = 5,
/* Attributes for set_roam_params */
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD = 6,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD = 7,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR = 8,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR = 9,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST = 10,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS = 11,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER = 12,
/* Attribute for set_lazy_roam */
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE = 13,
/* Attribute for set_lazy_roam with preferences */
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS = 14,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID = 15,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID = 16,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER = 17,
/* Attribute for setting ignored BSSID parameters */
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20,
/* Flag attribute indicates this entry as a hint */
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL = 22,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX =
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST - 1,
};
/*
* enum qca_wlan_vendor_roaming_subcmd: Referred by
* QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD.
*
* @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SSID_WHITE_LIST: Sub command to
* configure the white list SSIDs. These are configured through
* the following attributes.
* QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS,
* QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST,
* QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID
*
* @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_GSCAN_ROAM_PARAMS: Sub command to
* configure the Roam params. These parameters are evaluated on the GScan
* results. Refers the attributes PARAM_A_BAND_XX above to configure the
* params.
*
* @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_LAZY_ROAM: Sets the Lazy roam. Uses
* the attribute QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE
* to enable/disable Lazy roam.
*
* @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PREFS: Sets the BSSID
* preference. Contains the attribute
* QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS to set the BSSID
* preference.
*
* @QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID: Sets the list of BSSIDs
* to ignore in roaming decision. Uses
* QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS to set the list.
*
* @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET: Command to set the
* roam control config to the driver with the attribute
* QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL.
*
* @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET: Command to obtain the
* roam control config from driver with the attribute
* QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL.
* For the get, the attribute for the configuration to be queried shall
* carry any of its acceptable value to the driver. In return, the driver
* shall send the configured values within the same attribute to the user
* space.
*
* @QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR: Command to clear the
* roam control config in the driver with the attribute
* QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_CONTROL.
* The driver shall continue with its default roaming behavior when data
* corresponding to an attribute is cleared.
*/
enum qca_wlan_vendor_roaming_subcmd {
QCA_WLAN_VENDOR_ROAMING_SUBCMD_INVALID = 0,
QCA_WLAN_VENDOR_ROAMING_SUBCMD_SSID_WHITE_LIST = 1,
QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_GSCAN_ROAM_PARAMS = 2,
QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_LAZY_ROAM = 3,
QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PREFS = 4,
QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BSSID_PARAMS = 5,
QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID = 6,
QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_SET = 7,
QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_GET = 8,
QCA_WLAN_VENDOR_ROAMING_SUBCMD_CONTROL_CLEAR = 9,
};
enum qca_wlan_vendor_attr_gscan_config_params {
QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_INVALID = 0,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID = 1,
/* Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS sub command.
*/
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND
= 2,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS
= 3,
/* Attributes for input params used by
* QCA_NL80211_VENDOR_SUBCMD_GSCAN_START sub command.
*/
/* Unsigned 32-bit value; channel frequency */
QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CHANNEL = 4,
/* Unsigned 32-bit value; dwell time in ms. */
QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_DWELL_TIME = 5,
/* Unsigned 8-bit value; 0: active; 1: passive; N/A for DFS */
QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_PASSIVE = 6,
/* Unsigned 8-bit value; channel class */
QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CLASS = 7,
/* Unsigned 8-bit value; bucket index, 0 based */
QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_INDEX = 8,
/* Unsigned 8-bit value; band. */
QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BAND = 9,
/* Unsigned 32-bit value; desired period, in ms. */
QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_PERIOD = 10,
/* Unsigned 8-bit value; report events semantics. */
QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_REPORT_EVENTS = 11,
/* Unsigned 32-bit value. Followed by a nested array of
* GSCAN_CHANNEL_SPEC_* attributes.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS = 12,
/* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_* attributes.
* Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC = 13,
/* Unsigned 32-bit value; base timer period in ms. */
QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_BASE_PERIOD = 14,
/* Unsigned 32-bit value; number of APs to store in each scan in the
* BSSID/RSSI history buffer (keep the highest RSSI APs).
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN = 15,
/* Unsigned 8-bit value; in %, when scan buffer is this much full, wake
* up AP.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT
= 16,
/* Unsigned 8-bit value; number of scan bucket specs; followed by a
* nested array of_GSCAN_BUCKET_SPEC_* attributes and values. The size
* of the array is determined by NUM_BUCKETS.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS = 17,
/* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_* attributes.
* Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC = 18,
/* Unsigned 8-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH
= 19,
/* Unsigned 32-bit value; maximum number of results to be returned. */
QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX
= 20,
/* An array of 6 x unsigned 8-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_BSSID = 21,
/* Signed 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_LOW = 22,
/* Signed 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH = 23,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_CHANNEL = 24,
/* Number of hotlist APs as unsigned 32-bit value, followed by a nested
* array of AP_THRESHOLD_PARAM attributes and values. The size of the
* array is determined by NUM_AP.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_NUM_AP = 25,
/* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_* attributes.
* Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM = 26,
/* Unsigned 32-bit value; number of samples for averaging RSSI. */
QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE
= 27,
/* Unsigned 32-bit value; number of samples to confirm AP loss. */
QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE
= 28,
/* Unsigned 32-bit value; number of APs breaching threshold. */
QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING = 29,
/* Unsigned 32-bit value; number of APs. Followed by an array of
* AP_THRESHOLD_PARAM attributes. Size of the array is NUM_AP.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP = 30,
/* Unsigned 32-bit value; number of samples to confirm AP loss. */
QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE
= 31,
/* Unsigned 32-bit value. If max_period is non zero or different than
* period, then this bucket is an exponential backoff bucket.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_MAX_PERIOD = 32,
/* Unsigned 32-bit value. */
QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BASE = 33,
/* Unsigned 32-bit value. For exponential back off bucket, number of
* scans to perform for a given period.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_STEP_COUNT = 34,
/* Unsigned 8-bit value; in number of scans, wake up AP after these
* many scans.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS
= 35,
/* Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST sub command.
*/
/* Unsigned 3-2bit value; number of samples to confirm SSID loss. */
QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE
= 36,
/* Number of hotlist SSIDs as unsigned 32-bit value, followed by a
* nested array of SSID_THRESHOLD_PARAM_* attributes and values. The
* size of the array is determined by NUM_SSID.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID = 37,
/* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_*
* attributes.
* Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM = 38,
/* An array of 33 x unsigned 8-bit value; NULL terminated SSID */
QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_SSID = 39,
/* Unsigned 8-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_BAND = 40,
/* Signed 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW = 41,
/* Signed 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH = 42,
/* Unsigned 32-bit value; a bitmask with additional gscan config flag.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_CONFIGURATION_FLAGS = 43,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_MAX =
QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST - 1,
};
enum qca_wlan_vendor_attr_gscan_results {
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_INVALID = 0,
/* Unsigned 32-bit value; must match the request Id supplied by
* Wi-Fi HAL in the corresponding subcmd NL msg.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_REQUEST_ID = 1,
/* Unsigned 32-bit value; used to indicate the status response from
* firmware/driver for the vendor sub-command.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_STATUS = 2,
/* GSCAN Valid Channels attributes */
/* Unsigned 32bit value; followed by a nested array of CHANNELS. */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_CHANNELS = 3,
/* An array of NUM_CHANNELS x unsigned 32-bit value integers
* representing channel numbers.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CHANNELS = 4,
/* GSCAN Capabilities attributes */
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE = 5,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS = 6,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN
= 7,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE
= 8,
/* Signed 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD
= 9,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS = 10,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS
= 11,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES
= 12,
/* GSCAN Attributes used with
* QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE sub-command.
*/
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE = 13,
/* GSCAN attributes used with
* QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT sub-command.
*/
/* An array of NUM_RESULTS_AVAILABLE x
* QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_*
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST = 14,
/* Unsigned 64-bit value; age of sample at the time of retrieval */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP = 15,
/* 33 x unsigned 8-bit value; NULL terminated SSID */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_SSID = 16,
/* An array of 6 x unsigned 8-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BSSID = 17,
/* Unsigned 32-bit value; channel frequency in MHz */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CHANNEL = 18,
/* Signed 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RSSI = 19,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT = 20,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT_SD = 21,
/* Unsigned 16-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD = 22,
/* Unsigned 16-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CAPABILITY = 23,
/* Unsigned 32-bit value; size of the IE DATA blob */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_LENGTH = 24,
/* An array of IE_LENGTH x unsigned 8-bit value; blob of all the
* information elements found in the beacon; this data should be a
* packed list of wifi_information_element objects, one after the
* other.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_DATA = 25,
/* Unsigned 8-bit value; set by driver to indicate more scan results are
* available.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_MORE_DATA = 26,
/* GSCAN attributes for
* QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT sub-command.
*/
/* Unsigned 8-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_TYPE = 27,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_STATUS = 28,
/* GSCAN attributes for
* QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND sub-command.
*/
/* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
* to indicate number of results.
* Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the
* list of results.
*/
/* GSCAN attributes for
* QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE sub-command.
*/
/* An array of 6 x unsigned 8-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_BSSID = 29,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_CHANNEL
= 30,
/* Unsigned 32-bit value. */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_NUM_RSSI
= 31,
/* A nested array of signed 32-bit RSSI values. Size of the array is
* determined by (NUM_RSSI of SIGNIFICANT_CHANGE_RESULT_NUM_RSSI.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_RSSI_LIST
= 32,
/* GSCAN attributes used with
* QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS sub-command.
*/
/* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
* to indicate number of gscan cached results returned.
* Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST to indicate
* the list of gscan cached results.
*/
/* An array of NUM_RESULTS_AVAILABLE x
* QCA_NL80211_VENDOR_ATTR_GSCAN_CACHED_RESULTS_*
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST = 33,
/* Unsigned 32-bit value; a unique identifier for the scan unit. */
QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID = 34,
/* Unsigned 32-bit value; a bitmask w/additional information about scan.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_FLAGS = 35,
/* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
* to indicate number of wifi scan results/bssids retrieved by the scan.
* Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the
* list of wifi scan results returned for each cached result block.
*/
/* GSCAN attributes for
* QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND sub-command.
*/
/* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE for
* number of results.
* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested
* list of wifi scan results returned for each
* wifi_passpoint_match_result block.
* Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE.
*/
/* GSCAN attributes for
* QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND sub-command.
*/
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES
= 36,
/* A nested array of
* QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_*
* attributes. Array size =
* *_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_RESULT_LIST = 37,
/* Unsigned 32-bit value; network block id for the matched network */
QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ID = 38,
/* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested
* list of wifi scan results returned for each
* wifi_passpoint_match_result block.
*/
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP_LEN = 39,
/* An array size of PASSPOINT_MATCH_ANQP_LEN of unsigned 8-bit values;
* ANQP data in the information_element format.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP = 40,
/* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS = 41,
/* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS = 42,
/* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID
= 43,
/* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID
= 44,
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_BUCKETS_SCANNED = 45,
/* Unsigned 32-bit value; a GSCAN Capabilities attribute.
* This is used to limit the maximum number of BSSIDs while sending
* the vendor command QCA_NL80211_VENDOR_SUBCMD_ROAM with subcmd
* QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID and attribute
* QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID.
*/
QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID = 46,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX =
QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST - 1,
};
enum qca_wlan_vendor_attr_pno_config_params {
QCA_WLAN_VENDOR_ATTR_PNO_INVALID = 0,
/* Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST sub command.
*/
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM = 1,
/* Array of nested QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_*
* attributes. Array size =
* QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM.
*/
QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY = 2,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID = 3,
/* An array of 256 x unsigned 8-bit value; NULL terminated UTF-8 encoded
* realm, 0 if unspecified.
*/
QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM = 4,
/* An array of 16 x unsigned 32-bit value; roaming consortium ids to
* match, 0 if unspecified.
*/
QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID = 5,
/* An array of 6 x unsigned 8-bit value; MCC/MNC combination, 0s if
* unspecified.
*/
QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN = 6,
/* Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST sub command.
*/
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS = 7,
/* Array of nested
* QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_*
* attributes. Array size =
* QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS.
*/
QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST = 8,
/* An array of 33 x unsigned 8-bit value; NULL terminated SSID */
QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID = 9,
/* Signed 8-bit value; threshold for considering this SSID as found,
* required granularity for this threshold is 4 dBm to 8 dBm.
*/
QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_RSSI_THRESHOLD
= 10,
/* Unsigned 8-bit value; WIFI_PNO_FLAG_XXX */
QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS = 11,
/* Unsigned 8-bit value; auth bit field for matching WPA IE */
QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT = 12,
/* Unsigned 8-bit to indicate ePNO type;
* It takes values from qca_wlan_epno_type
*/
QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_TYPE = 13,
/* Nested attribute to send the channel list */
QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_CHANNEL_LIST = 14,
/* Unsigned 32-bit value; indicates the interval between PNO scan
* cycles in msec.
*/
QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_SCAN_INTERVAL = 15,
QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI = 16,
QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI = 17,
QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX = 18,
QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS = 19,
QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS = 20,
QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS = 21,
QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS = 22,
/* Unsigned 32-bit value, representing the PNO Request ID */
QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID = 23,
/* keep last */
QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_PNO_MAX =
QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST - 1,
};
/**
* qca_wlan_vendor_acs_select_reason: This represents the different reasons why
* the ACS has to be triggered. These values are used by
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON and
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON
*/
enum qca_wlan_vendor_acs_select_reason {
/* Represents the reason that the ACS triggered during the AP start */
QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT,
/* Represents the reason that DFS found with the current channel */
QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS,
/* Represents the reason that LTE co-exist in the current band. */
QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX,
/* Represents the reason that generic, uncategorized interference has
* been found in the current channel.
*/
QCA_WLAN_VENDOR_ACS_SELECT_REASON_GENERIC_INTERFERENCE,
/* Represents the reason that excessive 802.11 interference has been
* found in the current channel.
*/
QCA_WLAN_VENDOR_ACS_SELECT_REASON_80211_INTERFERENCE,
/* Represents the reason that generic Continuous Wave (CW) interference
* has been found in the current channel.
*/
QCA_WLAN_VENDOR_ACS_SELECT_REASON_CW_INTERFERENCE,
/* Represents the reason that Microwave Oven (MWO) interference has been
* found in the current channel.
*/
QCA_WLAN_VENDOR_ACS_SELECT_REASON_MWO_INTERFERENCE,
/* Represents the reason that generic Frequency-Hopping Spread Spectrum
* (FHSS) interference has been found in the current channel. This may
* include 802.11 waveforms.
*/
QCA_WLAN_VENDOR_ACS_SELECT_REASON_FHSS_INTERFERENCE,
/* Represents the reason that non-802.11 generic Frequency-Hopping
* Spread Spectrum (FHSS) interference has been found in the current
* channel.
*/
QCA_WLAN_VENDOR_ACS_SELECT_REASON_NON_80211_FHSS_INTERFERENCE,
/* Represents the reason that generic Wideband (WB) interference has
* been found in the current channel. This may include 802.11 waveforms.
*/
QCA_WLAN_VENDOR_ACS_SELECT_REASON_WB_INTERFERENCE,
/* Represents the reason that non-802.11 generic Wideband (WB)
* interference has been found in the current channel.
*/
QCA_WLAN_VENDOR_ACS_SELECT_REASON_NON_80211_WB_INTERFERENCE,
/* Represents the reason that Jammer interference has been found in the
* current channel.
*/
QCA_WLAN_VENDOR_ACS_SELECT_REASON_JAMMER_INTERFERENCE,
};
/**
* qca_wlan_vendor_attr_external_acs_policy: Attribute values for
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY to the vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This represents the
* external ACS policies to select the channels w.r.t. the PCL weights.
* (QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL represents the channels and
* their PCL weights.)
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY: Mandatory to
* select a channel with non-zero PCL weight.
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED: Prefer a
* channel with non-zero PCL weight.
*
*/
enum qca_wlan_vendor_attr_external_acs_policy {
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY,
};
/**
* qca_wlan_vendor_channel_prop_flags: This represent the flags for a channel.
* This is used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS.
*/
enum qca_wlan_vendor_channel_prop_flags {
/* Bits 0, 1, 2, and 3 are reserved */
/* Turbo channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_TURBO = 1 << 4,
/* CCK channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_CCK = 1 << 5,
/* OFDM channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_OFDM = 1 << 6,
/* 2.4 GHz spectrum channel. */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_2GHZ = 1 << 7,
/* 5 GHz spectrum channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_5GHZ = 1 << 8,
/* Only passive scan allowed */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_PASSIVE = 1 << 9,
/* Dynamic CCK-OFDM channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_DYN = 1 << 10,
/* GFSK channel (FHSS PHY) */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_GFSK = 1 << 11,
/* Radar found on channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_RADAR = 1 << 12,
/* 11a static turbo channel only */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_STURBO = 1 << 13,
/* Half rate channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HALF = 1 << 14,
/* Quarter rate channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_QUARTER = 1 << 15,
/* HT 20 channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT20 = 1 << 16,
/* HT 40 with extension channel above */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40PLUS = 1 << 17,
/* HT 40 with extension channel below */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40MINUS = 1 << 18,
/* HT 40 intolerant */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOL = 1 << 19,
/* VHT 20 channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT20 = 1 << 20,
/* VHT 40 with extension channel above */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40PLUS = 1 << 21,
/* VHT 40 with extension channel below */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40MINUS = 1 << 22,
/* VHT 80 channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80 = 1 << 23,
/* HT 40 intolerant mark bit for ACS use */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOLMARK = 1 << 24,
/* Channel temporarily blocked due to noise */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_BLOCKED = 1 << 25,
/* VHT 160 channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT160 = 1 << 26,
/* VHT 80+80 channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80_80 = 1 << 27,
/* HE 20 channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE20 = 1 << 28,
/* HE 40 with extension channel above */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40PLUS = 1 << 29,
/* HE 40 with extension channel below */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40MINUS = 1 << 30,
/* HE 40 intolerant */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL = 1 << 31,
};
/**
* qca_wlan_vendor_channel_prop_flags_2: This represents the flags for a
* channel, and is a continuation of qca_wlan_vendor_channel_prop_flags. This is
* used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2.
*/
enum qca_wlan_vendor_channel_prop_flags_2 {
/* HE 40 intolerant mark bit for ACS use */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOLMARK = 1 << 0,
/* HE 80 channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80 = 1 << 1,
/* HE 160 channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE160 = 1 << 2,
/* HE 80+80 channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80_80 = 1 << 3,
};
/**
* qca_wlan_vendor_channel_prop_flags_ext: This represent the extended flags for
* each channel. This is used by
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT.
*/
enum qca_wlan_vendor_channel_prop_flags_ext {
/* Radar found on channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_RADAR_FOUND = 1 << 0,
/* DFS required on channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS = 1 << 1,
/* DFS required on channel for 2nd band of 80+80 */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CFREQ2 = 1 << 2,
/* If channel has been checked for DFS */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CLEAR = 1 << 3,
/* Excluded in 802.11d */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_11D_EXCLUDED = 1 << 4,
/* Channel Switch Announcement received on this channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CSA_RECEIVED = 1 << 5,
/* Ad-hoc is not allowed */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_ADHOC = 1 << 6,
/* Station only channel */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_HOSTAP = 1 << 7,
/* DFS radar history for client device (STA mode) */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_HISTORY_RADAR = 1 << 8,
/* DFS CAC valid for client device (STA mode) */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CAC_VALID = 1 << 9,
};
/**
* qca_wlan_vendor_external_acs_event_chan_info_attr: Represents per channel
* information. These attributes are sent as part of
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO. Each set of the following
* attributes correspond to a single channel.
*/
enum qca_wlan_vendor_external_acs_event_chan_info_attr {
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_INVALID = 0,
/* A bitmask (u32) with flags specified in
* enum qca_wlan_vendor_channel_prop_flags.
*/
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS = 1,
/* A bitmask (u32) with flags specified in
* enum qca_wlan_vendor_channel_prop_flags_ext.
*/
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT = 2,
/* frequency in MHz (u32) */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ = 3,
/* maximum regulatory transmission power (u32) */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER = 4,
/* maximum transmission power (u32) */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER = 5,
/* minimum transmission power (u32) */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER = 6,
/* regulatory class id (u8) */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID = 7,
/* maximum antenna gain in (u8) */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN = 8,
/* VHT segment 0 (u8) */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 = 9,
/* VHT segment 1 (u8) */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 = 10,
/* A bitmask (u32) with flags specified in
* enum qca_wlan_vendor_channel_prop_flags_2.
*/
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11,
/*
* VHT segment 0 in MHz (u32) and the attribute is mandatory.
* Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
* along with
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0.
*
* If both the driver and user-space application supports the 6 GHz
* band, QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0
* is deprecated and
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
* should be used.
*
* To maintain backward compatibility,
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0
* is still used if either of the driver or user space application
* doesn't support the 6 GHz band.
*/
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_0 = 12,
/*
* VHT segment 1 in MHz (u32) and the attribute is mandatory.
* Note: Event QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS includes
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
* along with
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1.
*
* If both the driver and user-space application supports the 6 GHz
* band, QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1
* is deprecated and
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
* should be considered.
*
* To maintain backward compatibility,
* QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1
* is still used if either of the driver or user space application
* doesn't support the 6 GHz band.
*/
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ_VHT_SEG_1 = 13,
/* keep last */
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX =
QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST - 1,
};
/**
* qca_wlan_vendor_attr_pcl: Represents attributes for
* preferred channel list (PCL). These attributes are sent as part of
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL and
* QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST.
*/
enum qca_wlan_vendor_attr_pcl {
QCA_WLAN_VENDOR_ATTR_PCL_INVALID = 0,
/* Channel number (u8) */
QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL = 1,
/* Channel weightage (u8) */
QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT = 2,
/* Channel frequency (u32) in MHz */
QCA_WLAN_VENDOR_ATTR_PCL_FREQ = 3,
/* Channel flags (u32)
* bit 0 set: channel to be used for GO role,
* bit 1 set: channel to be used on CLI role,
* bit 2 set: channel must be considered for operating channel
* selection & peer chosen operating channel should be
* one of the channels with this flag set,
* bit 3 set: channel should be excluded in GO negotiation
*/
QCA_WLAN_VENDOR_ATTR_PCL_FLAG = 4,
};
/**
* qca_wlan_vendor_attr_external_acs_event: Attribute to vendor sub-command
* QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This attribute will be sent by
* host driver.
*/
enum qca_wlan_vendor_attr_external_acs_event {
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_INVALID = 0,
/* This reason (u8) refers to enum qca_wlan_vendor_acs_select_reason.
* This helps ACS module to understand why ACS needs to be started.
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON = 1,
/* Flag attribute to indicate if driver supports spectral scanning */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_SPECTRAL_SUPPORTED = 2,
/* Flag attribute to indicate if 11ac is offloaded to firmware */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED = 3,
/* Flag attribute to indicate if driver provides additional channel
* capability as part of scan operation
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT = 4,
/* Flag attribute to indicate interface status is UP */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AP_UP = 5,
/* Operating mode (u8) of interface. Takes one of enum nl80211_iftype
* values.
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_MODE = 6,
/* Channel width (u8). It takes one of enum nl80211_chan_width values.
* This is the upper bound of channel width. ACS logic should try to get
* a channel with the specified width and if not found, look for lower
* values.
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH = 7,
/* This (u8) will hold values of one of enum nl80211_bands */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND = 8,
/* PHY/HW mode (u8). Takes one of enum qca_wlan_vendor_acs_hw_mode
* values
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE = 9,
/* Array of (u32) supported frequency list among which ACS should choose
* best frequency.
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST = 10,
/* Preferred channel list by the driver which will have array of nested
* values as per enum qca_wlan_vendor_attr_pcl attribute.
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL = 11,
/* Array of nested attribute for each channel. It takes attr as defined
* in enum qca_wlan_vendor_external_acs_event_chan_info_attr.
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO = 12,
/* External ACS policy such as PCL mandatory, PCL preferred, etc.
* It uses values defined in enum
* qca_wlan_vendor_attr_external_acs_policy.
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY = 13,
/* Reference RF Operating Parameter (RROP) availability information
* (u16). It uses values defined in enum
* qca_wlan_vendor_attr_rropavail_info.
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_RROPAVAIL_INFO = 14,
/* keep last */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAX =
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_external_acs_channels: Attributes to vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This carries a list of channels
* in priority order as decided after ACS operation in userspace.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON: Required (u8).
* One of reason code from enum qca_wlan_vendor_acs_select_reason.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST: Required
* Array of nested values for each channel with following attributes:
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
* Note: If both the driver and user-space application supports the 6 GHz band,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST is deprecated and use
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST.
* To maintain backward compatibility,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST
* is still used if either of the driver or user space application doesn't
* support the 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY: Required (u8).
* Primary channel number
* Note: If both the driver and user-space application supports the 6 GHz band,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY is deprecated and use
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY.
* To maintain backward compatibility,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY
* is still used if either of the driver or user space application doesn't
* support the 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY: Required (u8).
* Secondary channel number, required only for 160 and 80+80 MHz bandwidths.
* Note: If both the driver and user-space application supports the 6 GHz band,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY is deprecated and use
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY.
* To maintain backward compatibility,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY
* is still used if either of the driver or user space application
* doesn't support the 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0: Required (u8).
* VHT seg0 channel number
* Note: If both the driver and user-space application supports the 6 GHz band,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 is deprecated and use
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0.
* To maintain backward compatibility,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0
* is still used if either of the driver or user space application
* doesn't support the 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1: Required (u8).
* VHT seg1 channel number
* Note: If both the driver and user-space application supports the 6 GHz band,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 is deprecated and use
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1.
* To maintain backward compatibility,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1
* is still used if either of the driver or user space application
* doesn't support the 6 GHz band.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH: Required (u8).
* Takes one of enum nl80211_chan_width values.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST: Required
* Array of nested values for each channel with following attributes:
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY in MHz (u32),
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY in MHz (u32),
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 in MHz (u32),
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 in MHz (u32),
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
* Note: If user-space application has no support of the 6 GHz band, this
* attribute is optional.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY: Required (u32)
* Primary channel frequency in MHz
* Note: If user-space application has no support of the 6 GHz band, this
* attribute is optional.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY: Required (u32)
* Secondary channel frequency in MHz used for HT 40 MHz channels.
* Note: If user-space application has no support of the 6 GHz band, this
* attribute is optional.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0: Required (u32)
* VHT seg0 channel frequency in MHz
* Note: If user-space application has no support of the 6GHz band, this
* attribute is optional.
*
* @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1: Required (u32)
* VHT seg1 channel frequency in MHz
* Note: If user-space application has no support of the 6 GHz band, this
* attribute is optional.
*/
enum qca_wlan_vendor_attr_external_acs_channels {
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
/* One of reason code (u8) from enum qca_wlan_vendor_acs_select_reason
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON = 1,
/* Array of nested values for each channel with following attributes:
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1,
* QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
*/
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST = 2,
/* This (u8) will hold values of one of enum nl80211_bands */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND = 3,
/* Primary channel (u8) */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY = 4,
/* Secondary channel (u8) used for HT 40 MHz channels */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY = 5,
/* VHT seg0 channel (u8) */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 = 6,
/* VHT seg1 channel (u8) */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 = 7,
/* Channel width (u8). Takes one of enum nl80211_chan_width values. */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH = 8,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_LIST = 9,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_PRIMARY = 10,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_SECONDARY = 11,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG0 = 12,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_FREQUENCY_CENTER_SEG1 = 13,
/* keep last */
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX =
QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST - 1
};
enum qca_chip_power_save_failure_reason {
/* Indicates if the reason for the failure is due to a protocol
* layer/module.
*/
QCA_CHIP_POWER_SAVE_FAILURE_REASON_PROTOCOL = 0,
/* Indicates if the reason for the failure is due to a hardware issue.
*/
QCA_CHIP_POWER_SAVE_FAILURE_REASON_HARDWARE = 1,
};
/**
* qca_attr_chip_power_save_failure: Attributes to vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE. This carries the requisite
* information leading to the power save failure.
*/
enum qca_attr_chip_power_save_failure {
QCA_ATTR_CHIP_POWER_SAVE_FAILURE_INVALID = 0,
/* Reason to cause the power save failure.
* These reasons are represented by
* enum qca_chip_power_save_failure_reason.
*/
QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON = 1,
/* keep last */
QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST,
QCA_ATTR_CHIP_POWER_SAVE_FAILURE_MAX =
QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST - 1,
};
/**
* qca_wlan_vendor_nud_stats_data_pkt_flags: Flag representing the various
* data types for which the stats have to get collected.
*/
enum qca_wlan_vendor_nud_stats_data_pkt_flags {
QCA_WLAN_VENDOR_NUD_STATS_DATA_ARP = 1 << 0,
QCA_WLAN_VENDOR_NUD_STATS_DATA_DNS = 1 << 1,
QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_HANDSHAKE = 1 << 2,
QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV4 = 1 << 3,
QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV6 = 1 << 4,
/* Used by QCA_ATTR_NUD_STATS_PKT_TYPE only in nud stats get
* to represent the stats of respective data type.
*/
QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN = 1 << 5,
QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN_ACK = 1 << 6,
QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_ACK = 1 << 7,
};
enum qca_wlan_vendor_nud_stats_set_data_pkt_info {
QCA_ATTR_NUD_STATS_DATA_PKT_INFO_INVALID = 0,
/* Represents the data packet type to be monitored (u32).
* Host driver tracks the stats corresponding to each data frame
* represented by these flags.
* These data packets are represented by
* enum qca_wlan_vendor_nud_stats_data_pkt_flags
*/
QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE = 1,
/* Name corresponding to the DNS frame for which the respective DNS
* stats have to get monitored (string). Max string length 255.
*/
QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DNS_DOMAIN_NAME = 2,
/* source port on which the respective proto stats have to get
* collected (u32).
*/
QCA_ATTR_NUD_STATS_DATA_PKT_INFO_SRC_PORT = 3,
/* destination port on which the respective proto stats have to get
* collected (u32).
*/
QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_PORT = 4,
/* IPv4 address for which the destined data packets have to be
* monitored. (in network byte order), u32.
*/
QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV4 = 5,
/* IPv6 address for which the destined data packets have to be
* monitored. (in network byte order), 16 bytes array.
*/
QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV6 = 6,
QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST,
QCA_ATTR_NUD_STATS_DATA_PKT_INFO_MAX =
QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST - 1,
};
/**
* qca_wlan_vendor_attr_nud_stats_set: Attributes to vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET. This carries the requisite
* information to start/stop the NUD statistics collection.
*/
enum qca_attr_nud_stats_set {
QCA_ATTR_NUD_STATS_SET_INVALID = 0,
/* Flag to start/stop the NUD statistics collection.
* Start - If included, Stop - If not included
*/
QCA_ATTR_NUD_STATS_SET_START = 1,
/* IPv4 address of the default gateway (in network byte order), u32 */
QCA_ATTR_NUD_STATS_GW_IPV4 = 2,
/* Represents the list of data packet types to be monitored.
* Host driver tracks the stats corresponding to each data frame
* represented by these flags.
* These data packets are represented by
* enum qca_wlan_vendor_nud_stats_set_data_pkt_info
*/
QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO = 3,
/* keep last */
QCA_ATTR_NUD_STATS_SET_LAST,
QCA_ATTR_NUD_STATS_SET_MAX =
QCA_ATTR_NUD_STATS_SET_LAST - 1,
};
enum qca_attr_nud_data_stats {
QCA_ATTR_NUD_DATA_STATS_INVALID = 0,
/* Data packet type for which the stats are collected (u32).
* Represented by enum qca_wlan_vendor_nud_stats_data_pkt_flags
*/
QCA_ATTR_NUD_STATS_PKT_TYPE = 1,
/* Name corresponding to the DNS frame for which the respective DNS
* stats are monitored (string). Max string length 255.
*/
QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME = 2,
/* source port on which the respective proto stats are collected (u32).
*/
QCA_ATTR_NUD_STATS_PKT_SRC_PORT = 3,
/* destination port on which the respective proto stats are collected
* (u32).
*/
QCA_ATTR_NUD_STATS_PKT_DEST_PORT = 4,
/* IPv4 address for which the destined data packets have to be
* monitored. (in network byte order), u32.
*/
QCA_ATTR_NUD_STATS_PKT_DEST_IPV4 = 5,
/* IPv6 address for which the destined data packets have to be
* monitored. (in network byte order), 16 bytes array.
*/
QCA_ATTR_NUD_STATS_PKT_DEST_IPV6 = 6,
/* Data packet Request count received from netdev (u32). */
QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV = 7,
/* Data packet Request count sent to lower MAC from upper MAC (u32). */
QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC = 8,
/* Data packet Request count received by lower MAC from upper MAC
* (u32)
*/
QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC = 9,
/* Data packet Request count successfully transmitted by the device
* (u32)
*/
QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS = 10,
/* Data packet Response count received by lower MAC (u32) */
QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC = 11,
/* Data packet Response count received by upper MAC (u32) */
QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC = 12,
/* Data packet Response count delivered to netdev (u32) */
QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV = 13,
/* Data Packet Response count that are dropped out of order (u32) */
QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP = 14,
/* keep last */
QCA_ATTR_NUD_DATA_STATS_LAST,
QCA_ATTR_NUD_DATA_STATS_MAX =
QCA_ATTR_NUD_DATA_STATS_LAST - 1,
};
/**
* qca_attr_nud_stats_get: Attributes to vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET. This carries the requisite
* NUD statistics collected when queried.
*/
enum qca_attr_nud_stats_get {
QCA_ATTR_NUD_STATS_GET_INVALID = 0,
/* ARP Request count from netdev (u32) */
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV = 1,
/* ARP Request count sent to lower MAC from upper MAC (u32) */
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC = 2,
/* ARP Request count received by lower MAC from upper MAC (u32) */
QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC = 3,
/* ARP Request count successfully transmitted by the device (u32) */
QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS = 4,
/* ARP Response count received by lower MAC (u32) */
QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC = 5,
/* ARP Response count received by upper MAC (u32) */
QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC = 6,
/* ARP Response count delivered to netdev (u32) */
QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV = 7,
/* ARP Response count dropped due to out of order reception (u32) */
QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP = 8,
/* Flag indicating if the station's link to the AP is active.
* Active Link - If included, Inactive link - If not included
*/
QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE = 9,
/* Flag indicating if there is any duplicate address detected (DAD).
* Yes - If detected, No - If not detected.
*/
QCA_ATTR_NUD_STATS_IS_DAD = 10,
/* List of Data packet types for which the stats are requested.
* This list does not carry ARP stats as they are done by the
* above attributes. Represented by enum qca_attr_nud_data_stats.
*/
QCA_ATTR_NUD_STATS_DATA_PKT_STATS = 11,
/* keep last */
QCA_ATTR_NUD_STATS_GET_LAST,
QCA_ATTR_NUD_STATS_GET_MAX =
QCA_ATTR_NUD_STATS_GET_LAST - 1,
};
enum qca_wlan_btm_candidate_status {
QCA_STATUS_ACCEPT = 0,
QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED = 1,
QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED = 2,
QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY = 3,
QCA_STATUS_REJECT_LOW_RSSI = 4,
QCA_STATUS_REJECT_HIGH_INTERFERENCE = 5,
QCA_STATUS_REJECT_UNKNOWN = 6,
};
enum qca_wlan_vendor_attr_btm_candidate_info {
QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_INVALID = 0,
/* 6-byte MAC address representing the BSSID of transition candidate */
QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID = 1,
/* Unsigned 32-bit value from enum qca_wlan_btm_candidate_status
* returned by the driver. It says whether the BSSID provided in
* QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID is acceptable by
* the driver, if not it specifies the reason for rejection.
* Note that the user-space can overwrite the transition reject reason
* codes provided by driver based on more information.
*/
QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX =
QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST - 1,
};
enum qca_attr_trace_level {
QCA_ATTR_TRACE_LEVEL_INVALID = 0,
/*
* Nested array of the following attributes:
* QCA_ATTR_TRACE_LEVEL_MODULE,
* QCA_ATTR_TRACE_LEVEL_MASK.
*/
QCA_ATTR_TRACE_LEVEL_PARAM = 1,
/*
* Specific QCA host driver module. Please refer to the QCA host
* driver implementation to get the specific module ID.
*/
QCA_ATTR_TRACE_LEVEL_MODULE = 2,
/* Different trace level masks represented in the QCA host driver. */
QCA_ATTR_TRACE_LEVEL_MASK = 3,
/* keep last */
QCA_ATTR_TRACE_LEVEL_AFTER_LAST,
QCA_ATTR_TRACE_LEVEL_MAX =
QCA_ATTR_TRACE_LEVEL_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_get_he_capabilities - IEEE 802.11ax HE capabilities
*/
enum qca_wlan_vendor_attr_get_he_capabilities {
QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_INVALID = 0,
/* Whether HE capabilities is supported
* (u8 attribute: 0 = not supported, 1 = supported)
*/
QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED = 1,
/* HE PHY capabilities, array of 3 u32 values */
QCA_WLAN_VENDOR_ATTR_PHY_CAPAB = 2,
/* HE MAC capabilities (u32 attribute) */
QCA_WLAN_VENDOR_ATTR_MAC_CAPAB = 3,
/* HE MCS map (u32 attribute) */
QCA_WLAN_VENDOR_ATTR_HE_MCS = 4,
/* Number of SS (u32 attribute) */
QCA_WLAN_VENDOR_ATTR_NUM_SS = 5,
/* RU count (u32 attribute) */
QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK = 6,
/* PPE threshold data, array of 8 u32 values */
QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD = 7,
/* keep last */
QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX =
QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_spectral_scan - Spectral scan config parameters
*/
enum qca_wlan_vendor_attr_spectral_scan {
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INVALID = 0,
/* Number of times the chip enters spectral scan mode before
* deactivating spectral scans. When set to 0, chip will enter spectral
* scan mode continuously. u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_COUNT = 1,
/* Spectral scan period. Period increment resolution is 256*Tclk,
* where Tclk = 1/44 MHz (Gmode), 1/40 MHz (Amode). u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_PERIOD = 2,
/* Spectral scan priority. u32 attribute. */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PRIORITY = 3,
/* Number of FFT data points to compute. u32 attribute. */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_SIZE = 4,
/* Enable targeted gain change before starting the spectral scan FFT.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_GC_ENA = 5,
/* Restart a queued spectral scan. u32 attribute. */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RESTART_ENA = 6,
/* Noise floor reference number for the calculation of bin power.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NOISE_FLOOR_REF = 7,
/* Disallow spectral scan triggers after TX/RX packets by setting
* this delay value to roughly SIFS time period or greater.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INIT_DELAY = 8,
/* Number of strong bins (inclusive) per sub-channel, below
* which a signal is declared a narrow band tone. u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NB_TONE_THR = 9,
/* Specify the threshold over which a bin is declared strong (for
* scan bandwidth analysis). u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_STR_BIN_THR = 10,
/* Spectral scan report mode. u32 attribute. */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_WB_RPT_MODE = 11,
/* RSSI report mode, if the ADC RSSI is below
* QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR,
* then FFTs will not trigger, but timestamps and summaries get
* reported. u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_RPT_MODE = 12,
/* ADC RSSI must be greater than or equal to this threshold (signed dB)
* to ensure spectral scan reporting with normal error code.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR = 13,
/* Format of frequency bin magnitude for spectral scan triggered FFTs:
* 0: linear magnitude, 1: log magnitude (20*log10(lin_mag)).
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PWR_FORMAT = 14,
/* Format of FFT report to software for spectral scan triggered FFTs.
* 0: No FFT report (only spectral scan summary report)
* 1: 2-dword summary of metrics for each completed FFT + spectral scan
* report
* 2: 2-dword summary of metrics for each completed FFT + 1x-oversampled
* bins (in-band) per FFT + spectral scan summary report
* 3: 2-dword summary of metrics for each completed FFT + 2x-oversampled
* bins (all) per FFT + spectral scan summary report
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RPT_MODE = 15,
/* Number of LSBs to shift out in order to scale the FFT bins.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_BIN_SCALE = 16,
/* Set to 1 (with spectral_scan_pwr_format=1), to report bin magnitudes
* in dBm power. u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DBM_ADJ = 17,
/* Per chain enable mask to select input ADC for search FFT.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_CHN_MASK = 18,
/* An unsigned 64-bit integer provided by host driver to identify the
* spectral scan request. This attribute is included in the scan
* response message for @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START
* and used as an attribute in
* @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP to identify the
* specific scan to be stopped.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE = 19,
/* Skip interval for FFT reports. u32 attribute */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_PERIOD = 20,
/* Set to report only one set of FFT results.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SHORT_REPORT = 21,
/* Debug level for spectral module in driver.
* 0 : Verbosity level 0
* 1 : Verbosity level 1
* 2 : Verbosity level 2
* 3 : Matched filterID display
* 4 : One time dump of FFT report
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DEBUG_LEVEL = 22,
/* Type of spectral scan request. u32 attribute.
* It uses values defined in enum
* qca_wlan_vendor_attr_spectral_scan_request_type.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE = 23,
/* This specifies the frequency span over which spectral
* scan would be carried out. Its value depends on the
* value of QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE and
* the relation is as follows.
* QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL
* Not applicable. Spectral scan would happen in the
* operating span.
* QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE
* Center frequency (in MHz) of the span of interest or
* for convenience, center frequency (in MHz) of any channel
* in the span of interest. For 80+80 MHz agile spectral scan
* request it represents center frequency (in MHz) of the primary
* 80 MHz span or for convenience, center frequency (in MHz) of any
* channel in the primary 80 MHz span. If agile spectral scan is
* initiated without setting a valid frequency it returns the
* error code
* (QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED).
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FREQUENCY = 24,
/* Spectral scan mode. u32 attribute.
* It uses values defined in enum qca_wlan_vendor_spectral_scan_mode.
* If this attribute is not present, it is assumed to be
* normal mode (QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL).
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE = 25,
/* Spectral scan error code. u32 attribute.
* It uses values defined in enum
* qca_wlan_vendor_spectral_scan_error_code.
* This attribute is included only in failure scenarios.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE = 26,
/* 8-bit unsigned value to enable/disable debug of the
* Spectral DMA ring.
* 1-enable, 0-disable
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DMA_RING_DEBUG = 27,
/* 8-bit unsigned value to enable/disable debug of the
* Spectral DMA buffers.
* 1-enable, 0-disable
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DMA_BUFFER_DEBUG = 28,
/* This specifies the frequency span over which spectral scan would be
* carried out. Its value depends on the value of
* QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE and the relation is as
* follows.
* QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL
* Not applicable. Spectral scan would happen in the operating span.
* QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE
* This attribute is applicable only for agile spectral scan
* requests in 80+80 MHz mode. It represents center frequency (in
* MHz) of the secondary 80 MHz span or for convenience, center
* frequency (in MHz) of any channel in the secondary 80 MHz span.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FREQUENCY_2 = 29,
+ /* This attribute specifies the bandwidth to be used for spectral scan
+ * operation. This is an u8 attribute and uses the values in enum
+ * nl80211_chan_width. This is an optional attribute.
+ * If this attribute is not populated, the driver should configure the
+ * spectral scan bandwidth to the maximum value supported by the target
+ * for the current operating bandwidth.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_BANDWIDTH = 30,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX =
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_spectral_diag_stats - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS.
*/
enum qca_wlan_vendor_attr_spectral_diag_stats {
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_INVALID = 0,
/* Number of spectral TLV signature mismatches.
* u64 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SIG_MISMATCH = 1,
/* Number of spectral phyerror events with insufficient length when
* parsing for secondary 80 search FFT report. u64 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SEC80_SFFT_INSUFFLEN = 2,
/* Number of spectral phyerror events without secondary 80
* search FFT report. u64 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_NOSEC80_SFFT = 3,
/* Number of spectral phyerror events with vht operation segment 1 id
* mismatches in search fft report. u64 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG1ID_MISMATCH = 4,
/* Number of spectral phyerror events with vht operation segment 2 id
* mismatches in search fft report. u64 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG2ID_MISMATCH = 5,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_MAX =
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_spectral_cap - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO.
*/
enum qca_wlan_vendor_attr_spectral_cap {
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_INVALID = 0,
/* Flag attribute to indicate phydiag capability */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_PHYDIAG = 1,
/* Flag attribute to indicate radar detection capability */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RADAR = 2,
/* Flag attribute to indicate spectral capability */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_SPECTRAL = 3,
/* Flag attribute to indicate advanced spectral capability */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_ADVANCED_SPECTRAL = 4,
/* Spectral hardware generation. u32 attribute.
* It uses values defined in enum
* qca_wlan_vendor_spectral_scan_cap_hw_gen.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN = 5,
/* Spectral bin scaling formula ID. u16 attribute.
* It uses values defined in enum
* qca_wlan_vendor_spectral_scan_cap_formula_id.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID = 6,
/* Spectral bin scaling param - low level offset.
* s16 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_LOW_LEVEL_OFFSET = 7,
/* Spectral bin scaling param - high level offset.
* s16 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HIGH_LEVEL_OFFSET = 8,
/* Spectral bin scaling param - RSSI threshold.
* s16 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RSSI_THR = 9,
/* Spectral bin scaling param - default AGC max gain.
* u8 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_DEFAULT_AGC_MAX_GAIN = 10,
/* Flag attribute to indicate agile spectral scan capability
* for 20/40/80 MHz modes.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL = 11,
/* Flag attribute to indicate agile spectral scan capability
* for 160 MHz mode.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL_160 = 12,
/* Flag attribute to indicate agile spectral scan capability
* for 80+80 MHz mode.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL_80_80 = 13,
/* Number of spectral detectors used for scan in 20 MHz.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_20_MHZ = 14,
/* Number of spectral detectors used for scan in 40 MHz.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_40_MHZ = 15,
/* Number of spectral detectors used for scan in 80 MHz.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_80_MHZ = 16,
/* Number of spectral detectors used for scan in 160 MHz.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_160_MHZ = 17,
/* Number of spectral detectors used for scan in 80+80 MHz.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_80P80_MHZ = 18,
+ /* Flag attribute to indicate agile spectral scan capability
+ * for 320 MHz mode.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AGILE_SPECTRAL_320 = 19,
+ /* Number of spectral detectors used for scan in 320 MHz.
+ * u32 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_NUM_DETECTORS_320_MHZ = 20,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX =
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_spectral_scan_status - used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS.
*/
enum qca_wlan_vendor_attr_spectral_scan_status {
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_INVALID = 0,
/* Flag attribute to indicate whether spectral scan is enabled */
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ENABLED = 1,
/* Flag attribute to indicate whether spectral scan is in progress*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ACTIVE = 2,
/* Spectral scan mode. u32 attribute.
* It uses values defined in enum qca_wlan_vendor_spectral_scan_mode.
* If this attribute is not present, normal mode
* (QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL is assumed to be
* requested.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MODE = 3,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MAX =
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST - 1,
};
/**
* qca_wlan_vendor_attr_spectral_scan_request_type: Attribute values for
* QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE to the vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START. This represents the
* spectral scan request types.
* @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG: Request to
* set the spectral parameters and start scan.
* @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN: Request to
* only set the spectral parameters.
* @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG: Request to
* only start the spectral scan.
*/
enum qca_wlan_vendor_attr_spectral_scan_request_type {
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG,
};
/**
* qca_wlan_vendor_spectral_scan_mode: Attribute values for
* QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_MODE in the vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START and
* QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MODE in the vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS. This represents the
* spectral scan modes.
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL: Normal spectral scan:
* spectral scan in the current operating span.
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE: Agile spectral scan:
* spectral scan in the configured agile span.
*/
enum qca_wlan_vendor_spectral_scan_mode {
QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_NORMAL = 0,
QCA_WLAN_VENDOR_SPECTRAL_SCAN_MODE_AGILE = 1,
};
/**
* qca_wlan_vendor_spectral_scan_error_code: Attribute values for
* QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_ERROR_CODE in the vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START.
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_UNSUPPORTED: Changing the value
* of a parameter is not supported.
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_MODE_UNSUPPORTED: Requested spectral scan
* mode is not supported.
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_INVALID_VALUE: A parameter
* has invalid value.
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED: A parameter
* is not initialized.
*/
enum qca_wlan_vendor_spectral_scan_error_code {
QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_UNSUPPORTED = 0,
QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_MODE_UNSUPPORTED = 1,
QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_INVALID_VALUE = 2,
QCA_WLAN_VENDOR_SPECTRAL_SCAN_ERR_PARAM_NOT_INITIALIZED = 3,
};
/**
* qca_wlan_vendor_spectral_scan_cap_hw_gen: Attribute values for
* QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN to the vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the
* spectral hardware generation.
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1: generation 1
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2: generation 2
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3: generation 3
*/
enum qca_wlan_vendor_spectral_scan_cap_hw_gen {
QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1 = 0,
QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2 = 1,
QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3 = 2,
};
enum qca_wlan_vendor_tos {
QCA_WLAN_VENDOR_TOS_BK = 0,
QCA_WLAN_VENDOR_TOS_BE = 1,
QCA_WLAN_VENDOR_TOS_VI = 2,
QCA_WLAN_VENDOR_TOS_VO = 3,
};
/**
* enum qca_wlan_vendor_attr_active_tos - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS.
*/
enum qca_wlan_vendor_attr_active_tos {
QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_INVALID = 0,
/* Type Of Service - Represented by qca_wlan_vendor_tos */
QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS = 1,
/* Flag attribute representing the start (attribute included) or stop
* (attribute not included) of the respective TOS.
*/
QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START = 2,
};
enum qca_wlan_vendor_hang_reason {
/* Unspecified reason */
QCA_WLAN_HANG_REASON_UNSPECIFIED = 0,
/* No Map for the MAC entry for the received frame */
QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND = 1,
/* Peer deletion timeout happened */
QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT = 2,
/* Peer unmap timeout */
QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT = 3,
/* Scan request timed out */
QCA_WLAN_HANG_SCAN_REQ_EXPIRED = 4,
/* Consecutive Scan attempt failures */
QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES = 5,
/* Unable to get the message buffer */
QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE = 6,
/* Current command processing is timedout */
QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT = 7,
/* Timeout for an ACK from FW for suspend request */
QCA_WLAN_HANG_SUSPEND_TIMEOUT = 8,
/* Timeout for an ACK from FW for resume request */
QCA_WLAN_HANG_RESUME_TIMEOUT = 9,
/* Transmission timeout for consecutive data frames */
QCA_WLAN_HANG_TRANSMISSIONS_TIMEOUT = 10,
/* Timeout for the TX completion status of data frame */
QCA_WLAN_HANG_TX_COMPLETE_TIMEOUT = 11,
/* DXE failure for TX/RX, DXE resource unavailability */
QCA_WLAN_HANG_DXE_FAILURE = 12,
/* WMI pending commands exceed the maximum count */
QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS = 13,
/* Timeout for peer STA connection accept command's response from the
* FW in AP mode. This command is triggered when a STA (peer) connects
* to AP (DUT).
*/
QCA_WLAN_HANG_AP_STA_CONNECT_REQ_TIMEOUT = 14,
/* Timeout for the AP connection accept command's response from the FW
* in STA mode. This command is triggered when the STA (DUT) connects
* to an AP (peer).
*/
QCA_WLAN_HANG_STA_AP_CONNECT_REQ_TIMEOUT = 15,
/* Timeout waiting for the response to the MAC HW mode change command
* sent to FW as a part of MAC mode switch among DBS (Dual Band
* Simultaneous), SCC (Single Channel Concurrency), and MCC (Multi
* Channel Concurrency) mode.
*/
QCA_WLAN_HANG_MAC_HW_MODE_CHANGE_TIMEOUT = 16,
/* Timeout waiting for the response from FW to configure the MAC HW's
* mode. This operation is to configure the single/two MACs in either
* SCC/MCC/DBS mode.
*/
QCA_WLAN_HANG_MAC_HW_MODE_CONFIG_TIMEOUT = 17,
/* Timeout waiting for response of VDEV start command from the FW */
QCA_WLAN_HANG_VDEV_START_RESPONSE_TIMED_OUT = 18,
/* Timeout waiting for response of VDEV restart command from the FW */
QCA_WLAN_HANG_VDEV_RESTART_RESPONSE_TIMED_OUT = 19,
/* Timeout waiting for response of VDEV stop command from the FW */
QCA_WLAN_HANG_VDEV_STOP_RESPONSE_TIMED_OUT = 20,
/* Timeout waiting for response of VDEV delete command from the FW */
QCA_WLAN_HANG_VDEV_DELETE_RESPONSE_TIMED_OUT = 21,
/* Timeout waiting for response of peer all delete request command to
* the FW on a specific VDEV.
*/
QCA_WLAN_HANG_VDEV_PEER_DELETE_ALL_RESPONSE_TIMED_OUT = 22,
/* WMI sequence mismatch between WMI command and Tx completion */
QCA_WLAN_HANG_WMI_BUF_SEQUENCE_MISMATCH = 23,
/* Write to Device HAL register failed */
QCA_WLAN_HANG_REG_WRITE_FAILURE = 24,
/* No credit left to send the wow_wakeup_from_sleep to firmware */
QCA_WLAN_HANG_SUSPEND_NO_CREDIT = 25,
/* Bus failure */
QCA_WLAN_HANG_BUS_FAILURE = 26,
/* tasklet/credit latency found */
QCA_WLAN_HANG_TASKLET_CREDIT_LATENCY_DETECT = 27,
};
/**
* enum qca_wlan_vendor_attr_hang - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_HANG.
*/
enum qca_wlan_vendor_attr_hang {
QCA_WLAN_VENDOR_ATTR_HANG_INVALID = 0,
/* Reason for the hang - u32 attribute with a value from enum
* qca_wlan_vendor_hang_reason.
*/
QCA_WLAN_VENDOR_ATTR_HANG_REASON = 1,
/* The binary blob data associated with the hang reason specified by
* QCA_WLAN_VENDOR_ATTR_HANG_REASON. This binary data is expected to
* contain the required dump to analyze the reason for the hang.
* NLA_BINARY attribute, the max size is 1024 bytes.
*/
QCA_WLAN_VENDOR_ATTR_HANG_REASON_DATA = 2,
QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_HANG_MAX =
QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_flush_pending - Attributes for
* flushing pending traffic in firmware.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_ADDR: Configure peer MAC address.
* @QCA_WLAN_VENDOR_ATTR_AC: Configure access category of the pending
* packets. It is u8 value with bit 0~3 represent AC_BE, AC_BK,
* AC_VI, AC_VO respectively. Set the corresponding bit to 1 to
* flush packets with access category.
*/
enum qca_wlan_vendor_attr_flush_pending {
QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_PEER_ADDR = 1,
QCA_WLAN_VENDOR_ATTR_AC = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX =
QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST - 1,
};
/**
* qca_wlan_vendor_spectral_scan_cap_formula_id: Attribute values for
* QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID in the vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the
* Spectral bin scaling formula ID.
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING: No scaling
* @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED: AGC gain
* and RSSI threshold based formula.
*/
enum qca_wlan_vendor_spectral_scan_cap_formula_id {
QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING = 0,
QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED = 1,
};
/**
* enum qca_wlan_vendor_attr_rropavail_info - Specifies whether Representative
* RF Operating Parameter (RROP) information is available, and if so, at which
* point in the application-driver interaction sequence it can be retrieved by
* the application from the driver. This point may vary by architecture and
* other factors. This is a u16 value.
*/
enum qca_wlan_vendor_attr_rropavail_info {
/* RROP information is unavailable. */
QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_UNAVAILABLE,
/* RROP information is available and the application can retrieve the
* information after receiving an QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS
* event from the driver.
*/
QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_EXTERNAL_ACS_START,
/* RROP information is available only after a vendor specific scan
* (requested using QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) has
* successfully completed. The application can retrieve the information
* after receiving the QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE event from
* the driver.
*/
QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_VSCAN_END,
};
/**
* enum qca_wlan_vendor_attr_rrop_info - Specifies vendor specific
* Representative RF Operating Parameter (RROP) information. It is sent for the
* vendor command QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO. This information is
* intended for use by external Auto Channel Selection applications. It provides
* guidance values for some RF parameters that are used by the system during
* operation. These values could vary by channel, band, radio, and so on.
*/
enum qca_wlan_vendor_attr_rrop_info {
QCA_WLAN_VENDOR_ATTR_RROP_INFO_INVALID = 0,
/* Representative Tx Power List (RTPL) which has an array of nested
* values as per attributes in enum qca_wlan_vendor_attr_rtplinst.
*/
QCA_WLAN_VENDOR_ATTR_RROP_INFO_RTPL = 1,
QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_RROP_INFO_MAX =
QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_attr_rtplinst - Specifies attributes for individual list
* entry instances in the Representative Tx Power List (RTPL). It provides
* simplified power values intended for helping external Auto channel Selection
* applications compare potential Tx power performance between channels, other
* operating conditions remaining identical. These values are not necessarily
* the actual Tx power values that will be used by the system. They are also not
* necessarily the max or average values that will be used. Instead, they are
* relative, summarized keys for algorithmic use computed by the driver or
* underlying firmware considering a number of vendor specific factors.
*/
enum qca_wlan_vendor_attr_rtplinst {
QCA_WLAN_VENDOR_ATTR_RTPLINST_INVALID = 0,
/* Primary channel number (u8).
* Note: If both the driver and user space application support the
* 6 GHz band, this attribute is deprecated and
* QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY_FREQUENCY should be used. To
* maintain backward compatibility,
* QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY is still used if either the
* driver or user space application or both do not support the 6 GHz
* band.
*/
QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY = 1,
/* Representative Tx power in dBm (s32) with emphasis on throughput. */
QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_THROUGHPUT = 2,
/* Representative Tx power in dBm (s32) with emphasis on range. */
QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_RANGE = 3,
/* Primary channel center frequency (u32) in MHz */
QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY_FREQUENCY = 4,
QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_RTPLINST_MAX =
QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_config_latency_level - Level for
* wlan latency module.
*
* There will be various of Wi-Fi functionality like scan/roaming/adaptive
* power saving which would causing data exchange out of service, this
* would be a big impact on latency. For latency sensitive applications over
* Wi-Fi are intolerant to such operations and thus would configure them
* to meet their respective needs. It is well understood by such applications
* that altering the default behavior would degrade the Wi-Fi functionality
* w.r.t the above pointed WLAN operations.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL:
* Default WLAN operation level which throughput orientated.
* @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE:
* Use moderate level to improve latency by limit scan duration.
* @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW:
* Use low latency level to benifit application like concurrent
* downloading or video streaming via constraint scan/adaptive PS.
* @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW:
* Use ultra low latency level to benefit for gaming/voice
* application via constraint scan/roaming/adaptive PS.
*/
enum qca_wlan_vendor_attr_config_latency_level {
QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL = 1,
QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE = 2,
QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW = 3,
QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MAX =
QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_wlan_mac - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO.
*/
enum qca_wlan_vendor_attr_mac {
QCA_WLAN_VENDOR_ATTR_MAC_INVALID = 0,
/* MAC mode info list which has an array of nested values as
* per attributes in enum qca_wlan_vendor_attr_mac_mode_info.
*/
QCA_WLAN_VENDOR_ATTR_MAC_INFO = 1,
/* keep last */
QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MAC_MAX =
QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_mac_iface_info - Information of the connected
* Wi-Fi netdev interface on a respective MAC.
* Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO.
*/
enum qca_wlan_vendor_attr_mac_iface_info {
QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_INVALID = 0,
/* Wi-Fi netdev's interface index (u32) */
QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX = 1,
/* Associated frequency in MHz of the connected Wi-Fi interface (u32) */
QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX =
QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_mac_info - Points to MAC the information.
* Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_INFO of the
* vendor command QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO.
*/
enum qca_wlan_vendor_attr_mac_info {
QCA_WLAN_VENDOR_ATTR_MAC_INFO_INVALID = 0,
/* Hardware MAC ID associated for the MAC (u32) */
QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID = 1,
/* Band supported by the MAC at a given point.
* This is a u32 bitmask of BIT(NL80211_BAND_*) as described in %enum
* nl80211_band.
*/
QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND = 2,
/* Refers to list of WLAN netdev interfaces associated with this MAC.
* Represented by enum qca_wlan_vendor_attr_mac_iface_info.
*/
QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO = 3,
/* keep last */
QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX =
QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_get_logger_features - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET.
*/
enum qca_wlan_vendor_attr_get_logger_features {
QCA_WLAN_VENDOR_ATTR_LOGGER_INVALID = 0,
/* Unsigned 32-bit enum value of wifi_logger_supported_features */
QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED = 1,
/* keep last */
QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LOGGER_MAX =
QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST - 1,
};
/**
* enum wifi_logger_supported_features - Values for supported logger features
*/
enum wifi_logger_supported_features {
WIFI_LOGGER_MEMORY_DUMP_FEATURE = (1 << (0)),
WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_FEATURE = (1 << (1)),
WIFI_LOGGER_CONNECT_EVENT_FEATURE = (1 << (2)),
WIFI_LOGGER_POWER_EVENT_FEATURE = (1 << (3)),
WIFI_LOGGER_WAKE_LOCK_FEATURE = (1 << (4)),
WIFI_LOGGER_VERBOSE_FEATURE = (1 << (5)),
WIFI_LOGGER_WATCHDOG_TIMER_FEATURE = (1 << (6)),
WIFI_LOGGER_DRIVER_DUMP_FEATURE = (1 << (7)),
WIFI_LOGGER_PACKET_FATE_FEATURE = (1 << (8)),
};
/**
* enum qca_wlan_tdls_caps_features_supported - Values for TDLS get
* capabilities features
*/
enum qca_wlan_tdls_caps_features_supported {
WIFI_TDLS_SUPPORT = (1 << (0)),
WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT = (1 << (1)),
WIFI_TDLS_OFFCHANNEL_SUPPORT = (1 << (2))
};
/**
* enum qca_wlan_vendor_attr_tdls_get_capabilities - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES.
*/
enum qca_wlan_vendor_attr_tdls_get_capabilities {
QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_INVALID = 0,
/* Indicates the max concurrent sessions */
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS,
/* Indicates the support for features */
/* Unsigned 32-bit bitmap qca_wlan_tdls_caps_features_supported
*/
QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX =
QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST - 1,
};
/**
* enum qca_wlan_offloaded_packets_sending_control - Offload packets control
* command used as value for the attribute
* QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL.
*/
enum qca_wlan_offloaded_packets_sending_control {
QCA_WLAN_OFFLOADED_PACKETS_SENDING_CONTROL_INVALID = 0,
QCA_WLAN_OFFLOADED_PACKETS_SENDING_START,
QCA_WLAN_OFFLOADED_PACKETS_SENDING_STOP
};
/**
* enum qca_wlan_vendor_attr_offloaded_packets - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS.
*/
enum qca_wlan_vendor_attr_offloaded_packets {
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_INVALID = 0,
/* Takes valid value from the enum
* qca_wlan_offloaded_packets_sending_control
* Unsigned 32-bit value
*/
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID,
/* array of u8 len: Max packet size */
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA,
/* 6-byte MAC address used to represent source MAC address */
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR,
/* 6-byte MAC address used to represent destination MAC address */
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR,
/* Unsigned 32-bit value, in milli seconds */
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD,
/* This optional unsigned 16-bit attribute is used for specifying
* ethernet protocol type. If not specified ethertype defaults to IPv4.
*/
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE,
/* keep last */
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX =
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST - 1,
};
/**
* enum qca_wlan_rssi_monitoring_control - RSSI control commands used as values
* by the attribute QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL.
*/
enum qca_wlan_rssi_monitoring_control {
QCA_WLAN_RSSI_MONITORING_CONTROL_INVALID = 0,
QCA_WLAN_RSSI_MONITORING_START,
QCA_WLAN_RSSI_MONITORING_STOP,
};
/**
* enum qca_wlan_vendor_attr_rssi_monitoring - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI.
*/
enum qca_wlan_vendor_attr_rssi_monitoring {
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_INVALID = 0,
/* Takes valid value from the enum
* qca_wlan_rssi_monitoring_control
* Unsigned 32-bit value enum qca_wlan_rssi_monitoring_control
*/
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL,
/* Unsigned 32-bit value */
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID,
/* Signed 8-bit value in dBm */
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI,
/* Signed 8-bit value in dBm */
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI,
/* attributes to be used/received in callback */
/* 6-byte MAC address used to represent current BSSID MAC address */
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID,
/* Signed 8-bit value indicating the current RSSI */
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI,
/* keep last */
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX =
QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_ndp_params - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_NDP.
*/
enum qca_wlan_vendor_attr_ndp_params {
QCA_WLAN_VENDOR_ATTR_NDP_PARAM_INVALID = 0,
/* Unsigned 32-bit value
* enum of sub commands values in qca_wlan_ndp_sub_cmd
*/
QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD,
/* Unsigned 16-bit value */
QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID,
/* NL attributes for data used NDP SUB cmds */
/* Unsigned 32-bit value indicating a service info */
QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID,
/* Unsigned 32-bit value; channel frequency in MHz */
QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL,
/* Interface Discovery MAC address. An array of 6 Unsigned int8 */
QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR,
/* Interface name on which NDP is being created */
QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR,
/* Unsigned 32-bit value for security */
/* CONFIG_SECURITY is deprecated, use NCS_SK_TYPE/PMK/SCID instead */
QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY,
/* Unsigned 32-bit value for QoS */
QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS,
/* Array of u8: len = QCA_WLAN_VENDOR_ATTR_NAN_DP_APP_INFO_LEN */
QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO,
/* Unsigned 32-bit value for NDP instance Id */
QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID,
/* Array of instance Ids */
QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY,
/* Unsigned 32-bit value for initiator/responder NDP response code
* accept/reject
*/
QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE,
/* NDI MAC address. An array of 6 Unsigned int8 */
QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR,
/* Unsigned 32-bit value errors types returned by driver
* The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy
* NanStatusType includes these values.
*/
QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE,
/* Unsigned 32-bit value error values returned by driver
* The nan_i.h in AOSP project platform/hardware/qcom/wlan
* NanInternalStatusType includes these values.
*/
QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE,
/* Unsigned 32-bit value for Channel setup configuration
* The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy
* NanDataPathChannelCfg includes these values.
*/
QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG,
/* Unsigned 32-bit value for Cipher Suite Shared Key Type */
QCA_WLAN_VENDOR_ATTR_NDP_CSID,
/* Array of u8: len = NAN_PMK_INFO_LEN 32 bytes */
QCA_WLAN_VENDOR_ATTR_NDP_PMK,
/* Security Context Identifier that contains the PMKID
* Array of u8: len = NAN_SCID_BUF_LEN 1024 bytes
*/
QCA_WLAN_VENDOR_ATTR_NDP_SCID,
/* Array of u8: len = NAN_SECURITY_MAX_PASSPHRASE_LEN 63 bytes */
QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE,
/* Array of u8: len = NAN_MAX_SERVICE_NAME_LEN 255 bytes */
QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME,
/* Unsigned 32-bit bitmap indicating schedule update
* BIT_0: NSS Update
* BIT_1: Channel list update
*/
QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REASON,
/* Unsigned 32-bit value for NSS */
QCA_WLAN_VENDOR_ATTR_NDP_NSS,
/* Unsigned 32-bit value for NUMBER NDP CHANNEL */
QCA_WLAN_VENDOR_ATTR_NDP_NUM_CHANNELS,
/* Unsigned 32-bit value for CHANNEL BANDWIDTH
* 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz
*/
QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH,
/* Array of channel/band width */
QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_INFO,
/* IPv6 address used by NDP (in network byte order), 16 bytes array.
* This attribute is used and optional for ndp request, ndp response,
* ndp indication, and ndp confirm.
*/
QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR = 27,
/* Unsigned 16-bit value indicating transport port used by NDP.
* This attribute is used and optional for ndp response, ndp indication,
* and ndp confirm.
*/
QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT = 28,
/* Unsigned 8-bit value indicating protocol used by NDP and assigned by
* the Internet Assigned Numbers Authority (IANA) as per:
* https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
* This attribute is used and optional for ndp response, ndp indication,
* and ndp confirm.
*/
QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL = 29,
/* Unsigned 8-bit value indicating if NDP remote peer supports NAN NDPE.
* 1:support 0:not support
*/
QCA_WLAN_VENDOR_ATTR_PEER_NDPE_SUPPORT = 30,
/* keep last */
QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX =
QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST - 1,
};
enum qca_wlan_ndp_sub_cmd {
QCA_WLAN_VENDOR_ATTR_NDP_INVALID = 0,
/* Command to create a NAN data path interface */
QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE = 1,
/* Command to delete a NAN data path interface */
QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE = 2,
/* Command to initiate a NAN data path session */
QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST = 3,
/* Command to notify if the NAN data path session was sent */
QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_RESPONSE = 4,
/* Command to respond to NAN data path session */
QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_REQUEST = 5,
/* Command to notify on the responder about the response */
QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE = 6,
/* Command to initiate a NAN data path end */
QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST = 7,
/* Command to notify the if end request was sent */
QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE = 8,
/* Command to notify the peer about the end request */
QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND = 9,
/* Command to confirm the NAN data path session is complete */
QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND = 10,
/* Command to indicate the peer about the end request being received */
QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 11,
/* Command to indicate the peer of schedule update */
QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12
};
/**
* enum qca_wlan_vendor_attr_nd_offload - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD.
*/
enum qca_wlan_vendor_attr_nd_offload {
QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_INVALID = 0,
/* Flag to set Neighbour Discovery offload */
QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG,
/* Keep last */
QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX =
QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST - 1,
};
/**
* enum packet_filter_sub_cmd - Packet filter sub commands
*/
enum packet_filter_sub_cmd {
/**
* Write packet filter program and/or data. The driver/firmware should
* disable APF before writing into local buffer and re-enable APF after
* writing is done.
*/
QCA_WLAN_SET_PACKET_FILTER = 1,
/* Get packet filter feature capabilities from driver */
QCA_WLAN_GET_PACKET_FILTER = 2,
/**
* Write packet filter program and/or data. User space will send the
* %QCA_WLAN_DISABLE_PACKET_FILTER command before issuing this command
* and will send the %QCA_WLAN_ENABLE_PACKET_FILTER afterwards. The key
* difference from that %QCA_WLAN_SET_PACKET_FILTER is the control over
* enable/disable is given to user space with this command. Also,
* user space sends the length of program portion in the buffer within
* %QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH.
*/
QCA_WLAN_WRITE_PACKET_FILTER = 3,
/* Read packet filter program and/or data */
QCA_WLAN_READ_PACKET_FILTER = 4,
/* Enable APF feature */
QCA_WLAN_ENABLE_PACKET_FILTER = 5,
/* Disable APF feature */
QCA_WLAN_DISABLE_PACKET_FILTER = 6,
};
/**
* enum qca_wlan_vendor_attr_packet_filter - BPF control commands used by
* vendor QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER.
*/
enum qca_wlan_vendor_attr_packet_filter {
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID = 0,
/* Unsigned 32-bit enum passed using packet_filter_sub_cmd */
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD,
/* Unsigned 32-bit value indicating the packet filter version */
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION,
/* Unsigned 32-bit value indicating the packet filter id */
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID,
/**
* Unsigned 32-bit value indicating the packet filter size including
* program + data.
*/
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE,
/* Unsigned 32-bit value indicating the packet filter current offset */
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET,
/* Program and/or data in bytes */
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM,
/* Unsigned 32-bit value of the length of the program section in packet
* filter buffer.
*/
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH = 7,
/* keep last */
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX =
QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_drv_info - WLAN driver info used by vendor command
* QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE.
*/
enum qca_wlan_vendor_drv_info {
QCA_WLAN_VENDOR_ATTR_DRV_INFO_INVALID = 0,
/* Maximum Message size info between firmware & HOST
* Unsigned 32-bit value
*/
QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE,
/* keep last */
QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX =
QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_wake_stats - Wake lock stats used by vendor
* command QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS.
*/
enum qca_wlan_vendor_attr_wake_stats {
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_INVALID = 0,
/* Unsigned 32-bit value indicating the total count of wake event */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_CMD_EVENT_WAKE,
/* Array of individual wake count, each index representing wake reason
*/
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR,
/* Unsigned 32-bit value representing wake count array */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_SZ,
/* Unsigned 32-bit total wake count value of driver/fw */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_DRIVER_FW_LOCAL_WAKE,
/* Array of wake stats of driver/fw */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR,
/* Unsigned 32-bit total wake count value of driver/fw */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_SZ,
/* Unsigned 32-bit total wake count value of packets received */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_RX_DATA_WAKE,
/* Unsigned 32-bit wake count value unicast packets received */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_UNICAST_CNT,
/* Unsigned 32-bit wake count value multicast packets received */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_MULTICAST_CNT,
/* Unsigned 32-bit wake count value broadcast packets received */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_BROADCAST_CNT,
/* Unsigned 32-bit wake count value of ICMP packets */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP_PKT,
/* Unsigned 32-bit wake count value of ICMP6 packets */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_PKT,
/* Unsigned 32-bit value ICMP6 router advertisement */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RA,
/* Unsigned 32-bit value ICMP6 neighbor advertisement */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NA,
/* Unsigned 32-bit value ICMP6 neighbor solicitation */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NS,
/* Unsigned 32-bit wake count value of receive side ICMP4 multicast */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP4_RX_MULTICAST_CNT,
/* Unsigned 32-bit wake count value of receive side ICMP6 multicast */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RX_MULTICAST_CNT,
/* Unsigned 32-bit wake count value of receive side multicast */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_OTHER_RX_MULTICAST_CNT,
/* Unsigned 32-bit wake count value of a given RSSI breach */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RSSI_BREACH_CNT,
/* Unsigned 32-bit wake count value of low RSSI */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_LOW_RSSI_CNT,
/* Unsigned 32-bit value GSCAN count */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_GSCAN_CNT,
/* Unsigned 32-bit value PNO complete count */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_COMPLETE_CNT,
/* Unsigned 32-bit value PNO match count */
QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_MATCH_CNT,
/* keep last */
QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST,
QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX =
QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_thermal_level - Defines various thermal levels
* configured by userspace to the driver/firmware.
* The values can be encapsulated in QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL or
* QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL attribute.
* The driver/firmware takes actions requested by userspace such as throttling
* wifi TX etc. in order to mitigate high temperature.
*
* @QCA_WLAN_VENDOR_THERMAL_LEVEL_NONE: Stop/clear all throttling actions.
* @QCA_WLAN_VENDOR_THERMAL_LEVEL_LIGHT: Throttle TX lightly.
* @QCA_WLAN_VENDOR_THERMAL_LEVEL_MODERATE: Throttle TX moderately.
* @QCA_WLAN_VENDOR_THERMAL_LEVEL_SEVERE: Throttle TX severely.
* @QCA_WLAN_VENDOR_THERMAL_LEVEL_CRITICAL: Critical thermal level reached.
* @QCA_WLAN_VENDOR_THERMAL_LEVEL_EMERGENCY: Emergency thermal level reached.
*/
enum qca_wlan_vendor_thermal_level {
QCA_WLAN_VENDOR_THERMAL_LEVEL_NONE = 0,
QCA_WLAN_VENDOR_THERMAL_LEVEL_LIGHT = 1,
QCA_WLAN_VENDOR_THERMAL_LEVEL_MODERATE = 2,
QCA_WLAN_VENDOR_THERMAL_LEVEL_SEVERE = 3,
QCA_WLAN_VENDOR_THERMAL_LEVEL_CRITICAL = 4,
QCA_WLAN_VENDOR_THERMAL_LEVEL_EMERGENCY = 5,
};
/**
* enum qca_wlan_vendor_attr_thermal_cmd - Vendor subcmd attributes to set
* cmd value. Used for NL attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
*/
enum qca_wlan_vendor_attr_thermal_cmd {
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_INVALID = 0,
/* The value of command, driver will implement different operations
* according to this value. It uses values defined in
* enum qca_wlan_vendor_attr_thermal_cmd_type.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE = 1,
/* Userspace uses this attribute to configure thermal level to the
* driver/firmware, or get thermal level from the driver/firmware.
* Used in request or response, u32 attribute,
* possible values are defined in enum qca_wlan_vendor_thermal_level.
*/
QCA_WLAN_VENDOR_ATTR_THERMAL_LEVEL = 2,
/* Userspace uses this attribute to configure the time in which the
* driver/firmware should complete applying settings it received from
* userspace with QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL
* command type. Used in request, u32 attribute, value is in
* milliseconds. A value of zero indicates to apply the settings
* immediately. The driver/firmware can delay applying the configured
* thermal settings within the time specified in this attribute if
* there is any critical ongoing operation.
*/
QCA_WLAN_VENDOR_ATTR_THERMAL_COMPLETION_WINDOW = 3,
+ /* Nested attribute, the driver/firmware uses this attribute to report
+ * thermal statistics of different thermal levels to userspace when
+ * requested using the
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_THERMAL_STATS command
+ * type. This attribute contains a nested array of records of thermal
+ * statistics of multiple levels. The attributes used inside this nested
+ * attribute are defined in enum qca_wlan_vendor_attr_thermal_stats.
+ */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_STATS = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX =
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST - 1
};
/**
* qca_wlan_vendor_attr_thermal_cmd_type: Attribute values for
* QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE to the vendor subcmd
* QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD. This represents the
* thermal command types sent to driver.
* @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS: Request to
* get thermal shutdown configuration parameters for display. Parameters
* responded from driver are defined in
* enum qca_wlan_vendor_attr_get_thermal_params_rsp.
* @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE: Request to
* get temperature. Host should respond with a temperature data. It is defined
* in enum qca_wlan_vendor_attr_thermal_get_temperature.
* @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND: Request to execute thermal
* suspend action.
* @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME: Request to execute thermal
* resume action.
* @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL: Configure thermal level to
* the driver/firmware.
* @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL: Request to get the current
* thermal level from the driver/firmware. The driver should respond with a
* thermal level defined in enum qca_wlan_vendor_thermal_level.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_THERMAL_STATS: Request to get the
+ * current thermal statistics from the driver/firmware. The driver should
+ * respond with statistics of all thermal levels encapsulated in the attribute
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_STATS.
+ * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_CLEAR_THERMAL_STATS: Request to clear
+ * the current thermal statistics for all thermal levels maintained in the
+ * driver/firmware and start counting from zero again.
*/
enum qca_wlan_vendor_attr_thermal_cmd_type {
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS,
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE,
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND,
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME,
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SET_LEVEL,
QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_LEVEL,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_THERMAL_STATS,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_CLEAR_THERMAL_STATS,
};
/**
* enum qca_wlan_vendor_attr_thermal_get_temperature - vendor subcmd attributes
* to get chip temperature by user.
* enum values are used for NL attributes for data used by
* QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE command for data used
* by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
*/
enum qca_wlan_vendor_attr_thermal_get_temperature {
QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_INVALID = 0,
/* Temperature value (degree Celsius) from driver.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_DATA,
/* keep last */
QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_MAX =
QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_get_thermal_params_rsp - vendor subcmd attributes
* to get configuration parameters of thermal shutdown feature. Enum values are
* used by QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS command for data
* used by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command.
*/
enum qca_wlan_vendor_attr_get_thermal_params_rsp {
QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_INVALID = 0,
/* Indicate if the thermal shutdown feature is enabled.
* NLA_FLAG attribute.
*/
QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_EN,
/* Indicate if the auto mode is enabled.
* Enable: Driver triggers the suspend/resume action.
* Disable: User space triggers the suspend/resume action.
* NLA_FLAG attribute.
*/
QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_AUTO_EN,
/* Thermal resume threshold (degree Celsius). Issue the resume command
* if the temperature value is lower than this threshold.
* u16 attribute.
*/
QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_RESUME_THRESH,
/* Thermal warning threshold (degree Celsius). FW reports temperature
* to driver if it's higher than this threshold.
* u16 attribute.
*/
QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_WARNING_THRESH,
/* Thermal suspend threshold (degree Celsius). Issue the suspend command
* if the temperature value is higher than this threshold.
* u16 attribute.
*/
QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SUSPEND_THRESH,
/* FW reports temperature data periodically at this interval (ms).
* u16 attribute.
*/
QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SAMPLE_RATE,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_MAX =
QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_thermal_event - vendor subcmd attributes to
* report thermal events from driver to user space.
* enum values are used for NL attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT sub command.
*/
enum qca_wlan_vendor_attr_thermal_event {
QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_INVALID = 0,
/* Temperature value (degree Celsius) from driver.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_TEMPERATURE,
/* Indication of resume completion from power save mode.
* NLA_FLAG attribute.
*/
QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_RESUME_COMPLETE,
/* Thermal level from the driver.
* u32 attribute. Possible values are defined in
* enum qca_wlan_vendor_thermal_level.
*/
QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_LEVEL = 3,
/* keep last */
QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_MAX =
QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_attr_thermal_stats - vendor subcmd attributes
+ * to get thermal status from the driver/firmware.
+ * enum values are used for NL attributes encapsulated inside the
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_STATS nested attribute.
+ *
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_MIN_TEMPERATURE: Minimum temperature
+ * of a thermal level in Celsius. u32 size.
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_MAX_TEMPERATURE: Maximum temperature
+ * of a thermal level in Celsius. u32 size.
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_DWELL_TIME: The total time spent on each
+ * thermal level in milliseconds. u32 size.
+ * QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_TEMP_LEVEL_COUNTER: Indicates the number
+ * of times the temperature crossed into the temperature range defined by the
+ * thermal level from both higher and lower directions. u32 size.
+ */
+enum qca_wlan_vendor_attr_thermal_stats {
+ QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_MIN_TEMPERATURE,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_MAX_TEMPERATURE,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_DWELL_TIME,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_TEMP_LEVEL_COUNTER,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_THERMAL_STATS_AFTER_LAST - 1,
+};
+
/**
* enum he_fragmentation_val - HE fragmentation support values
* Indicates level of dynamic fragmentation that is supported by
* a STA as a recipient.
* HE fragmentation values are defined in IEEE P802.11ax/D2.0, 9.4.2.237.2
* (HE MAC Capabilities Information field) and are used in HE Capabilities
* element to advertise the support. These values are validated in the driver
* to check the device capability and advertised in the HE Capabilities
* element. These values are used to configure testbed device to allow the
* advertised hardware capabilities to be downgraded for testing purposes.
*
* @HE_FRAG_DISABLE: no support for dynamic fragmentation
* @HE_FRAG_LEVEL1: support for dynamic fragments that are
* contained within an MPDU or S-MPDU, no support for dynamic fragments
* within an A-MPDU that is not an S-MPDU.
* @HE_FRAG_LEVEL2: support for dynamic fragments that are
* contained within an MPDU or S-MPDU and support for up to one dynamic
* fragment for each MSDU, each A-MSDU if supported by the recipient, and
* each MMPDU within an A-MPDU or multi-TID A-MPDU that is not an
* MPDU or S-MPDU.
* @HE_FRAG_LEVEL3: support for dynamic fragments that are
* contained within an MPDU or S-MPDU and support for multiple dynamic
* fragments for each MSDU and for each A-MSDU if supported by the
* recipient within an A-MPDU or multi-TID AMPDU and up to one dynamic
* fragment for each MMPDU in a multi-TID A-MPDU that is not an S-MPDU.
*/
enum he_fragmentation_val {
HE_FRAG_DISABLE,
HE_FRAG_LEVEL1,
HE_FRAG_LEVEL2,
HE_FRAG_LEVEL3,
};
/**
* enum he_mcs_config - HE MCS support configuration
*
* Configures the HE Tx/Rx MCS map in HE capability IE for given bandwidth.
* These values are used in driver to configure the HE MCS map to advertise
* Tx/Rx MCS map in HE capability and these values are applied for all the
* streams supported by the device. To configure MCS for different bandwidths,
* vendor command needs to be sent using this attribute with appropriate value.
* For example, to configure HE_80_MCS_0_7, send vendor command using HE MCS
* attribute with HE_80_MCS0_7. And to configure HE MCS for HE_160_MCS0_11
* send this command using HE MCS config attribute with value HE_160_MCS0_11.
* These values are used to configure testbed device to allow the advertised
* hardware capabilities to be downgraded for testing purposes. The enum values
* are defined such that BIT[1:0] indicates the MCS map value. Values 3,7 and
* 11 are not used as BIT[1:0] value is 3 which is used to disable MCS map.
* These values are validated in the driver before setting the MCS map and
* driver returns error if the input is other than these enum values.
*
* @HE_80_MCS0_7: support for HE 80/40/20 MHz MCS 0 to 7
* @HE_80_MCS0_9: support for HE 80/40/20 MHz MCS 0 to 9
* @HE_80_MCS0_11: support for HE 80/40/20 MHz MCS 0 to 11
* @HE_160_MCS0_7: support for HE 160 MHz MCS 0 to 7
* @HE_160_MCS0_9: support for HE 160 MHz MCS 0 to 9
* @HE_160_MCS0_11: support for HE 160 MHz MCS 0 to 11
* @HE_80P80_MCS0_7: support for HE 80p80 MHz MCS 0 to 7
* @HE_80P80_MCS0_9: support for HE 80p80 MHz MCS 0 to 9
* @HE_80P80_MCS0_11: support for HE 80p80 MHz MCS 0 to 11
*/
enum he_mcs_config {
HE_80_MCS0_7 = 0,
HE_80_MCS0_9 = 1,
HE_80_MCS0_11 = 2,
HE_160_MCS0_7 = 4,
HE_160_MCS0_9 = 5,
HE_160_MCS0_11 = 6,
HE_80P80_MCS0_7 = 8,
HE_80P80_MCS0_9 = 9,
HE_80P80_MCS0_11 = 10,
};
/**
* enum qca_wlan_ba_session_config - BA session configuration
*
* Indicates the configuration values for BA session configuration attribute.
*
* @QCA_WLAN_ADD_BA: Establish a new BA session with given configuration.
* @QCA_WLAN_DELETE_BA: Delete the existing BA session for given TID.
*/
enum qca_wlan_ba_session_config {
QCA_WLAN_ADD_BA = 1,
QCA_WLAN_DELETE_BA = 2,
};
/**
* enum qca_wlan_ac_type - Access category type
*
* Indicates the access category type value.
*
* @QCA_WLAN_AC_BE: BE access category
* @QCA_WLAN_AC_BK: BK access category
* @QCA_WLAN_AC_VI: VI access category
* @QCA_WLAN_AC_VO: VO access category
* @QCA_WLAN_AC_ALL: All ACs
*/
enum qca_wlan_ac_type {
QCA_WLAN_AC_BE = 0,
QCA_WLAN_AC_BK = 1,
QCA_WLAN_AC_VI = 2,
QCA_WLAN_AC_VO = 3,
QCA_WLAN_AC_ALL = 4,
};
/**
* enum qca_wlan_he_ltf_cfg - HE LTF configuration
*
* Indicates the HE LTF configuration value.
*
* @QCA_WLAN_HE_LTF_AUTO: HE-LTF is automatically set to the mandatory HE-LTF,
* based on the GI setting
* @QCA_WLAN_HE_LTF_1X: 1X HE LTF is 3.2us LTF
* @QCA_WLAN_HE_LTF_2X: 2X HE LTF is 6.4us LTF
* @QCA_WLAN_HE_LTF_4X: 4X HE LTF is 12.8us LTF
*/
enum qca_wlan_he_ltf_cfg {
QCA_WLAN_HE_LTF_AUTO = 0,
QCA_WLAN_HE_LTF_1X = 1,
QCA_WLAN_HE_LTF_2X = 2,
QCA_WLAN_HE_LTF_4X = 3,
};
/**
* enum qca_wlan_he_mac_padding_dur - HE trigger frame MAC padding duration
*
* Indicates the HE trigger frame MAC padding duration value.
*
* @QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME: no additional time required to
* process the trigger frame.
* @QCA_WLAN_HE_8US_OF_PROCESS_TIME: indicates the 8us of processing time for
* trigger frame.
* @QCA_WLAN_HE_16US_OF_PROCESS_TIME: indicates the 16us of processing time for
* trigger frame.
*/
enum qca_wlan_he_mac_padding_dur {
QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME = 0,
QCA_WLAN_HE_8US_OF_PROCESS_TIME = 1,
QCA_WLAN_HE_16US_OF_PROCESS_TIME = 2,
};
/**
* enum qca_wlan_he_om_ctrl_ch_bw - HE OM control field BW configuration
*
* Indicates the HE Operating mode control channel width setting value.
*
* @QCA_WLAN_HE_OM_CTRL_BW_20M: Primary 20 MHz
* @QCA_WLAN_HE_OM_CTRL_BW_40M: Primary 40 MHz
* @QCA_WLAN_HE_OM_CTRL_BW_80M: Primary 80 MHz
* @QCA_WLAN_HE_OM_CTRL_BW_160M: 160 MHz and 80+80 MHz
*/
enum qca_wlan_he_om_ctrl_ch_bw {
QCA_WLAN_HE_OM_CTRL_BW_20M = 0,
QCA_WLAN_HE_OM_CTRL_BW_40M = 1,
QCA_WLAN_HE_OM_CTRL_BW_80M = 2,
QCA_WLAN_HE_OM_CTRL_BW_160M = 3,
};
/**
* enum qca_wlan_keep_alive_data_type - Keep alive data type configuration
*
* Indicates the frame types to use for keep alive data.
*
* @QCA_WLAN_KEEP_ALIVE_DEFAULT: Driver default type used for keep alive.
* @QCA_WLAN_KEEP_ALIVE_DATA: Data frame type for keep alive.
* @QCA_WLAN_KEEP_ALIVE_MGMT: Management frame type for keep alive.
*/
enum qca_wlan_keep_alive_data_type {
QCA_WLAN_KEEP_ALIVE_DEFAULT = 0,
QCA_WLAN_KEEP_ALIVE_DATA = 1,
QCA_WLAN_KEEP_ALIVE_MGMT = 2,
};
/**
* enum qca_wlan_vendor_attr_he_omi_tx: Represents attributes for
* HE operating mode control transmit request. These attributes are
* sent as part of QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX and
* QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
*
* @QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS: Mandatory 8-bit unsigned value
* indicates the maximum number of spatial streams, NSS, that the STA
* supports in reception for PPDU bandwidths less than or equal to 80 MHz
* and is set to NSS - 1.
*
* @QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW: Mandatory 8-bit unsigned value
* indicates the operating channel width supported by the STA for both
* reception and transmission. Uses enum qca_wlan_he_om_ctrl_ch_bw values.
*
* @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE: Mandatory 8-bit unsigned value
* indicates the all trigger based UL MU operations by the STA.
* 0 - UL MU operations are enabled by the STA.
* 1 - All triggered UL MU transmissions are suspended by the STA.
*
* @QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS: Mandatory 8-bit unsigned value
* indicates the maximum number of space-time streams, NSTS, that
* the STA supports in transmission and is set to NSTS - 1.
*
* @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE: 8-bit unsigned value
* combined with the UL MU Disable subfield and the recipient's setting
* of the OM Control UL MU Data Disable RX Support subfield in the HE MAC
* capabilities to determine which HE TB PPDUs are possible by the
* STA to transmit.
* 0 - UL MU data operations are enabled by the STA.
* 1 - Determine which HE TB PPDU types are allowed by the STA if UL MU disable
* bit is not set, else UL MU Tx is suspended.
*
*/
enum qca_wlan_vendor_attr_he_omi_tx {
QCA_WLAN_VENDOR_ATTR_HE_OMI_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS = 1,
QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW = 2,
QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE = 3,
QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS = 4,
QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE = 5,
/* keep last */
QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX =
QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_phy_mode - Different PHY modes
* These values are used with %QCA_WLAN_VENDOR_ATTR_CONFIG_PHY_MODE.
*
* @QCA_WLAN_VENDOR_PHY_MODE_AUTO: autoselect
* @QCA_WLAN_VENDOR_PHY_MODE_2G_AUTO: 2.4 GHz 802.11b/g/n/ax autoselect
* @QCA_WLAN_VENDOR_PHY_MODE_5G_AUTO: 5 GHz 802.11a/n/ac/ax autoselect
* @QCA_WLAN_VENDOR_PHY_MODE_11A: 5 GHz, OFDM
* @QCA_WLAN_VENDOR_PHY_MODE_11B: 2.4 GHz, CCK
* @QCA_WLAN_VENDOR_PHY_MODE_11G: 2.4 GHz, OFDM
* @QCA_WLAN_VENDOR_PHY_MODE_11AGN: Support 802.11n in both 2.4 GHz and 5 GHz
* @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT20: 2.4 GHz, HT20
* @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40PLUS: 2.4 GHz, HT40 (ext ch +1)
* @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40MINUS: 2.4 GHz, HT40 (ext ch -1)
* @QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40: 2.4 GHz, Auto HT40
* @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT20: 5 GHz, HT20
* @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40PLUS: 5 GHz, HT40 (ext ch +1)
* @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40MINUS: 5 GHz, HT40 (ext ch -1)
* @QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40: 5 GHz, Auto HT40
* @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT20: 5 GHz, VHT20
* @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40PLUS: 5 GHz, VHT40 (Ext ch +1)
* @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40MINUS: 5 GHz VHT40 (Ext ch -1)
* @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40: 5 GHz, VHT40
* @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80: 5 GHz, VHT80
* @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80P80: 5 GHz, VHT80+80
* @QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT160: 5 GHz, VHT160
* @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE20: HE20
* @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40: HE40
* @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40PLUS: HE40 (ext ch +1)
* @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40MINUS: HE40 (ext ch -1)
* @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80: HE80
* @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80: HE 80P80
* @QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160: HE160
*/
enum qca_wlan_vendor_phy_mode {
QCA_WLAN_VENDOR_PHY_MODE_AUTO = 0,
QCA_WLAN_VENDOR_PHY_MODE_2G_AUTO = 1,
QCA_WLAN_VENDOR_PHY_MODE_5G_AUTO = 2,
QCA_WLAN_VENDOR_PHY_MODE_11A = 3,
QCA_WLAN_VENDOR_PHY_MODE_11B = 4,
QCA_WLAN_VENDOR_PHY_MODE_11G = 5,
QCA_WLAN_VENDOR_PHY_MODE_11AGN = 6,
QCA_WLAN_VENDOR_PHY_MODE_11NG_HT20 = 7,
QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40PLUS = 8,
QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40MINUS = 9,
QCA_WLAN_VENDOR_PHY_MODE_11NG_HT40 = 10,
QCA_WLAN_VENDOR_PHY_MODE_11NA_HT20 = 11,
QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40PLUS = 12,
QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40MINUS = 13,
QCA_WLAN_VENDOR_PHY_MODE_11NA_HT40 = 14,
QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT20 = 15,
QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40PLUS = 16,
QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40MINUS = 17,
QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT40 = 18,
QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80 = 19,
QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT80P80 = 20,
QCA_WLAN_VENDOR_PHY_MODE_11AC_VHT160 = 21,
QCA_WLAN_VENDOR_PHY_MODE_11AX_HE20 = 22,
QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40 = 23,
QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40PLUS = 24,
QCA_WLAN_VENDOR_PHY_MODE_11AX_HE40MINUS = 25,
QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80 = 26,
QCA_WLAN_VENDOR_PHY_MODE_11AX_HE80P80 = 27,
QCA_WLAN_VENDOR_PHY_MODE_11AX_HE160 = 28,
};
/* Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION
*/
enum qca_wlan_vendor_attr_wifi_test_config {
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_INVALID = 0,
/* 8-bit unsigned value to configure the driver to enable/disable
* WMM feature. This attribute is used to configure testbed device.
* 1-enable, 0-disable
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE = 1,
/* 8-bit unsigned value to configure the driver to accept/reject
* the addba request from peer. This attribute is used to configure
* the testbed device.
* 1-accept addba, 0-reject addba
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ = 2,
/* 8-bit unsigned value to configure the driver to send or not to
* send the addba request to peer.
* This attribute is used to configure the testbed device.
* 1-send addba, 0-do not send addba
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ = 3,
/* 8-bit unsigned value to indicate the HE fragmentation support.
* Uses enum he_fragmentation_val values.
* This attribute is used to configure the testbed device to
* allow the advertised hardware capabilities to be downgraded
* for testing purposes.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION = 4,
/* 8-bit unsigned value to indicate the HE MCS support.
* Uses enum he_mcs_config values.
* This attribute is used to configure the testbed device to
* allow the advertised hardware capabilities to be downgraded
* for testing purposes.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS = 5,
/* 8-bit unsigned value to configure the driver to allow or not to
* allow the connection with WEP/TKIP in HT/VHT/HE modes.
* This attribute is used to configure the testbed device.
* 1-allow WEP/TKIP in HT/VHT/HE, 0-do not allow WEP/TKIP.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE = 6,
/* 8-bit unsigned value to configure the driver to add a
* new BA session or delete the existing BA session for
* given TID. ADDBA command uses the buffer size and TID
* configuration if user specifies the values else default
* value for buffer size is used for all TIDs if the TID
* also not specified. For DEL_BA command TID value is
* required to process the command.
* Uses enum qca_wlan_ba_session_config values.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION = 7,
/* 16-bit unsigned value to configure the buffer size in addba
* request and response frames.
* This attribute is used to configure the testbed device.
* The range of the value is 0 to 256.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE = 8,
/* 8-bit unsigned value to configure the buffer size in addba
* request and response frames.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID = 9,
/* 8-bit unsigned value to configure the no ack policy.
* To configure no ack policy, access category value is
* required to process the command.
* This attribute is used to configure the testbed device.
* 1 - enable no ack, 0 - disable no ack.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK = 10,
/* 8-bit unsigned value to configure the AC for no ack policy
* This attribute is used to configure the testbed device.
* Uses the enum qca_wlan_ac_type values.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC = 11,
/* 8-bit unsigned value to configure the HE LTF
* This attribute is used to configure the testbed device.
* Uses the enum qca_wlan_he_ltf_cfg values.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF = 12,
/* 8-bit unsigned value to configure the tx beamformee.
* This attribute is used to configure the testbed device.
* 1-enable, 0-disable.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE = 13,
/* 8-bit unsigned value to configure the tx beamformee number
* of space-time streams.
* This attribute is used to configure the testbed device.
* The range of the value is 0 to 8.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS = 14,
/* 8-bit unsigned value to configure the MU EDCA params for given AC
* This attribute is used to configure the testbed device.
* Uses the enum qca_wlan_ac_type values.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC = 15,
/* 8-bit unsigned value to configure the MU EDCA AIFSN for given AC
* To configure MU EDCA AIFSN value, MU EDCA access category value
* is required to process the command.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AIFSN = 16,
/* 8-bit unsigned value to configure the MU EDCA ECW min value for
* given AC.
* To configure MU EDCA ECW min value, MU EDCA access category value
* is required to process the command.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMIN = 17,
/* 8-bit unsigned value to configure the MU EDCA ECW max value for
* given AC.
* To configure MU EDCA ECW max value, MU EDCA access category value
* is required to process the command.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMAX = 18,
/* 8-bit unsigned value to configure the MU EDCA timer for given AC
* To configure MU EDCA timer value, MU EDCA access category value
* is required to process the command.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_TIMER = 19,
/* 8-bit unsigned value to configure the HE trigger frame MAC padding
* duration.
* This attribute is used to configure the testbed device.
* Uses the enum qca_wlan_he_mac_padding_dur values.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR = 20,
/* 8-bit unsigned value to override the MU EDCA params to defaults
* regardless of the AP beacon MU EDCA params. If it is enabled use
* the default values else use the MU EDCA params from AP beacon.
* This attribute is used to configure the testbed device.
* 1-enable, 0-disable.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA = 21,
/* 8-bit unsigned value to configure the support for receiving
* an MPDU that contains an operating mode control subfield.
* This attribute is used to configure the testbed device.
* 1-enable, 0-disable.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP = 22,
/* Nested attribute values required to setup the TWT session.
* enum qca_wlan_vendor_attr_twt_setup provides the necessary
* information to set up the session. It contains broadcast flags,
* set_up flags, trigger value, flow type, flow ID, wake interval
* exponent, protection, target wake time, wake duration, wake interval
* mantissa. These nested attributes are used to setup a host triggered
* TWT session.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP = 23,
/* This nested attribute is used to terminate the current TWT session.
* It does not currently carry any attributes.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE = 24,
/* This nested attribute is used to suspend the current TWT session.
* It does not currently carry any attributes.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SUSPEND = 25,
/* Nested attribute values to indicate the request for resume.
* This attribute is used to resume the TWT session.
* enum qca_wlan_vendor_attr_twt_resume provides the necessary
* parameters required to resume the TWT session.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME = 26,
/* 8-bit unsigned value to set the HE operating mode control
* (OM CTRL) Channel Width subfield.
* The Channel Width subfield indicates the operating channel width
* supported by the STA for both reception and transmission.
* Uses the enum qca_wlan_he_om_ctrl_ch_bw values.
* This setting is cleared with the
* QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG
* flag attribute to reset defaults.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_BW = 27,
/* 8-bit unsigned value to configure the number of spatial
* streams in HE operating mode control field.
* This setting is cleared with the
* QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG
* flag attribute to reset defaults.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_NSS = 28,
/* Flag attribute to configure the UL MU disable bit in
* HE operating mode control field.
* This setting is cleared with the
* QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG
* flag attribute to reset defaults.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_UL_MU_DISABLE = 29,
/* Flag attribute to clear the previously set HE operating mode
* control field configuration.
* This attribute is used to configure the testbed device to reset
* defaults to clear any previously set HE operating mode control
* field configuration.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG = 30,
/* 8-bit unsigned value to configure HE single user PPDU
* transmission. By default this setting is disabled and it
* is disabled in the reset defaults of the device configuration.
* This attribute is used to configure the testbed device.
* 1-enable, 0-disable
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU = 31,
/* 8-bit unsigned value to configure action frame transmission
* in HE trigger based PPDU transmission.
* By default this setting is disabled and it is disabled in
* the reset defaults of the device configuration.
* This attribute is used to configure the testbed device.
* 1-enable, 0-disable
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU = 32,
/* Nested attribute to indicate HE operating mode control field
* transmission. It contains operating mode control field Nss,
* channel bandwidth, Tx Nsts and UL MU disable attributes.
* These nested attributes are used to send HE operating mode control
* with configured values.
* Uses the enum qca_wlan_vendor_attr_he_omi_tx attributes.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX = 33,
/* 8-bit unsigned value to configure +HTC_HE support to indicate the
* support for the reception of a frame that carries an HE variant
* HT Control field.
* This attribute is used to configure the testbed device.
* 1-enable, 0-disable
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP = 34,
/* 8-bit unsigned value to configure VHT support in 2.4G band.
* This attribute is used to configure the testbed device.
* 1-enable, 0-disable
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT = 35,
/* 8-bit unsigned value to configure HE testbed defaults.
* This attribute is used to configure the testbed device.
* 1-set the device HE capabilities to testbed defaults.
* 0-reset the device HE capabilities to supported config.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS = 36,
/* 8-bit unsigned value to configure TWT request support.
* This attribute is used to configure the testbed device.
* 1-enable, 0-disable.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37,
/* 8-bit unsigned value to configure protection for Management
* frames when PMF is enabled for the association.
* This attribute is used to configure the testbed device.
* 0-use the correct key, 1-use an incorrect key, 2-disable protection.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PMF_PROTECTION = 38,
/* Flag attribute to inject Disassociation frame to the connected AP.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISASSOC_TX = 39,
/* 8-bit unsigned value to configure an override for the RSNXE Used
* subfield in the MIC control field of the FTE in FT Reassociation
* Request frame.
* 0 - Default behavior, 1 - override with 1, 2 - override with 0.
* This attribute is used to configure the testbed device.
* This attribute can be configured only when STA is in associated state
* and the configuration is valid until the disconnection.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FT_REASSOCREQ_RSNXE_USED = 40,
/* 8-bit unsigned value to configure the driver to ignore CSA (Channel
* Switch Announcement) when STA is in connected state.
* 0 - Default behavior, 1 - Ignore CSA.
* This attribute is used to configure the testbed device.
* This attribute can be configured only when STA is in associated state
* and the configuration is valid until the disconnection.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_CSA = 41,
/* Nested attribute values required to configure OCI (Operating Channel
* Information). Attributes defined in enum
* qca_wlan_vendor_attr_oci_override are nested within this attribute.
* This attribute is used to configure the testbed device.
* This attribute can be configured only when STA is in associated state
* and the configuration is valid until the disconnection.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE = 42,
/* 8-bit unsigned value to configure the driver/firmware to ignore SA
* Query timeout. If this configuration is enabled STA shall not send
* Deauthentication frmae when SA Query times out (mainly, after a
* channel switch when OCV is enabled).
* 0 - Default behavior, 1 - Ignore SA Query timeout.
* This attribute is used to configure the testbed device.
* This attribute can be configured only when STA is in associated state
* and the configuration is valid until the disconnection.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_SA_QUERY_TIMEOUT = 43,
/* 8-bit unsigned value to configure the driver/firmware to start or
* stop transmitting FILS discovery frames.
* 0 - Stop transmitting FILS discovery frames
* 1 - Start transmitting FILS discovery frames
* This attribute is used to configure the testbed device.
* This attribute can be configured only in AP mode and the
* configuration is valid until AP restart.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FILS_DISCOVERY_FRAMES_TX = 44,
/* 8-bit unsigned value to configure the driver/firmware to enable or
* disable full bandwidth UL MU-MIMO subfield in the HE PHY capabilities
* information field.
* 0 - Disable full bandwidth UL MU-MIMO subfield
* 1 - Enable full bandwidth UL MU-MIMO subfield
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_FULL_BW_UL_MU_MIMO = 45,
/* 16-bit unsigned value to configure the driver with a specific BSS
* max idle period to advertise in the BSS Max Idle Period element
* (IEEE Std 802.11-2016, 9.4.2.79) in (Re)Association Request frames.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BSS_MAX_IDLE_PERIOD = 46,
/* 8-bit unsigned value to configure the driver to use only RU 242 tone
* for data transmission.
* 0 - Default behavior, 1 - Configure RU 242 tone for data Tx.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RU_242_TONE_TX = 47,
/* 8-bit unsigned value to configure the driver to disable data and
* management response frame transmission to test the BSS max idle
* feature.
* 0 - Default behavior, 1 - Disable data and management response Tx.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_DISABLE_DATA_MGMT_RSP_TX = 48,
/* 8-bit unsigned value to configure the driver/firmware to enable or
* disable Punctured Preamble Rx subfield in the HE PHY capabilities
* information field.
* 0 - Disable Punctured Preamble Rx subfield
* 1 - Enable Punctured Preamble Rx subfield
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_PUNCTURED_PREAMBLE_RX = 49,
/* 8-bit unsigned value to configure the driver to ignore the SAE H2E
* requirement mismatch for 6 GHz connection.
* 0 - Default behavior, 1 - Ignore SAE H2E requirement mismatch.
* This attribute is used to configure the testbed device.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_IGNORE_H2E_RSNXE = 50,
/* 8-bit unsigned value to configure the driver to allow 6 GHz
* connection with all security modes.
* 0 - Default behavior, 1 - Allow 6 GHz connection with all security
* modes.
* This attribute is used for testing purposes.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_6GHZ_SECURITY_TEST_MODE = 51,
/* 8-bit unsigned value to configure the driver to transmit data with
* ER SU PPDU type.
*
* 0 - Default behavior, 1 - Enable ER SU PPDU type TX.
* This attribute is used for testing purposes.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ER_SU_PPDU_TYPE = 52,
/* 8-bit unsigned value to configure the driver to use Data or
* Management frame type for keep alive data.
* Uses enum qca_wlan_keep_alive_data_type values.
*
* This attribute is used for testing purposes.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_KEEP_ALIVE_FRAME_TYPE = 53,
/* 8-bit unsigned value to configure the driver to use scan request
* BSSID value in Probe Request frame RA(A1) during the scan. The
* driver saves this configuration and applies this setting to all user
* space scan requests until the setting is cleared. If this
* configuration is set, the driver uses the BSSID value from the scan
* request to set the RA(A1) in the Probe Request frames during the
* scan.
*
* 0 - Default behavior uses the broadcast RA in Probe Request frames.
* 1 - Uses the scan request BSSID in RA in Probe Request frames.
* This attribute is used for testing purposes.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_USE_BSSID_IN_PROBE_REQ_RA = 54,
/* 8-bit unsigned value to configure the driver to enable/disable the
* BSS max idle period support.
*
* 0 - Disable the BSS max idle support.
* 1 - Enable the BSS max idle support.
* This attribute is used for testing purposes.
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BSS_MAX_IDLE_PERIOD_ENABLE = 55,
+ /* 8-bit unsigned value to configure the driver/firmware to enable or
+ * disable Rx control frame to MultiBSS subfield in the HE MAC
+ * capabilities information field.
+ * 0 - Disable Rx control frame to MultiBSS subfield
+ * 1 - Enable Rx control frame to MultiBSS subfield
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_RX_CTRL_FRAME_TO_MBSS = 56,
+
+ /* 8-bit unsigned value to configure the driver/firmware to enable or
+ * disable Broadcast TWT support subfield in the HE MAC capabilities
+ * information field.
+ * 0 - Disable Broadcast TWT support subfield
+ * 1 - Enable Broadcast TWT support subfield
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BCAST_TWT_SUPPORT = 57,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST - 1,
};
/**
* enum qca_wlan_twt_operation - Operation of the config TWT request
* Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION.
* The response for the respective operations can be either synchronous or
* asynchronous (wherever specified). If synchronous, the response to this
* operation is obtained in the corresponding vendor command reply to the user
* space. For the asynchronous case the response is obtained as an event with
* the same operation type.
*
* Drivers shall support either of these modes but not both simultaneously.
* This support for asynchronous mode is advertised through the flag
* QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT. If this flag is not advertised,
* the driver shall support synchronous mode.
*
* @QCA_WLAN_TWT_SET: Setup a TWT session. Required parameters are configured
* through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
* qca_wlan_vendor_attr_twt_setup. Depending upon the
* @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT capability, this is either a
* synchronous or asynchronous operation.
*
* @QCA_WLAN_TWT_GET: Get the configured TWT parameters. Required parameters are
* obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
* qca_wlan_vendor_attr_twt_setup. This is a synchronous operation.
*
* @QCA_WLAN_TWT_TERMINATE: Terminate the TWT session. Required parameters are
* obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
* qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup.
* This terminate can either get triggered by the user space or can as well be
* a notification from the firmware if it initiates a terminate.
* Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT capability,
* the request from user space can either be a synchronous or asynchronous
* operation.
*
* @QCA_WLAN_TWT_SUSPEND: Suspend the TWT session. Required parameters are
* obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
* qca_wlan_vendor_attr_twt_setup. Valid only after the TWT session is setup.
* Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT capability,
* this is either a synchronous or asynchronous operation.
*
* @QCA_WLAN_TWT_RESUME: Resume the TWT session. Required parameters are
* configured through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the enum
* qca_wlan_vendor_attr_twt_resume. Valid only after the TWT session is setup.
* This can as well be a notification from the firmware on a QCA_WLAN_TWT_NUDGE
* request. Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT
* capability, this is either a synchronous or asynchronous operation.
*
* @QCA_WLAN_TWT_NUDGE: Suspend and resume the TWT session. TWT nudge is a
* combination of suspend and resume in a single request. Required parameters
* are configured through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refers the
* enum qca_wlan_vendor_attr_twt_nudge. Valid only after the TWT session is
* setup. Depending upon the @QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT
* capability, this is either a synchronous or asynchronous operation.
*
* @QCA_WLAN_TWT_GET_STATS: Get the TWT session traffic statistics information.
* Refers the enum qca_wlan_vendor_attr_twt_stats. Valid only after the TWT
* session is setup. It's a synchronous operation.
*
* @QCA_WLAN_TWT_CLEAR_STATS: Clear TWT session traffic statistics information.
* Valid only after the TWT session is setup. It's a synchronous operation.
*
* @QCA_WLAN_TWT_GET_CAPABILITIES: Get TWT capabilities of this device and its
* peer. Refers the enum qca_wlan_vendor_attr_twt_capability. It's a synchronous
* operation.
*
* @QCA_WLAN_TWT_SETUP_READY_NOTIFY: Notify userspace that the firmare is
* ready for a new TWT session setup after it issued a TWT teardown.
+ *
+ * @QCA_WLAN_TWT_SET_PARAM: Configure TWT related parameters. Required
+ * parameters are obtained through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS. Refer
+ * the enum qca_wlan_vendor_attr_twt_set_param.
*/
enum qca_wlan_twt_operation {
QCA_WLAN_TWT_SET = 0,
QCA_WLAN_TWT_GET = 1,
QCA_WLAN_TWT_TERMINATE = 2,
QCA_WLAN_TWT_SUSPEND = 3,
QCA_WLAN_TWT_RESUME = 4,
QCA_WLAN_TWT_NUDGE = 5,
QCA_WLAN_TWT_GET_STATS = 6,
QCA_WLAN_TWT_CLEAR_STATS = 7,
QCA_WLAN_TWT_GET_CAPABILITIES = 8,
QCA_WLAN_TWT_SETUP_READY_NOTIFY = 9,
+ QCA_WLAN_TWT_SET_PARAM = 10,
};
/**
* enum qca_wlan_vendor_attr_config_twt: Defines attributes used by
* %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION: u8 attribute. Specify the TWT
* operation of this request. Possible values are defined in enum
* qca_wlan_twt_operation. The parameters for the respective operation is
* specified through QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS: Nested attribute representing the
* parameters configured for TWT. These parameters are represented by
* enum qca_wlan_vendor_attr_twt_setup, enum qca_wlan_vendor_attr_twt_resume,
- * or enum qca_wlan_vendor_attr_twt_stats based on the operation.
+ * enum qca_wlan_vendor_attr_twt_set_param, or
+ * enum qca_wlan_vendor_attr_twt_stats based on the operation.
*/
enum qca_wlan_vendor_attr_config_twt {
QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION = 1,
QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_PARAMS = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_MAX =
QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_bss_filter - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
* The user can add/delete the filter by specifying the BSSID/STA MAC address in
* QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, filter type in
* QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, add/delete action in
* QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user can get the
* statistics of an unassociated station by specifying the MAC address in
* QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, station type in
* QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, GET action in
* QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user also can get
* the statistics of all unassociated stations by specifying the Broadcast MAC
* address (ff:ff:ff:ff:ff:ff) in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR with
* above procedure. In the response, driver shall specify statistics
* information nested in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS.
*/
enum qca_wlan_vendor_attr_bss_filter {
QCA_WLAN_VENDOR_ATTR_BSS_FILTER_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR = 1,
/* Other BSS filter type, unsigned 8 bit value. One of the values
* in enum qca_wlan_vendor_bss_filter_type.
*/
QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE = 2,
/* Other BSS filter action, unsigned 8 bit value. One of the values
* in enum qca_wlan_vendor_bss_filter_action.
*/
QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION = 3,
/* Array of nested attributes where each entry is the statistics
* information of the specified station that belong to another BSS.
* Attributes for each entry are taken from enum
* qca_wlan_vendor_bss_filter_sta_stats.
* Other BSS station configured in
* QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER with filter type
* QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA.
* Statistics returned by QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER
* with filter action QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET.
*/
QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAX =
QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_bss_filter_type - Type of
* filter used in other BSS filter operations. Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
*
* @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID: BSSID filter
* @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA: Station MAC address filter
*/
enum qca_wlan_vendor_bss_filter_type {
QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID,
QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA,
};
/**
* enum qca_wlan_vendor_bss_filter_action - Type of
* action in other BSS filter operations. Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
*
* @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD: Add filter
* @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL: Delete filter
* @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET: Get the statistics
*/
enum qca_wlan_vendor_bss_filter_action {
QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD,
QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL,
QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET,
};
/**
* enum qca_wlan_vendor_bss_filter_sta_stats - Attributes for
* the statistics of a specific unassociated station belonging to another BSS.
* The statistics provides information of the unassociated station
* filtered by other BSS operation - such as MAC, signal value.
* Used by the vendor command QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER.
*
* @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC: MAC address of the station.
* @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI: Last received signal strength
* of the station. Unsigned 8 bit number containing RSSI.
* @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS: Time stamp of the host
* driver for the last received RSSI. Unsigned 64 bit number containing
* nanoseconds from the boottime.
*/
enum qca_wlan_vendor_bss_filter_sta_stats {
QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_INVALID = 0,
QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC = 1,
QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI = 2,
QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS = 3,
/* keep last */
QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST,
QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAX =
QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST - 1
};
/* enum qca_wlan_nan_subcmd_type - Type of NAN command used by attribute
* QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE as a part of vendor command
* QCA_NL80211_VENDOR_SUBCMD_NAN_EXT.
*/
enum qca_wlan_nan_ext_subcmd_type {
/* Subcmd of type NAN Enable Request */
QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ = 1,
/* Subcmd of type NAN Disable Request */
QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ = 2,
};
/**
* enum qca_wlan_vendor_attr_nan_params - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_NAN_EXT.
*/
enum qca_wlan_vendor_attr_nan_params {
QCA_WLAN_VENDOR_ATTR_NAN_INVALID = 0,
/* Carries NAN command for firmware component. Every vendor command
* QCA_NL80211_VENDOR_SUBCMD_NAN_EXT must contain this attribute with a
* payload containing the NAN command. NLA_BINARY attribute.
*/
QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA = 1,
/* Indicates the type of NAN command sent with
* QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. enum qca_wlan_nan_ext_subcmd_type
* describes the possible range of values. This attribute is mandatory
* if the command being issued is either
* QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ or
* QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ. NLA_U32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE = 2,
/* Frequency (in MHz) of primary NAN discovery social channel in 2.4 GHz
* band. This attribute is mandatory when command type is
* QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ. NLA_U32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_NAN_DISC_24GHZ_BAND_FREQ = 3,
/* Frequency (in MHz) of secondary NAN discovery social channel in 5 GHz
* band. This attribute is optional and should be included when command
* type is QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ and NAN discovery
* has to be started on 5GHz along with 2.4GHz. NLA_U32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_NAN_DISC_5GHZ_BAND_FREQ = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_MAX =
QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST - 1
};
/**
* qca_wlan_twt_setup_state: Represents the TWT session states.
*
* QCA_WLAN_TWT_SETUP_STATE_NOT_ESTABLISHED: TWT session not established.
* QCA_WLAN_TWT_SETUP_STATE_ACTIVE: TWT session is active.
* QCA_WLAN_TWT_SETUP_STATE_SUSPEND: TWT session is in suspended state.
*/
enum qca_wlan_twt_setup_state {
QCA_WLAN_TWT_SETUP_STATE_NOT_ESTABLISHED = 0,
QCA_WLAN_TWT_SETUP_STATE_ACTIVE = 1,
QCA_WLAN_TWT_SETUP_STATE_SUSPEND = 2,
};
/**
* enum qca_wlan_vendor_attr_twt_setup: Represents attributes for
* TWT (Target Wake Time) setup request. These attributes are sent as part of
* %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and
* %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by
* attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute.
* Disable (flag attribute not present) - Individual TWT
* Enable (flag attribute present) - Broadcast TWT.
* Individual means the session is between the STA and the AP.
* This session is established using a separate negotiation between
* STA and AP.
* Broadcast means the session is across multiple STAs and an AP. The
* configuration parameters are announced in Beacon frames by the AP.
* This is used in
* 1. TWT SET Request and Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE: Required (u8).
* Unsigned 8-bit qca_wlan_vendor_twt_setup_req_type to
* specify the TWT request type. This is used in TWT SET operation.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER: Flag attribute
* Enable (flag attribute present) - TWT with trigger support.
* Disable (flag attribute not present) - TWT without trigger support.
* Trigger means the AP will send the trigger frame to allow STA to send data.
* Without trigger, the STA will wait for the MU EDCA timer before
* transmitting the data.
* This is used in
* 1. TWT SET Request and Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE: Required (u8)
* 0 - Announced TWT - In this mode, STA may skip few service periods to
* save more power. If STA wants to wake up, it will send a PS-POLL/QoS
* NULL frame to AP.
* 1 - Unannounced TWT - The STA will wakeup during every SP.
* This is a required parameter for
* 1. TWT SET Request and Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID: Optional (u8)
* Flow ID is the unique identifier for each TWT session.
* If not provided then dialog ID will be set to zero.
* This is an optional parameter for
* 1. TWT SET Request and Response
* 2. TWT GET Request and Response
* 3. TWT TERMINATE Request and Response
* 4. TWT SUSPEND Request and Response
* Flow ID values from 0 to 254 represent a single TWT session
* Flow ID value of 255 represents all TWT sessions for the following
* 1. TWT TERMINATE Request and Response
* 2. TWT SUSPEND Request and Response
* 4. TWT CLEAR STATISTICS request
* 5. TWT GET STATISTICS request and response
* If an invalid dialog ID is provided, status
* QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST will be returned.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8)
* This attribute (exp) is used along with the mantissa to derive the
* wake interval using the following formula:
* pow(2,exp) = wake_intvl_us/wake_intvl_mantis
* Wake interval is the interval between 2 successive SP.
* This is a required parameter for
* 1. TWT SET Request and Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION: Flag attribute
* Enable (flag attribute present) - Protection required.
* Disable (flag attribute not present) - Protection not required.
* If protection is enabled, then the AP will use protection
* mechanism using RTS/CTS to self to reserve the airtime.
* This is used in
* 1. TWT SET Request and Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME: Optional (u32)
* This attribute is used as the SP offset which is the offset from
* TSF after which the wake happens. The units are in microseconds. If
* this attribute is not provided, then the value will be set to zero.
* This is an optional parameter for
* 1. TWT SET Request and Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION: Required (u32)
* This is the duration of the service period. This is specified as
* multiples of 256 microseconds. Valid values are 0x1 to 0xFF.
* This is a required parameter for
* 1. TWT SET Request and Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA: Required (u32)
* This attribute is used to configure wake interval mantissa.
* The units are in TU.
* This is a required parameter for
* 1. TWT SET Request and Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS: Required (u8)
* This field is applicable for TWT response only.
* This contains status values in enum qca_wlan_vendor_twt_status
* and is passed to the userspace. This is used in TWT SET operation.
* This is a required parameter for
* 1. TWT SET Response
* 2. TWT TERMINATE Response
* 3. TWT SUSPEND Response
* 4. TWT RESUME Response
* 5. TWT NUDGE Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESP_TYPE: Required (u8)
* This field is applicable for TWT response only.
* This field contains response type from the TWT responder and is
* passed to the userspace. The values for this field are defined in
* enum qca_wlan_vendor_twt_setup_resp_type. This is used in TWT SET
* response.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME_TSF: Required (u64)
- * This field is applicable for TWT response only.
- * This field contains absolute TSF value of the wake time received
- * from the TWT responder and is passed to the userspace.
+ * In TWT setup command this field contains absolute TSF that will
+ * be used by TWT requester during setup.
+ * In TWT response this field contains absolute TSF value of the
+ * wake time received from the TWT responder and is passed to
+ * the userspace.
+ * This is an optional parameter for
+ * 1. TWT SET Request
* This is a required parameter for
* 1. TWT SET Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED: Flag attribute.
* Enable (flag attribute present) - Indicates that the TWT responder
* supports reception of TWT information frame from the TWT requestor.
* Disable (flag attribute not present) - Indicates that the responder
* doesn't support reception of TWT information frame from requestor.
* This is used in
* 1. TWT SET Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR: 6-byte MAC address
* Represents the MAC address of the peer for which the TWT session
* is being configured. This is used in AP mode to represent the respective
* client.
* In AP mode, this is a required parameter in response for
* 1. TWT SET
* 2. TWT GET
* 3. TWT TERMINATE
* 4. TWT SUSPEND
* In STA mode, this is an optional parameter in request and response for
* the above four TWT operations.
* In AP mode, this is a required parameter in request for
* 1. TWT GET
* 2. TWT TERMINATE
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_INTVL: Optional (u32)
* Minimum tolerance limit of wake interval parameter in microseconds.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_INTVL: Optional (u32)
* Maximum tolerance limit of wake interval parameter in microseconds.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_DURATION: Optional (u32)
* Minimum tolerance limit of wake duration parameter in microseconds.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_DURATION: Optional (u32)
* Maximum tolerance limit of wake duration parameter in microseconds.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATE: Optional (u32)
* TWT state for the given dialog id. The values for this are represented
* by enum qca_wlan_twt_setup_state.
* This is obtained through TWT GET operation.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL2_MANTISSA: Optional (u32)
* This attribute is used to configure wake interval mantissa.
* The unit is microseconds. This attribute, when specified, takes
* precedence over QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA.
* This parameter is used for
* 1. TWT SET Request and Response
* 2. TWT GET Response
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_ID: Optional (u8)
* This attribute is used to configure Broadcast TWT ID.
* The Broadcast TWT ID indicates a specific Broadcast TWT for which the
* transmitting STA is providing TWT parameters. The allowed values are 0 to 31.
* This parameter is used for
* 1. TWT SET Request
* 2. TWT TERMINATE Request
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_RECOMMENDATION: Optional (u8)
* This attribute is used to configure Broadcast TWT recommendation.
* The Broadcast TWT Recommendation subfield contains a value that indicates
* recommendations on the types of frames that are transmitted by TWT
* scheduled STAs and scheduling AP during the broadcast TWT SP.
* The allowed values are 0 - 3.
* This parameter is used for
* 1. TWT SET Request
*
* @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_PERSISTENCE: Optional (u8)
* This attribute is used to configure Broadcast TWT Persistence.
* The Broadcast TWT Persistence subfield indicates the number of
* TBTTs during which the Broadcast TWT SPs corresponding to this
* broadcast TWT Parameter set are present. The number of beacon intervals
* during which the Broadcast TWT SPs are present is equal to the value in the
* Broadcast TWT Persistence subfield plus 1 except that the value 255
* indicates that the Broadcast TWT SPs are present until explicitly terminated.
* This parameter is used for
* 1. TWT SET Request
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESPONDER_PM_MODE: Optional (u8)
+ * This attribute contains the value of the Responder PM Mode subfield (0 or 1)
+ * from TWT response frame.
+ * This parameter is used for
+ * 1. TWT SET Response
+ * 2. TWT GET Response
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_ANNOUNCE_TIMEOUT: Optional (u32)
+ * This attribute is used to configure the announce timeout value (in us) in
+ * the firmware. This timeout value is only applicable for the announced TWT. If
+ * the timeout value is non-zero the firmware waits up to the timeout value to
+ * use Data frame as an announcement frame. If the timeout value is 0 the
+ * firmware sends an explicit QoS NULL frame as the announcement frame on SP
+ * start. The default value in the firmware is 0.
+ * This parameter is used for
+ * 1. TWT SET Request
*/
enum qca_wlan_vendor_attr_twt_setup {
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST = 1,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE = 2,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER = 3,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE = 4,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID = 5,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP = 6,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION = 7,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME = 8,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION = 9,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA = 10,
/* TWT Response only attributes */
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS = 11,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESP_TYPE = 12,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME_TSF = 13,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TWT_INFO_ENABLED = 14,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAC_ADDR = 15,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_INTVL = 16,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_INTVL = 17,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MIN_WAKE_DURATION = 18,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX_WAKE_DURATION = 19,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATE = 20,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL2_MANTISSA = 21,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_ID = 22,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_RECOMMENDATION = 23,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST_PERSISTENCE = 24,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_RESPONDER_PM_MODE = 25,
+ QCA_WLAN_VENDOR_ATTR_TWT_SETUP_ANNOUNCE_TIMEOUT = 26,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX =
QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_twt_status - Represents the status of the requested
* TWT operation
*
* @QCA_WLAN_VENDOR_TWT_STATUS_OK: TWT request successfully completed
* @QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_ENABLED: TWT not enabled
* @QCA_WLAN_VENDOR_TWT_STATUS_USED_DIALOG_ID: TWT dialog ID is already used
* @QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY: TWT session is busy
* @QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST: TWT session does not exist
* @QCA_WLAN_VENDOR_TWT_STATUS_NOT_SUSPENDED: TWT session not in suspend state
* @QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM: Invalid parameters
* @QCA_WLAN_VENDOR_TWT_STATUS_NOT_READY: FW not ready
* @QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE: FW resource exhausted
* @QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK: Peer AP/STA did not ACK the
* request/response frame
* @QCA_WLAN_VENDOR_TWT_STATUS_NO_RESPONSE: Peer AP did not send the response
* frame
* @QCA_WLAN_VENDOR_TWT_STATUS_DENIED: AP did not accept the request
* @QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR: Adding TWT dialog failed due to an
* unknown reason
* @QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED: TWT session already in
* suspend state
* @QCA_WLAN_VENDOR_TWT_STATUS_IE_INVALID: FW has dropped the frame due to
* invalid IE in the received TWT frame
* @QCA_WLAN_VENDOR_TWT_STATUS_PARAMS_NOT_IN_RANGE: Parameters received from
* the responder are not in the specified range
* @QCA_WLAN_VENDOR_TWT_STATUS_PEER_INITIATED_TERMINATE: FW terminated the TWT
* session due to request from the responder. Used on the TWT_TERMINATE
* notification from the firmware.
* @QCA_WLAN_VENDOR_TWT_STATUS_ROAM_INITIATED_TERMINATE: FW terminated the TWT
* session due to roaming. Used on the TWT_TERMINATE notification from the
* firmware.
* @QCA_WLAN_VENDOR_TWT_STATUS_SCC_MCC_CONCURRENCY_TERMINATE: FW terminated the
* TWT session due to SCC (Single Channel Concurrency) and MCC (Multi Channel
* Concurrency). Used on the TWT_TERMINATE notification from the firmware.
* @QCA_WLAN_VENDOR_TWT_STATUS_ROAMING_IN_PROGRESS: FW rejected the TWT setup
* request due to roaming in progress.
* @QCA_WLAN_VENDOR_TWT_STATUS_CHANNEL_SWITCH_IN_PROGRESS: FW rejected the TWT
* setup request due to channel switch in progress.
* @QCA_WLAN_VENDOR_TWT_STATUS_SCAN_IN_PROGRESS: FW rejected the TWT setup
* request due to scan in progress.
+ * QCA_WLAN_VENDOR_TWT_STATUS_POWER_SAVE_EXIT_TERMINATE: The driver requested to
+ * terminate an existing TWT session on power save exit request from userspace.
+ * Used on the TWT_TERMINATE notification from the driver/firmware.
*/
enum qca_wlan_vendor_twt_status {
QCA_WLAN_VENDOR_TWT_STATUS_OK = 0,
QCA_WLAN_VENDOR_TWT_STATUS_TWT_NOT_ENABLED = 1,
QCA_WLAN_VENDOR_TWT_STATUS_USED_DIALOG_ID = 2,
QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY = 3,
QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST = 4,
QCA_WLAN_VENDOR_TWT_STATUS_NOT_SUSPENDED = 5,
QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM = 6,
QCA_WLAN_VENDOR_TWT_STATUS_NOT_READY = 7,
QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE = 8,
QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK = 9,
QCA_WLAN_VENDOR_TWT_STATUS_NO_RESPONSE = 10,
QCA_WLAN_VENDOR_TWT_STATUS_DENIED = 11,
QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR = 12,
QCA_WLAN_VENDOR_TWT_STATUS_ALREADY_SUSPENDED = 13,
QCA_WLAN_VENDOR_TWT_STATUS_IE_INVALID = 14,
QCA_WLAN_VENDOR_TWT_STATUS_PARAMS_NOT_IN_RANGE = 15,
QCA_WLAN_VENDOR_TWT_STATUS_PEER_INITIATED_TERMINATE = 16,
QCA_WLAN_VENDOR_TWT_STATUS_ROAM_INITIATED_TERMINATE = 17,
QCA_WLAN_VENDOR_TWT_STATUS_SCC_MCC_CONCURRENCY_TERMINATE = 18,
QCA_WLAN_VENDOR_TWT_STATUS_ROAMING_IN_PROGRESS = 19,
QCA_WLAN_VENDOR_TWT_STATUS_CHANNEL_SWITCH_IN_PROGRESS = 20,
QCA_WLAN_VENDOR_TWT_STATUS_SCAN_IN_PROGRESS = 21,
+ QCA_WLAN_VENDOR_TWT_STATUS_POWER_SAVE_EXIT_TERMINATE = 22,
};
/**
* enum qca_wlan_vendor_attr_twt_resume - Represents attributes for
* TWT (Target Wake Time) resume request. These attributes are sent as part of
* %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME and
* %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. Also used by
* attributes through %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT: Optional (u8)
* @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT2_TWT: Optional (u32)
* These attributes are used as the SP offset which is the offset from TSF after
* which the wake happens. The units are in microseconds. Please note that
* _NEXT_TWT is limited to u8 whereas _NEXT2_TWT takes the u32 data.
* _NEXT2_TWT takes the precedence over _NEXT_TWT and thus the recommendation
* is to use _NEXT2_TWT. If neither of these attributes is provided, the value
* will be set to zero.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE: Required (u32)
* This attribute represents the next TWT subfield size.
* Value 0 represents 0 bits, 1 represents 32 bits, 2 for 48 bits,
* and 4 for 64 bits.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID: Required (u8).
* Flow ID is the unique identifier for each TWT session. This attribute
* represents the respective TWT session to resume.
* Flow ID values from 0 to 254 represent a single TWT session
* Flow ID value of 255 represents all TWT sessions.
* If an invalid dialog id is provided, status
* QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST will be returned.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR: 6-byte MAC address
* Represents the MAC address of the peer to which TWT Resume is
* being sent. This is used in AP mode to represent the respective
* client and is a required parameter. In STA mode, this is an optional
* parameter
*/
enum qca_wlan_vendor_attr_twt_resume {
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT = 1,
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE = 2,
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_FLOW_ID = 3,
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT2_TWT = 4,
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAC_ADDR = 5,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAX =
QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_twt_nudge - Represents attributes for
* TWT (Target Wake Time) nudge request. TWT nudge is a combination of suspend
* and resume in a single request. These attributes are sent as part of
* %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_FLOW_ID: Required (u8)
* Flow ID is the unique identifier for each TWT session. This attribute
* represents the respective TWT session to suspend and resume.
* Flow ID values from 0 to 254 represent a single TWT session
* Flow ID value of 255 represents all TWT sessions in TWT NUDGE request
* and response.
* If an invalid dialog id is provided, status
* QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST will be returned.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME: Required (u32)
* This attribute is used as the SP offset which is the offset from
* TSF after which the wake happens. The units are in microseconds.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE: Required (u32)
* This attribute represents the next TWT subfield size.
* Value 0 represents 0 bits, 1 represents 32 bits, 2 for 48 bits,
* and 4 for 64 bits.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR: 6-byte MAC address
* Represents the MAC address of the peer to which TWT Suspend and Resume is
* being sent. This is used in AP mode to represent the respective
* client and is a required parameter. In STA mode, this is an optional
* parameter.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME_TSF: Optional (u64)
* This field contains absolute TSF value of the time at which the TWT
* session will be resumed.
*/
enum qca_wlan_vendor_attr_twt_nudge {
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_FLOW_ID = 1,
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME = 2,
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE = 3,
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR = 4,
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME_TSF = 5,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAX =
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_twt_stats: Represents attributes for
* TWT (Target Wake Time) get statistics and clear statistics request.
* These attributes are sent as part of
* %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_FLOW_ID: Required (u8)
* Flow ID is the unique identifier for each TWT session. This attribute
* represents the respective TWT session for get and clear TWT statistics.
* Flow ID values from 0 to 254 represent a single TWT session
* Flow ID value of 255 represents all TWT sessions in
* 1) TWT GET STATISTICS request and response
* 2) TWT CLEAR STATISTICS request
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAC_ADDR: 6-byte MAC address
* Represents the MAC address of the peer for which TWT Statistics
* is required.
* In AP mode this is used to represent the respective
* client and is a required parameter for
* 1) TWT GET STATISTICS request and response
* 2) TWT CLEAR STATISTICS request and response
* In STA mode, this is an optional parameter.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_SESSION_WAKE_DURATION: Required (u32)
* This is the duration of the service period in microseconds.
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_WAKE_DURATION: Required (u32)
* Average of the actual wake duration observed so far. Unit is microseconds.
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS: Required (u32)
* The number of TWT service periods elapsed so far.
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_MIN_WAKE_DURATION: Required (u32)
* This is the minimum value of the wake duration observed across
* QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. Unit is
* microseconds.
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX_WAKE_DURATION: Required (u32)
* This is the maximum value of wake duration observed across
* QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS. Unit is
* microseconds.
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_MPDU: Required (u32)
* Average number of MPDUs transmitted successfully across
* QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS.
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_MPDU: Required (u32)
* Average number of MPDUs received successfully across
* QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS.
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE: Required (u32)
* Average number of bytes transmitted successfully across
* QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS.
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE: Required (u32)
* Average number of bytes received successfully across
* QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS.
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS: Required (u32)
* Status of the TWT GET STATISTICS request.
* This contains status values in enum qca_wlan_vendor_twt_status
* Obtained in the QCA_WLAN_TWT_GET_STATS response from the firmware.
*/
enum qca_wlan_vendor_attr_twt_stats {
QCA_WLAN_VENDOR_ATTR_TWT_STATS_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_FLOW_ID = 1,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAC_ADDR = 2,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_SESSION_WAKE_DURATION = 3,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVG_WAKE_DURATION = 4,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_NUM_SP_ITERATIONS = 5,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_MIN_WAKE_DURATION = 6,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX_WAKE_DURATION = 7,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_MPDU = 8,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_MPDU = 9,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_TX_PACKET_SIZE = 10,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_AVERAGE_RX_PACKET_SIZE = 11,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_STATUS = 12,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_STATS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TWT_STATS_MAX =
QCA_WLAN_VENDOR_ATTR_TWT_STATS_AFTER_LAST - 1,
};
/**
* qca_wlan_twt_get_capa - Represents the bitmap of TWT capabilities
* supported by the device and the peer.
* Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_GET_CAPABILITIES
*
* @QCA_WLAN_TWT_CAPA_REQUESTOR: TWT requestor support is advertised by
* TWT non-scheduling STA. This capability is advertised in the HE
* Capability/Extended Capabilities information element in the
* Association Request frame by the device.
*
* @QCA_WLAN_TWT_CAPA_RESPONDER: TWT responder support is advertised by
* the TWT scheduling AP. This capability is advertised in the Extended
* Capabilities/HE Capabilities information element.
*
* @QCA_WLAN_TWT_CAPA_BROADCAST: On the requestor side, this indicates support
* for the broadcast TWT functionality. On the responder side, this indicates
* support for the role of broadcast TWT scheduling functionality. This
* capability is advertised in the HE Capabilities information element.
*
* @QCA_WLAN_TWT_CAPA_TWT_FLEXIBLE: The device supports flexible TWT schedule.
* This capability is advertised in the HE Capabilities information element.
*
* @QCA_WLAN_TWT_CAPA_REQUIRED: The TWT Required is advertised by AP to indicate
* that it mandates the associated HE STAs to support TWT. This capability is
* advertised by AP in the HE Operation Parameters field of the HE Operation
* information element.
*/
enum qca_wlan_twt_capa {
QCA_WLAN_TWT_CAPA_REQUESTOR = BIT(0),
QCA_WLAN_TWT_CAPA_RESPONDER = BIT(1),
QCA_WLAN_TWT_CAPA_BROADCAST = BIT(2),
QCA_WLAN_TWT_CAPA_FLEXIBLE = BIT(3),
QCA_WLAN_TWT_CAPA_REQUIRED = BIT(4),
};
/**
* enum qca_wlan_vendor_attr_twt_capability - Represents attributes for TWT
* get capabilities request type. Used by QCA_WLAN_TWT_GET_CAPABILITIES TWT
* operation.
* @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAC_ADDR: 6-byte MAC address
* Represents the MAC address of the peer for which the TWT capabilities
* are being queried. This is used in AP mode to represent the respective
* client. In STA mode, this is an optional parameter.
*
* @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_SELF: (u16).
* Self TWT capabilities. Carries a bitmap of TWT capabilities specified in
* enum qca_wlan_twt_capa.
* @QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER: (u16).
* Peer TWT capabilities. Carries a bitmap of TWT capabilities specified in
* enum qca_wlan_twt_capa.
*/
enum qca_wlan_vendor_attr_twt_capability {
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAC_ADDR = 1,
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_SELF = 2,
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_PEER = 3,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_MAX =
QCA_WLAN_VENDOR_ATTR_TWT_CAPABILITIES_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_attr_twt_set_param: Represents attributes for
+ * TWT (Target Wake Time) related parameters. It is used when
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_TWT_OPERATION is set to %QCA_WLAN_TWT_SET_PARAM.
+ * These attributes are sent as part of %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TWT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_AP_AC_VALUE: Optional (u8)
+ * This attribute configures AC parameters to be used for all TWT
+ * sessions in AP mode.
+ * Uses the enum qca_wlan_ac_type values.
+ */
+enum qca_wlan_vendor_attr_twt_set_param {
+ QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_AP_AC_VALUE = 1,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_MAX =
+ QCA_WLAN_VENDOR_ATTR_TWT_SET_PARAM_AFTER_LAST - 1,
+};
+
/**
* enum qca_wlan_vendor_twt_setup_resp_type - Represents the response type by
* the TWT responder
*
* @QCA_WLAN_VENDOR_TWT_RESP_ALTERNATE: TWT responder suggests TWT
* parameters that are different from TWT requesting STA suggested
* or demanded TWT parameters
* @QCA_WLAN_VENDOR_TWT_RESP_DICTATE: TWT responder demands TWT
* parameters that are different from TWT requesting STA TWT suggested
* or demanded parameters
* @QCA_WLAN_VENDOR_TWT_RESP_REJECT: TWT responder rejects TWT
* setup
* @QCA_WLAN_VENDOR_TWT_RESP_ACCEPT: TWT responder accepts the TWT
* setup.
*/
enum qca_wlan_vendor_twt_setup_resp_type {
QCA_WLAN_VENDOR_TWT_RESP_ALTERNATE = 1,
QCA_WLAN_VENDOR_TWT_RESP_DICTATE = 2,
QCA_WLAN_VENDOR_TWT_RESP_REJECT = 3,
QCA_WLAN_VENDOR_TWT_RESP_ACCEPT = 4,
};
/**
* enum qca_wlan_vendor_twt_setup_req_type - Required (u8)
* Represents the setup type being requested for TWT.
* @QCA_WLAN_VENDOR_TWT_SETUP_REQUEST: STA is not specifying all the TWT
* parameters but relying on AP to fill the parameters during the negotiation.
* @QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST: STA will provide all the suggested
* values which the AP may accept or AP may provide alternative parameters
* which the STA may accept.
* @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any
* alternate parameters than the requested ones.
*/
enum qca_wlan_vendor_twt_setup_req_type {
QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1,
QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2,
QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3,
};
/**
* enum qca_wlan_roam_scan_event_type - Type of roam scan event
*
* Indicates the type of roam scan event sent by firmware/driver.
*
* @QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT: Roam scan trigger event type.
* @QCA_WLAN_ROAM_SCAN_STOP_EVENT: Roam scan stopped event type.
*/
enum qca_wlan_roam_scan_event_type {
QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT = 0,
QCA_WLAN_ROAM_SCAN_STOP_EVENT = 1,
};
/**
* enum qca_wlan_roam_scan_trigger_reason - Roam scan trigger reason
*
* Indicates the reason for triggering roam scan by firmware/driver.
*
* @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI: Due to low RSSI of current AP.
* @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER: Due to high packet error rate.
*/
enum qca_wlan_roam_scan_trigger_reason {
QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI = 0,
QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER = 1,
};
/**
* enum qca_wlan_vendor_attr_roam_scan - Vendor subcmd attributes to report
* roam scan related details from driver/firmware to user space. enum values
* are used for NL attributes sent with
* %QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT sub command.
*/
enum qca_wlan_vendor_attr_roam_scan {
QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_INVALID = 0,
/* Encapsulates type of roam scan event being reported. enum
* qca_wlan_roam_scan_event_type describes the possible range of
* values. u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_EVENT_TYPE = 1,
/* Encapsulates reason for triggering roam scan. enum
* qca_wlan_roam_scan_trigger_reason describes the possible range of
* values. u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_TRIGGER_REASON = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_MAX =
QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_cfr_data_transport_modes - Defines QCA vendor CFR data
* transport modes and is used by the attribute
* QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_TRANSPORT_MODE as a part of the vendor
* command QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG.
* @QCA_WLAN_VENDOR_CFR_DATA_RELAY_FS: Use relayfs to send CFR data.
* @QCA_WLAN_VENDOR_CFR_DATA_NETLINK_EVENTS: Use netlink events to send CFR
* data. The data shall be encapsulated within
* QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA along with the vendor sub command
* QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG as an asynchronous event.
*/
enum qca_wlan_vendor_cfr_data_transport_modes {
QCA_WLAN_VENDOR_CFR_DATA_RELAY_FS = 0,
QCA_WLAN_VENDOR_CFR_DATA_NETLINK_EVENTS = 1,
};
/**
* enum qca_wlan_vendor_cfr_method - QCA vendor CFR methods used by
* attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD as part of vendor
* command QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG.
* @QCA_WLAN_VENDOR_CFR_METHOD_QOS_NULL: CFR method using QoS Null frame
* @QCA_WLAN_VENDOR_CFR_QOS_NULL_WITH_PHASE: CFR method using QoS Null frame
* with phase
* @QCA_WLAN_VENDOR_CFR_PROBE_RESPONSE: CFR method using Probe Response frame
*/
enum qca_wlan_vendor_cfr_method {
QCA_WLAN_VENDOR_CFR_METHOD_QOS_NULL = 0,
QCA_WLAN_VENDOR_CFR_QOS_NULL_WITH_PHASE = 1,
QCA_WLAN_VENDOR_CFR_PROBE_RESPONSE = 2,
};
/**
* enum qca_wlan_vendor_cfr_capture_type - QCA vendor CFR capture type used by
* attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE.
* @QCA_WLAN_VENDOR_CFR_DIRECT_FTM: Filter directed FTM ACK frames.
* @QCA_WLAN_VENDOR_CFR_ALL_FTM_ACK: Filter all FTM ACK frames.
* @QCA_WLAN_VENDOR_CFR_DIRECT_NDPA_NDP: Filter NDPA NDP directed frames.
* @QCA_WLAN_VENDOR_CFR_TA_RA: Filter frames based on TA/RA/Subtype which
* is provided by one or more of below attributes:
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER
* @QCA_WLAN_CFR_ALL_PACKET: Filter all packets.
* @QCA_WLAN_VENDOR_CFR_NDPA_NDP_ALL: Filter all NDPA NDP frames.
*/
enum qca_wlan_vendor_cfr_capture_type {
QCA_WLAN_VENDOR_CFR_DIRECT_FTM = 0,
QCA_WLAN_VENDOR_CFR_ALL_FTM_ACK = 1,
QCA_WLAN_VENDOR_CFR_DIRECT_NDPA_NDP = 2,
QCA_WLAN_VENDOR_CFR_TA_RA = 3,
QCA_WLAN_VENDOR_CFR_ALL_PACKET = 4,
QCA_WLAN_VENDOR_CFR_NDPA_NDP_ALL = 5,
};
/**
* enum qca_wlan_vendor_peer_cfr_capture_attr - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG to configure peer
* Channel Frequency Response capture parameters and enable periodic CFR
* capture.
*
* @QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR: Optional (6-byte MAC address)
* MAC address of peer. This is for CFR version 1 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE: Required (flag)
* Enable peer CFR capture. This attribute is mandatory to enable peer CFR
* capture. If this attribute is not present, peer CFR capture is disabled.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH: Optional (u8)
* BW of measurement, attribute uses the values in enum nl80211_chan_width
* Supported values: 20, 40, 80, 80+80, 160.
* Note that all targets may not support all bandwidths.
* This attribute is mandatory for version 1 if attribute
* QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY: Optional (u32)
* Periodicity of CFR measurement in milliseconds.
* Periodicity should be a multiple of Base timer.
* Current Base timer value supported is 10 milliseconds (default).
* 0 for one shot capture.
* This attribute is mandatory for version 1 if attribute
* QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD: Optional (u8)
* Method used to capture Channel Frequency Response.
* Attribute uses the values defined in enum qca_wlan_vendor_cfr_method.
* This attribute is mandatory for version 1 if attribute
* QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used.
*
* @QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE: Optional (flag)
* Enable periodic CFR capture.
* This attribute is mandatory for version 1 to enable Periodic CFR capture.
* If this attribute is not present, periodic CFR capture is disabled.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION: Optional (u8)
* Value is 1 or 2 since there are two versions of CFR capture. Two versions
* can't be enabled at same time. This attribute is mandatory if target
* support both versions and use one of them.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP: Optional (u32)
* This attribute is mandatory for version 2 if
* QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_ENTRY is used.
* Bits 15:0 bitfield indicates which group is to be enabled.
* Bits 31:16 Reserved for future use.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION: Optional (u32)
* CFR capture duration in microsecond. This attribute is mandatory for
* version 2 if attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL is used.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL: Optional (u32)
* CFR capture interval in microsecond. This attribute is mandatory for
* version 2 if attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION is used.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE: Optional (u32)
* CFR capture type is defined in enum qca_wlan_vendor_cfr_capture_type.
* This attribute is mandatory for version 2.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_UL_MU_MASK: Optional (u64)
* Bitfield indicating which user in the current UL MU transmissions are
* enabled for CFR capture. Bits 36 to 0 indicate user indexes for 37 users in
* a UL MU transmission. If bit 0 is set, the CFR capture will happen for user
* index 0 in the current UL MU transmission. If bits 0 and 2 are set, CFR
* capture for UL MU TX corresponds to user indices 0 and 2. Bits 63:37 are
* reserved for future use. This is for CFR version 2 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREEZE_TLV_DELAY_COUNT: Optional (u32)
* Indicates the number of consecutive RX frames to be skipped before CFR
* capture is enabled again. This is for CFR version 2 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TABLE: Nested attribute containing
* one or more %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_ENTRY attributes.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_ENTRY: Nested attribute containing
* the following group attributes:
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER,
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA,
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA,
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK,
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK,
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS,
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW,
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER,
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER,
* %QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER: Optional (u32)
* Target supports multiple groups for some configurations. The group number
* can be any value between 0 and 15. This is for CFR version 2 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA: Optional (6-byte MAC address)
* Transmitter address which is used to filter frames. This MAC address takes
* effect with QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK. This is for CFR
* version 2 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA: Optional (6-byte MAC address)
* Receiver address which is used to filter frames. This MAC address takes
* effect with QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK. This is for CFR
* version 2 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK: Optional (6-byte MAC address)
* Mask of transmitter address which is used to filter frames. This is for CFR
* version 2 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK: Optional (6-byte MAC address)
* Mask of receiver address which is used to filter frames. This is for CFR
* version 2 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS: Optional (u32)
* Indicates frames with a specific NSS will be filtered for CFR capture.
* This is for CFR version 2 only. This is a bitmask. Bits 7:0 request CFR
* capture to be done for frames matching the NSS specified within this bitmask.
* Bits 31:8 are reserved for future use. Bits 7:0 map to NSS:
* bit 0 : NSS 1
* bit 1 : NSS 2
* ...
* bit 7 : NSS 8
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW: Optional (u32)
* Indicates frames with a specific bandwidth will be filtered for CFR capture.
* This is for CFR version 2 only. This is a bitmask. Bits 4:0 request CFR
* capture to be done for frames matching the bandwidths specified within this
* bitmask. Bits 31:5 are reserved for future use. Bits 4:0 map to bandwidth
* numerated in enum nl80211_band (although not all bands may be supported
* by a given device).
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER: Optional (u32)
* Management frames matching the subtype filter categories will be filtered in
* by MAC for CFR capture. This is a bitmask in which each bit represents the
* corresponding Management frame subtype value per IEEE Std 802.11-2016,
* 9.2.4.1.3 Type and Subtype subfields. For example, Beacon frame control type
* is 8 and its value is 1 << 8 = 0x100. This is for CFR version 2 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER: Optional (u32)
* Control frames matching the subtype filter categories will be filtered in by
* MAC for CFR capture. This is a bitmask in which each bit represents the
* corresponding Control frame subtype value per IEEE Std 802.11-2016,
* 9.2.4.1.3 Type and Subtype subfields. This is for CFR version 2 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER: Optional (u32)
* Data frames matching the subtype filter categories will be filtered in by
* MAC for CFR capture. This is a bitmask in which each bit represents the
* corresponding Data frame subtype value per IEEE Std 802.11-2016,
* 9.2.4.1.3 Type and Subtype subfields. This is for CFR version 2 only.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_TRANSPORT_MODE: Optional (u8)
* Userspace can use this attribute to specify the driver about which transport
* mode shall be used by the driver to send CFR data to userspace. Uses values
* from enum qca_wlan_vendor_cfr_data_transport_modes. When this attribute is
* not present, the driver shall use the default transport mechanism which is
* QCA_WLAN_VENDOR_CFR_DATA_RELAY_FS.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_RECEIVER_PID: Optional (u32)
* Userspace can use this attribute to specify the nl port id of the application
* which receives the CFR data and processes it further so that the drivers can
* unicast the netlink events to a specific application. Optionally included
* when QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_TRANSPORT_MODE is set to
* QCA_WLAN_VENDOR_CFR_DATA_NETLINK_EVENTS, not required otherwise. The drivers
* shall multicast the netlink events when this attribute is not included.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA: Required (NLA_BINARY).
* This attribute will be used by the driver to encapsulate and send CFR data
* to userspace along with QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG as an
* asynchronous event when the driver is configured to send CFR data using
* netlink events with %QCA_WLAN_VENDOR_CFR_DATA_NETLINK_EVENTS.
*/
enum qca_wlan_vendor_peer_cfr_capture_attr {
QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR = 1,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE = 2,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH = 3,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY = 4,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD = 5,
QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE = 6,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_VERSION = 7,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE_GROUP_BITMAP = 8,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_DURATION = 9,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_INTERVAL = 10,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_TYPE = 11,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_UL_MU_MASK = 12,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_FREEZE_TLV_DELAY_COUNT = 13,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TABLE = 14,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_ENTRY = 15,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NUMBER = 16,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA = 17,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA = 18,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_TA_MASK = 19,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_RA_MASK = 20,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_NSS = 21,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_BW = 22,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_MGMT_FILTER = 23,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_CTRL_FILTER = 24,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_GROUP_DATA_FILTER = 25,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_TRANSPORT_MODE = 26,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_DATA_RECEIVER_PID = 27,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_RESP_DATA = 28,
/* Keep last */
QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX =
QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST - 1,
};
/**
* enum qca_wlan_throughput_level - Current throughput level
*
* Indicates the current level of throughput calculated by the driver. The
* driver may choose different thresholds to decide whether the throughput level
* is low or medium or high based on variety of parameters like physical link
* capacity of the current connection, the number of packets being dispatched
* per second, etc. The throughput level events might not be consistent with the
* actual current throughput value being observed.
*
* @QCA_WLAN_THROUGHPUT_LEVEL_LOW: Low level of throughput
* @QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM: Medium level of throughput
* @QCA_WLAN_THROUGHPUT_LEVEL_HIGH: High level of throughput
*/
enum qca_wlan_throughput_level {
QCA_WLAN_THROUGHPUT_LEVEL_LOW = 0,
QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM = 1,
QCA_WLAN_THROUGHPUT_LEVEL_HIGH = 2,
};
/**
* enum qca_wlan_vendor_attr_throughput_change - Vendor subcmd attributes to
* report throughput changes from the driver to user space. enum values are used
* for netlink attributes sent with
* %QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT sub command.
*/
enum qca_wlan_vendor_attr_throughput_change {
QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_INVALID = 0,
/* Indicates the direction of throughput in which the change is being
* reported. u8 attribute. Value is 0 for TX and 1 for RX.
*/
QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_DIRECTION = 1,
/* Indicates the newly observed throughput level. enum
* qca_wlan_throughput_level describes the possible range of values.
* u8 attribute.
*/
QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_THROUGHPUT_LEVEL = 2,
/* Indicates the driver's guidance on the new value to be set to
* kernel's TCP parameter tcp_limit_output_bytes. u32 attribute. The
* driver may optionally include this attribute.
*/
QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_LIMIT_OUTPUT_BYTES = 3,
/* Indicates the driver's guidance on the new value to be set to
* kernel's TCP parameter tcp_adv_win_scale. s8 attribute. Possible
* values are from -31 to 31. The driver may optionally include this
* attribute.
*/
QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_ADV_WIN_SCALE = 4,
/* Indicates the driver's guidance on the new value to be set to
* kernel's TCP parameter tcp_delack_seg. u32 attribute. The driver may
* optionally include this attribute.
*/
QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_DELACK_SEG = 5,
/* keep last */
QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_MAX =
QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST - 1,
};
/**
* enum qca_coex_config_profiles - This enum defines different types of
* traffic streams that can be prioritized one over the other during coex
* scenarios.
* The types defined in this enum are categorized in the below manner.
* 0 - 31 values corresponds to WLAN
* 32 - 63 values corresponds to BT
* 64 - 95 values corresponds to Zigbee
* @QCA_WIFI_STA_DISCOVERY: Prioritize discovery frames for WLAN STA
* @QCA_WIFI_STA_CONNECTION: Prioritize connection frames for WLAN STA
* @QCA_WIFI_STA_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN STA
* @QCA_WIFI_STA_DATA : Prioritize data frames for WLAN STA
* @QCA_WIFI_STA_ALL: Priritize all frames for WLAN STA
* @QCA_WIFI_SAP_DISCOVERY: Prioritize discovery frames for WLAN SAP
* @QCA_WIFI_SAP_CONNECTION: Prioritize connection frames for WLAN SAP
* @QCA_WIFI_SAP_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN SAP
* @QCA_WIFI_SAP_DATA: Prioritize data frames for WLAN SAP
* @QCA_WIFI_SAP_ALL: Prioritize all frames for WLAN SAP
* @QCA_BT_A2DP: Prioritize BT A2DP
* @QCA_BT_BLE: Prioritize BT BLE
* @QCA_BT_SCO: Prioritize BT SCO
* @QCA_ZB_LOW: Prioritize Zigbee Low
* @QCA_ZB_HIGH: Prioritize Zigbee High
*/
enum qca_coex_config_profiles {
/* 0 - 31 corresponds to WLAN */
QCA_WIFI_STA_DISCOVERY = 0,
QCA_WIFI_STA_CONNECTION = 1,
QCA_WIFI_STA_CLASS_3_MGMT = 2,
QCA_WIFI_STA_DATA = 3,
QCA_WIFI_STA_ALL = 4,
QCA_WIFI_SAP_DISCOVERY = 5,
QCA_WIFI_SAP_CONNECTION = 6,
QCA_WIFI_SAP_CLASS_3_MGMT = 7,
QCA_WIFI_SAP_DATA = 8,
QCA_WIFI_SAP_ALL = 9,
QCA_WIFI_CASE_MAX = 31,
/* 32 - 63 corresponds to BT */
QCA_BT_A2DP = 32,
QCA_BT_BLE = 33,
QCA_BT_SCO = 34,
QCA_BT_CASE_MAX = 63,
/* 64 - 95 corresponds to Zigbee */
QCA_ZB_LOW = 64,
QCA_ZB_HIGH = 65,
QCA_ZB_CASE_MAX = 95,
/* 0xff is default value if the u8 profile value is not set. */
QCA_COEX_CONFIG_PROFILE_DEFAULT_VALUE = 255
};
/**
* enum qca_vendor_attr_coex_config_types - Coex configurations types.
* This enum defines the valid set of values of coex configuration types. These
* values may used by attribute
* %QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE.
*
* @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET: Reset all the
* weights to default values.
* @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START: Start to config
* weights with configurability value.
*/
enum qca_vendor_attr_coex_config_types {
QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET = 1,
QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START = 2,
};
/**
* enum qca_vendor_attr_coex_config - Specifies vendor coex config attributes
*
* @QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES: This attribute contains variable
* length array of 8-bit values from enum qca_coex_config_profiles.
* FW will prioritize the profiles in the order given in the array encapsulated
* in this attribute.
* For example:
* -----------------------------------------------------------------------
* | 1 | 34 | 32 | 65 |
* -----------------------------------------------------------------------
* If the attribute contains the values defined in above array then it means
* 1) Wifi STA connection has priority over BT_SCO, BT_A2DP and ZIGBEE HIGH.
* 2) BT_SCO has priority over BT_A2DP.
* 3) BT_A2DP has priority over ZIGBEE HIGH.
* Profiles which are not listed in this array shall not be preferred over the
* profiles which are listed in the array as a part of this attribute.
*/
enum qca_vendor_attr_coex_config {
QCA_VENDOR_ATTR_COEX_CONFIG_INVALID = 0,
QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES = 1,
/* Keep last */
QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST,
QCA_VENDOR_ATTR_COEX_CONFIG_MAX =
QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST - 1,
};
/**
* enum qca_vendor_attr_coex_config_three_way - Specifies vendor coex config
* attributes
* Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG
*
* QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE: u32 attribute.
* Indicate config type.
* The config types are 32-bit values from qca_vendor_attr_coex_config_types
*
* @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1: u32 attribute.
* Indicate the Priority 1 profiles.
* The profiles are 8-bit values from enum qca_coex_config_profiles.
* In same priority level, maximum to 4 profiles can be set here.
* @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2: u32 attribute.
* Indicate the Priority 2 profiles.
* The profiles are 8-bit values from enum qca_coex_config_profiles.
* In same priority level, maximum to 4 profiles can be set here.
* @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3: u32 attribute.
* Indicate the Priority 3 profiles.
* The profiles are 8-bit values from enum qca_coex_config_profiles.
* In same priority level, maximum to 4 profiles can be set here.
* @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4: u32 attribute.
* Indicate the Priority 4 profiles.
* The profiles are 8-bit values from enum qca_coex_config_profiles.
* In same priority level, maximum to 4 profiles can be set here.
* NOTE:
* Limitations for QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_x priority
* arrangement:
* 1: In the same u32 attribute (priority x), the profiles enum values own
* same priority level.
* 2: 0xff is default value if the u8 profile value is not set.
* 3: max to 4 rules/profiles in same priority level.
* 4: max to 4 priority level (priority 1 - priority 4)
* 5: one priority level only supports one scenario from WLAN/BT/ZB,
* hybrid rules not support.
* 6: if WMI_COEX_CONFIG_THREE_WAY_COEX_RESET called, priority x will
* remain blank to reset all parameters.
* For example:
*
* If the attributes as follow:
* priority 1:
* ------------------------------------
* | 0xff | 0 | 1 | 2 |
* ------------------------------------
* priority 2:
* -------------------------------------
* | 0xff | 0xff | 0xff | 32 |
* -------------------------------------
* priority 3:
* -------------------------------------
* | 0xff | 0xff | 0xff | 65 |
* -------------------------------------
* then it means:
* 1: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION
* owns same priority level.
* 2: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION
* has priority over BT_A2DP and ZB_HIGH.
* 3: BT_A2DP has priority over ZB_HIGH.
*/
enum qca_vendor_attr_coex_config_three_way {
QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_INVALID = 0,
QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE = 1,
QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1 = 2,
QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2 = 3,
QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3 = 4,
QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4 = 5,
/* Keep last */
QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST,
QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX =
QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_link_properties - Represent the link properties.
*
* @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer
* (STA/AP) for the connected link.
* @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS: Attribute containing a
* &struct nl80211_sta_flag_update for the respective connected link. MAC
* address of the peer represented by
* QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR.
*/
enum qca_wlan_vendor_attr_link_properties {
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_INVALID = 0,
/* 1 - 3 are reserved */
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR = 4,
QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS = 5,
/* Keep last */
QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST,
QCA_VENDOR_ATTR_LINK_PROPERTIES_MAX =
QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1,
};
/**
* enum qca_vendor_attr_peer_stats_cache_type - Represents peer stats cache type
* This enum defines the valid set of values of peer stats cache types. These
* values are used by attribute
* %QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS: Represents peer TX rate statistics
* @QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS: Represents peer RX rate statistics
* @QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS: Represents peer TX sojourn
* statistics
*/
enum qca_vendor_attr_peer_stats_cache_type {
QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS,
QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS,
QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS,
};
/**
* enum qca_wlan_vendor_attr_peer_stats_cache_params - This enum defines
* attributes required for QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH
* Information in these attributes is used to flush peer rate statistics from
* the driver to user application.
*
* @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE: Unsigned 32-bit attribute
* Indicate peer statistics cache type.
* The statistics types are 32-bit values from
* enum qca_vendor_attr_peer_stats_cache_type.
* @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC: Unsigned 8-bit array
* of size 6 octets, representing the peer MAC address.
* @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA: Opaque data attribute
* containing buffer of statistics to send to application layer entity.
* @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE: Unsigned 64-bit attribute
* representing a cookie for peer unique session.
*/
enum qca_wlan_vendor_attr_peer_stats_cache_params {
QCA_WLAN_VENDOR_ATTR_PEER_STATS_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE = 1,
QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC = 2,
QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA = 3,
QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE = 4,
/* Keep last */
QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST,
QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX =
QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST - 1
};
/**
* enum qca_mpta_helper_attr_zigbee_state - Current Zigbee state
* This enum defines all the possible states of Zigbee, which can be
* delivered in the QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE attribute.
*
* @ZIGBEE_IDLE: Zigbee in idle state
* @ZIGBEE_FORM_NETWORK: Zigbee forming network
* @ZIGBEE_WAIT_JOIN: Zigbee waiting for joining network
* @ZIGBEE_JOIN: Zigbee joining network
* @ZIGBEE_NETWORK_UP: Zigbee network is up
* @ZIGBEE_HMI: Zigbee in HMI mode
*/
enum qca_mpta_helper_attr_zigbee_state {
ZIGBEE_IDLE = 0,
ZIGBEE_FORM_NETWORK = 1,
ZIGBEE_WAIT_JOIN = 2,
ZIGBEE_JOIN = 3,
ZIGBEE_NETWORK_UP = 4,
ZIGBEE_HMI = 5,
};
/*
* enum qca_mpta_helper_vendor_attr - Attributes used in vendor sub-command
* QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG.
*/
enum qca_mpta_helper_vendor_attr {
QCA_MPTA_HELPER_VENDOR_ATTR_INVALID = 0,
/* Optional attribute used to update Zigbee state.
* enum qca_mpta_helper_attr_zigbee_state.
* NLA_U32 attribute.
*/
QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE = 1,
/* Optional attribute used to configure WLAN duration for Shape-OCS
* during interrupt.
* Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION.
* Value range 0 ~ 300 (ms).
* NLA_U32 attribute.
*/
QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION = 2,
/* Optional attribute used to configure non-WLAN duration for Shape-OCS
* during interrupt.
* Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION.
* Value range 0 ~ 300 (ms).
* NLA_U32 attribute.
*/
QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION = 3,
/* Optional attribute used to configure WLAN duration for Shape-OCS
* monitor period.
* Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION.
* Value range 0 ~ 300 (ms)
* NLA_U32 attribute
*/
QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION = 4,
/* Optional attribute used to configure non-WLAN duration for Shape-OCS
* monitor period.
* Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION.
* Value range 0 ~ 300 (ms)
* NLA_U32 attribute
*/
QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION = 5,
/* Optional attribute used to configure OCS interrupt duration.
* Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION.
* Value range 1000 ~ 20000 (ms)
* NLA_U32 attribute
*/
QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION = 6,
/* Optional attribute used to configure OCS monitor duration.
* Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION.
* Value range 1000 ~ 20000 (ms)
* NLA_U32 attribute
*/
QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION = 7,
/* Optional attribute used to notify WLAN firmware the current Zigbee
* channel.
* Value range 11 ~ 26
* NLA_U32 attribute
*/
QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN = 8,
/* Optional attribute used to configure WLAN mute duration.
* Value range 0 ~ 400 (ms)
* NLA_U32 attribute
*/
QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION = 9,
/* keep last */
QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST,
QCA_MPTA_HELPER_VENDOR_ATTR_MAX =
QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_beacon_reporting_op_types - Defines different types of
* operations for which %QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING can be used.
* Will be used by %QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE.
*
* @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START: Sent by userspace to the driver
* to request the driver to start reporting Beacon frames.
* @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_STOP: Sent by userspace to the driver to
* request the driver to stop reporting Beacon frames.
* @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO: Sent by the driver to
* userspace to report received Beacon frames.
* @QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE: Sent by the driver to userspace
* to indicate that the driver is going to pause reporting Beacon frames.
*/
enum qca_wlan_vendor_beacon_reporting_op_types {
QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START = 0,
QCA_WLAN_VENDOR_BEACON_REPORTING_OP_STOP = 1,
QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO = 2,
QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE = 3,
};
/**
* enum qca_wlan_vendor_beacon_reporting_pause_reasons - Defines different types
* of reasons for which the driver is pausing reporting Beacon frames. Will be
* used by %QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON.
*
* @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_UNSPECIFIED: For unspecified
* reasons.
* @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_SCAN_STARTED: When the
* driver/firmware is starting a scan.
* @QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED: When the
* driver/firmware disconnects from the ESS and indicates the disconnection to
* userspace (non-seamless roaming case). This reason code will be used by the
* driver/firmware to indicate stopping of beacon report events. Userspace will
* need to start beacon reporting again (if desired) by sending vendor command
* QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING with
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START after the next connection is
* completed.
*/
enum qca_wlan_vendor_beacon_reporting_pause_reasons {
QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_UNSPECIFIED = 0,
QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_SCAN_STARTED = 1,
QCA_WLAN_VENDOR_BEACON_REPORTING_PAUSE_REASON_DISCONNECTED = 2,
};
/*
* enum qca_wlan_vendor_attr_beacon_reporting_params - List of attributes used
* in vendor sub-command QCA_NL80211_VENDOR_SUBCMD_BEACON_REPORTING.
*/
enum qca_wlan_vendor_attr_beacon_reporting_params {
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_INVALID = 0,
/* Specifies the type of operation that the vendor command/event is
* intended for. Possible values for this attribute are defined in
* enum qca_wlan_vendor_beacon_reporting_op_types. u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE = 1,
/* Optionally set by userspace to request the driver to report Beacon
* frames using asynchronous vendor events when the
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START. NLA_FLAG attribute.
* If this flag is not set, the driver will only update Beacon frames in
* cfg80211 scan cache but not send any vendor events.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_ACTIVE_REPORTING = 2,
/* Optionally used by userspace to request the driver/firmware to report
* Beacon frames periodically when the
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START.
* u32 attribute, indicates the period of Beacon frames to be reported
* and in the units of beacon interval.
* If this attribute is missing in the command, then the default value
* of 1 will be assumed by driver, i.e., to report every Beacon frame.
* Zero is an invalid value.
* If a valid value is received for this attribute, the driver will
* update the cfg80211 scan cache periodically as per the value received
* in this attribute in addition to updating the cfg80211 scan cache
* when there is significant change in Beacon frame IEs.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PERIOD = 3,
/* Used by the driver to encapsulate the SSID when the
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO.
* u8 array with a maximum size of 32.
*
* When generating beacon report from non-MBSSID Beacon frame, the SSID
* will be taken from the SSID element of the received Beacon frame.
*
* When generating beacon report from Multiple BSSID Beacon frame and if
* the BSSID of the current connected BSS matches the BSSID of the
* transmitting BSS, the SSID will be taken from the SSID element of the
* received Beacon frame.
*
* When generating beacon report from Multiple BSSID Beacon frame and if
* the BSSID of the current connected BSS matches the BSSID of one of
* the* nontransmitting BSSs, the SSID will be taken from the SSID field
* included in the nontransmitted BSS profile whose derived BSSID is
* same as the BSSID of the current connected BSS. When there is no
* nontransmitted BSS profile whose derived BSSID is same as the BSSID
* of current connected* BSS, this attribute will not be present.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_SSID = 4,
/* Used by the driver to encapsulate the BSSID of the AP to which STA is
* currently connected to when the
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u8 array with a
* fixed size of 6 bytes.
*
* When generating beacon report from a Multiple BSSID beacon and the
* current connected BSSID matches one of the nontransmitted BSSIDs in a
* Multiple BSSID set, this BSSID will be that particular nontransmitted
* BSSID and not the transmitted BSSID (i.e., the transmitting address
* of the Beacon frame).
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BSSID = 5,
/* Used by the driver to encapsulate the frequency in MHz on which
* the Beacon frame was received when the
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is
* set to QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO.
* u32 attribute.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_FREQ = 6,
/* Used by the driver to encapsulate the Beacon interval
* when the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO.
* u16 attribute. The value will be copied from the Beacon frame and the
* units are TUs.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BI = 7,
/* Used by the driver to encapsulate the Timestamp field from the Beacon
* frame when the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set
* to QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO.
* u64 attribute.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_TSF = 8,
/* Used by the driver to encapsulate the CLOCK_BOOTTIME when this
* Beacon frame is received in the driver when the
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u64 attribute, in
* the units of nanoseconds. This value is expected to have accuracy of
* about 10 ms.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_BOOTTIME_WHEN_RECEIVED = 9,
/* Used by the driver to encapsulate the IEs of the Beacon frame from
* which this event is generated when the
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_BEACON_INFO. u8 array.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_IES = 10,
/* Used by the driver to specify the reason for the driver/firmware to
* pause sending beacons to userspace when the
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE. Possible values are
* defined in enum qca_wlan_vendor_beacon_reporting_pause_reasons, u32
* attribute.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_PAUSE_REASON = 11,
/* Used by the driver to specify whether the driver will automatically
* resume reporting beacon events to userspace later (for example after
* the ongoing off-channel activity is completed etc.) when the
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE. NLA_FLAG attribute.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES = 12,
/* Optionally set by userspace to request the driver not to resume
* beacon reporting after a pause is completed, when the
* QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_OP_TYPE is set to
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START. NLA_FLAG attribute.
* If this flag is set, the driver will not resume beacon reporting
* after any pause in beacon reporting is completed. Userspace has to
* send QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START command again in order
* to initiate beacon reporting again. If this flag is set in the recent
* QCA_WLAN_VENDOR_BEACON_REPORTING_OP_START command, then in the
* subsequent QCA_WLAN_VENDOR_BEACON_REPORTING_OP_PAUSE event (if any)
* the QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_AUTO_RESUMES shall not be
* set by the driver. Setting this flag until and unless there is a
* specific need is not recommended as there is a chance of some beacons
* received after pause command and next start command being not
* reported.
*/
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_DO_NOT_RESUME = 13,
/* Keep last */
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_LAST,
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_MAX =
QCA_WLAN_VENDOR_ATTR_BEACON_REPORTING_LAST - 1
};
/**
* enum qca_vendor_interop_issues_ap_type - Interop issue types
* This enum defines the valid set of values of interop issue types. These
* values are used by attribute %QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE.
*
* @QCA_VENDOR_INTEROP_ISSUES_AP_ON_STA_PS: The AP has power save interop issue
* when the STA's Qpower feature is enabled.
*/
enum qca_vendor_interop_issues_ap_type {
QCA_VENDOR_INTEROP_ISSUES_AP_INVALID = 0,
QCA_VENDOR_INTEROP_ISSUES_AP_ON_STA_PS = 1,
};
/**
* enum qca_vendor_attr_interop_issues_ap - attribute for AP with interop issues
* Values are used by %QCA_NL80211_VENDOR_SUBCMD_INTEROP_ISSUES_AP.
*
* @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_INVALID: Invalid value
* @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE: Interop issue type
* 32-bit unsigned value. The values defined in enum
* qca_vendor_interop_issues_ap_type are used.
* @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST: APs' BSSID container
* array of nested QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID attributes.
* It is present and mandatory for the command but is not used for the event
* since only a single BSSID is reported in an event.
* @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID: AP's BSSID 6-byte MAC address.
* It is used within the nested QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST
* attribute in command case and without such encapsulation in the event case.
* @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST: last value
* @QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX: max value
*/
enum qca_vendor_attr_interop_issues_ap {
QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_INVALID,
QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_TYPE,
QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_LIST,
QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_BSSID,
/* keep last */
QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_MAX =
QCA_WLAN_VENDOR_ATTR_INTEROP_ISSUES_AP_AFTER_LAST - 1
};
/**
* enum qca_vendor_oem_device_type - Represents the target device in firmware.
* It is used by QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO.
*
* @QCA_VENDOR_OEM_DEVICE_VIRTUAL: The command is intended for
* a virtual device.
*
* @QCA_VENDOR_OEM_DEVICE_PHYSICAL: The command is intended for
* a physical device.
*/
enum qca_vendor_oem_device_type {
QCA_VENDOR_OEM_DEVICE_VIRTUAL = 0,
QCA_VENDOR_OEM_DEVICE_PHYSICAL = 1,
};
/**
* enum qca_wlan_vendor_attr_oem_data_params - Used by the vendor command/event
* QCA_NL80211_VENDOR_SUBCMD_OEM_DATA.
*
* @QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA: This NLA_BINARY attribute is
* used to set/query the data to/from the firmware. On query, the same
* attribute is used to carry the respective data in the reply sent by the
* driver to userspace. The request to set/query the data and the format of the
* respective data from the firmware are embedded in the attribute. The
* maximum size of the attribute payload is 1024 bytes.
* Userspace has to set the QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED
* attribute when the data is queried from the firmware.
*
* @QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO: The binary blob will be routed
* based on this field. This optional attribute is included to specify whether
* the device type is a virtual device or a physical device for the
* command/event. This attribute can be omitted for a virtual device (default)
* command/event.
* This u8 attribute is used to carry information for the device type using
* values defined by enum qca_vendor_oem_device_type.
*
* @QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED: This NLA_FLAG attribute
* is set when the userspace queries data from the firmware. This attribute
* should not be set when userspace sets the OEM data to the firmware.
*/
enum qca_wlan_vendor_attr_oem_data_params {
QCA_WLAN_VENDOR_ATTR_OEM_DATA_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_OEM_DATA_CMD_DATA = 1,
QCA_WLAN_VENDOR_ATTR_OEM_DEVICE_INFO = 2,
QCA_WLAN_VENDOR_ATTR_OEM_DATA_RESPONSE_EXPECTED = 3,
/* keep last */
QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_MAX =
QCA_WLAN_VENDOR_ATTR_OEM_DATA_PARAMS_AFTER_LAST - 1
};
/**
* enum qca_wlan_vendor_attr_avoid_frequency_ext - Defines attributes to be
* used with vendor command/event QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY_EXT.
*
* @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_RANGE: Required
* Nested attribute containing multiple ranges with following attributes:
* QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START and
* QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END.
*
* @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START: Required (u32)
* Starting center frequency in MHz.
*
* @QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END: Required (u32)
* Ending center frequency in MHz.
*/
enum qca_wlan_vendor_attr_avoid_frequency_ext {
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_RANGE = 1,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_START = 2,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_END = 3,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_MAX =
QCA_WLAN_VENDOR_ATTR_AVOID_FREQUENCY_AFTER_LAST - 1
};
/*
* enum qca_wlan_vendor_attr_add_sta_node_params - Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE.
*/
enum qca_wlan_vendor_attr_add_sta_node_params {
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_INVALID = 0,
/* 6 byte MAC address of STA */
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_MAC_ADDR = 1,
/* Authentication algorithm used by the station of size u16;
* defined in enum nl80211_auth_type.
*/
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_AUTH_ALGO = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_MAX =
QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_PARAM_AFTER_LAST - 1
};
/**
* enum qca_btc_chain_mode - Specifies BT coex chain mode.
* This enum defines the valid set of values of BT coex chain mode.
* These values are used by attribute %QCA_VENDOR_ATTR_BTC_CHAIN_MODE of
* %QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE.
*
* @QCA_BTC_CHAIN_SHARED: chains of BT and WLAN 2.4G are shared.
* @QCA_BTC_CHAIN_SEPARATED: chains of BT and WLAN 2.4G are separated.
*/
enum qca_btc_chain_mode {
QCA_BTC_CHAIN_SHARED = 0,
QCA_BTC_CHAIN_SEPARATED = 1,
};
/**
* enum qca_vendor_attr_btc_chain_mode - Specifies attributes for BT coex
* chain mode.
* Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE.
*
* @QCA_VENDOR_ATTR_COEX_BTC_CHAIN_MODE: u32 attribute.
* Indicates the BT coex chain mode, are 32-bit values from
* enum qca_btc_chain_mode. This attribute is mandatory.
*
* @QCA_VENDOR_ATTR_COEX_BTC_CHAIN_MODE_RESTART: flag attribute.
* If set, vdev should be restarted when BT coex chain mode is updated.
* This attribute is optional.
*/
enum qca_vendor_attr_btc_chain_mode {
QCA_VENDOR_ATTR_BTC_CHAIN_MODE_INVALID = 0,
QCA_VENDOR_ATTR_BTC_CHAIN_MODE = 1,
QCA_VENDOR_ATTR_BTC_CHAIN_MODE_RESTART = 2,
/* Keep last */
QCA_VENDOR_ATTR_BTC_CHAIN_MODE_LAST,
QCA_VENDOR_ATTR_BTC_CHAIN_MODE_MAX =
QCA_VENDOR_ATTR_BTC_CHAIN_MODE_LAST - 1,
};
/**
* enum qca_vendor_wlan_sta_flags - Station feature flags
* Bits will be set to 1 if the corresponding features are enabled.
* @QCA_VENDOR_WLAN_STA_FLAG_AMPDU: AMPDU is enabled for the station
* @QCA_VENDOR_WLAN_STA_FLAG_TX_STBC: TX Space-time block coding is enabled
for the station
* @QCA_VENDOR_WLAN_STA_FLAG_RX_STBC: RX Space-time block coding is enabled
for the station
*/
enum qca_vendor_wlan_sta_flags {
QCA_VENDOR_WLAN_STA_FLAG_AMPDU = BIT(0),
QCA_VENDOR_WLAN_STA_FLAG_TX_STBC = BIT(1),
QCA_VENDOR_WLAN_STA_FLAG_RX_STBC = BIT(2),
};
/**
* enum qca_vendor_wlan_sta_guard_interval - Station guard interval
* @QCA_VENDOR_WLAN_STA_GI_800_NS: Legacy normal guard interval
* @QCA_VENDOR_WLAN_STA_GI_400_NS: Legacy short guard interval
* @QCA_VENDOR_WLAN_STA_GI_1600_NS: Guard interval used by HE
* @QCA_VENDOR_WLAN_STA_GI_3200_NS: Guard interval used by HE
*/
enum qca_vendor_wlan_sta_guard_interval {
QCA_VENDOR_WLAN_STA_GI_800_NS = 0,
QCA_VENDOR_WLAN_STA_GI_400_NS = 1,
QCA_VENDOR_WLAN_STA_GI_1600_NS = 2,
QCA_VENDOR_WLAN_STA_GI_3200_NS = 3,
};
/**
* enum qca_wlan_vendor_attr_get_sta_info - Defines attributes
* used by QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO vendor command.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC:
* Required attribute in request for AP mode only, 6-byte MAC address,
* corresponding to the station's MAC address for which information is
* requested. For STA mode this is not required as the info always correspond
* to the self STA and the current/last association.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_FLAGS:
* Optionally used in response, u32 attribute, contains a bitmap of different
* fields defined in enum qca_vendor_wlan_sta_flags, used in AP mode only.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_GUARD_INTERVAL:
* Optionally used in response, u32 attribute, possible values are defined in
* enum qca_vendor_wlan_sta_guard_interval, used in AP mode only.
* Guard interval used by the station.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_RETRY_COUNT:
* Optionally used in response, u32 attribute, used in AP mode only.
* Value indicates the number of data frames received from station with retry
* bit set to 1 in FC.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BC_MC_COUNT:
* Optionally used in response, u32 attribute, used in AP mode only.
* Counter for number of data frames with broadcast or multicast address in
* the destination address received from the station.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_SUCCEED:
* Optionally used in response, u32 attribute, used in both STA and AP modes.
* Value indicates the number of data frames successfully transmitted only
* after retrying the packets and for which the TX status has been updated
* back to host from target.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_EXHAUSTED:
* Optionally used in response, u32 attribute, used in both STA and AP mode.
* Value indicates the number of data frames not transmitted successfully even
* after retrying the packets for the number of times equal to the total number
* of retries allowed for that packet and for which the TX status has been
* updated back to host from target.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_TOTAL:
* Optionally used in response, u32 attribute, used in AP mode only.
* Counter in the target for the number of data frames successfully transmitted
* to the station.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY:
* Optionally used in response, u32 attribute, used in AP mode only.
* Value indicates the number of data frames successfully transmitted only
* after retrying the packets.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY_EXHAUSTED:
* Optionally used in response, u32 attribute, used in both STA & AP mode.
* Value indicates the number of data frames not transmitted successfully even
* after retrying the packets for the number of times equal to the total number
* of retries allowed for that packet.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_PROBE_REQ_BMISS_COUNT: u32, used in
* the STA mode only. Represent the number of probe requests sent by the STA
* while attempting to roam on missing certain number of beacons from the
* connected AP. If queried in the disconnected state, this represents the
* count for the last connected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_PROBE_RESP_BMISS_COUNT: u32, used in
* the STA mode. Represent the number of probe responses received by the station
* while attempting to roam on missing certain number of beacons from the
* connected AP. When queried in the disconnected state, this represents the
* count when in last connected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_ALL_COUNT: u32, used in the
* STA mode only. Represents the total number of frames sent out by STA
* including Data, ACK, RTS, CTS, Control Management. This data is maintained
* only for the connect session. Represents the count of last connected session,
* when queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RTS_COUNT: u32, used in the STA mode.
* Total number of RTS sent out by the STA. This data is maintained per connect
* session. Represents the count of last connected session, when queried in the
* disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RTS_RETRY_FAIL_COUNT: u32, used in the
* STA mode.Represent the number of RTS transmission failure that reach retry
* limit. This data is maintained per connect session. Represents the count of
* last connected session, when queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_DATA_NON_AGGREGATED_COUNT: u32, used in
* the STA mode. Represent the total number of non aggregated frames transmitted
* by the STA. This data is maintained per connect session. Represents the count
* of last connected session, when queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_DATA_AGGREGATED_COUNT: u32, used in the
* STA mode. Represent the total number of aggregated frames transmitted by the
* STA. This data is maintained per connect session. Represents the count of
* last connected session, when queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_GOOD_PLCP_COUNT: u32, used in
* the STA mode. Represents the number of received frames with a good PLCP. This
* data is maintained per connect session. Represents the count of last
* connected session, when queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_INVALID_DELIMITER_COUNT: u32,
* used in the STA mode. Represents the number of occasions that no valid
* delimiter is detected by A-MPDU parser. This data is maintained per connect
* session. Represents the count of last connected session, when queried in the
* disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_CRC_FAIL_COUNT: u32, used in the
* STA mode. Represents the number of frames for which CRC check failed in the
* MAC. This data is maintained per connect session. Represents the count of
* last connected session, when queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_ACKS_GOOD_FCS_COUNT: u32, used in the
* STA mode. Represents the number of unicast ACKs received with good FCS. This
* data is maintained per connect session. Represents the count of last
* connected session, when queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BLOCKACK_COUNT: u32, used in the STA
* mode. Represents the number of received Block Acks. This data is maintained
* per connect session. Represents the count of last connected session, when
* queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BEACON_COUNT: u32, used in the STA
* mode. Represents the number of beacons received from the connected BSS. This
* data is maintained per connect session. Represents the count of last
* connected session, when queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_OTHER_BEACON_COUNT: u32, used in the
* STA mode. Represents the number of beacons received by the other BSS when in
* connected state (through the probes done by the STA). This data is maintained
* per connect session. Represents the count of last connected session, when
* queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_UCAST_DATA_GOOD_FCS_COUNT: u64, used in
* the STA mode. Represents the number of received DATA frames with good FCS and
* matching Receiver Address when in connected state. This data is maintained
* per connect session. Represents the count of last connected session, when
* queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_DATA_BC_MC_DROP_COUNT: u32, used in the
* STA mode. Represents the number of RX Data multicast frames dropped by the HW
* when in the connected state. This data is maintained per connect session.
* Represents the count of last connected session, when queried in the
* disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_1MBPS: u32, used in the
* STA mode. This represents the target power in dBm for the transmissions done
* to the AP in 2.4 GHz at 1 Mbps (DSSS) rate. This data is maintained per
* connect session. Represents the count of last connected session, when
* queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_6MBPS: u32, used in the
* STA mode. This represents the Target power in dBm for transmissions done to
* the AP in 2.4 GHz at 6 Mbps (OFDM) rate. This data is maintained per connect
* session. Represents the count of last connected session, when queried in the
* disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_MCS0: u32, used in the
* STA mode. This represents the Target power in dBm for transmissions done to
* the AP in 2.4 GHz at MCS0 rate. This data is maintained per connect session.
* Represents the count of last connected session, when queried in the
* disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_5G_6MBPS: u32, used in the
* STA mode. This represents the Target power in dBm for transmissions done to
* the AP in 5 GHz at 6 Mbps (OFDM) rate. This data is maintained per connect
* session. Represents the count of last connected session, when queried in
* the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_5G_MCS0: u32, used in the
* STA mode. This represents the Target power in dBm for for transmissions done
* to the AP in 5 GHz at MCS0 rate. This data is maintained per connect session.
* Represents the count of last connected session, when queried in the
* disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_HW_BUFFERS_OVERFLOW_COUNT: u32, used
* in the STA mode. This represents the Nested attribute representing the
* overflow counts of each receive buffer allocated to the hardware during the
* STA's connection. The number of hw buffers might vary for each WLAN
* solution and hence this attribute represents the nested array of all such
* HW buffer count. This data is maintained per connect session. Represents
* the count of last connected session, when queried in the disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX_TX_POWER: u32, Max TX power (dBm)
* allowed as per the regulatory requirements for the current or last connected
* session. Used in the STA mode.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_TX_POWER: u32, Latest TX power
* (dBm) used by the station in its latest unicast frame while communicating
* to the AP in the connected state. When queried in the disconnected state,
* this represents the TX power used by the STA with last AP communication
* when in connected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ANI_LEVEL: u32, Adaptive noise immunity
* level used to adjust the RX sensitivity. Represents the current ANI level
* when queried in the connected state. When queried in the disconnected
* state, this corresponds to the latest ANI level at the instance of
* disconnection.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_IES: Binary attribute containing
* the raw information elements from Beacon frames. Represents the Beacon frames
* of the current BSS in the connected state. When queried in the disconnected
* state, these IEs correspond to the last connected BSSID.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PROBE_RESP_IES: Binary attribute
* containing the raw information elements from Probe Response frames.
* Represents the Probe Response frames of the current BSS in the connected
* state. When queried in the disconnected state, these IEs correspond to the
* last connected BSSID.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_DRIVER_DISCONNECT_REASON: u32, Driver
* disconnect reason for the last disconnection if the disconnection is
* triggered from the host driver. The values are referred from
* enum qca_disconnect_reason_codes.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_MIC_ERROR_COUNT: u32, used in STA mode
* only. This represents the number of group addressed robust management frames
* received from this station with an invalid MIC or a missing MME when PMF is
* enabled.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_REPLAY_COUNT: u32, used in STA mode
* only. This represents the number of group addressed robust management frames
* received from this station with the packet number less than or equal to the
* last received packet number when PMF is enabled.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT: u32, used in STA
* mode only. This represents the number of Beacon frames received from this
* station with an invalid MIC or a missing MME when beacon protection is
* enabled.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT: u32, used in STA mode
* only. This represents number of Beacon frames received from this station with
* the packet number less than or equal to the last received packet number when
* beacon protection is enabled.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE: u32, used in
* STA mode only. The driver uses this attribute to populate the connection
* failure reason codes and the values are defined in
* enum qca_sta_connect_fail_reason_codes. Userspace applications can send
* QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO vendor command after receiving
* a connection failure indication from the driver. The driver shall not
* include this attribute in response to the
* QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO command if there is no connection
* failure observed in the last attempted connection.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_TX_RATE: u32, latest TX rate (Kbps)
* used by the station in its last TX frame while communicating to the AP in the
* connected state. When queried in the disconnected state, this represents the
* rate used by the STA in the last TX frame to the AP when it was connected.
* This attribute is used for STA mode only.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_RIX: u32, used in STA mode only.
* This represents the rate index used by the STA for the last TX frame to the
* AP. When queried in the disconnected state, this gives the last RIX used by
* the STA in the last TX frame to the AP when it was connected.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TSF_OUT_OF_SYNC_COUNT: u32, used in STA
* mode only. This represents the number of times the STA TSF goes out of sync
* from the AP after the connection. If queried in the disconnected state, this
* gives the count of TSF out of sync for the last connection.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_TRIGGER_REASON: u32, used in STA
* mode only. This represents the roam trigger reason for the last roaming
* attempted by the firmware. This can be queried either in connected state or
* disconnected state. Each bit of this attribute represents the different
* roam trigger reason code which are defined in enum qca_vendor_roam_triggers.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON: u32, used in STA mode
* only. This represents the roam fail reason for the last failed roaming
* attempt by the firmware. Different roam failure reason codes are specified
* in enum qca_vendor_roam_fail_reasons. This can be queried either in
* connected state or disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON: u32, used in
* STA mode only. This represents the roam invoke fail reason for the last
* failed roam invoke. Different roam invoke failure reason codes
* are specified in enum qca_vendor_roam_invoke_fail_reasons. This can be
* queried either in connected state or disconnected state.
*
* @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY: u32, used in STA mode only.
* This represents the average congestion duration of uplink frames in MAC
* queue in unit of ms. This can be queried either in connected state or
* disconnected state.
*/
enum qca_wlan_vendor_attr_get_sta_info {
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAC = 1,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_FLAGS = 2,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_GUARD_INTERVAL = 3,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_RETRY_COUNT = 4,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BC_MC_COUNT = 5,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_SUCCEED = 6,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RETRY_EXHAUSTED = 7,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_TOTAL = 8,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY = 9,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_RETRY_EXHAUSTED = 10,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_PROBE_REQ_BMISS_COUNT = 11,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_PROBE_RESP_BMISS_COUNT = 12,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_TX_ALL_COUNT = 13,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RTS_COUNT = 14,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_RTS_RETRY_FAIL_COUNT = 15,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_DATA_NON_AGGREGATED_COUNT = 16,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TX_DATA_AGGREGATED_COUNT = 17,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_GOOD_PLCP_COUNT = 18,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_INVALID_DELIMITER_COUNT = 19,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_FRAMES_CRC_FAIL_COUNT = 20,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_ACKS_GOOD_FCS_COUNT = 21,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BLOCKACK_COUNT = 22,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_BEACON_COUNT = 23,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_OTHER_BEACON_COUNT = 24,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_UCAST_DATA_GOOD_FCS_COUNT = 25,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_DATA_BC_MC_DROP_COUNT = 26,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_1MBPS = 27,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_6MBPS = 28,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_24G_MCS0 = 29,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_5G_6MBPS = 30,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TARGET_POWER_5G_MCS0 = 31,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_RX_HW_BUFFERS_OVERFLOW_COUNT = 32,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX_TX_POWER = 33,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_TX_POWER = 34,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ANI_LEVEL = 35,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_IES = 36,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PROBE_RESP_IES = 37,
QCA_WLAN_VENDOR_ATTR_GET_STA_DRIVER_DISCONNECT_REASON = 38,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_MIC_ERROR_COUNT = 39,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BIP_REPLAY_COUNT = 40,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_MIC_ERROR_COUNT = 41,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_BEACON_REPLAY_COUNT = 42,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE = 43,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_TX_RATE = 44,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_LATEST_RIX = 45,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_TSF_OUT_OF_SYNC_COUNT = 46,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_TRIGGER_REASON = 47,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON = 48,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON = 49,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY = 50,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX =
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_update_sta_info - Defines attributes
* used by QCA_NL80211_VENDOR_SUBCMD_UPDATE_STA_INFO vendor command.
*
* @QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_CONNECT_CHANNELS: Type is NLA_UNSPEC.
* Used in STA mode. This attribute represents the list of channel center
* frequencies in MHz (u32) the station has learnt during the last connection
* or roaming attempt. This information shall not signify the channels for
* an explicit scan request from the user space. Host drivers can update this
* information to the user space in both connected and disconnected state.
* In the disconnected state this information shall signify the channels
* scanned in the last connection/roam attempt that lead to the disconnection.
*/
enum qca_wlan_vendor_attr_update_sta_info {
QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_CONNECT_CHANNELS = 1,
/* keep last */
QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_MAX =
QCA_WLAN_VENDOR_ATTR_UPDATE_STA_INFO_AFTER_LAST - 1,
};
/**
* enum qca_disconnect_reason_codes - Specifies driver disconnect reason codes.
* Used when the driver triggers the STA to disconnect from the AP.
*
* @QCA_DISCONNECT_REASON_UNSPECIFIED: The host driver triggered the
* disconnection with the AP due to unspecified reasons.
*
* @QCA_DISCONNECT_REASON_INTERNAL_ROAM_FAILURE: The host driver triggered the
* disconnection with the AP due to a roaming failure. This roaming is triggered
* internally (host driver/firmware).
*
* @QCA_DISCONNECT_REASON_EXTERNAL_ROAM_FAILURE: The driver disconnected from
* the AP when the user/external triggered roaming fails.
*
* @QCA_DISCONNECT_REASON_GATEWAY_REACHABILITY_FAILURE: This reason code is used
* by the host driver whenever gateway reachability failure is detected and the
* driver disconnects with AP.
*
* @QCA_DISCONNECT_REASON_UNSUPPORTED_CHANNEL_CSA: The driver disconnected from
* the AP on a channel switch announcement from it with an unsupported channel.
*
* @QCA_DISCONNECT_REASON_OPER_CHANNEL_DISABLED_INDOOR: On a concurrent AP start
* with indoor channels disabled and if the STA is connected on one of these
* disabled channels, the host driver disconnected the STA with this reason
* code.
*
* @QCA_DISCONNECT_REASON_OPER_CHANNEL_USER_DISABLED: Disconnection due to an
* explicit request from the user to disable the current operating channel.
*
* @QCA_DISCONNECT_REASON_DEVICE_RECOVERY: STA disconnected from the AP due to
* the internal host driver/firmware recovery.
*
* @QCA_DISCONNECT_REASON_KEY_TIMEOUT: The driver triggered the disconnection on
* a timeout for the key installations from the user space.
*
* @QCA_DISCONNECT_REASON_OPER_CHANNEL_BAND_CHANGE: The dDriver disconnected the
* STA on a band change request from the user space to a different band from the
* current operation channel/band.
*
* @QCA_DISCONNECT_REASON_IFACE_DOWN: The STA disconnected from the AP on an
* interface down trigger from the user space.
*
* @QCA_DISCONNECT_REASON_PEER_XRETRY_FAIL: The host driver disconnected the
* STA on getting continuous transmission failures for multiple Data frames.
*
* @QCA_DISCONNECT_REASON_PEER_INACTIVITY: The STA does a keep alive
* notification to the AP by transmitting NULL/G-ARP frames. This disconnection
* represents inactivity from AP on such transmissions.
* @QCA_DISCONNECT_REASON_SA_QUERY_TIMEOUT: This reason code is used on
* disconnection when SA Query times out (AP does not respond to SA Query).
*
* @QCA_DISCONNECT_REASON_BEACON_MISS_FAILURE: The host driver disconnected the
* STA on missing the beacons continuously from the AP.
*
* @QCA_DISCONNECT_REASON_CHANNEL_SWITCH_FAILURE: Disconnection due to STA not
* able to move to the channel mentioned by the AP in CSA.
*
* @QCA_DISCONNECT_REASON_USER_TRIGGERED: User triggered disconnection.
*/
enum qca_disconnect_reason_codes {
QCA_DISCONNECT_REASON_UNSPECIFIED = 0,
QCA_DISCONNECT_REASON_INTERNAL_ROAM_FAILURE = 1,
QCA_DISCONNECT_REASON_EXTERNAL_ROAM_FAILURE = 2,
QCA_DISCONNECT_REASON_GATEWAY_REACHABILITY_FAILURE = 3,
QCA_DISCONNECT_REASON_UNSUPPORTED_CHANNEL_CSA = 4,
QCA_DISCONNECT_REASON_OPER_CHANNEL_DISABLED_INDOOR = 5,
QCA_DISCONNECT_REASON_OPER_CHANNEL_USER_DISABLED = 6,
QCA_DISCONNECT_REASON_DEVICE_RECOVERY = 7,
QCA_DISCONNECT_REASON_KEY_TIMEOUT = 8,
QCA_DISCONNECT_REASON_OPER_CHANNEL_BAND_CHANGE = 9,
QCA_DISCONNECT_REASON_IFACE_DOWN = 10,
QCA_DISCONNECT_REASON_PEER_XRETRY_FAIL = 11,
QCA_DISCONNECT_REASON_PEER_INACTIVITY = 12,
QCA_DISCONNECT_REASON_SA_QUERY_TIMEOUT = 13,
QCA_DISCONNECT_REASON_BEACON_MISS_FAILURE = 14,
QCA_DISCONNECT_REASON_CHANNEL_SWITCH_FAILURE = 15,
QCA_DISCONNECT_REASON_USER_TRIGGERED = 16,
};
/**
* enum qca_wlan_vendor_attr_driver_disconnect_reason - Defines attributes
* used by %QCA_NL80211_VENDOR_SUBCMD_DRIVER_DISCONNECT_REASON vendor command.
*
* @QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASCON_CODE: u32 attribute.
* This attribute represents the driver specific reason codes (local
* driver/firmware initiated reasons for disconnection) defined
* in enum qca_disconnect_reason_codes.
*/
enum qca_wlan_vendor_attr_driver_disconnect_reason {
QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASCON_CODE = 1,
/* keep last */
QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_MAX =
QCA_WLAN_VENDOR_ATTR_DRIVER_DISCONNECT_REASON_AFTER_LAST - 1,
};
/**
* enum qca_wlan_tspec_operation - Operation of the config TSPEC request
*
* Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION.
*/
enum qca_wlan_tspec_operation {
QCA_WLAN_TSPEC_ADD = 0,
QCA_WLAN_TSPEC_DEL = 1,
QCA_WLAN_TSPEC_GET = 2,
};
/**
* enum qca_wlan_tspec_direction - Direction in TSPEC
* As what is defined in IEEE Std 802.11-2016, Table 9-139.
*
* Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION.
*/
enum qca_wlan_tspec_direction {
QCA_WLAN_TSPEC_DIRECTION_UPLINK = 0,
QCA_WLAN_TSPEC_DIRECTION_DOWNLINK = 1,
QCA_WLAN_TSPEC_DIRECTION_DIRECT = 2,
QCA_WLAN_TSPEC_DIRECTION_BOTH = 3,
};
/**
* enum qca_wlan_tspec_ack_policy - MAC acknowledgement policy in TSPEC
* As what is defined in IEEE Std 802.11-2016, Table 9-141.
*
* Values for %QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY.
*/
enum qca_wlan_tspec_ack_policy {
QCA_WLAN_TSPEC_NORMAL_ACK = 0,
QCA_WLAN_TSPEC_NO_ACK = 1,
/* Reserved */
QCA_WLAN_TSPEC_BLOCK_ACK = 3,
};
/**
* enum qca_wlan_vendor_attr_config_tspec - Defines attributes
* used by %QCA_NL80211_VENDOR_SUBCMD_CONFIG_TSPEC vendor command.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION:
* u8 attribute. Specify the TSPEC operation of this request. Possible values
* are defined in enum qca_wlan_tspec_operation.
* Mandatory attribute for all kinds of config TSPEC requests.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID:
* u8 attribute. TS ID. Possible values are 0-7.
* Applicable for operation: QCA_WLAN_TSPEC_ADD, QCA_WLAN_TSPEC_DEL,
* QCA_WLAN_TSPEC_GET. A mandatory attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION:
* u8 attribute. Direction of data carried by the TS. Possible values are
* defined in enum qca_wlan_tspec_direction.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD:
* Flag attribute. Indicate whether APSD is enabled for the traffic associated
* with the TS. set - enabled, not set - disabled.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY:
* u8 attribute. User priority to be used for the transport of MSDUs/A-MSDUs
* belonging to this TS. Possible values are 0-7.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY:
* u8 attribute. Indicate whether MAC acknowledgements are required for
* MPDUs/A-MSDUs belonging to this TS and the form of those acknowledgements.
* Possible values are defined in enum qca_wlan_tspec_ack_policy.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE:
* u16 attribute. Specify the nominal size in bytes of MSDUs/A-MSDUs
* belonging to this TS.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE:
* u16 attribute. Specify the maximum size in bytes of MSDUs/A-MSDUs
* belonging to this TS.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL:
* u32 attribute. Specify the minimum interval in microseconds between the
* start of two successive SPs.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL:
* u32 attribute. Specify the maximum interval in microseconds between the
* start of two successive SPs.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL:
* u32 attribute. Specify the minimum interval in microseconds that can elapse
* without arrival or transfer of an MPDU belonging to the TS before this TS
* is deleted by the MAC entity at the HC.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL:
* u32 attribute. Specify the minimum interval in microseconds that can elapse
* without arrival or transfer of an MSDU belonging to the TS before the
* generation of successive QoS(+)CF-Poll is stopped for this TS. A value of
* 0xFFFFFFFF disables the suspension interval. The value of the suspension
* interval is always less than or equal to the inactivity interval.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE:
* u32 attribute. Indicate the lowest data rate in bps specified at the MAC
* SAP for transport of MSDUs or A-MSDUs belonging to this TS within the
* bounds of this TSPEC.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE:
* u32 attribute. Indicate the average data rate in bps specified at the MAC
* SAP for transport of MSDUs or A-MSDUs belonging to this TS within the
* bounds of this TSPEC.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE:
* u32 attribute. Indicate the maximum allowable data rate in bps specified at
* the MAC SAP for transport of MSDUs or A-MSDUs belonging to this TS within
* the bounds of this TSPEC.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE:
* u32 attribute. Specify the maximum burst size in bytes of the MSDUs/A-MSDUs
* belonging to this TS that arrive at the MAC SAP at the peak data rate. A
* value of 0 indicates that there are no bursts.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE:
* u32 attribute. Indicate the minimum PHY rate in bps for transport of
* MSDUs/A-MSDUs belonging to this TS within the bounds of this TSPEC.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. An optional attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE:
* u16 attribute. Specify the excess allocation of time (and bandwidth) over
* and above the stated application rates required to transport an MSDU/A-MSDU
* belonging to the TS in this TSPEC.
* Applicable for operation: QCA_WLAN_TSPEC_ADD. A mandatory attribute.
*/
enum qca_wlan_vendor_attr_config_tspec {
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_OPERATION = 1,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_TSID = 2,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_DIRECTION = 3,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_APSD = 4,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_USER_PRIORITY = 5,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_ACK_POLICY = 6,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_NOMINAL_MSDU_SIZE = 7,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAXIMUM_MSDU_SIZE = 8,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MIN_SERVICE_INTERVAL = 9,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX_SERVICE_INTERVAL = 10,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_INACTIVITY_INTERVAL = 11,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SUSPENSION_INTERVAL = 12,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_DATA_RATE = 13,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MEAN_DATA_RATE = 14,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_PEAK_DATA_RATE = 15,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_BURST_SIZE = 16,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MINIMUM_PHY_RATE = 17,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_SURPLUS_BANDWIDTH_ALLOWANCE = 18,
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_MAX =
QCA_WLAN_VENDOR_ATTR_CONFIG_TSPEC_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_oci_override_frame_type - OCI override frame type
* @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ: SA Query Request frame
* @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP: SA Query Response frame
* @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ: FT Reassociation Request
* frame
* @QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ: FILS Reassociation
* Request frame.
*/
enum qca_wlan_vendor_oci_override_frame_type {
QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_REQ = 1,
QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_SA_QUERY_RESP = 2,
QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FT_REASSOC_REQ = 3,
QCA_WLAN_VENDOR_OCI_OVERRIDE_FRAME_FILS_REASSOC_REQ = 4,
};
/**
* enum qca_wlan_vendor_attr_oci_override: Represents attributes for
* OCI override request. These attributes are used inside nested attribute
* %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OCI_OVERRIDE in QCA vendor command
* %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
*
* @QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE: Required attribute, u8.
* Values from enum qca_wlan_vendor_oci_override_frame_type used in this
* attribute to specify the frame type in which the OCI is to be overridden.
*
* @QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY: Required (u32)
* OCI frequency (in MHz) to override in the specified frame type.
*/
enum qca_wlan_vendor_attr_oci_override {
QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FRAME_TYPE = 1,
QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_FREQUENCY = 2,
/* keep last */
QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_MAX =
QCA_WLAN_VENDOR_ATTR_OCI_OVERRIDE_AFTER_LAST - 1,
};
/**
* enum qca_wlan_medium_assess_type - Type of medium assess request
*
* Values for %QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE.
*/
enum qca_wlan_medium_assess_type {
QCA_WLAN_MEDIUM_ASSESS_CCA = 0,
QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT = 1,
};
/**
* enum qca_wlan_vendor_attr_medium_assess - Attributes used by
* %QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS vendor command.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE:
* u8 attribute. Mandatory in all kinds of medium assess requests/responses.
* Specify the type of medium assess request and indicate its type in response.
* Possible values are defined in enum qca_wlan_medium_assess_type.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD:
* u32 attribute. Mandatory in CCA request.
* Specify the assessment period in terms of seconds. Assessment result will be
* sent as the response to the CCA request after the assessment period.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT:
* u32 attribute. Mandatory in response to CCA request.
* Total timer tick count of the assessment cycle.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT:
* u32 attribute. Mandatory in response to CCA request.
* Timer tick count of idle time in the assessment cycle.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT:
* u32 attribute. Mandatory in response to CCA request.
* Timer tick count of Intra BSS traffic RX time in the assessment cycle.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT:
* u32 attribute. Mandatory in response to CCA request.
* Timer tick count of Overlapping BSS traffic RX time in the assessment cycle.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI:
* s32 attribute. Mandatory in response to CCA request.
* Maximum RSSI of Intra BSS traffic in the assessment cycle.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI:
* s32 attribute. Mandatory in response to CCA request.
* Minimum RSSI of Intra BSS traffic in the assessment cycle.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE:
* u8 attribute. Mandatory in congestion report request.
* 1-enable 0-disable.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD:
* u8 attribute. Mandatory in congestion report enable request and will be
* ignored if present in congestion report disable request. Possible values are
* 0-100. A vendor event QCA_NL80211_VENDOR_SUBCMD_MEDIUM_ASSESS with the type
* QCA_WLAN_MEDIUM_ASSESS_CONGESTION_REPORT will be sent to userspace if
* congestion percentage reaches the configured threshold.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL:
* u8 attribute. Optional in congestion report enable request and will be
* ignored if present in congestion report disable request.
* Specify the interval of congestion report event in terms of seconds. Possible
* values are 1-255. Default value 1 will be used if this attribute is omitted
* or using invalid values.
*
* @QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE:
* u8 attribute. Mandatory in congestion report event.
* Indicate the actual congestion percentage. Possible values are 0-100.
*/
enum qca_wlan_vendor_attr_medium_assess {
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TYPE = 1,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_PERIOD = 2,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_TOTAL_CYCLE_COUNT = 3,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IDLE_COUNT = 4,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_IBSS_RX_COUNT = 5,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_OBSS_RX_COUNT = 6,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX_IBSS_RSSI = 7,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MIN_IBSS_RSSI = 8,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_ENABLE = 9,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_THRESHOLD = 10,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_REPORT_INTERVAL = 11,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_CONGESTION_PERCENTAGE = 12,
/* keep last */
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_MAX =
QCA_WLAN_VENDOR_ATTR_MEDIUM_ASSESS_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_mbssid_tx_vdev_status - Defines attributes
* used by QCA_NL80211_VENDOR_SUBCMD_MBSSID_TX_VDEV_STATUS vendor command.
*
* @QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_VAL:
* u8 attribute. Notify the TX VDEV status. Possible values 0, 1
* belonging to MBSSID/EMA_AP configuration. 0 means Non-Tx VDEV,
* 1 means Tx VDEV. Mandatory attribute for all MBSSID VDEV status events.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_EVENT:
+ * u8 attribute, required. 1 means Tx VDEV up event. 0 means Tx VDEV down event.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_ID:
+ * u8 attribute, required. Indicates group id of Tx VDEV.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO:
+ * Nested attribute. This attribute shall be used by the driver to send
+ * group information. The attributes defined in enum
+ * qca_wlan_vendor_attr_mbssid_tx_vdev_group_info
+ * are nested in this attribute.
*/
enum qca_wlan_vendor_attr_mbssid_tx_vdev_status {
QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_VAL = 1,
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_EVENT = 2,
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_ID = 3,
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_MAX =
QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_STATUS_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_attr_mbssid_tx_vdev_group_info - Attributes used
+ * inside %QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO_IF_INDEX:
+ * u32 attribute, required. Contains interface index.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO_STATUS:
+ * u8 attribute, required. 0 - means vdev is in down state.
+ * 1 - means vdev is in up state.
+ */
+enum qca_wlan_vendor_attr_mbssid_tx_vdev_group_info {
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO_IF_INDEX = 1,
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO_STATUS = 2,
+
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_MBSSID_TX_VDEV_GROUP_INFO - 1,
+};
+
/**
* enum qca_wlan_concurrent_sta_policy_config - Concurrent STA policies
*
* @QCA_WLAN_CONCURRENT_STA_POLICY_PREFER_PRIMARY: Preference to the primary
* STA interface has to be given while selecting the connection policies
* (e.g., BSSID, band, TX/RX chains, etc.) for the subsequent STA interface.
* An interface is set as primary through the attribute
* QCA_WLAN_VENDOR_ATTR_CONFIG_CONCURRENT_STA_PRIMARY. This policy is not
* applicable if the primary interface has not been set earlier.
*
* The intention is not to downgrade the primary STA performance, such as:
* - Do not reduce the number of TX/RX chains of primary connection.
* - Do not optimize DBS vs. MCC/SCC, if DBS ends up reducing the number of
* chains.
* - If using MCC, should set the MCC duty cycle of the primary connection to
* be higher than the secondary connection.
*
* @QCA_WLAN_CONCURRENT_STA_POLICY_UNBIASED: The connection policies for the
* subsequent STA connection shall be chosen to balance with the existing
* concurrent STA's performance.
* Such as
* - Can choose MCC or DBS mode depending on the MCC efficiency and hardware
* capability.
* - If using MCC, set the MCC duty cycle of the primary connection to be equal
* to the secondary.
* - Prefer BSSID candidates which will help provide the best "overall"
* performance for all the STA connections.
*/
enum qca_wlan_concurrent_sta_policy_config {
QCA_WLAN_CONCURRENT_STA_POLICY_PREFER_PRIMARY = 0,
QCA_WLAN_CONCURRENT_STA_POLICY_UNBIASED = 1,
};
/**
* enum qca_wlan_vendor_attr_concurrent_sta_policy - Defines attributes
* used by QCA_NL80211_VENDOR_SUBCMD_CONCURRENT_MULTI_STA_POLICY vendor command.
*
* @QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_CONFIG:
* u8 attribute. Configures the concurrent STA policy configuration.
* Possible values are defined in enum qca_wlan_concurrent_sta_policy_config.
*/
enum qca_wlan_vendor_attr_concurrent_sta_policy {
QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_CONFIG = 1,
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_MAX =
QCA_WLAN_VENDOR_ATTR_CONCURRENT_STA_POLICY_AFTER_LAST - 1,
};
/**
* enum qca_sta_connect_fail_reason_codes - Defines values carried
* by QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_CONNECT_FAIL_REASON_CODE vendor
* attribute.
* @QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: No Probe Response frame received
* for unicast Probe Request frame.
* @QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: STA failed to send auth request.
* @QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: AP didn't send ACK for
* auth request.
* @QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: Auth response is not
* received from AP.
* @QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: STA failed to send
* Association Request frame.
* @QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: AP didn't send ACK for
* Association Request frame.
* @QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: Association Response
* frame is not received from AP.
*/
enum qca_sta_connect_fail_reason_codes {
QCA_STA_CONNECT_FAIL_REASON_NO_BSS_FOUND = 1,
QCA_STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL = 2,
QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED = 3,
QCA_STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED = 4,
QCA_STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL = 5,
QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED = 6,
QCA_STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED = 7,
};
/**
* enum qca_wlan_vendor_usable_channels_filter - Bitmask of different
* filters defined in this enum are used in attribute
* %QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_FILTER_MASK.
*
* @QCA_WLAN_VENDOR_FILTER_CELLULAR_COEX: When this bit is set, the driver
* shall filter the channels which are not usable because of coexistence with
* cellular radio.
* @QCA_WLAN_VENDOR_FILTER_WLAN_CONCURRENCY: When this bit is set, the driver
* shall filter the channels which are not usable because of existing active
* interfaces in the driver and will result in Multi Channel Concurrency, etc.
*
*/
enum qca_wlan_vendor_usable_channels_filter {
QCA_WLAN_VENDOR_FILTER_CELLULAR_COEX = 0,
QCA_WLAN_VENDOR_FILTER_WLAN_CONCURRENCY = 1,
};
/**
* enum qca_wlan_vendor_attr_chan_info - Attributes used inside
* %QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO nested attribute.
*
* @QCA_WLAN_VENDOR_ATTR_CHAN_INFO_PRIMARY_FREQ:
* u32 attribute, required. Indicates the center frequency of the primary
* channel in MHz.
*
* @QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG0_FREQ:
* u32 attribute. Indicates the center frequency of the primary segment of the
* channel in MHz. This attribute is required when reporting 40 MHz, 80 MHz,
* 160 MHz, and 320 MHz channels.
*
* @QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG1_FREQ:
* u32 attribute. Indicates the center frequency of the secondary segment of
* 80+80 channel in MHz. This attribute is required only when
* QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH is set to NL80211_CHAN_WIDTH_80P80.
*
* @QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH:
* u32 attribute, required. Indicates the bandwidth of the channel, possible
* values are defined in enum nl80211_chan_width.
*
* @QCA_WLAN_VENDOR_ATTR_CHAN_INFO_IFACE_MODE_MASK:
* u32 attribute, required. Indicates all the interface types for which this
* channel is usable. This attribute encapsulates bitmasks of interface types
* defined in enum nl80211_iftype.
*
*/
enum qca_wlan_vendor_attr_chan_info {
QCA_WLAN_VENDOR_ATTR_CHAN_INFO_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_CHAN_INFO_PRIMARY_FREQ = 1,
QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG0_FREQ = 2,
QCA_WLAN_VENDOR_ATTR_CHAN_INFO_SEG1_FREQ = 3,
QCA_WLAN_VENDOR_ATTR_CHAN_INFO_BANDWIDTH = 4,
QCA_WLAN_VENDOR_ATTR_CHAN_INFO_IFACE_MODE_MASK = 5,
/* keep last */
QCA_WLAN_VENDOR_ATTR_CHAN_INFO_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CHAN_INFO_MAX =
QCA_WLAN_VENDOR_ATTR_CHAN_INFO_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_usable_channels - Attributes used by
* %QCA_NL80211_VENDOR_SUBCMD_USABLE_CHANNELS vendor command.
*
* @QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_BAND_MASK:
* u32 attribute. Indicates the bands from which the channels should be reported
* in response. This attribute encapsulates bit masks of bands defined in enum
* nl80211_band. Optional attribute, if not present in the request the driver
* shall return channels from all supported bands.
*
* @QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_IFACE_MODE_MASK:
* u32 attribute. Indicates all the interface types for which the usable
* channels information is requested. This attribute encapsulates bitmasks of
* interface types defined in enum nl80211_iftype. Optional attribute, if not
* present in the request the driver shall send information of all supported
* interface modes.
*
* @QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_FILTER_MASK:
* u32 attribute. This attribute carries information of all filters that shall
* be applied while populating usable channels information by the driver. This
* attribute carries bit masks of different filters defined in enum
* qca_wlan_vendor_usable_channels_filter. Optional attribute, if not present
* in the request the driver shall send information of channels without applying
* any of the filters that can be configured through this attribute.
*
* @QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO:
* Nested attribute. This attribute shall be used by the driver to send
* usability information of each channel. The attributes defined in enum
* qca_wlan_vendor_attr_chan_info are used inside this attribute.
*/
enum qca_wlan_vendor_attr_usable_channels {
QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_BAND_MASK = 1,
QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_IFACE_MODE_MASK = 2,
QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_FILTER_MASK = 3,
QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_CHAN_INFO = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_MAX =
QCA_WLAN_VENDOR_ATTR_USABLE_CHANNELS_AFTER_LAST - 1,
};
/**
* enum qca_wlan_vendor_attr_radar_history: Used by the vendor command
* QCA_NL80211_VENDOR_SUBCMD_GET_RADAR_HISTORY to get DFS radar history.
*
* @QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES: Nested attribute to carry
* the list of radar history entries.
* Each entry contains freq, timestamp, and radar signal detect flag.
* The driver shall add an entry when CAC has finished, or radar signal
* has been detected post AP beaconing. The driver shall maintain at least
* 8 entries in order to save CAC result for a 160 MHz channel.
* @QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ: u32 attribute.
* Channel frequency in MHz.
* @QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP: u64 nanoseconds.
* CLOCK_BOOTTIME timestamp when this entry is updated due to CAC
* or radar detection.
* @QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED: NLA_FLAG attribute.
* This flag indicates radar signal has been detected.
*/
enum qca_wlan_vendor_attr_radar_history {
QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_ENTRIES = 1,
QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_FREQ = 2,
QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_TIMESTAMP = 3,
QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_DETECTED = 4,
/* keep last */
QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_LAST,
QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_MAX =
QCA_WLAN_VENDOR_ATTR_RADAR_HISTORY_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_attr_mdns_offload - Attributes used by
+ * %QCA_NL80211_VENDOR_SUBCMD_MDNS_OFFLOAD vendor command.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ENABLE: Required (flag)
+ * Enable mDNS offload. This attribute is mandatory to enable
+ * mDNS offload feature. If this attribute is not present, mDNS offload
+ * is disabled.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_TABLE: Nested attribute containing
+ * one or more %QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ENTRY attributes. This
+ * attribute is mandatory when enabling the feature, and not required when
+ * disabling the feature.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ENTRY: Nested attribute containing
+ * the following attributes:
+ * %QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_FQDN
+ * %QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ANSWER_RESOURCE_RECORDS_COUNT
+ * %QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ANSWER_PAYLOAD
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_FQDN: Required string attribute.
+ * It consists of a hostname and ".local" as the domain name. The character
+ * set is limited to UTF-8 encoding. The maximum allowed size is 63 bytes.
+ * It is used to compare the domain in the "QU" query. Only 1 FQDN is
+ * supported per vdev.
+ * For example: myphone.local
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ANSWER_RESOURCE_RECORDS_COUNT: Required
+ * u16 attribute. It specifies the total number of resource records present
+ * in the answer section of the answer payload. This attribute is needed by the
+ * firmware to populate the mDNS response frame for mDNS queries without having
+ * to parse the answer payload.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ANSWER_PAYLOAD: Required binary blob
+ * attribute sent by the mdnsResponder from userspace. It contains resource
+ * records of various types (e.g., A, AAAA, PTR, TXT) and service list. This
+ * payload is passed down to the firmware and is transmitted in response to
+ * mDNS queries.
+ * The maximum supported size of the answer payload is 512 bytes.
+ */
+enum qca_wlan_vendor_attr_mdns_offload {
+ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ENABLE = 1,
+ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_TABLE = 2,
+ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ENTRY = 3,
+ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_FQDN = 4,
+ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ANSWER_RESOURCE_RECORDS_COUNT = 5,
+ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_ANSWER_PAYLOAD = 6,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_MAX =
+ QCA_WLAN_VENDOR_ATTR_MDNS_OFFLOAD_AFTER_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_monitor_data_frame_type - Represent the various
+ * Data frame types to be sent over the monitor interface.
+ */
+enum qca_wlan_vendor_monitor_data_frame_type {
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_ALL = BIT(0),
+ /* valid only if QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_ALL is not set
+ */
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_ARP = BIT(1),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_DHCPV4 = BIT(2),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_DHCPV6 = BIT(3),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_EAPOL = BIT(4),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_DNSV4 = BIT(5),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_DNSV6 = BIT(6),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_TCP_SYN = BIT(7),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_TCP_SYNACK = BIT(8),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_TCP_FIN = BIT(9),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_TCP_FINACK = BIT(10),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_TCP_ACK = BIT(11),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_TCP_RST = BIT(12),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_ICMPV4 = BIT(13),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_ICMPV6 = BIT(14),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_RTP = BIT(15),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_TYPE_SIP = BIT(16),
+ QCA_WLAN_VENDOR_MONITOR_DATA_FRAME_QOS_NULL = BIT(17),
+};
+
+/**
+ * qca_wlan_vendor_monitor_mgmt_frame_type - Represent the various
+ * Management frame types to be sent over the monitor interface.
+ * @QCA_WLAN_VENDOR_MONITOR_MGMT_FRAME_TYPE_ALL: All the Management Frames.
+ * @QCA_WLAN_VENDOR_MONITOR_MGMT_CONNECT_NO_BEACON: All the Management frames
+ * except the Beacon frame.
+ * @QCA_WLAN_VENDOR_MONITOR_MGMT_CONNECT_BEACON: Only the connected
+ * BSSID Beacon frames. Valid only in the connected state.
+ * @QCA_WLAN_VENDOR_MONITOR_MGMT_CONNECT_SCAN_BEACON: Represents
+ * the Beacon frames obtained during the scan (off channel and connected
+ * channel), when in connected state.
+ */
+
+enum qca_wlan_vendor_monitor_mgmt_frame_type {
+ QCA_WLAN_VENDOR_MONITOR_MGMT_FRAME_TYPE_ALL = BIT(0),
+ /* valid only if QCA_WLAN_VENDOR_MONITOR_MGMT_FRAME_TYPE_ALL is not set
+ */
+ QCA_WLAN_VENDOR_MONITOR_MGMT_NO_BEACON = BIT(1),
+ QCA_WLAN_VENDOR_MONITOR_MGMT_CONNECT_BEACON = BIT(2),
+ QCA_WLAN_VENDOR_MONITOR_MGMT_CONNECT_SCAN_BEACON = BIT(3),
+};
+
+/**
+ * qca_wlan_vendor_monitor_ctrl_frame_type - Represent the various
+ * Control frame types to be sent over the monitor interface.
+ * @QCA_WLAN_VENDOR_MONITOR_CTRL_FRAME_TYPE_ALL: All the Control frames
+ * @QCA_WLAN_VENDOR_MONITOR_CTRL_TRIGGER_FRAME: Trigger frame
+ */
+enum qca_wlan_vendor_monitor_ctrl_frame_type {
+ QCA_WLAN_VENDOR_MONITOR_CTRL_FRAME_TYPE_ALL = BIT(0),
+ /* valid only if QCA_WLAN_VENDOR_MONITOR_CTRL_FRAME_TYPE_ALL is not set
+ */
+ QCA_WLAN_VENDOR_MONITOR_CTRL_TRIGGER_FRAME = BIT(1),
+};
+
+/**
+ * enum qca_wlan_vendor_attr_set_monitor_mode - Used by the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_SET_MONITOR_MODE to set the
+ * monitor mode.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_DATA_TX_FRAME_TYPE: u32 attribute.
+ * Represents the TX Data frame types to be monitored (u32). These Data frames
+ * are represented by enum qca_wlan_vendor_monitor_data_frame_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_DATA_RX_FRAME_TYPE: u32 attribute.
+ * Represents the RX Data frame types to be monitored (u32). These Data frames
+ * are represented by enum qca_wlan_vendor_monitor_data_frame_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_MGMT_TX_FRAME_TYPE: u32 attribute.
+ * Represents the TX Management frame types to be monitored (u32). These
+ * Management frames are represented by
+ * enum qca_wlan_vendor_monitor_mgmt_frame_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_MGMT_RX_FRAME_TYPE: u32 attribute.
+ * Represents the RX Management frame types to be monitored (u32). These
+ * Management frames are represented by
+ * enum qca_wlan_vendor_monitor_mgmt_frame_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_TX_FRAME_TYPE: u32 attribute.
+ * Represents the TX Control frame types to be monitored (u32). These Control
+ * frames are represented by enum qca_wlan_vendor_monitor_ctrl_frame_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_RX_FRAME_TYPE: u32 attribute.
+ * Represents the RX Control frame types to be monitored (u32). These Control
+ * frames are represented by enum qca_wlan_vendor_monitor_ctrl_frame_type.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CONNECTED_BEACON_INTERVAL: u32
+ * attribute.
+ * Represents the interval in milliseconds only for the connected Beacon frames,
+ * expecting the connected BSS's Beacon frames to be sent on the monitor
+ * interface at this specific interval.
+ */
+enum qca_wlan_vendor_attr_set_monitor_mode
+{
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_DATA_TX_FRAME_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_DATA_RX_FRAME_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_MGMT_TX_FRAME_TYPE = 3,
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_MGMT_RX_FRAME_TYPE = 4,
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_TX_FRAME_TYPE = 5,
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CTRL_RX_FRAME_TYPE = 6,
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_CONNECTED_BEACON_INTERVAL = 7,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_MAX =
+ QCA_WLAN_VENDOR_ATTR_SET_MONITOR_MODE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_roam_scan_state - Roam scan state flags.
+ * Bits will be set to 1 if the corresponding state is enabled.
+ *
+ * @QCA_VENDOR_WLAN_ROAM_SCAN_STATE_START: Scan Start.
+ * @QCA_VENDOR_WLAN_ROAM_SCAN_STATE_END: Scan end.
+ */
+enum qca_wlan_vendor_roam_scan_state {
+ QCA_WLAN_VENDOR_ROAM_SCAN_STATE_START = BIT(0),
+ QCA_WLAN_VENDOR_ROAM_SCAN_STATE_END = BIT(1),
+};
+
+/**
+ * enum qca_wlan_vendor_roam_event_type - Roam event type flags.
+ * Bits will be set to 1 if the corresponding event is notified.
+ *
+ * @QCA_WLAN_VENDOR_ROAM_EVENT_TRIGGER_REASON: Represents that the roam event
+ * carries the trigger reason. When set, it is expected that the roam event
+ * carries the respective reason via the attribute
+ * QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_TRIGGER_REASON. This event also carries
+ * the BSSID, RSSI, frequency info of the AP to which the roam is attempted.
+ *
+ * @QCA_WLAN_VENDOR_ROAM_EVENT_FAIL_REASON: Represents that the roam event
+ * carries the roam fail reason. When set, it is expected that the roam event
+ * carries the respective reason via the attribute
+ * QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_FAIL_REASON. This event also carries the
+ * BSSID, RSSI, frequency info of the AP to which the roam was attempted.
+ *
+ * @QCA_WLAN_VENDOR_ROAM_EVENT_INVOKE_FAIL_REASON: Represents that the roam
+ * event carries the roam invoke fail reason. When set, it is expected that
+ * the roam event carries the respective reason via the attribute
+ * QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_INVOKE_FAIL_REASON.
+ *
+ * @QCA_WLAN_VENDOR_ROAM_EVENT_SCAN_STATE: Represents that the roam event
+ * carries the roam scan state. When set, it is expected that the roam event
+ * carries the respective scan state via the attribute
+ * QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_STATE and the corresponding
+ * frequency info via QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_FREQ_LIST.
+ */
+enum qca_wlan_vendor_roam_event_type {
+ QCA_WLAN_VENDOR_ROAM_EVENT_TRIGGER_REASON = BIT(0),
+ QCA_WLAN_VENDOR_ROAM_EVENT_FAIL_REASON = BIT(1),
+ QCA_WLAN_VENDOR_ROAM_EVENT_INVOKE_FAIL_REASON = BIT(2),
+ QCA_WLAN_VENDOR_ROAM_EVENT_ROAM_SCAN_STATE = BIT(3),
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_events_candidate_info: Roam candidate info.
+ * Referred by QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_BSSID: 6-byte MAC address
+ * representing the BSSID of the AP to which the roam is attempted.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_RSSI: Signed 32-bit value
+ * in dBm, signifying the RSSI of the candidate BSSID to which the Roaming is
+ * attempted.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_FREQ: u32, frequency in MHz
+ * on which the roam is attempted.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_FAIL_REASON: u32, used in
+ * STA mode only. This represents the roam fail reason for the last failed
+ * roaming attempt by the firmware for the specific BSSID. Different roam
+ * failure reason codes are specified in enum qca_vendor_roam_fail_reasons.
+ */
+enum qca_wlan_vendor_attr_roam_events_candidate_info {
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_BSSID = 1,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_RSSI = 2,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_FREQ = 3,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_FAIL_REASON = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_roam_events - Used by the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS to either configure the
+ * roam events to the driver or notify these events from the driver.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CONFIGURE: u8 attribute. Configures the
+ * driver/firmware to enable/disable the notification of roam events. It's a
+ * mandatory attribute and used only in the request from the userspace to the
+ * host driver. 1-Enable, 0-Disable.
+ * If the roaming is totally offloaded to the firmware, this request when
+ * enabled shall mandate the firmware to notify all the relevant roam events
+ * represented by the below attributes. If the host is in the suspend mode,
+ * the behavior of the firmware to notify these events is guided by
+ * QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_DEVICE_STATE, and if the request is to get
+ * these events in the suspend state, the firmware is expected to wake up the
+ * host before the respective events are notified. Please note that such a
+ * request to get the events in the suspend state will have a definite power
+ * implication.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_SUSPEND_STATE: flag attribute. Represents
+ * that the roam events need to be notified in the suspend state too. By
+ * default, these roam events are notified in the resume state. With this flag,
+ * the roam events are notified in both resume and suspend states.
+ * This attribute is used in the request from the userspace to the host driver.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_TYPE: u32, used in STA mode only.
+ * Represents the different roam event types, signified by the enum
+ * qca_wlan_vendor_roam_event_type.
+ * Each bit of this attribute represents the different roam even types reported
+ * through QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS.
+ * This is sent as an event through QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_TRIGGER_REASON: u32, used in STA
+ * mode only. This represents the roam trigger reason for the last roaming
+ * attempted by the firmware. Each bit of this attribute represents the
+ * different roam trigger reason code which are defined in enum
+ * qca_vendor_roam_triggers.
+ * This is sent as an event through QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_INVOKE_FAIL_REASON: u32, used in
+ * STA mode only. This represents the roam invoke fail reason for the last
+ * failed roam invoke. Different roam invoke failure reason codes
+ * are specified in enum qca_vendor_roam_invoke_fail_reasons.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO: Array of candidates info
+ * for which the roam is attempted. Each entry is a nested attribute defined
+ * by enum qca_wlan_vendor_attr_roam_events_candidate_info.
+ * This is sent as an event through QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_STATE: u8 attribute. Represents
+ * the scan state on which the roam events need to be notified. The values for
+ * this attribute are referred from enum qca_wlan_vendor_roam_scan_state.
+ * This is sent as an event through QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_FREQ_LIST: Nested attribute of
+ * u32 values. List of frequencies in MHz considered for a roam scan.
+ * This is sent as an event through QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS.
+ */
+
+enum qca_wlan_vendor_attr_roam_events
+{
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CONFIGURE = 1,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_SUSPEND_STATE = 2,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_TYPE = 3,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_TRIGGER_REASON = 4,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_INVOKE_FAIL_REASON = 5,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_CANDIDATE_INFO = 6,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_STATE = 7,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_ROAM_SCAN_FREQ_LIST = 8,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_MAX =
+ QCA_WLAN_VENDOR_ATTR_ROAM_EVENTS_AFTER_LAST -1,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/contrib/wpa/src/common/sae.c b/contrib/wpa/src/common/sae.c
index 74920a78e46a..b768c22faa9d 100644
--- a/contrib/wpa/src/common/sae.c
+++ b/contrib/wpa/src/common/sae.c
@@ -1,2352 +1,2352 @@
/*
* Simultaneous authentication of equals
* Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "utils/const_time.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
#include "crypto/sha384.h"
#include "crypto/sha512.h"
#include "crypto/random.h"
#include "crypto/dh_groups.h"
#include "ieee802_11_defs.h"
#include "dragonfly.h"
#include "sae.h"
int sae_set_group(struct sae_data *sae, int group)
{
struct sae_temporary_data *tmp;
#ifdef CONFIG_TESTING_OPTIONS
/* Allow all groups for testing purposes in non-production builds. */
#else /* CONFIG_TESTING_OPTIONS */
if (!dragonfly_suitable_group(group, 0)) {
wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group);
return -1;
}
#endif /* CONFIG_TESTING_OPTIONS */
sae_clear_data(sae);
tmp = sae->tmp = os_zalloc(sizeof(*tmp));
if (tmp == NULL)
return -1;
/* First, check if this is an ECC group */
tmp->ec = crypto_ec_init(group);
if (tmp->ec) {
wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d",
group);
sae->group = group;
tmp->prime_len = crypto_ec_prime_len(tmp->ec);
tmp->prime = crypto_ec_get_prime(tmp->ec);
tmp->order_len = crypto_ec_order_len(tmp->ec);
tmp->order = crypto_ec_get_order(tmp->ec);
return 0;
}
/* Not an ECC group, check FFC */
tmp->dh = dh_groups_get(group);
if (tmp->dh) {
wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d",
group);
sae->group = group;
tmp->prime_len = tmp->dh->prime_len;
if (tmp->prime_len > SAE_MAX_PRIME_LEN) {
sae_clear_data(sae);
return -1;
}
tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime,
tmp->prime_len);
if (tmp->prime_buf == NULL) {
sae_clear_data(sae);
return -1;
}
tmp->prime = tmp->prime_buf;
tmp->order_len = tmp->dh->order_len;
tmp->order_buf = crypto_bignum_init_set(tmp->dh->order,
tmp->dh->order_len);
if (tmp->order_buf == NULL) {
sae_clear_data(sae);
return -1;
}
tmp->order = tmp->order_buf;
return 0;
}
/* Unsupported group */
wpa_printf(MSG_DEBUG,
"SAE: Group %d not supported by the crypto library", group);
return -1;
}
void sae_clear_temp_data(struct sae_data *sae)
{
struct sae_temporary_data *tmp;
if (sae == NULL || sae->tmp == NULL)
return;
tmp = sae->tmp;
crypto_ec_deinit(tmp->ec);
crypto_bignum_deinit(tmp->prime_buf, 0);
crypto_bignum_deinit(tmp->order_buf, 0);
crypto_bignum_deinit(tmp->sae_rand, 1);
crypto_bignum_deinit(tmp->pwe_ffc, 1);
crypto_bignum_deinit(tmp->own_commit_scalar, 0);
crypto_bignum_deinit(tmp->own_commit_element_ffc, 0);
crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0);
crypto_ec_point_deinit(tmp->pwe_ecc, 1);
crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
wpabuf_free(tmp->anti_clogging_token);
wpabuf_free(tmp->own_rejected_groups);
wpabuf_free(tmp->peer_rejected_groups);
os_free(tmp->pw_id);
bin_clear_free(tmp, sizeof(*tmp));
sae->tmp = NULL;
}
void sae_clear_data(struct sae_data *sae)
{
if (sae == NULL)
return;
sae_clear_temp_data(sae);
crypto_bignum_deinit(sae->peer_commit_scalar, 0);
crypto_bignum_deinit(sae->peer_commit_scalar_accepted, 0);
os_memset(sae, 0, sizeof(*sae));
}
static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
{
wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR
" addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2));
if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
os_memcpy(key, addr1, ETH_ALEN);
os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN);
} else {
os_memcpy(key, addr2, ETH_ALEN);
os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN);
}
}
static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
const u8 *prime, const u8 *qr, const u8 *qnr,
u8 *pwd_value)
{
struct crypto_bignum *y_sqr, *x_cand;
int res;
size_t bits;
int cmp_prime;
unsigned int in_range;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
bits = crypto_ec_prime_len_bits(sae->tmp->ec);
if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
prime, sae->tmp->prime_len, pwd_value, bits) < 0)
return -1;
if (bits % 8)
buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
pwd_value, sae->tmp->prime_len);
cmp_prime = const_time_memcmp(pwd_value, prime, sae->tmp->prime_len);
/* Create a const_time mask for selection based on prf result
* being smaller than prime. */
in_range = const_time_fill_msb((unsigned int) cmp_prime);
/* The algorithm description would skip the next steps if
* cmp_prime >= 0 (return 0 here), but go through them regardless to
* minimize externally observable differences in behavior. */
x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
if (!x_cand)
return -1;
y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
crypto_bignum_deinit(x_cand, 1);
if (!y_sqr)
return -1;
res = dragonfly_is_quadratic_residue_blind(sae->tmp->ec, qr, qnr,
y_sqr);
crypto_bignum_deinit(y_sqr, 1);
if (res < 0)
return res;
return const_time_select_int(in_range, res, 0);
}
/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
* pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
struct crypto_bignum *pwe)
{
u8 pwd_value[SAE_MAX_PRIME_LEN];
size_t bits = sae->tmp->prime_len * 8;
u8 exp[1];
struct crypto_bignum *a, *b = NULL;
int res, is_val;
u8 pwd_value_valid;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
bits) < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
sae->tmp->prime_len);
/* Check whether pwd-value < p */
res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
sae->tmp->prime_len);
/* pwd-value >= p is invalid, so res is < 0 for the valid cases and
* the negative sign can be used to fill the mask for constant time
* selection */
pwd_value_valid = const_time_fill_msb(res);
/* If pwd-value >= p, force pwd-value to be < p and perform the
* calculations anyway to hide timing difference. The derived PWE will
* be ignored in that case. */
pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
/* PWE = pwd-value^((p-1)/r) modulo p */
res = -1;
a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
if (!a)
goto fail;
/* This is an optimization based on the used group that does not depend
* on the password in any way, so it is fine to use separate branches
* for this step without constant time operations. */
if (sae->tmp->dh->safe_prime) {
/*
* r = (p-1)/2 for the group used here, so this becomes:
* PWE = pwd-value^2 modulo p
*/
exp[0] = 2;
b = crypto_bignum_init_set(exp, sizeof(exp));
} else {
/* Calculate exponent: (p-1)/r */
exp[0] = 1;
b = crypto_bignum_init_set(exp, sizeof(exp));
if (b == NULL ||
crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
crypto_bignum_div(b, sae->tmp->order, b) < 0)
goto fail;
}
if (!b)
goto fail;
res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
if (res < 0)
goto fail;
/* There were no fatal errors in calculations, so determine the return
* value using constant time operations. We get here for number of
* invalid cases which are cleared here after having performed all the
* computation. PWE is valid if pwd-value was less than prime and
* PWE > 1. Start with pwd-value check first and then use constant time
* operations to clear res to 0 if PWE is 0 or 1.
*/
res = const_time_select_u8(pwd_value_valid, 1, 0);
is_val = crypto_bignum_is_zero(pwe);
res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
is_val = crypto_bignum_is_one(pwe);
res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
fail:
crypto_bignum_deinit(a, 1);
crypto_bignum_deinit(b, 1);
return res;
}
static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
size_t password_len)
{
u8 counter, k;
u8 addrs[2 * ETH_ALEN];
const u8 *addr[2];
size_t len[2];
- u8 *dummy_password, *tmp_password;
+ u8 *stub_password, *tmp_password;
int pwd_seed_odd = 0;
u8 prime[SAE_MAX_ECC_PRIME_LEN];
size_t prime_len;
struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
int res = -1;
u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
* mask */
os_memset(x_bin, 0, sizeof(x_bin));
- dummy_password = os_malloc(password_len);
+ stub_password = os_malloc(password_len);
tmp_password = os_malloc(password_len);
- if (!dummy_password || !tmp_password ||
- random_get_bytes(dummy_password, password_len) < 0)
+ if (!stub_password || !tmp_password ||
+ random_get_bytes(stub_password, password_len) < 0)
goto fail;
prime_len = sae->tmp->prime_len;
if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
prime_len) < 0)
goto fail;
/*
* Create a random quadratic residue (qr) and quadratic non-residue
* (qnr) modulo p for blinding purposes during the loop.
*/
if (dragonfly_get_random_qr_qnr(sae->tmp->prime, &qr, &qnr) < 0 ||
crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 ||
crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0)
goto fail;
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
/*
* H(salt, ikm) = HMAC-SHA256(salt, ikm)
* base = password
* pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
* base || counter)
*/
sae_pwd_seed_key(addr1, addr2, addrs);
addr[0] = tmp_password;
len[0] = password_len;
addr[1] = &counter;
len[1] = sizeof(counter);
/*
* Continue for at least k iterations to protect against side-channel
* attacks that attempt to determine the number of iterations required
* in the loop.
*/
k = dragonfly_min_pwe_loop_iter(sae->group);
for (counter = 1; counter <= k || !found; counter++) {
u8 pwd_seed[SHA256_MAC_LEN];
if (counter > 200) {
/* This should not happen in practice */
wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
break;
}
wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
- const_time_select_bin(found, dummy_password, password,
+ const_time_select_bin(found, stub_password, password,
password_len, tmp_password);
if (hmac_sha256_vector(addrs, sizeof(addrs), 2,
addr, len, pwd_seed) < 0)
break;
res = sae_test_pwd_seed_ecc(sae, pwd_seed,
prime, qr_bin, qnr_bin, x_cand_bin);
const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
x_bin);
pwd_seed_odd = const_time_select_u8(
found, pwd_seed_odd,
pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
os_memset(pwd_seed, 0, sizeof(pwd_seed));
if (res < 0)
goto fail;
/* Need to minimize differences in handling res == 0 and 1 here
* to avoid differences in timing and instruction cache access,
* so use const_time_select_*() to make local copies of the
* values based on whether this loop iteration was the one that
* found the pwd-seed/x. */
/* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
* (with res converted to 0/0xff) handles this in constant time.
*/
found |= res * 0xff;
wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
res, found);
}
if (!found) {
wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
res = -1;
goto fail;
}
x = crypto_bignum_init_set(x_bin, prime_len);
if (!x) {
res = -1;
goto fail;
}
if (!sae->tmp->pwe_ecc)
sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
if (!sae->tmp->pwe_ecc)
res = -1;
else
res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
sae->tmp->pwe_ecc, x,
pwd_seed_odd);
if (res < 0) {
/*
* This should not happen since we already checked that there
* is a result.
*/
wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
}
fail:
crypto_bignum_deinit(qr, 0);
crypto_bignum_deinit(qnr, 0);
- os_free(dummy_password);
+ os_free(stub_password);
bin_clear_free(tmp_password, password_len);
crypto_bignum_deinit(x, 1);
os_memset(x_bin, 0, sizeof(x_bin));
os_memset(x_cand_bin, 0, sizeof(x_cand_bin));
return res;
}
static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
size_t password_len)
{
u8 counter, k, sel_counter = 0;
u8 addrs[2 * ETH_ALEN];
const u8 *addr[2];
size_t len[2];
u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
* mask */
u8 mask;
struct crypto_bignum *pwe;
size_t prime_len = sae->tmp->prime_len * 8;
u8 *pwe_buf;
crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
sae->tmp->pwe_ffc = NULL;
/* Allocate a buffer to maintain selected and candidate PWE for constant
* time selection. */
pwe_buf = os_zalloc(prime_len * 2);
pwe = crypto_bignum_init();
if (!pwe_buf || !pwe)
goto fail;
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
/*
* H(salt, ikm) = HMAC-SHA256(salt, ikm)
* pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
* password || counter)
*/
sae_pwd_seed_key(addr1, addr2, addrs);
addr[0] = password;
len[0] = password_len;
addr[1] = &counter;
len[1] = sizeof(counter);
k = dragonfly_min_pwe_loop_iter(sae->group);
for (counter = 1; counter <= k || !found; counter++) {
u8 pwd_seed[SHA256_MAC_LEN];
int res;
if (counter > 200) {
/* This should not happen in practice */
wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
break;
}
wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter);
if (hmac_sha256_vector(addrs, sizeof(addrs), 2,
addr, len, pwd_seed) < 0)
break;
res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
/* res is -1 for fatal failure, 0 if a valid PWE was not found,
* or 1 if a valid PWE was found. */
if (res < 0)
break;
/* Store the candidate PWE into the second half of pwe_buf and
* the selected PWE in the beginning of pwe_buf using constant
* time selection. */
if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len,
prime_len) < 0)
break;
const_time_select_bin(found, pwe_buf, pwe_buf + prime_len,
prime_len, pwe_buf);
sel_counter = const_time_select_u8(found, sel_counter, counter);
mask = const_time_eq_u8(res, 1);
found = const_time_select_u8(found, found, mask);
}
if (!found)
goto fail;
wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter);
sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len);
fail:
crypto_bignum_deinit(pwe, 1);
bin_clear_free(pwe_buf, prime_len * 2);
return sae->tmp->pwe_ffc ? 0 : -1;
}
static int hkdf_extract(size_t hash_len, const u8 *salt, size_t salt_len,
size_t num_elem, const u8 *addr[], const size_t len[],
u8 *prk)
{
if (hash_len == 32)
return hmac_sha256_vector(salt, salt_len, num_elem, addr, len,
prk);
#ifdef CONFIG_SHA384
if (hash_len == 48)
return hmac_sha384_vector(salt, salt_len, num_elem, addr, len,
prk);
#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SHA512
if (hash_len == 64)
return hmac_sha512_vector(salt, salt_len, num_elem, addr, len,
prk);
#endif /* CONFIG_SHA512 */
return -1;
}
static int hkdf_expand(size_t hash_len, const u8 *prk, size_t prk_len,
const char *info, u8 *okm, size_t okm_len)
{
size_t info_len = os_strlen(info);
if (hash_len == 32)
return hmac_sha256_kdf(prk, prk_len, NULL,
(const u8 *) info, info_len,
okm, okm_len);
#ifdef CONFIG_SHA384
if (hash_len == 48)
return hmac_sha384_kdf(prk, prk_len, NULL,
(const u8 *) info, info_len,
okm, okm_len);
#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SHA512
if (hash_len == 64)
return hmac_sha512_kdf(prk, prk_len, NULL,
(const u8 *) info, info_len,
okm, okm_len);
#endif /* CONFIG_SHA512 */
return -1;
}
static int sswu_curve_param(int group, int *z)
{
switch (group) {
case 19:
*z = -10;
return 0;
case 20:
*z = -12;
return 0;
case 21:
*z = -4;
return 0;
case 25:
case 29:
*z = -5;
return 0;
case 26:
*z = 31;
return 0;
case 28:
*z = -2;
return 0;
case 30:
*z = 7;
return 0;
}
return -1;
}
static void debug_print_bignum(const char *title, const struct crypto_bignum *a,
size_t prime_len)
{
u8 *bin;
bin = os_malloc(prime_len);
if (bin && crypto_bignum_to_bin(a, bin, prime_len, prime_len) >= 0)
wpa_hexdump_key(MSG_DEBUG, title, bin, prime_len);
else
wpa_printf(MSG_DEBUG, "Could not print bignum (%s)", title);
bin_clear_free(bin, prime_len);
}
static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
const struct crypto_bignum *u)
{
int z_int;
const struct crypto_bignum *a, *b, *prime;
struct crypto_bignum *u2, *t1, *t2, *z, *t, *zero, *one, *two, *three,
*x1a, *x1b, *y = NULL;
struct crypto_bignum *x1 = NULL, *x2, *gx1, *gx2, *v = NULL;
unsigned int m_is_zero, is_qr, is_eq;
size_t prime_len;
u8 bin[SAE_MAX_ECC_PRIME_LEN];
u8 bin1[SAE_MAX_ECC_PRIME_LEN];
u8 bin2[SAE_MAX_ECC_PRIME_LEN];
u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN];
struct crypto_ec_point *p = NULL;
if (sswu_curve_param(group, &z_int) < 0)
return NULL;
prime = crypto_ec_get_prime(ec);
prime_len = crypto_ec_prime_len(ec);
a = crypto_ec_get_a(ec);
b = crypto_ec_get_b(ec);
u2 = crypto_bignum_init();
t1 = crypto_bignum_init();
t2 = crypto_bignum_init();
z = crypto_bignum_init_uint(abs(z_int));
t = crypto_bignum_init();
zero = crypto_bignum_init_uint(0);
one = crypto_bignum_init_uint(1);
two = crypto_bignum_init_uint(2);
three = crypto_bignum_init_uint(3);
x1a = crypto_bignum_init();
x1b = crypto_bignum_init();
x2 = crypto_bignum_init();
gx1 = crypto_bignum_init();
gx2 = crypto_bignum_init();
if (!u2 || !t1 || !t2 || !z || !t || !zero || !one || !two || !three ||
!x1a || !x1b || !x2 || !gx1 || !gx2)
goto fail;
if (z_int < 0 && crypto_bignum_sub(prime, z, z) < 0)
goto fail;
/* m = z^2 * u^4 + z * u^2 */
/* --> tmp = z * u^2, m = tmp^2 + tmp */
/* u2 = u^2
* t1 = z * u2
* t2 = t1^2
* m = t1 = t1 + t2 */
if (crypto_bignum_sqrmod(u, prime, u2) < 0 ||
crypto_bignum_mulmod(z, u2, prime, t1) < 0 ||
crypto_bignum_sqrmod(t1, prime, t2) < 0 ||
crypto_bignum_addmod(t1, t2, prime, t1) < 0)
goto fail;
debug_print_bignum("SSWU: m", t1, prime_len);
/* l = CEQ(m, 0)
* t = CSEL(l, 0, inverse(m); where inverse(x) is calculated as
* x^(p-2) modulo p which will handle m == 0 case correctly */
/* TODO: Make sure crypto_bignum_is_zero() is constant time */
m_is_zero = const_time_eq(crypto_bignum_is_zero(t1), 1);
/* t = m^(p-2) modulo p */
if (crypto_bignum_sub(prime, two, t2) < 0 ||
crypto_bignum_exptmod(t1, t2, prime, t) < 0)
goto fail;
debug_print_bignum("SSWU: t", t, prime_len);
/* b / (z * a) */
if (crypto_bignum_mulmod(z, a, prime, t1) < 0 ||
crypto_bignum_inverse(t1, prime, t1) < 0 ||
crypto_bignum_mulmod(b, t1, prime, x1a) < 0)
goto fail;
debug_print_bignum("SSWU: x1a = b / (z * a)", x1a, prime_len);
/* (-b/a) * (1 + t) */
if (crypto_bignum_sub(prime, b, t1) < 0 ||
crypto_bignum_inverse(a, prime, t2) < 0 ||
crypto_bignum_mulmod(t1, t2, prime, t1) < 0 ||
crypto_bignum_addmod(one, t, prime, t2) < 0 ||
crypto_bignum_mulmod(t1, t2, prime, x1b) < 0)
goto fail;
debug_print_bignum("SSWU: x1b = (-b/a) * (1 + t)", x1b, prime_len);
/* x1 = CSEL(CEQ(m, 0), x1a, x1b) */
if (crypto_bignum_to_bin(x1a, bin1, sizeof(bin1), prime_len) < 0 ||
crypto_bignum_to_bin(x1b, bin2, sizeof(bin2), prime_len) < 0)
goto fail;
const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin);
x1 = crypto_bignum_init_set(bin, prime_len);
if (!x1)
goto fail;
debug_print_bignum("SSWU: x1 = CSEL(l, x1a, x1b)", x1, prime_len);
/* gx1 = x1^3 + a * x1 + b */
if (crypto_bignum_exptmod(x1, three, prime, t1) < 0 ||
crypto_bignum_mulmod(a, x1, prime, t2) < 0 ||
crypto_bignum_addmod(t1, t2, prime, t1) < 0 ||
crypto_bignum_addmod(t1, b, prime, gx1) < 0)
goto fail;
debug_print_bignum("SSWU: gx1 = x1^3 + a * x1 + b", gx1, prime_len);
/* x2 = z * u^2 * x1 */
if (crypto_bignum_mulmod(z, u2, prime, t1) < 0 ||
crypto_bignum_mulmod(t1, x1, prime, x2) < 0)
goto fail;
debug_print_bignum("SSWU: x2 = z * u^2 * x1", x2, prime_len);
/* gx2 = x2^3 + a * x2 + b */
if (crypto_bignum_exptmod(x2, three, prime, t1) < 0 ||
crypto_bignum_mulmod(a, x2, prime, t2) < 0 ||
crypto_bignum_addmod(t1, t2, prime, t1) < 0 ||
crypto_bignum_addmod(t1, b, prime, gx2) < 0)
goto fail;
debug_print_bignum("SSWU: gx2 = x2^3 + a * x2 + b", gx2, prime_len);
/* l = gx1 is a quadratic residue modulo p
* --> gx1^((p-1)/2) modulo p is zero or one */
if (crypto_bignum_sub(prime, one, t1) < 0 ||
crypto_bignum_rshift(t1, 1, t1) < 0 ||
crypto_bignum_exptmod(gx1, t1, prime, t1) < 0)
goto fail;
debug_print_bignum("SSWU: gx1^((p-1)/2) modulo p", t1, prime_len);
is_qr = const_time_eq(crypto_bignum_is_zero(t1) |
crypto_bignum_is_one(t1), 1);
/* v = CSEL(l, gx1, gx2) */
if (crypto_bignum_to_bin(gx1, bin1, sizeof(bin1), prime_len) < 0 ||
crypto_bignum_to_bin(gx2, bin2, sizeof(bin2), prime_len) < 0)
goto fail;
const_time_select_bin(is_qr, bin1, bin2, prime_len, bin);
v = crypto_bignum_init_set(bin, prime_len);
if (!v)
goto fail;
debug_print_bignum("SSWU: v = CSEL(l, gx1, gx2)", v, prime_len);
/* x = CSEL(l, x1, x2) */
if (crypto_bignum_to_bin(x1, bin1, sizeof(bin1), prime_len) < 0 ||
crypto_bignum_to_bin(x2, bin2, sizeof(bin2), prime_len) < 0)
goto fail;
const_time_select_bin(is_qr, bin1, bin2, prime_len, x_y);
wpa_hexdump_key(MSG_DEBUG, "SSWU: x = CSEL(l, x1, x2)", x_y, prime_len);
/* y = sqrt(v)
* For prime p such that p = 3 mod 4 --> v^((p+1)/4) */
if (crypto_bignum_to_bin(prime, bin1, sizeof(bin1), prime_len) < 0)
goto fail;
if ((bin1[prime_len - 1] & 0x03) != 3) {
wpa_printf(MSG_DEBUG, "SSWU: prime does not have p = 3 mod 4");
goto fail;
}
y = crypto_bignum_init();
if (!y ||
crypto_bignum_add(prime, one, t1) < 0 ||
crypto_bignum_rshift(t1, 2, t1) < 0 ||
crypto_bignum_exptmod(v, t1, prime, y) < 0)
goto fail;
debug_print_bignum("SSWU: y = sqrt(v)", y, prime_len);
/* l = CEQ(LSB(u), LSB(y)) */
if (crypto_bignum_to_bin(u, bin1, sizeof(bin1), prime_len) < 0 ||
crypto_bignum_to_bin(y, bin2, sizeof(bin2), prime_len) < 0)
goto fail;
is_eq = const_time_eq(bin1[prime_len - 1] & 0x01,
bin2[prime_len - 1] & 0x01);
/* P = CSEL(l, (x,y), (x, p-y)) */
if (crypto_bignum_sub(prime, y, t1) < 0)
goto fail;
debug_print_bignum("SSWU: p - y", t1, prime_len);
if (crypto_bignum_to_bin(y, bin1, sizeof(bin1), prime_len) < 0 ||
crypto_bignum_to_bin(t1, bin2, sizeof(bin2), prime_len) < 0)
goto fail;
const_time_select_bin(is_eq, bin1, bin2, prime_len, &x_y[prime_len]);
/* output P */
wpa_hexdump_key(MSG_DEBUG, "SSWU: P.x", x_y, prime_len);
wpa_hexdump_key(MSG_DEBUG, "SSWU: P.y", &x_y[prime_len], prime_len);
p = crypto_ec_point_from_bin(ec, x_y);
fail:
crypto_bignum_deinit(u2, 1);
crypto_bignum_deinit(t1, 1);
crypto_bignum_deinit(t2, 1);
crypto_bignum_deinit(z, 0);
crypto_bignum_deinit(t, 1);
crypto_bignum_deinit(x1a, 1);
crypto_bignum_deinit(x1b, 1);
crypto_bignum_deinit(x1, 1);
crypto_bignum_deinit(x2, 1);
crypto_bignum_deinit(gx1, 1);
crypto_bignum_deinit(gx2, 1);
crypto_bignum_deinit(y, 1);
crypto_bignum_deinit(v, 1);
crypto_bignum_deinit(zero, 0);
crypto_bignum_deinit(one, 0);
crypto_bignum_deinit(two, 0);
crypto_bignum_deinit(three, 0);
forced_memzero(bin, sizeof(bin));
forced_memzero(bin1, sizeof(bin1));
forced_memzero(bin2, sizeof(bin2));
forced_memzero(x_y, sizeof(x_y));
return p;
}
static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
const char *identifier, u8 *pwd_seed)
{
const u8 *addr[2];
size_t len[2];
size_t num_elem;
/* pwd-seed = HKDF-Extract(ssid, password [ || identifier ]) */
addr[0] = password;
len[0] = password_len;
num_elem = 1;
wpa_hexdump_ascii(MSG_DEBUG, "SAE: SSID", ssid, ssid_len);
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
if (identifier) {
wpa_printf(MSG_DEBUG, "SAE: password identifier: %s",
identifier);
addr[num_elem] = (const u8 *) identifier;
len[num_elem] = os_strlen(identifier);
num_elem++;
}
if (hkdf_extract(hash_len, ssid, ssid_len, num_elem, addr, len,
pwd_seed) < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, hash_len);
return 0;
}
size_t sae_ecc_prime_len_2_hash_len(size_t prime_len)
{
if (prime_len <= 256 / 8)
return 32;
if (prime_len <= 384 / 8)
return 48;
return 64;
}
static struct crypto_ec_point *
sae_derive_pt_ecc(struct crypto_ec *ec, int group,
const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
const char *identifier)
{
u8 pwd_seed[64];
u8 pwd_value[SAE_MAX_ECC_PRIME_LEN * 2];
size_t pwd_value_len, hash_len, prime_len;
const struct crypto_bignum *prime;
struct crypto_bignum *bn = NULL;
struct crypto_ec_point *p1 = NULL, *p2 = NULL, *pt = NULL;
prime = crypto_ec_get_prime(ec);
prime_len = crypto_ec_prime_len(ec);
if (prime_len > SAE_MAX_ECC_PRIME_LEN)
goto fail;
hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
/* len = olen(p) + ceil(olen(p)/2) */
pwd_value_len = prime_len + (prime_len + 1) / 2;
if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len,
identifier, pwd_seed) < 0)
goto fail;
/* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u1 P1", len)
*/
if (hkdf_expand(hash_len, pwd_seed, hash_len,
"SAE Hash to Element u1 P1", pwd_value, pwd_value_len) <
0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u1 P1)",
pwd_value, pwd_value_len);
/* u1 = pwd-value modulo p */
bn = crypto_bignum_init_set(pwd_value, pwd_value_len);
if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 ||
crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value),
prime_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "SAE: u1", pwd_value, prime_len);
/* P1 = SSWU(u1) */
p1 = sswu(ec, group, bn);
if (!p1)
goto fail;
/* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u2 P2", len)
*/
if (hkdf_expand(hash_len, pwd_seed, hash_len,
"SAE Hash to Element u2 P2", pwd_value,
pwd_value_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u2 P2)",
pwd_value, pwd_value_len);
/* u2 = pwd-value modulo p */
crypto_bignum_deinit(bn, 1);
bn = crypto_bignum_init_set(pwd_value, pwd_value_len);
if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 ||
crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value),
prime_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "SAE: u2", pwd_value, prime_len);
/* P2 = SSWU(u2) */
p2 = sswu(ec, group, bn);
if (!p2)
goto fail;
/* PT = elem-op(P1, P2) */
pt = crypto_ec_point_init(ec);
if (!pt)
goto fail;
if (crypto_ec_point_add(ec, p1, p2, pt) < 0) {
crypto_ec_point_deinit(pt, 1);
pt = NULL;
}
fail:
forced_memzero(pwd_seed, sizeof(pwd_seed));
forced_memzero(pwd_value, sizeof(pwd_value));
crypto_bignum_deinit(bn, 1);
crypto_ec_point_deinit(p1, 1);
crypto_ec_point_deinit(p2, 1);
return pt;
}
size_t sae_ffc_prime_len_2_hash_len(size_t prime_len)
{
if (prime_len <= 2048 / 8)
return 32;
if (prime_len <= 3072 / 8)
return 48;
return 64;
}
static struct crypto_bignum *
sae_derive_pt_ffc(const struct dh_group *dh, int group,
const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
const char *identifier)
{
size_t hash_len, prime_len, pwd_value_len;
struct crypto_bignum *prime, *order;
struct crypto_bignum *one = NULL, *two = NULL, *bn = NULL, *tmp = NULL,
*pt = NULL;
u8 pwd_seed[64];
u8 pwd_value[SAE_MAX_PRIME_LEN + SAE_MAX_PRIME_LEN / 2];
prime = crypto_bignum_init_set(dh->prime, dh->prime_len);
order = crypto_bignum_init_set(dh->order, dh->order_len);
if (!prime || !order)
goto fail;
prime_len = dh->prime_len;
if (prime_len > SAE_MAX_PRIME_LEN)
goto fail;
hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
/* len = olen(p) + ceil(olen(p)/2) */
pwd_value_len = prime_len + (prime_len + 1) / 2;
if (pwd_value_len > sizeof(pwd_value))
goto fail;
if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len,
identifier, pwd_seed) < 0)
goto fail;
/* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element", len) */
if (hkdf_expand(hash_len, pwd_seed, hash_len,
"SAE Hash to Element", pwd_value, pwd_value_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
pwd_value, pwd_value_len);
/* pwd-value = (pwd-value modulo (p-2)) + 2 */
bn = crypto_bignum_init_set(pwd_value, pwd_value_len);
one = crypto_bignum_init_uint(1);
two = crypto_bignum_init_uint(2);
tmp = crypto_bignum_init();
if (!bn || !one || !two || !tmp ||
crypto_bignum_sub(prime, two, tmp) < 0 ||
crypto_bignum_mod(bn, tmp, bn) < 0 ||
crypto_bignum_add(bn, two, bn) < 0 ||
crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value),
prime_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value(reduced)",
pwd_value, prime_len);
/* PT = pwd-value^((p-1)/q) modulo p */
pt = crypto_bignum_init();
if (!pt ||
crypto_bignum_sub(prime, one, tmp) < 0 ||
crypto_bignum_div(tmp, order, tmp) < 0 ||
crypto_bignum_exptmod(bn, tmp, prime, pt) < 0) {
crypto_bignum_deinit(pt, 1);
pt = NULL;
goto fail;
}
debug_print_bignum("SAE: PT", pt, prime_len);
fail:
forced_memzero(pwd_seed, sizeof(pwd_seed));
forced_memzero(pwd_value, sizeof(pwd_value));
crypto_bignum_deinit(bn, 1);
crypto_bignum_deinit(tmp, 1);
crypto_bignum_deinit(one, 0);
crypto_bignum_deinit(two, 0);
crypto_bignum_deinit(prime, 0);
crypto_bignum_deinit(order, 0);
return pt;
}
static struct sae_pt *
sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
const char *identifier)
{
struct sae_pt *pt;
wpa_printf(MSG_DEBUG, "SAE: Derive PT - group %d", group);
if (ssid_len > 32)
return NULL;
pt = os_zalloc(sizeof(*pt));
if (!pt)
return NULL;
#ifdef CONFIG_SAE_PK
os_memcpy(pt->ssid, ssid, ssid_len);
pt->ssid_len = ssid_len;
#endif /* CONFIG_SAE_PK */
pt->group = group;
pt->ec = crypto_ec_init(group);
if (pt->ec) {
pt->ecc_pt = sae_derive_pt_ecc(pt->ec, group, ssid, ssid_len,
password, password_len,
identifier);
if (!pt->ecc_pt) {
wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT");
goto fail;
}
return pt;
}
pt->dh = dh_groups_get(group);
if (!pt->dh) {
wpa_printf(MSG_DEBUG, "SAE: Unsupported group %d", group);
goto fail;
}
pt->ffc_pt = sae_derive_pt_ffc(pt->dh, group, ssid, ssid_len,
password, password_len, identifier);
if (!pt->ffc_pt) {
wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT");
goto fail;
}
return pt;
fail:
sae_deinit_pt(pt);
return NULL;
}
struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
const char *identifier)
{
struct sae_pt *pt = NULL, *last = NULL, *tmp;
int default_groups[] = { 19, 0 };
int i;
if (!groups)
groups = default_groups;
for (i = 0; groups[i] > 0; i++) {
tmp = sae_derive_pt_group(groups[i], ssid, ssid_len, password,
password_len, identifier);
if (!tmp)
continue;
if (last)
last->next = tmp;
else
pt = tmp;
last = tmp;
}
return pt;
}
static void sae_max_min_addr(const u8 *addr[], size_t len[],
const u8 *addr1, const u8 *addr2)
{
len[0] = ETH_ALEN;
len[1] = ETH_ALEN;
if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
addr[0] = addr1;
addr[1] = addr2;
} else {
addr[0] = addr2;
addr[1] = addr1;
}
}
struct crypto_ec_point *
sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2)
{
u8 bin[SAE_MAX_ECC_PRIME_LEN * 2];
size_t prime_len;
const u8 *addr[2];
size_t len[2];
u8 salt[64], hash[64];
size_t hash_len;
const struct crypto_bignum *order;
struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL;
struct crypto_ec_point *pwe = NULL;
wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT");
prime_len = crypto_ec_prime_len(pt->ec);
if (crypto_ec_point_to_bin(pt->ec, pt->ecc_pt,
bin, bin + prime_len) < 0)
return NULL;
wpa_hexdump_key(MSG_DEBUG, "SAE: PT.x", bin, prime_len);
wpa_hexdump_key(MSG_DEBUG, "SAE: PT.y", bin + prime_len, prime_len);
sae_max_min_addr(addr, len, addr1, addr2);
/* val = H(0^n,
* MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */
wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))");
hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
os_memset(salt, 0, hash_len);
if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len);
/* val = val modulo (q - 1) + 1 */
order = crypto_ec_get_order(pt->ec);
tmp = crypto_bignum_init();
val = crypto_bignum_init_set(hash, hash_len);
one = crypto_bignum_init_uint(1);
if (!tmp || !val || !one ||
crypto_bignum_sub(order, one, tmp) < 0 ||
crypto_bignum_mod(val, tmp, val) < 0 ||
crypto_bignum_add(val, one, val) < 0)
goto fail;
debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len);
/* PWE = scalar-op(val, PT) */
pwe = crypto_ec_point_init(pt->ec);
if (!pwe ||
crypto_ec_point_mul(pt->ec, pt->ecc_pt, val, pwe) < 0 ||
crypto_ec_point_to_bin(pt->ec, pwe, bin, bin + prime_len) < 0) {
crypto_ec_point_deinit(pwe, 1);
pwe = NULL;
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.x", bin, prime_len);
wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.y", bin + prime_len, prime_len);
fail:
crypto_bignum_deinit(tmp, 1);
crypto_bignum_deinit(val, 1);
crypto_bignum_deinit(one, 0);
return pwe;
}
struct crypto_bignum *
sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2)
{
size_t prime_len;
const u8 *addr[2];
size_t len[2];
u8 salt[64], hash[64];
size_t hash_len;
struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL;
struct crypto_bignum *pwe = NULL, *order = NULL, *prime = NULL;
wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT");
prime = crypto_bignum_init_set(pt->dh->prime, pt->dh->prime_len);
order = crypto_bignum_init_set(pt->dh->order, pt->dh->order_len);
if (!prime || !order)
goto fail;
prime_len = pt->dh->prime_len;
sae_max_min_addr(addr, len, addr1, addr2);
/* val = H(0^n,
* MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */
wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))");
hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
os_memset(salt, 0, hash_len);
if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len);
/* val = val modulo (q - 1) + 1 */
tmp = crypto_bignum_init();
val = crypto_bignum_init_set(hash, hash_len);
one = crypto_bignum_init_uint(1);
if (!tmp || !val || !one ||
crypto_bignum_sub(order, one, tmp) < 0 ||
crypto_bignum_mod(val, tmp, val) < 0 ||
crypto_bignum_add(val, one, val) < 0)
goto fail;
debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len);
/* PWE = scalar-op(val, PT) */
pwe = crypto_bignum_init();
if (!pwe || crypto_bignum_exptmod(pt->ffc_pt, val, prime, pwe) < 0) {
crypto_bignum_deinit(pwe, 1);
pwe = NULL;
goto fail;
}
debug_print_bignum("SAE: PWE", pwe, prime_len);
fail:
crypto_bignum_deinit(tmp, 1);
crypto_bignum_deinit(val, 1);
crypto_bignum_deinit(one, 0);
crypto_bignum_deinit(prime, 0);
crypto_bignum_deinit(order, 0);
return pwe;
}
void sae_deinit_pt(struct sae_pt *pt)
{
struct sae_pt *prev;
while (pt) {
crypto_ec_point_deinit(pt->ecc_pt, 1);
crypto_bignum_deinit(pt->ffc_pt, 1);
crypto_ec_deinit(pt->ec);
prev = pt;
pt = pt->next;
os_free(prev);
}
}
static int sae_derive_commit_element_ecc(struct sae_data *sae,
struct crypto_bignum *mask)
{
/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
if (!sae->tmp->own_commit_element_ecc) {
sae->tmp->own_commit_element_ecc =
crypto_ec_point_init(sae->tmp->ec);
if (!sae->tmp->own_commit_element_ecc)
return -1;
}
if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask,
sae->tmp->own_commit_element_ecc) < 0 ||
crypto_ec_point_invert(sae->tmp->ec,
sae->tmp->own_commit_element_ecc) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
return -1;
}
return 0;
}
static int sae_derive_commit_element_ffc(struct sae_data *sae,
struct crypto_bignum *mask)
{
/* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
if (!sae->tmp->own_commit_element_ffc) {
sae->tmp->own_commit_element_ffc = crypto_bignum_init();
if (!sae->tmp->own_commit_element_ffc)
return -1;
}
if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime,
sae->tmp->own_commit_element_ffc) < 0 ||
crypto_bignum_inverse(sae->tmp->own_commit_element_ffc,
sae->tmp->prime,
sae->tmp->own_commit_element_ffc) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
return -1;
}
return 0;
}
static int sae_derive_commit(struct sae_data *sae)
{
struct crypto_bignum *mask;
int ret;
mask = crypto_bignum_init();
if (!sae->tmp->sae_rand)
sae->tmp->sae_rand = crypto_bignum_init();
if (!sae->tmp->own_commit_scalar)
sae->tmp->own_commit_scalar = crypto_bignum_init();
ret = !mask || !sae->tmp->sae_rand || !sae->tmp->own_commit_scalar ||
dragonfly_generate_scalar(sae->tmp->order, sae->tmp->sae_rand,
mask,
sae->tmp->own_commit_scalar) < 0 ||
(sae->tmp->ec &&
sae_derive_commit_element_ecc(sae, mask) < 0) ||
(sae->tmp->dh &&
sae_derive_commit_element_ffc(sae, mask) < 0);
crypto_bignum_deinit(mask, 1);
return ret ? -1 : 0;
}
int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
const u8 *password, size_t password_len,
struct sae_data *sae)
{
if (sae->tmp == NULL ||
(sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
password_len) < 0) ||
(sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
password_len) < 0))
return -1;
sae->h2e = 0;
sae->pk = 0;
return sae_derive_commit(sae);
}
int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2,
int *rejected_groups, const struct sae_pk *pk)
{
if (!sae->tmp)
return -1;
while (pt) {
if (pt->group == sae->group)
break;
pt = pt->next;
}
if (!pt) {
wpa_printf(MSG_INFO, "SAE: Could not find PT for group %u",
sae->group);
return -1;
}
#ifdef CONFIG_SAE_PK
os_memcpy(sae->tmp->ssid, pt->ssid, pt->ssid_len);
sae->tmp->ssid_len = pt->ssid_len;
sae->tmp->ap_pk = pk;
#endif /* CONFIG_SAE_PK */
sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0;
wpabuf_free(sae->tmp->own_rejected_groups);
sae->tmp->own_rejected_groups = NULL;
if (rejected_groups) {
int count, i;
struct wpabuf *groups;
count = int_array_len(rejected_groups);
groups = wpabuf_alloc(count * 2);
if (!groups)
return -1;
for (i = 0; i < count; i++)
wpabuf_put_le16(groups, rejected_groups[i]);
sae->tmp->own_rejected_groups = groups;
}
if (pt->ec) {
crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1);
sae->tmp->pwe_ecc = sae_derive_pwe_from_pt_ecc(pt, addr1,
addr2);
if (!sae->tmp->pwe_ecc)
return -1;
}
if (pt->dh) {
crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
sae->tmp->pwe_ffc = sae_derive_pwe_from_pt_ffc(pt, addr1,
addr2);
if (!sae->tmp->pwe_ffc)
return -1;
}
sae->h2e = 1;
return sae_derive_commit(sae);
}
static int sae_derive_k_ecc(struct sae_data *sae, u8 *k)
{
struct crypto_ec_point *K;
int ret = -1;
K = crypto_ec_point_init(sae->tmp->ec);
if (K == NULL)
goto fail;
/*
* K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
* PEER-COMMIT-ELEMENT)))
* If K is identity element (point-at-infinity), reject
* k = F(K) (= x coordinate)
*/
if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc,
sae->peer_commit_scalar, K) < 0 ||
crypto_ec_point_add(sae->tmp->ec, K,
sae->tmp->peer_commit_element_ecc, K) < 0 ||
crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 ||
crypto_ec_point_is_at_infinity(sae->tmp->ec, K) ||
crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
ret = 0;
fail:
crypto_ec_point_deinit(K, 1);
return ret;
}
static int sae_derive_k_ffc(struct sae_data *sae, u8 *k)
{
struct crypto_bignum *K;
int ret = -1;
K = crypto_bignum_init();
if (K == NULL)
goto fail;
/*
* K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
* PEER-COMMIT-ELEMENT)))
* If K is identity element (one), reject.
* k = F(K) (= x coordinate)
*/
if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar,
sae->tmp->prime, K) < 0 ||
crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc,
sae->tmp->prime, K) < 0 ||
crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0
||
crypto_bignum_is_one(K) ||
crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) <
0) {
wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
ret = 0;
fail:
crypto_bignum_deinit(K, 1);
return ret;
}
static int sae_kdf_hash(size_t hash_len, const u8 *k, const char *label,
const u8 *context, size_t context_len,
u8 *out, size_t out_len)
{
if (hash_len == 32)
return sha256_prf(k, hash_len, label,
context, context_len, out, out_len);
#ifdef CONFIG_SHA384
if (hash_len == 48)
return sha384_prf(k, hash_len, label,
context, context_len, out, out_len);
#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SHA512
if (hash_len == 64)
return sha512_prf(k, hash_len, label,
context, context_len, out, out_len);
#endif /* CONFIG_SHA512 */
return -1;
}
static int sae_derive_keys(struct sae_data *sae, const u8 *k)
{
u8 zero[SAE_MAX_HASH_LEN], val[SAE_MAX_PRIME_LEN];
const u8 *salt;
struct wpabuf *rejected_groups = NULL;
u8 keyseed[SAE_MAX_HASH_LEN];
u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN];
struct crypto_bignum *tmp;
int ret = -1;
size_t hash_len, salt_len, prime_len = sae->tmp->prime_len;
const u8 *addr[1];
size_t len[1];
tmp = crypto_bignum_init();
if (tmp == NULL)
goto fail;
/* keyseed = H(salt, k)
* KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK",
* (commit-scalar + peer-commit-scalar) modulo r)
* PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
*
* When SAE-PK is used,
* KCK || PMK || KEK = KDF-Hash-Length(keyseed, "SAE-PK keys", context)
*/
if (!sae->h2e)
hash_len = SHA256_MAC_LEN;
else if (sae->tmp->dh)
hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
else
hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
if (sae->h2e && (sae->tmp->own_rejected_groups ||
sae->tmp->peer_rejected_groups)) {
struct wpabuf *own, *peer;
own = sae->tmp->own_rejected_groups;
peer = sae->tmp->peer_rejected_groups;
salt_len = 0;
if (own)
salt_len += wpabuf_len(own);
if (peer)
salt_len += wpabuf_len(peer);
rejected_groups = wpabuf_alloc(salt_len);
if (!rejected_groups)
goto fail;
if (sae->tmp->own_addr_higher) {
if (own)
wpabuf_put_buf(rejected_groups, own);
if (peer)
wpabuf_put_buf(rejected_groups, peer);
} else {
if (peer)
wpabuf_put_buf(rejected_groups, peer);
if (own)
wpabuf_put_buf(rejected_groups, own);
}
salt = wpabuf_head(rejected_groups);
salt_len = wpabuf_len(rejected_groups);
} else {
os_memset(zero, 0, hash_len);
salt = zero;
salt_len = hash_len;
}
wpa_hexdump(MSG_DEBUG, "SAE: salt for keyseed derivation",
salt, salt_len);
addr[0] = k;
len[0] = prime_len;
if (hkdf_extract(hash_len, salt, salt_len, 1, addr, len, keyseed) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, hash_len);
if (crypto_bignum_add(sae->tmp->own_commit_scalar,
sae->peer_commit_scalar, tmp) < 0 ||
crypto_bignum_mod(tmp, sae->tmp->order, tmp) < 0)
goto fail;
/* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit
* string that is needed for KCK, PMK, and PMKID derivation, but it
* seems to make most sense to encode the
* (commit-scalar + peer-commit-scalar) mod r part as a bit string by
* zero padding it from left to the length of the order (in full
* octets). */
crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
#ifdef CONFIG_SAE_PK
if (sae->pk) {
if (sae_kdf_hash(hash_len, keyseed, "SAE-PK keys",
val, sae->tmp->order_len,
keys, 2 * hash_len + SAE_PMK_LEN) < 0)
goto fail;
} else {
if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
val, sae->tmp->order_len,
keys, hash_len + SAE_PMK_LEN) < 0)
goto fail;
}
#else /* CONFIG_SAE_PK */
if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
val, sae->tmp->order_len,
keys, hash_len + SAE_PMK_LEN) < 0)
goto fail;
#endif /* !CONFIG_SAE_PK */
forced_memzero(keyseed, sizeof(keyseed));
os_memcpy(sae->tmp->kck, keys, hash_len);
sae->tmp->kck_len = hash_len;
os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN);
os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
#ifdef CONFIG_SAE_PK
if (sae->pk) {
os_memcpy(sae->tmp->kek, keys + hash_len + SAE_PMK_LEN,
hash_len);
sae->tmp->kek_len = hash_len;
wpa_hexdump_key(MSG_DEBUG, "SAE: KEK for SAE-PK",
sae->tmp->kek, sae->tmp->kek_len);
}
#endif /* CONFIG_SAE_PK */
forced_memzero(keys, sizeof(keys));
wpa_hexdump_key(MSG_DEBUG, "SAE: KCK",
sae->tmp->kck, sae->tmp->kck_len);
wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
ret = 0;
fail:
wpabuf_free(rejected_groups);
crypto_bignum_deinit(tmp, 0);
return ret;
}
int sae_process_commit(struct sae_data *sae)
{
u8 k[SAE_MAX_PRIME_LEN];
if (sae->tmp == NULL ||
(sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
(sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||
sae_derive_keys(sae, k) < 0)
return -1;
return 0;
}
int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
const struct wpabuf *token, const char *identifier)
{
u8 *pos;
if (sae->tmp == NULL)
return -1;
wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
if (!sae->h2e && token) {
wpabuf_put_buf(buf, token);
wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token",
wpabuf_head(token), wpabuf_len(token));
}
pos = wpabuf_put(buf, sae->tmp->prime_len);
if (crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos,
sae->tmp->prime_len, sae->tmp->prime_len) < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar",
pos, sae->tmp->prime_len);
if (sae->tmp->ec) {
pos = wpabuf_put(buf, 2 * sae->tmp->prime_len);
if (crypto_ec_point_to_bin(sae->tmp->ec,
sae->tmp->own_commit_element_ecc,
pos, pos + sae->tmp->prime_len) < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)",
pos, sae->tmp->prime_len);
wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)",
pos + sae->tmp->prime_len, sae->tmp->prime_len);
} else {
pos = wpabuf_put(buf, sae->tmp->prime_len);
if (crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos,
sae->tmp->prime_len,
sae->tmp->prime_len) < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
pos, sae->tmp->prime_len);
}
if (identifier) {
/* Password Identifier element */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 1 + os_strlen(identifier));
wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER);
wpabuf_put_str(buf, identifier);
wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s",
identifier);
}
if (sae->h2e && sae->tmp->own_rejected_groups) {
wpa_hexdump_buf(MSG_DEBUG, "SAE: own Rejected Groups",
sae->tmp->own_rejected_groups);
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf,
1 + wpabuf_len(sae->tmp->own_rejected_groups));
wpabuf_put_u8(buf, WLAN_EID_EXT_REJECTED_GROUPS);
wpabuf_put_buf(buf, sae->tmp->own_rejected_groups);
}
if (sae->h2e && token) {
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 1 + wpabuf_len(token));
wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
wpabuf_put_buf(buf, token);
wpa_hexdump_buf(MSG_DEBUG,
"SAE: Anti-clogging token (in container)",
token);
}
return 0;
}
u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group)
{
if (allowed_groups) {
int i;
for (i = 0; allowed_groups[i] > 0; i++) {
if (allowed_groups[i] == group)
break;
}
if (allowed_groups[i] != group) {
wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not "
"enabled in the current configuration",
group);
return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
}
}
if (sae->state == SAE_COMMITTED && group != sae->group) {
wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed");
return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
}
if (group != sae->group && sae_set_group(sae, group) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
group);
return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
}
if (sae->tmp == NULL) {
wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (sae->tmp->dh && !allowed_groups) {
wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without "
"explicit configuration enabling it", group);
return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
}
return WLAN_STATUS_SUCCESS;
}
static int sae_is_password_id_elem(const u8 *pos, const u8 *end)
{
return end - pos >= 3 &&
pos[0] == WLAN_EID_EXTENSION &&
pos[1] >= 1 &&
end - pos - 2 >= pos[1] &&
pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER;
}
static int sae_is_rejected_groups_elem(const u8 *pos, const u8 *end)
{
return end - pos >= 3 &&
pos[0] == WLAN_EID_EXTENSION &&
pos[1] >= 2 &&
end - pos - 2 >= pos[1] &&
pos[2] == WLAN_EID_EXT_REJECTED_GROUPS;
}
static int sae_is_token_container_elem(const u8 *pos, const u8 *end)
{
return end - pos >= 3 &&
pos[0] == WLAN_EID_EXTENSION &&
pos[1] >= 1 &&
end - pos - 2 >= pos[1] &&
pos[2] == WLAN_EID_EXT_ANTI_CLOGGING_TOKEN;
}
static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
const u8 *end, const u8 **token,
size_t *token_len, int h2e)
{
size_t scalar_elem_len, tlen;
if (token)
*token = NULL;
if (token_len)
*token_len = 0;
if (h2e)
return; /* No Anti-Clogging Token field outside container IE */
scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len;
if (scalar_elem_len >= (size_t) (end - *pos))
return; /* No extra data beyond peer scalar and element */
tlen = end - (*pos + scalar_elem_len);
if (tlen < SHA256_MAC_LEN) {
wpa_printf(MSG_DEBUG,
"SAE: Too short optional data (%u octets) to include our Anti-Clogging Token",
(unsigned int) tlen);
return;
}
wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
if (token)
*token = *pos;
if (token_len)
*token_len = tlen;
*pos += tlen;
}
static void sae_parse_token_container(struct sae_data *sae,
const u8 *pos, const u8 *end,
const u8 **token, size_t *token_len)
{
wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
pos, end - pos);
if (!sae_is_token_container_elem(pos, end))
return;
*token = pos + 3;
*token_len = pos[1] - 1;
wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token (in container)",
*token, *token_len);
}
static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
struct crypto_bignum *peer_scalar;
if (sae->tmp->prime_len > end - *pos) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len);
if (peer_scalar == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
/*
* IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for
* the peer and it is in Authenticated state, the new Commit Message
* shall be dropped if the peer-scalar is identical to the one used in
* the existing protocol instance.
*/
if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar_accepted &&
crypto_bignum_cmp(sae->peer_commit_scalar_accepted,
peer_scalar) == 0) {
wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous "
"peer-commit-scalar");
crypto_bignum_deinit(peer_scalar, 0);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
/* 1 < scalar < r */
if (crypto_bignum_is_zero(peer_scalar) ||
crypto_bignum_is_one(peer_scalar) ||
crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) {
wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar");
crypto_bignum_deinit(peer_scalar, 0);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
crypto_bignum_deinit(sae->peer_commit_scalar, 0);
sae->peer_commit_scalar = peer_scalar;
wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar",
*pos, sae->tmp->prime_len);
*pos += sae->tmp->prime_len;
return WLAN_STATUS_SUCCESS;
}
static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
u8 prime[SAE_MAX_ECC_PRIME_LEN];
if (2 * sae->tmp->prime_len > end - *pos) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
"commit-element");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
sae->tmp->prime_len) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
/* element x and y coordinates < p */
if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 ||
os_memcmp(*pos + sae->tmp->prime_len, prime,
sae->tmp->prime_len) >= 0) {
wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
"element");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
*pos, sae->tmp->prime_len);
wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
*pos + sae->tmp->prime_len, sae->tmp->prime_len);
crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
sae->tmp->peer_commit_element_ecc =
crypto_ec_point_from_bin(sae->tmp->ec, *pos);
if (sae->tmp->peer_commit_element_ecc == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
sae->tmp->peer_commit_element_ecc)) {
wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
*pos += 2 * sae->tmp->prime_len;
return WLAN_STATUS_SUCCESS;
}
static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
struct crypto_bignum *res, *one;
const u8 one_bin[1] = { 0x01 };
if (sae->tmp->prime_len > end - *pos) {
wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
"commit-element");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos,
sae->tmp->prime_len);
crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
sae->tmp->peer_commit_element_ffc =
crypto_bignum_init_set(*pos, sae->tmp->prime_len);
if (sae->tmp->peer_commit_element_ffc == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
/* 1 < element < p - 1 */
res = crypto_bignum_init();
one = crypto_bignum_init_set(one_bin, sizeof(one_bin));
if (!res || !one ||
crypto_bignum_sub(sae->tmp->prime, one, res) ||
crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) ||
crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) ||
crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) {
crypto_bignum_deinit(res, 0);
crypto_bignum_deinit(one, 0);
wpa_printf(MSG_DEBUG, "SAE: Invalid peer element");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
crypto_bignum_deinit(one, 0);
/* scalar-op(r, ELEMENT) = 1 modulo p */
if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc,
sae->tmp->order, sae->tmp->prime, res) < 0 ||
!crypto_bignum_is_one(res)) {
wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)");
crypto_bignum_deinit(res, 0);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
crypto_bignum_deinit(res, 0);
*pos += sae->tmp->prime_len;
return WLAN_STATUS_SUCCESS;
}
static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
if (sae->tmp->dh)
return sae_parse_commit_element_ffc(sae, pos, end);
return sae_parse_commit_element_ecc(sae, pos, end);
}
static int sae_parse_password_identifier(struct sae_data *sae,
const u8 **pos, const u8 *end)
{
const u8 *epos;
u8 len;
wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
*pos, end - *pos);
if (!sae_is_password_id_elem(*pos, end)) {
if (sae->tmp->pw_id) {
wpa_printf(MSG_DEBUG,
"SAE: No Password Identifier included, but expected one (%s)",
sae->tmp->pw_id);
return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
}
os_free(sae->tmp->pw_id);
sae->tmp->pw_id = NULL;
return WLAN_STATUS_SUCCESS; /* No Password Identifier */
}
epos = *pos;
epos++; /* skip IE type */
len = *epos++; /* IE length */
if (len > end - epos || len < 1)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
epos++; /* skip ext ID */
len--;
if (sae->tmp->pw_id &&
(len != os_strlen(sae->tmp->pw_id) ||
os_memcmp(sae->tmp->pw_id, epos, len) != 0)) {
wpa_printf(MSG_DEBUG,
"SAE: The included Password Identifier does not match the expected one (%s)",
sae->tmp->pw_id);
return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
}
os_free(sae->tmp->pw_id);
sae->tmp->pw_id = os_malloc(len + 1);
if (!sae->tmp->pw_id)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
os_memcpy(sae->tmp->pw_id, epos, len);
sae->tmp->pw_id[len] = '\0';
wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier",
sae->tmp->pw_id, len);
*pos = epos + len;
return WLAN_STATUS_SUCCESS;
}
static int sae_parse_rejected_groups(struct sae_data *sae,
const u8 **pos, const u8 *end)
{
const u8 *epos;
u8 len;
wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
*pos, end - *pos);
if (!sae_is_rejected_groups_elem(*pos, end))
return WLAN_STATUS_SUCCESS;
epos = *pos;
epos++; /* skip IE type */
len = *epos++; /* IE length */
if (len > end - epos || len < 1)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
epos++; /* skip ext ID */
len--;
wpabuf_free(sae->tmp->peer_rejected_groups);
sae->tmp->peer_rejected_groups = wpabuf_alloc(len);
if (!sae->tmp->peer_rejected_groups)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
wpabuf_put_data(sae->tmp->peer_rejected_groups, epos, len);
wpa_hexdump_buf(MSG_DEBUG, "SAE: Received Rejected Groups list",
sae->tmp->peer_rejected_groups);
*pos = epos + len;
return WLAN_STATUS_SUCCESS;
}
u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
const u8 **token, size_t *token_len, int *allowed_groups,
int h2e)
{
const u8 *pos = data, *end = data + len;
u16 res;
/* Check Finite Cyclic Group */
if (end - pos < 2)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
if (res != WLAN_STATUS_SUCCESS)
return res;
pos += 2;
/* Optional Anti-Clogging Token */
sae_parse_commit_token(sae, &pos, end, token, token_len, h2e);
/* commit-scalar */
res = sae_parse_commit_scalar(sae, &pos, end);
if (res != WLAN_STATUS_SUCCESS)
return res;
/* commit-element */
res = sae_parse_commit_element(sae, &pos, end);
if (res != WLAN_STATUS_SUCCESS)
return res;
/* Optional Password Identifier element */
res = sae_parse_password_identifier(sae, &pos, end);
if (res != WLAN_STATUS_SUCCESS)
return res;
/* Conditional Rejected Groups element */
if (h2e) {
res = sae_parse_rejected_groups(sae, &pos, end);
if (res != WLAN_STATUS_SUCCESS)
return res;
}
/* Optional Anti-Clogging Token Container element */
if (h2e)
sae_parse_token_container(sae, pos, end, token, token_len);
/*
* Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
* the values we sent which would be evidence of a reflection attack.
*/
if (!sae->tmp->own_commit_scalar ||
crypto_bignum_cmp(sae->tmp->own_commit_scalar,
sae->peer_commit_scalar) != 0 ||
(sae->tmp->dh &&
(!sae->tmp->own_commit_element_ffc ||
crypto_bignum_cmp(sae->tmp->own_commit_element_ffc,
sae->tmp->peer_commit_element_ffc) != 0)) ||
(sae->tmp->ec &&
(!sae->tmp->own_commit_element_ecc ||
crypto_ec_point_cmp(sae->tmp->ec,
sae->tmp->own_commit_element_ecc,
sae->tmp->peer_commit_element_ecc) != 0)))
return WLAN_STATUS_SUCCESS; /* scalars/elements are different */
/*
* This is a reflection attack - return special value to trigger caller
* to silently discard the frame instead of replying with a specific
* status code.
*/
return SAE_SILENTLY_DISCARD;
}
static int sae_cn_confirm(struct sae_data *sae, const u8 *sc,
const struct crypto_bignum *scalar1,
const u8 *element1, size_t element1_len,
const struct crypto_bignum *scalar2,
const u8 *element2, size_t element2_len,
u8 *confirm)
{
const u8 *addr[5];
size_t len[5];
u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN];
/* Confirm
* CN(key, X, Y, Z, ...) =
* HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...)
* confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT,
* peer-commit-scalar, PEER-COMMIT-ELEMENT)
* verifier = CN(KCK, peer-send-confirm, peer-commit-scalar,
* PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT)
*/
if (crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1),
sae->tmp->prime_len) < 0 ||
crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2),
sae->tmp->prime_len) < 0)
return -1;
addr[0] = sc;
len[0] = 2;
addr[1] = scalar_b1;
len[1] = sae->tmp->prime_len;
addr[2] = element1;
len[2] = element1_len;
addr[3] = scalar_b2;
len[3] = sae->tmp->prime_len;
addr[4] = element2;
len[4] = element2_len;
return hkdf_extract(sae->tmp->kck_len, sae->tmp->kck, sae->tmp->kck_len,
5, addr, len, confirm);
}
static int sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
const struct crypto_bignum *scalar1,
const struct crypto_ec_point *element1,
const struct crypto_bignum *scalar2,
const struct crypto_ec_point *element2,
u8 *confirm)
{
u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN];
u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN];
if (crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1,
element_b1 + sae->tmp->prime_len) < 0 ||
crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2,
element_b2 + sae->tmp->prime_len) < 0 ||
sae_cn_confirm(sae, sc, scalar1, element_b1,
2 * sae->tmp->prime_len,
scalar2, element_b2, 2 * sae->tmp->prime_len,
confirm) < 0)
return -1;
return 0;
}
static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
const struct crypto_bignum *scalar1,
const struct crypto_bignum *element1,
const struct crypto_bignum *scalar2,
const struct crypto_bignum *element2,
u8 *confirm)
{
u8 element_b1[SAE_MAX_PRIME_LEN];
u8 element_b2[SAE_MAX_PRIME_LEN];
if (crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1),
sae->tmp->prime_len) < 0 ||
crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2),
sae->tmp->prime_len) < 0 ||
sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len,
scalar2, element_b2, sae->tmp->prime_len,
confirm) < 0)
return -1;
return 0;
}
int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
{
const u8 *sc;
size_t hash_len;
int res;
if (sae->tmp == NULL)
return -1;
hash_len = sae->tmp->kck_len;
/* Send-Confirm */
if (sae->send_confirm < 0xffff)
sae->send_confirm++;
sc = wpabuf_put(buf, 0);
wpabuf_put_le16(buf, sae->send_confirm);
if (sae->tmp->ec)
res = sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
sae->tmp->own_commit_element_ecc,
sae->peer_commit_scalar,
sae->tmp->peer_commit_element_ecc,
wpabuf_put(buf, hash_len));
else
res = sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
sae->tmp->own_commit_element_ffc,
sae->peer_commit_scalar,
sae->tmp->peer_commit_element_ffc,
wpabuf_put(buf, hash_len));
if (res)
return res;
#ifdef CONFIG_SAE_PK
if (sae_write_confirm_pk(sae, buf) < 0)
return -1;
#endif /* CONFIG_SAE_PK */
return 0;
}
int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
{
u8 verifier[SAE_MAX_HASH_LEN];
size_t hash_len;
if (!sae->tmp)
return -1;
hash_len = sae->tmp->kck_len;
if (len < 2 + hash_len) {
wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
return -1;
}
wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
if (!sae->peer_commit_scalar || !sae->tmp->own_commit_scalar) {
wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
return -1;
}
if (sae->tmp->ec) {
if (!sae->tmp->peer_commit_element_ecc ||
!sae->tmp->own_commit_element_ecc ||
sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
sae->tmp->peer_commit_element_ecc,
sae->tmp->own_commit_scalar,
sae->tmp->own_commit_element_ecc,
verifier) < 0)
return -1;
} else {
if (!sae->tmp->peer_commit_element_ffc ||
!sae->tmp->own_commit_element_ffc ||
sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
sae->tmp->peer_commit_element_ffc,
sae->tmp->own_commit_scalar,
sae->tmp->own_commit_element_ffc,
verifier) < 0)
return -1;
}
if (os_memcmp_const(verifier, data + 2, hash_len) != 0) {
wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
data + 2, hash_len);
wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier",
verifier, hash_len);
return -1;
}
#ifdef CONFIG_SAE_PK
if (sae_check_confirm_pk(sae, data + 2 + hash_len,
len - 2 - hash_len) < 0)
return -1;
#endif /* CONFIG_SAE_PK */
return 0;
}
const char * sae_state_txt(enum sae_state state)
{
switch (state) {
case SAE_NOTHING:
return "Nothing";
case SAE_COMMITTED:
return "Committed";
case SAE_CONFIRMED:
return "Confirmed";
case SAE_ACCEPTED:
return "Accepted";
}
return "?";
}
diff --git a/contrib/wpa/src/common/wpa_common.c b/contrib/wpa/src/common/wpa_common.c
index 04461516f138..b78db05a1430 100644
--- a/contrib/wpa/src/common/wpa_common.c
+++ b/contrib/wpa/src/common/wpa_common.c
@@ -1,3732 +1,3737 @@
/*
* WPA/RSN - Shared functions for supplicant and authenticator
* Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/md5.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
#include "crypto/sha384.h"
#include "crypto/sha512.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "ieee802_11_defs.h"
#include "defs.h"
#include "wpa_common.h"
static unsigned int wpa_kck_len(int akmp, size_t pmk_len)
{
switch (akmp) {
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
return 24;
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FT_FILS_SHA256:
case WPA_KEY_MGMT_FILS_SHA384:
case WPA_KEY_MGMT_FT_FILS_SHA384:
return 0;
case WPA_KEY_MGMT_DPP:
return pmk_len / 2;
case WPA_KEY_MGMT_OWE:
return pmk_len / 2;
default:
return 16;
}
}
#ifdef CONFIG_IEEE80211R
static unsigned int wpa_kck2_len(int akmp)
{
switch (akmp) {
case WPA_KEY_MGMT_FT_FILS_SHA256:
return 16;
case WPA_KEY_MGMT_FT_FILS_SHA384:
return 24;
default:
return 0;
}
}
#endif /* CONFIG_IEEE80211R */
static unsigned int wpa_kek_len(int akmp, size_t pmk_len)
{
switch (akmp) {
case WPA_KEY_MGMT_FILS_SHA384:
case WPA_KEY_MGMT_FT_FILS_SHA384:
return 64;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FT_FILS_SHA256:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
return 32;
case WPA_KEY_MGMT_DPP:
return pmk_len <= 32 ? 16 : 32;
case WPA_KEY_MGMT_OWE:
return pmk_len <= 32 ? 16 : 32;
default:
return 16;
}
}
#ifdef CONFIG_IEEE80211R
static unsigned int wpa_kek2_len(int akmp)
{
switch (akmp) {
case WPA_KEY_MGMT_FT_FILS_SHA256:
return 16;
case WPA_KEY_MGMT_FT_FILS_SHA384:
return 32;
default:
return 0;
}
}
#endif /* CONFIG_IEEE80211R */
unsigned int wpa_mic_len(int akmp, size_t pmk_len)
{
switch (akmp) {
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
return 24;
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FILS_SHA384:
case WPA_KEY_MGMT_FT_FILS_SHA256:
case WPA_KEY_MGMT_FT_FILS_SHA384:
return 0;
case WPA_KEY_MGMT_DPP:
return pmk_len / 2;
case WPA_KEY_MGMT_OWE:
return pmk_len / 2;
default:
return 16;
}
}
/**
* wpa_use_akm_defined - Is AKM-defined Key Descriptor Version used
* @akmp: WPA_KEY_MGMT_* used in key derivation
* Returns: 1 if AKM-defined Key Descriptor Version is used; 0 otherwise
*/
int wpa_use_akm_defined(int akmp)
{
return akmp == WPA_KEY_MGMT_OSEN ||
akmp == WPA_KEY_MGMT_OWE ||
akmp == WPA_KEY_MGMT_DPP ||
akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
wpa_key_mgmt_sae(akmp) ||
wpa_key_mgmt_suite_b(akmp) ||
wpa_key_mgmt_fils(akmp);
}
/**
* wpa_use_cmac - Is CMAC integrity algorithm used for EAPOL-Key MIC
* @akmp: WPA_KEY_MGMT_* used in key derivation
* Returns: 1 if CMAC is used; 0 otherwise
*/
int wpa_use_cmac(int akmp)
{
return akmp == WPA_KEY_MGMT_OSEN ||
akmp == WPA_KEY_MGMT_OWE ||
akmp == WPA_KEY_MGMT_DPP ||
wpa_key_mgmt_ft(akmp) ||
wpa_key_mgmt_sha256(akmp) ||
wpa_key_mgmt_sae(akmp) ||
wpa_key_mgmt_suite_b(akmp);
}
/**
* wpa_use_aes_key_wrap - Is AES Keywrap algorithm used for EAPOL-Key Key Data
* @akmp: WPA_KEY_MGMT_* used in key derivation
* Returns: 1 if AES Keywrap is used; 0 otherwise
*
* Note: AKM 00-0F-AC:1 and 00-0F-AC:2 have special rules for selecting whether
* to use AES Keywrap based on the negotiated pairwise cipher. This function
* does not cover those special cases.
*/
int wpa_use_aes_key_wrap(int akmp)
{
return akmp == WPA_KEY_MGMT_OSEN ||
akmp == WPA_KEY_MGMT_OWE ||
akmp == WPA_KEY_MGMT_DPP ||
wpa_key_mgmt_ft(akmp) ||
wpa_key_mgmt_sha256(akmp) ||
wpa_key_mgmt_sae(akmp) ||
wpa_key_mgmt_suite_b(akmp);
}
/**
* wpa_eapol_key_mic - Calculate EAPOL-Key MIC
* @key: EAPOL-Key Key Confirmation Key (KCK)
* @key_len: KCK length in octets
* @akmp: WPA_KEY_MGMT_* used in key derivation
* @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
* @buf: Pointer to the beginning of the EAPOL header (version field)
* @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
* @mic: Pointer to the buffer to which the EAPOL-Key MIC is written
* Returns: 0 on success, -1 on failure
*
* Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has
* to be cleared (all zeroes) when calling this function.
*
* Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the
* description of the Key MIC calculation. It includes packet data from the
* beginning of the EAPOL-Key header, not EAPOL header. This incorrect change
* happened during final editing of the standard and the correct behavior is
* defined in the last draft (IEEE 802.11i/D10).
*/
int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
const u8 *buf, size_t len, u8 *mic)
{
u8 hash[SHA512_MAC_LEN];
if (key_len == 0) {
wpa_printf(MSG_DEBUG,
"WPA: KCK not set - cannot calculate MIC");
return -1;
}
switch (ver) {
#ifndef CONFIG_FIPS
case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-MD5");
return hmac_md5(key, key_len, buf, len, mic);
#endif /* CONFIG_FIPS */
case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-SHA1");
if (hmac_sha1(key, key_len, buf, len, hash))
return -1;
os_memcpy(mic, hash, MD5_MAC_LEN);
break;
case WPA_KEY_INFO_TYPE_AES_128_CMAC:
wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using AES-CMAC");
return omac1_aes_128(key, buf, len, mic);
case WPA_KEY_INFO_TYPE_AKM_DEFINED:
switch (akmp) {
#ifdef CONFIG_SAE
case WPA_KEY_MGMT_SAE:
case WPA_KEY_MGMT_FT_SAE:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)");
return omac1_aes_128(key, buf, len, mic);
#endif /* CONFIG_SAE */
#ifdef CONFIG_HS20
case WPA_KEY_MGMT_OSEN:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)");
return omac1_aes_128(key, buf, len, mic);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_SUITEB
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC using HMAC-SHA256 (AKM-defined - Suite B)");
if (hmac_sha256(key, key_len, buf, len, hash))
return -1;
os_memcpy(mic, hash, MD5_MAC_LEN);
break;
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_SUITEB192
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - Suite B 192-bit)");
if (hmac_sha384(key, key_len, buf, len, hash))
return -1;
os_memcpy(mic, hash, 24);
break;
#endif /* CONFIG_SUITEB192 */
#ifdef CONFIG_OWE
case WPA_KEY_MGMT_OWE:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - OWE)",
(unsigned int) key_len * 8 * 2);
if (key_len == 128 / 8) {
if (hmac_sha256(key, key_len, buf, len, hash))
return -1;
} else if (key_len == 192 / 8) {
if (hmac_sha384(key, key_len, buf, len, hash))
return -1;
} else if (key_len == 256 / 8) {
if (hmac_sha512(key, key_len, buf, len, hash))
return -1;
} else {
wpa_printf(MSG_INFO,
"OWE: Unsupported KCK length: %u",
(unsigned int) key_len);
return -1;
}
os_memcpy(mic, hash, key_len);
break;
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
case WPA_KEY_MGMT_DPP:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - DPP)",
(unsigned int) key_len * 8 * 2);
if (key_len == 128 / 8) {
if (hmac_sha256(key, key_len, buf, len, hash))
return -1;
} else if (key_len == 192 / 8) {
if (hmac_sha384(key, key_len, buf, len, hash))
return -1;
} else if (key_len == 256 / 8) {
if (hmac_sha512(key, key_len, buf, len, hash))
return -1;
} else {
wpa_printf(MSG_INFO,
"DPP: Unsupported KCK length: %u",
(unsigned int) key_len);
return -1;
}
os_memcpy(mic, hash, key_len);
break;
#endif /* CONFIG_DPP */
#if defined(CONFIG_IEEE80211R) && defined(CONFIG_SHA384)
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - FT 802.1X SHA384)");
if (hmac_sha384(key, key_len, buf, len, hash))
return -1;
os_memcpy(mic, hash, 24);
break;
#endif /* CONFIG_IEEE80211R && CONFIG_SHA384 */
default:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC algorithm not known (AKM-defined - akmp=0x%x)",
akmp);
return -1;
}
break;
default:
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC algorithm not known (ver=%d)",
ver);
return -1;
}
return 0;
}
/**
* wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces
* @pmk: Pairwise master key
* @pmk_len: Length of PMK
* @label: Label to use in derivation
* @addr1: AA or SA
* @addr2: SA or AA
* @nonce1: ANonce or SNonce
* @nonce2: SNonce or ANonce
* @ptk: Buffer for pairwise transient key
* @akmp: Negotiated AKM
* @cipher: Negotiated pairwise cipher
* @kdk_len: The length in octets that should be derived for KDK
* Returns: 0 on success, -1 on failure
*
* IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
* PTK = PRF-X(PMK, "Pairwise key expansion",
* Min(AA, SA) || Max(AA, SA) ||
* Min(ANonce, SNonce) || Max(ANonce, SNonce)
* [ || Z.x ])
*
* The optional Z.x component is used only with DPP and that part is not defined
* in IEEE 802.11.
*/
int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
const u8 *addr1, const u8 *addr2,
const u8 *nonce1, const u8 *nonce2,
struct wpa_ptk *ptk, int akmp, int cipher,
const u8 *z, size_t z_len, size_t kdk_len)
{
#define MAX_Z_LEN 66 /* with NIST P-521 */
u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN];
size_t data_len = 2 * ETH_ALEN + 2 * WPA_NONCE_LEN;
u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
WPA_KDK_MAX_LEN];
size_t ptk_len;
#ifdef CONFIG_OWE
int owe_ptk_workaround = 0;
if (akmp == (WPA_KEY_MGMT_OWE | WPA_KEY_MGMT_PSK_SHA256)) {
owe_ptk_workaround = 1;
akmp = WPA_KEY_MGMT_OWE;
}
#endif /* CONFIG_OWE */
if (pmk_len == 0) {
wpa_printf(MSG_ERROR, "WPA: No PMK set for PTK derivation");
return -1;
}
if (z_len > MAX_Z_LEN)
return -1;
if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
os_memcpy(data, addr1, ETH_ALEN);
os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
} else {
os_memcpy(data, addr2, ETH_ALEN);
os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN);
}
if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) {
os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN);
os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2,
WPA_NONCE_LEN);
} else {
os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN);
os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1,
WPA_NONCE_LEN);
}
if (z && z_len) {
os_memcpy(data + 2 * ETH_ALEN + 2 * WPA_NONCE_LEN, z, z_len);
data_len += z_len;
}
if (kdk_len > WPA_KDK_MAX_LEN) {
wpa_printf(MSG_ERROR,
"WPA: KDK len=%zu exceeds max supported len",
kdk_len);
return -1;
}
ptk->kck_len = wpa_kck_len(akmp, pmk_len);
ptk->kek_len = wpa_kek_len(akmp, pmk_len);
ptk->tk_len = wpa_cipher_key_len(cipher);
ptk->kdk_len = kdk_len;
if (ptk->tk_len == 0) {
wpa_printf(MSG_ERROR,
"WPA: Unsupported cipher (0x%x) used in PTK derivation",
cipher);
return -1;
}
ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + ptk->kdk_len;
if (wpa_key_mgmt_sha384(akmp)) {
#if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS)
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
#else /* CONFIG_SUITEB192 || CONFIG_FILS */
return -1;
#endif /* CONFIG_SUITEB192 || CONFIG_FILS */
} else if (wpa_key_mgmt_sha256(akmp)) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
#ifdef CONFIG_OWE
} else if (akmp == WPA_KEY_MGMT_OWE && (pmk_len == 32 ||
owe_ptk_workaround)) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_OWE && pmk_len == 48) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_OWE && pmk_len == 64) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
if (sha512_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_OWE) {
wpa_printf(MSG_INFO, "OWE: Unknown PMK length %u",
(unsigned int) pmk_len);
return -1;
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
if (sha512_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_DPP) {
wpa_printf(MSG_INFO, "DPP: Unknown PMK length %u",
(unsigned int) pmk_len);
return -1;
#endif /* CONFIG_DPP */
} else {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)");
if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp,
ptk_len) < 0)
return -1;
}
wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
MAC2STR(addr1), MAC2STR(addr2));
wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
if (z && z_len)
wpa_hexdump_key(MSG_DEBUG, "WPA: Z.x", z, z_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
os_memcpy(ptk->kck, tmp, ptk->kck_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len);
os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len);
if (kdk_len) {
os_memcpy(ptk->kdk, tmp + ptk->kck_len + ptk->kek_len +
ptk->tk_len, ptk->kdk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: KDK", ptk->kdk, ptk->kdk_len);
}
ptk->kek2_len = 0;
ptk->kck2_len = 0;
os_memset(tmp, 0, sizeof(tmp));
os_memset(data, 0, data_len);
return 0;
}
#ifdef CONFIG_FILS
int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
size_t dh_ss_len, u8 *pmk, size_t *pmk_len)
{
u8 nonces[2 * FILS_NONCE_LEN];
const u8 *addr[2];
size_t len[2];
size_t num_elem;
int res;
/* PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ]) */
wpa_printf(MSG_DEBUG, "FILS: rMSK to PMK derivation");
if (wpa_key_mgmt_sha384(akmp))
*pmk_len = SHA384_MAC_LEN;
else if (wpa_key_mgmt_sha256(akmp))
*pmk_len = SHA256_MAC_LEN;
else
return -1;
wpa_hexdump_key(MSG_DEBUG, "FILS: rMSK", rmsk, rmsk_len);
wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: DHss", dh_ss, dh_ss_len);
os_memcpy(nonces, snonce, FILS_NONCE_LEN);
os_memcpy(&nonces[FILS_NONCE_LEN], anonce, FILS_NONCE_LEN);
addr[0] = rmsk;
len[0] = rmsk_len;
num_elem = 1;
if (dh_ss) {
addr[1] = dh_ss;
len[1] = dh_ss_len;
num_elem++;
}
if (wpa_key_mgmt_sha384(akmp))
res = hmac_sha384_vector(nonces, 2 * FILS_NONCE_LEN, num_elem,
addr, len, pmk);
else
res = hmac_sha256_vector(nonces, 2 * FILS_NONCE_LEN, num_elem,
addr, len, pmk);
if (res == 0)
wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len);
else
*pmk_len = 0;
return res;
}
int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
u8 *pmkid)
{
const u8 *addr[1];
size_t len[1];
u8 hash[SHA384_MAC_LEN];
int res;
/* PMKID = Truncate-128(Hash(EAP-Initiate/Reauth)) */
addr[0] = reauth;
len[0] = reauth_len;
if (wpa_key_mgmt_sha384(akmp))
res = sha384_vector(1, addr, len, hash);
else if (wpa_key_mgmt_sha256(akmp))
res = sha256_vector(1, addr, len, hash);
else
return -1;
if (res)
return res;
os_memcpy(pmkid, hash, PMKID_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
return 0;
}
int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
const u8 *snonce, const u8 *anonce, const u8 *dhss,
size_t dhss_len, struct wpa_ptk *ptk,
u8 *ick, size_t *ick_len, int akmp, int cipher,
u8 *fils_ft, size_t *fils_ft_len, size_t kdk_len)
{
u8 *data, *pos;
size_t data_len;
u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
FILS_FT_MAX_LEN + WPA_KDK_MAX_LEN];
size_t key_data_len;
const char *label = "FILS PTK Derivation";
int ret = -1;
size_t offset;
/*
* FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation",
* SPA || AA || SNonce || ANonce [ || DHss ])
* ICK = L(FILS-Key-Data, 0, ICK_bits)
* KEK = L(FILS-Key-Data, ICK_bits, KEK_bits)
* TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits)
* If doing FT initial mobility domain association:
* FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits,
* FILS-FT_bits)
* When a KDK is derived:
* KDK = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits + FILS-FT_bits,
* KDK_bits)
*/
data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len;
data = os_malloc(data_len);
if (!data)
goto err;
pos = data;
os_memcpy(pos, spa, ETH_ALEN);
pos += ETH_ALEN;
os_memcpy(pos, aa, ETH_ALEN);
pos += ETH_ALEN;
os_memcpy(pos, snonce, FILS_NONCE_LEN);
pos += FILS_NONCE_LEN;
os_memcpy(pos, anonce, FILS_NONCE_LEN);
pos += FILS_NONCE_LEN;
if (dhss)
os_memcpy(pos, dhss, dhss_len);
ptk->kck_len = 0;
ptk->kek_len = wpa_kek_len(akmp, pmk_len);
ptk->tk_len = wpa_cipher_key_len(cipher);
if (wpa_key_mgmt_sha384(akmp))
*ick_len = 48;
else if (wpa_key_mgmt_sha256(akmp))
*ick_len = 32;
else
goto err;
key_data_len = *ick_len + ptk->kek_len + ptk->tk_len;
if (kdk_len) {
if (kdk_len > WPA_KDK_MAX_LEN) {
wpa_printf(MSG_ERROR, "FILS: KDK len=%zu too big",
kdk_len);
goto err;
}
ptk->kdk_len = kdk_len;
key_data_len += kdk_len;
} else {
ptk->kdk_len = 0;
}
if (fils_ft && fils_ft_len) {
if (akmp == WPA_KEY_MGMT_FT_FILS_SHA256) {
*fils_ft_len = 32;
} else if (akmp == WPA_KEY_MGMT_FT_FILS_SHA384) {
*fils_ft_len = 48;
} else {
*fils_ft_len = 0;
fils_ft = NULL;
}
key_data_len += *fils_ft_len;
}
if (wpa_key_mgmt_sha384(akmp)) {
wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA384)");
if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, key_data_len) < 0)
goto err;
} else {
wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA256)");
if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, key_data_len) < 0)
goto err;
}
wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR
" AA=" MACSTR, MAC2STR(spa), MAC2STR(aa));
wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
if (dhss)
wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len);
wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len);
wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len);
os_memcpy(ick, tmp, *ick_len);
offset = *ick_len;
wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, *ick_len);
os_memcpy(ptk->kek, tmp + offset, ptk->kek_len);
wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", ptk->kek, ptk->kek_len);
offset += ptk->kek_len;
os_memcpy(ptk->tk, tmp + offset, ptk->tk_len);
wpa_hexdump_key(MSG_DEBUG, "FILS: TK", ptk->tk, ptk->tk_len);
offset += ptk->tk_len;
if (fils_ft && fils_ft_len) {
os_memcpy(fils_ft, tmp + offset, *fils_ft_len);
wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-FT",
fils_ft, *fils_ft_len);
offset += *fils_ft_len;
}
if (ptk->kdk_len) {
os_memcpy(ptk->kdk, tmp + offset, ptk->kdk_len);
wpa_hexdump_key(MSG_DEBUG, "FILS: KDK", ptk->kdk, ptk->kdk_len);
}
ptk->kek2_len = 0;
ptk->kck2_len = 0;
os_memset(tmp, 0, sizeof(tmp));
ret = 0;
err:
bin_clear_free(data, data_len);
return ret;
}
int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
const u8 *g_sta, size_t g_sta_len,
const u8 *g_ap, size_t g_ap_len,
int akmp, u8 *key_auth_sta, u8 *key_auth_ap,
size_t *key_auth_len)
{
const u8 *addr[6];
size_t len[6];
size_t num_elem = 4;
int res;
wpa_printf(MSG_DEBUG, "FILS: Key-Auth derivation: STA-MAC=" MACSTR
" AP-BSSID=" MACSTR, MAC2STR(sta_addr), MAC2STR(bssid));
wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, ick_len);
wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: gSTA", g_sta, g_sta_len);
wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
/*
* For (Re)Association Request frame (STA->AP):
* Key-Auth = HMAC-Hash(ICK, SNonce || ANonce || STA-MAC || AP-BSSID
* [ || gSTA || gAP ])
*/
addr[0] = snonce;
len[0] = FILS_NONCE_LEN;
addr[1] = anonce;
len[1] = FILS_NONCE_LEN;
addr[2] = sta_addr;
len[2] = ETH_ALEN;
addr[3] = bssid;
len[3] = ETH_ALEN;
if (g_sta && g_sta_len && g_ap && g_ap_len) {
addr[4] = g_sta;
len[4] = g_sta_len;
addr[5] = g_ap;
len[5] = g_ap_len;
num_elem = 6;
}
if (wpa_key_mgmt_sha384(akmp)) {
*key_auth_len = 48;
res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len,
key_auth_sta);
} else if (wpa_key_mgmt_sha256(akmp)) {
*key_auth_len = 32;
res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len,
key_auth_sta);
} else {
return -1;
}
if (res < 0)
return res;
/*
* For (Re)Association Response frame (AP->STA):
* Key-Auth = HMAC-Hash(ICK, ANonce || SNonce || AP-BSSID || STA-MAC
* [ || gAP || gSTA ])
*/
addr[0] = anonce;
addr[1] = snonce;
addr[2] = bssid;
addr[3] = sta_addr;
if (g_sta && g_sta_len && g_ap && g_ap_len) {
addr[4] = g_ap;
len[4] = g_ap_len;
addr[5] = g_sta;
len[5] = g_sta_len;
}
if (wpa_key_mgmt_sha384(akmp))
res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len,
key_auth_ap);
else if (wpa_key_mgmt_sha256(akmp))
res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len,
key_auth_ap);
if (res < 0)
return res;
wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (STA)",
key_auth_sta, *key_auth_len);
wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (AP)",
key_auth_ap, *key_auth_len);
return 0;
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
const u8 *ap_addr, u8 transaction_seqnum,
const u8 *mdie, size_t mdie_len,
const u8 *ftie, size_t ftie_len,
const u8 *rsnie, size_t rsnie_len,
const u8 *ric, size_t ric_len,
const u8 *rsnxe, size_t rsnxe_len,
u8 *mic)
{
const u8 *addr[10];
size_t len[10];
size_t i, num_elem = 0;
u8 zero_mic[24];
size_t mic_len, fte_fixed_len;
if (kck_len == 16) {
mic_len = 16;
#ifdef CONFIG_SHA384
} else if (kck_len == 24) {
mic_len = 24;
#endif /* CONFIG_SHA384 */
} else {
wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
(unsigned int) kck_len);
return -1;
}
fte_fixed_len = sizeof(struct rsn_ftie) - 16 + mic_len;
addr[num_elem] = sta_addr;
len[num_elem] = ETH_ALEN;
num_elem++;
addr[num_elem] = ap_addr;
len[num_elem] = ETH_ALEN;
num_elem++;
addr[num_elem] = &transaction_seqnum;
len[num_elem] = 1;
num_elem++;
if (rsnie) {
addr[num_elem] = rsnie;
len[num_elem] = rsnie_len;
num_elem++;
}
if (mdie) {
addr[num_elem] = mdie;
len[num_elem] = mdie_len;
num_elem++;
}
if (ftie) {
if (ftie_len < 2 + fte_fixed_len)
return -1;
/* IE hdr and mic_control */
addr[num_elem] = ftie;
len[num_elem] = 2 + 2;
num_elem++;
/* MIC field with all zeros */
os_memset(zero_mic, 0, mic_len);
addr[num_elem] = zero_mic;
len[num_elem] = mic_len;
num_elem++;
/* Rest of FTIE */
addr[num_elem] = ftie + 2 + 2 + mic_len;
len[num_elem] = ftie_len - (2 + 2 + mic_len);
num_elem++;
}
if (ric) {
addr[num_elem] = ric;
len[num_elem] = ric_len;
num_elem++;
}
if (rsnxe) {
addr[num_elem] = rsnxe;
len[num_elem] = rsnxe_len;
num_elem++;
}
for (i = 0; i < num_elem; i++)
wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
#ifdef CONFIG_SHA384
if (kck_len == 24) {
u8 hash[SHA384_MAC_LEN];
if (hmac_sha384_vector(kck, kck_len, num_elem, addr, len, hash))
return -1;
os_memcpy(mic, hash, 24);
}
#endif /* CONFIG_SHA384 */
if (kck_len == 16 &&
omac1_aes_128_vector(kck, num_elem, addr, len, mic))
return -1;
return 0;
}
static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
struct wpa_ft_ies *parse, int use_sha384)
{
const u8 *end, *pos;
parse->ftie = ie;
parse->ftie_len = ie_len;
pos = ie + (use_sha384 ? sizeof(struct rsn_ftie_sha384) :
sizeof(struct rsn_ftie));
end = ie + ie_len;
wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos);
while (end - pos >= 2) {
u8 id, len;
id = *pos++;
len = *pos++;
if (len > end - pos) {
wpa_printf(MSG_DEBUG, "FT: Truncated subelement");
break;
}
switch (id) {
case FTIE_SUBELEM_R1KH_ID:
if (len != FT_R1KH_ID_LEN) {
wpa_printf(MSG_DEBUG,
"FT: Invalid R1KH-ID length in FTIE: %d",
len);
return -1;
}
parse->r1kh_id = pos;
break;
case FTIE_SUBELEM_GTK:
parse->gtk = pos;
parse->gtk_len = len;
break;
case FTIE_SUBELEM_R0KH_ID:
if (len < 1 || len > FT_R0KH_ID_MAX_LEN) {
wpa_printf(MSG_DEBUG,
"FT: Invalid R0KH-ID length in FTIE: %d",
len);
return -1;
}
parse->r0kh_id = pos;
parse->r0kh_id_len = len;
break;
case FTIE_SUBELEM_IGTK:
parse->igtk = pos;
parse->igtk_len = len;
break;
#ifdef CONFIG_OCV
case FTIE_SUBELEM_OCI:
parse->oci = pos;
parse->oci_len = len;
break;
#endif /* CONFIG_OCV */
case FTIE_SUBELEM_BIGTK:
parse->bigtk = pos;
parse->bigtk_len = len;
break;
default:
wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id);
break;
}
pos += len;
}
return 0;
}
int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
struct wpa_ft_ies *parse, int use_sha384)
{
const u8 *end, *pos;
struct wpa_ie_data data;
int ret;
const struct rsn_ftie *ftie;
int prot_ie_count = 0;
int update_use_sha384 = 0;
if (use_sha384 < 0) {
use_sha384 = 0;
update_use_sha384 = 1;
}
os_memset(parse, 0, sizeof(*parse));
if (ies == NULL)
return 0;
pos = ies;
end = ies + ies_len;
while (end - pos >= 2) {
u8 id, len;
id = *pos++;
len = *pos++;
if (len > end - pos)
break;
switch (id) {
case WLAN_EID_RSN:
wpa_hexdump(MSG_DEBUG, "FT: RSNE", pos, len);
parse->rsn = pos;
parse->rsn_len = len;
ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
parse->rsn_len + 2,
&data);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "FT: Failed to parse "
"RSN IE: %d", ret);
return -1;
}
parse->rsn_capab = data.capabilities;
if (data.num_pmkid == 1 && data.pmkid)
parse->rsn_pmkid = data.pmkid;
parse->key_mgmt = data.key_mgmt;
parse->pairwise_cipher = data.pairwise_cipher;
if (update_use_sha384) {
use_sha384 =
wpa_key_mgmt_sha384(parse->key_mgmt);
update_use_sha384 = 0;
}
break;
case WLAN_EID_RSNX:
wpa_hexdump(MSG_DEBUG, "FT: RSNXE", pos, len);
if (len < 1)
break;
parse->rsnxe = pos;
parse->rsnxe_len = len;
break;
case WLAN_EID_MOBILITY_DOMAIN:
wpa_hexdump(MSG_DEBUG, "FT: MDE", pos, len);
if (len < sizeof(struct rsn_mdie))
return -1;
parse->mdie = pos;
parse->mdie_len = len;
break;
case WLAN_EID_FAST_BSS_TRANSITION:
wpa_hexdump(MSG_DEBUG, "FT: FTE", pos, len);
if (use_sha384) {
const struct rsn_ftie_sha384 *ftie_sha384;
if (len < sizeof(*ftie_sha384))
return -1;
ftie_sha384 =
(const struct rsn_ftie_sha384 *) pos;
wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
ftie_sha384->mic_control, 2);
wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
ftie_sha384->mic,
sizeof(ftie_sha384->mic));
parse->fte_anonce = ftie_sha384->anonce;
wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
ftie_sha384->anonce,
WPA_NONCE_LEN);
parse->fte_snonce = ftie_sha384->snonce;
wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
ftie_sha384->snonce,
WPA_NONCE_LEN);
prot_ie_count = ftie_sha384->mic_control[1];
if (wpa_ft_parse_ftie(pos, len, parse, 1) < 0)
return -1;
break;
}
if (len < sizeof(*ftie))
return -1;
ftie = (const struct rsn_ftie *) pos;
wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control",
ftie->mic_control, 2);
wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
ftie->mic, sizeof(ftie->mic));
parse->fte_anonce = ftie->anonce;
wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
ftie->anonce, WPA_NONCE_LEN);
parse->fte_snonce = ftie->snonce;
wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
ftie->snonce, WPA_NONCE_LEN);
prot_ie_count = ftie->mic_control[1];
if (wpa_ft_parse_ftie(pos, len, parse, 0) < 0)
return -1;
break;
case WLAN_EID_TIMEOUT_INTERVAL:
wpa_hexdump(MSG_DEBUG, "FT: Timeout Interval",
pos, len);
if (len != 5)
break;
parse->tie = pos;
parse->tie_len = len;
break;
case WLAN_EID_RIC_DATA:
if (parse->ric == NULL)
parse->ric = pos - 2;
break;
}
pos += len;
}
if (prot_ie_count == 0)
return 0; /* no MIC */
/*
* Check that the protected IE count matches with IEs included in the
* frame.
*/
if (parse->rsn)
prot_ie_count--;
if (parse->mdie)
prot_ie_count--;
if (parse->ftie)
prot_ie_count--;
if (parse->rsnxe)
prot_ie_count--;
if (prot_ie_count < 0) {
wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
"the protected IE count");
return -1;
}
if (prot_ie_count == 0 && parse->ric) {
wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
"included in protected IE count");
return -1;
}
/* Determine the end of the RIC IE(s) */
if (parse->ric) {
pos = parse->ric;
while (end - pos >= 2 && 2 + pos[1] <= end - pos &&
prot_ie_count) {
prot_ie_count--;
pos += 2 + pos[1];
}
parse->ric_len = pos - parse->ric;
}
if (prot_ie_count) {
wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
"frame", (int) prot_ie_count);
return -1;
}
return 0;
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_PASN
/*
* pasn_use_sha384 - Should SHA384 be used or SHA256
*
* @akmp: Authentication and key management protocol
* @cipher: The cipher suite
*
* According to IEEE P802.11az/D2.7, 12.12.7, the hash algorithm to use is the
* hash algorithm defined for the Base AKM (see Table 9-151 (AKM suite
* selectors)). When there is no Base AKM, the hash algorithm is selected based
* on the pairwise cipher suite provided in the RSNE by the AP in the second
* PASN frame. SHA-256 is used as the hash algorithm, except for the ciphers
* 00-0F-AC:9 and 00-0F-AC:10 for which SHA-384 is used.
*/
static bool pasn_use_sha384(int akmp, int cipher)
{
return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_CCMP_256 ||
cipher == WPA_CIPHER_GCMP_256)) ||
wpa_key_mgmt_sha384(akmp);
}
/**
* pasn_pmk_to_ptk - Calculate PASN PTK from PMK, addresses, etc.
* @pmk: Pairwise master key
* @pmk_len: Length of PMK
* @spa: Suppplicant address
* @bssid: AP BSSID
* @dhss: Is the shared secret (DHss) derived from the PASN ephemeral key
* exchange encoded as an octet string
* @dhss_len: The length of dhss in octets
* @ptk: Buffer for pairwise transient key
* @akmp: Negotiated AKM
* @cipher: Negotiated pairwise cipher
* @kdk_len: the length in octets that should be derived for HTLK. Can be zero.
* Returns: 0 on success, -1 on failure
*/
int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
const u8 *spa, const u8 *bssid,
const u8 *dhss, size_t dhss_len,
struct wpa_ptk *ptk, int akmp, int cipher,
size_t kdk_len)
{
u8 tmp[WPA_KCK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN];
u8 *data;
size_t data_len, ptk_len;
int ret = -1;
const char *label = "PASN PTK Derivation";
if (!pmk || !pmk_len) {
wpa_printf(MSG_ERROR, "PASN: No PMK set for PTK derivation");
return -1;
}
if (!dhss || !dhss_len) {
wpa_printf(MSG_ERROR, "PASN: No DHss set for PTK derivation");
return -1;
}
/*
* PASN-PTK = KDF(PMK, “PASN PTK Derivation”, SPA || BSSID || DHss)
*
* KCK = L(PASN-PTK, 0, 256)
* TK = L(PASN-PTK, 256, TK_bits)
* KDK = L(PASN-PTK, 256 + TK_bits, kdk_len * 8)
*/
data_len = 2 * ETH_ALEN + dhss_len;
data = os_zalloc(data_len);
if (!data)
return -1;
os_memcpy(data, spa, ETH_ALEN);
os_memcpy(data + ETH_ALEN, bssid, ETH_ALEN);
os_memcpy(data + 2 * ETH_ALEN, dhss, dhss_len);
ptk->kck_len = WPA_PASN_KCK_LEN;
ptk->tk_len = wpa_cipher_key_len(cipher);
ptk->kdk_len = kdk_len;
ptk->kek_len = 0;
ptk->kek2_len = 0;
ptk->kck2_len = 0;
if (ptk->tk_len == 0) {
wpa_printf(MSG_ERROR,
"PASN: Unsupported cipher (0x%x) used in PTK derivation",
cipher);
goto err;
}
ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len;
if (ptk_len > sizeof(tmp))
goto err;
if (pasn_use_sha384(akmp, cipher)) {
wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA384");
if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp,
ptk_len) < 0)
goto err;
} else {
wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA256");
if (sha256_prf(pmk, pmk_len, label, data, data_len, tmp,
ptk_len) < 0)
goto err;
}
wpa_printf(MSG_DEBUG,
"PASN: PTK derivation: SPA=" MACSTR " BSSID=" MACSTR,
MAC2STR(spa), MAC2STR(bssid));
wpa_hexdump_key(MSG_DEBUG, "PASN: DHss", dhss, dhss_len);
wpa_hexdump_key(MSG_DEBUG, "PASN: PMK", pmk, pmk_len);
wpa_hexdump_key(MSG_DEBUG, "PASN: PASN-PTK", tmp, ptk_len);
os_memcpy(ptk->kck, tmp, WPA_PASN_KCK_LEN);
wpa_hexdump_key(MSG_DEBUG, "PASN: KCK:", ptk->kck, WPA_PASN_KCK_LEN);
os_memcpy(ptk->tk, tmp + WPA_PASN_KCK_LEN, ptk->tk_len);
wpa_hexdump_key(MSG_DEBUG, "PASN: TK:", ptk->tk, ptk->tk_len);
if (kdk_len) {
os_memcpy(ptk->kdk, tmp + WPA_PASN_KCK_LEN + ptk->tk_len,
ptk->kdk_len);
wpa_hexdump_key(MSG_DEBUG, "PASN: KDK:",
ptk->kdk, ptk->kdk_len);
}
forced_memzero(tmp, sizeof(tmp));
ret = 0;
err:
bin_clear_free(data, data_len);
return ret;
}
/*
* pasn_mic_len - Returns the MIC length for PASN authentication
*/
u8 pasn_mic_len(int akmp, int cipher)
{
if (pasn_use_sha384(akmp, cipher))
return 24;
return 16;
}
/**
* pasn_mic - Calculate PASN MIC
* @kck: The key confirmation key for the PASN PTKSA
* @akmp: Negotiated AKM
* @cipher: Negotiated pairwise cipher
* @addr1: For the 2nd PASN frame supplicant address; for the 3rd frame the
* BSSID
* @addr2: For the 2nd PASN frame the BSSID; for the 3rd frame the supplicant
* address
* @data: For calculating the MIC for the 2nd PASN frame, this should hold the
* Beacon frame RSNE + RSNXE. For calculating the MIC for the 3rd PASN
* frame, this should hold the hash of the body of the PASN 1st frame.
* @data_len: The length of data
* @frame: The body of the PASN frame including the MIC element with the octets
* in the MIC field of the MIC element set to 0.
* @frame_len: The length of frame
* @mic: Buffer to hold the MIC on success. Should be big enough to handle the
* maximal MIC length
* Returns: 0 on success, -1 on failure
*/
int pasn_mic(const u8 *kck, int akmp, int cipher,
const u8 *addr1, const u8 *addr2,
const u8 *data, size_t data_len,
const u8 *frame, size_t frame_len, u8 *mic)
{
u8 *buf;
u8 hash[SHA384_MAC_LEN];
size_t buf_len = 2 * ETH_ALEN + data_len + frame_len;
int ret = -1;
if (!kck) {
wpa_printf(MSG_ERROR, "PASN: No KCK for MIC calculation");
return -1;
}
if (!data || !data_len) {
wpa_printf(MSG_ERROR, "PASN: invalid data for MIC calculation");
return -1;
}
if (!frame || !frame_len) {
wpa_printf(MSG_ERROR, "PASN: invalid data for MIC calculation");
return -1;
}
buf = os_zalloc(buf_len);
if (!buf)
return -1;
os_memcpy(buf, addr1, ETH_ALEN);
os_memcpy(buf + ETH_ALEN, addr2, ETH_ALEN);
wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: data", data, data_len);
os_memcpy(buf + 2 * ETH_ALEN, data, data_len);
wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: frame", frame, frame_len);
os_memcpy(buf + 2 * ETH_ALEN + data_len, frame, frame_len);
wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: KCK", kck, WPA_PASN_KCK_LEN);
wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: buf", buf, buf_len);
if (pasn_use_sha384(akmp, cipher)) {
wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA384");
if (hmac_sha384(kck, WPA_PASN_KCK_LEN, buf, buf_len, hash))
goto err;
os_memcpy(mic, hash, 24);
wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: mic: ", mic, 24);
} else {
wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA256");
if (hmac_sha256(kck, WPA_PASN_KCK_LEN, buf, buf_len, hash))
goto err;
os_memcpy(mic, hash, 16);
wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: mic: ", mic, 16);
}
ret = 0;
err:
bin_clear_free(buf, buf_len);
return ret;
}
/**
* pasn_auth_frame_hash - Computes a hash of an Authentication frame body
* @akmp: Negotiated AKM
* @cipher: Negotiated pairwise cipher
* @data: Pointer to the Authentication frame body
* @len: Length of the Authentication frame body
* @hash: On return would hold the computed hash. Should be big enough to handle
* SHA384.
* Returns: 0 on success, -1 on failure
*/
int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len,
u8 *hash)
{
if (pasn_use_sha384(akmp, cipher)) {
wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-384");
return sha384_vector(1, &data, &len, hash);
} else {
wpa_printf(MSG_DEBUG, "PASN: Frame hash using SHA-256");
return sha256_vector(1, &data, &len, hash);
}
}
#endif /* CONFIG_PASN */
static int rsn_selector_to_bitfield(const u8 *s)
{
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE)
return WPA_CIPHER_NONE;
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP)
return WPA_CIPHER_TKIP;
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP)
return WPA_CIPHER_CCMP;
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
return WPA_CIPHER_AES_128_CMAC;
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP)
return WPA_CIPHER_GCMP;
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256)
return WPA_CIPHER_CCMP_256;
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256)
return WPA_CIPHER_GCMP_256;
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128)
return WPA_CIPHER_BIP_GMAC_128;
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256)
return WPA_CIPHER_BIP_GMAC_256;
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256)
return WPA_CIPHER_BIP_CMAC_256;
if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED)
return WPA_CIPHER_GTK_NOT_USED;
return 0;
}
static int rsn_key_mgmt_to_bitfield(const u8 *s)
{
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X)
return WPA_KEY_MGMT_IEEE8021X;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X)
return WPA_KEY_MGMT_PSK;
#ifdef CONFIG_IEEE80211R
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X)
return WPA_KEY_MGMT_FT_IEEE8021X;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
return WPA_KEY_MGMT_FT_PSK;
#ifdef CONFIG_SHA384
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384)
return WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
return WPA_KEY_MGMT_IEEE8021X_SHA256;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
return WPA_KEY_MGMT_PSK_SHA256;
#ifdef CONFIG_SAE
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE)
return WPA_KEY_MGMT_SAE;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE)
return WPA_KEY_MGMT_FT_SAE;
#endif /* CONFIG_SAE */
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B)
return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA256)
return WPA_KEY_MGMT_FILS_SHA256;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA384)
return WPA_KEY_MGMT_FILS_SHA384;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA256)
return WPA_KEY_MGMT_FT_FILS_SHA256;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384)
return WPA_KEY_MGMT_FT_FILS_SHA384;
#ifdef CONFIG_OWE
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OWE)
return WPA_KEY_MGMT_OWE;
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_DPP)
return WPA_KEY_MGMT_DPP;
#endif /* CONFIG_DPP */
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
return WPA_KEY_MGMT_OSEN;
#ifdef CONFIG_PASN
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PASN)
return WPA_KEY_MGMT_PASN;
#endif /* CONFIG_PASN */
return 0;
}
int wpa_cipher_valid_group(int cipher)
{
return wpa_cipher_valid_pairwise(cipher) ||
cipher == WPA_CIPHER_GTK_NOT_USED;
}
int wpa_cipher_valid_mgmt_group(int cipher)
{
return cipher == WPA_CIPHER_GTK_NOT_USED ||
cipher == WPA_CIPHER_AES_128_CMAC ||
cipher == WPA_CIPHER_BIP_GMAC_128 ||
cipher == WPA_CIPHER_BIP_GMAC_256 ||
cipher == WPA_CIPHER_BIP_CMAC_256;
}
/**
* wpa_parse_wpa_ie_rsn - Parse RSN IE
* @rsn_ie: Buffer containing RSN IE
* @rsn_ie_len: RSN IE buffer length (including IE number and length octets)
* @data: Pointer to structure that will be filled in with parsed data
* Returns: 0 on success, <0 on failure
*/
int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
struct wpa_ie_data *data)
{
const u8 *pos;
int left;
int i, count;
os_memset(data, 0, sizeof(*data));
data->proto = WPA_PROTO_RSN;
data->pairwise_cipher = WPA_CIPHER_CCMP;
data->group_cipher = WPA_CIPHER_CCMP;
data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
data->capabilities = 0;
data->pmkid = NULL;
data->num_pmkid = 0;
data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
if (rsn_ie_len == 0) {
/* No RSN IE - fail silently */
return -1;
}
if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) {
wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
__func__, (unsigned long) rsn_ie_len);
return -1;
}
if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 &&
rsn_ie[1] == rsn_ie_len - 2 &&
WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) {
pos = rsn_ie + 6;
left = rsn_ie_len - 6;
data->group_cipher = WPA_CIPHER_GTK_NOT_USED;
data->has_group = 1;
data->key_mgmt = WPA_KEY_MGMT_OSEN;
data->proto = WPA_PROTO_OSEN;
} else {
const struct rsn_ie_hdr *hdr;
hdr = (const struct rsn_ie_hdr *) rsn_ie;
if (hdr->elem_id != WLAN_EID_RSN ||
hdr->len != rsn_ie_len - 2 ||
WPA_GET_LE16(hdr->version) != RSN_VERSION) {
wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
__func__);
return -2;
}
pos = (const u8 *) (hdr + 1);
left = rsn_ie_len - sizeof(*hdr);
}
if (left >= RSN_SELECTOR_LEN) {
data->group_cipher = rsn_selector_to_bitfield(pos);
data->has_group = 1;
if (!wpa_cipher_valid_group(data->group_cipher)) {
wpa_printf(MSG_DEBUG,
"%s: invalid group cipher 0x%x (%08x)",
__func__, data->group_cipher,
WPA_GET_BE32(pos));
return -1;
}
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
} else if (left > 0) {
wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
__func__, left);
return -3;
}
if (left >= 2) {
data->pairwise_cipher = 0;
count = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
if (count == 0 || count > left / RSN_SELECTOR_LEN) {
wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
"count %u left %u", __func__, count, left);
return -4;
}
if (count)
data->has_pairwise = 1;
for (i = 0; i < count; i++) {
data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
}
if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) {
wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as "
"pairwise cipher", __func__);
return -1;
}
} else if (left == 1) {
wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
__func__);
return -5;
}
if (left >= 2) {
data->key_mgmt = 0;
count = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
if (count == 0 || count > left / RSN_SELECTOR_LEN) {
wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
"count %u left %u", __func__, count, left);
return -6;
}
for (i = 0; i < count; i++) {
data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
}
} else if (left == 1) {
wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
__func__);
return -7;
}
if (left >= 2) {
data->capabilities = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
}
if (left >= 2) {
u16 num_pmkid = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
if (num_pmkid > (unsigned int) left / PMKID_LEN) {
wpa_printf(MSG_DEBUG, "%s: PMKID underflow "
"(num_pmkid=%u left=%d)",
__func__, num_pmkid, left);
data->num_pmkid = 0;
return -9;
} else {
data->num_pmkid = num_pmkid;
data->pmkid = pos;
pos += data->num_pmkid * PMKID_LEN;
left -= data->num_pmkid * PMKID_LEN;
}
}
if (left >= 4) {
data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) {
wpa_printf(MSG_DEBUG,
"%s: Unsupported management group cipher 0x%x (%08x)",
__func__, data->mgmt_group_cipher,
WPA_GET_BE32(pos));
return -10;
}
pos += RSN_SELECTOR_LEN;
left -= RSN_SELECTOR_LEN;
}
if (left > 0) {
wpa_hexdump(MSG_DEBUG,
"wpa_parse_wpa_ie_rsn: ignore trailing bytes",
pos, left);
}
return 0;
}
static int wpa_selector_to_bitfield(const u8 *s)
{
if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
return WPA_CIPHER_NONE;
if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
return WPA_CIPHER_TKIP;
if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
return WPA_CIPHER_CCMP;
return 0;
}
static int wpa_key_mgmt_to_bitfield(const u8 *s)
{
if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
return WPA_KEY_MGMT_IEEE8021X;
if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
return WPA_KEY_MGMT_PSK;
if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
return WPA_KEY_MGMT_WPA_NONE;
return 0;
}
int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ie_data *data)
{
const struct wpa_ie_hdr *hdr;
const u8 *pos;
int left;
int i, count;
os_memset(data, 0, sizeof(*data));
data->proto = WPA_PROTO_WPA;
data->pairwise_cipher = WPA_CIPHER_TKIP;
data->group_cipher = WPA_CIPHER_TKIP;
data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
data->capabilities = 0;
data->pmkid = NULL;
data->num_pmkid = 0;
data->mgmt_group_cipher = 0;
if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
__func__, (unsigned long) wpa_ie_len);
return -1;
}
hdr = (const struct wpa_ie_hdr *) wpa_ie;
if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
hdr->len != wpa_ie_len - 2 ||
RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
WPA_GET_LE16(hdr->version) != WPA_VERSION) {
wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
__func__);
return -2;
}
pos = (const u8 *) (hdr + 1);
left = wpa_ie_len - sizeof(*hdr);
if (left >= WPA_SELECTOR_LEN) {
data->group_cipher = wpa_selector_to_bitfield(pos);
pos += WPA_SELECTOR_LEN;
left -= WPA_SELECTOR_LEN;
} else if (left > 0) {
wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
__func__, left);
return -3;
}
if (left >= 2) {
data->pairwise_cipher = 0;
count = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
if (count == 0 || count > left / WPA_SELECTOR_LEN) {
wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
"count %u left %u", __func__, count, left);
return -4;
}
for (i = 0; i < count; i++) {
data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
pos += WPA_SELECTOR_LEN;
left -= WPA_SELECTOR_LEN;
}
} else if (left == 1) {
wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
__func__);
return -5;
}
if (left >= 2) {
data->key_mgmt = 0;
count = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
if (count == 0 || count > left / WPA_SELECTOR_LEN) {
wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
"count %u left %u", __func__, count, left);
return -6;
}
for (i = 0; i < count; i++) {
data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
pos += WPA_SELECTOR_LEN;
left -= WPA_SELECTOR_LEN;
}
} else if (left == 1) {
wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
__func__);
return -7;
}
if (left >= 2) {
data->capabilities = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
}
if (left > 0) {
wpa_hexdump(MSG_DEBUG,
"wpa_parse_wpa_ie_wpa: ignore trailing bytes",
pos, left);
}
return 0;
}
int wpa_default_rsn_cipher(int freq)
{
if (freq > 56160)
return WPA_CIPHER_GCMP; /* DMG */
return WPA_CIPHER_CCMP;
}
#ifdef CONFIG_IEEE80211R
/**
* wpa_derive_pmk_r0 - Derive PMK-R0 and PMKR0Name
*
* IEEE Std 802.11r-2008 - 8.5.1.5.3
*/
int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
const u8 *ssid, size_t ssid_len,
const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
int use_sha384)
{
u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
FT_R0KH_ID_MAX_LEN + ETH_ALEN];
u8 *pos, r0_key_data[64], hash[48];
const u8 *addr[2];
size_t len[2];
size_t q = use_sha384 ? 48 : 32;
size_t r0_key_data_len = q + 16;
/*
* R0-Key-Data = KDF-384(XXKey, "FT-R0",
* SSIDlength || SSID || MDID || R0KHlength ||
* R0KH-ID || S0KH-ID)
* XXKey is either the second 256 bits of MSK or PSK; or the first
* 384 bits of MSK for FT-EAP-SHA384.
* PMK-R0 = L(R0-Key-Data, 0, Q)
* PMK-R0Name-Salt = L(R0-Key-Data, Q, 128)
* Q = 384 for FT-EAP-SHA384; otherwise, 256
*/
if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
return -1;
wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-%s",
use_sha384 ? "SHA384" : "SHA256");
wpa_hexdump_key(MSG_DEBUG, "FT: XXKey", xxkey, xxkey_len);
wpa_hexdump_ascii(MSG_DEBUG, "FT: SSID", ssid, ssid_len);
wpa_hexdump(MSG_DEBUG, "FT: MDID", mdid, MOBILITY_DOMAIN_ID_LEN);
wpa_hexdump_ascii(MSG_DEBUG, "FT: R0KH-ID", r0kh_id, r0kh_id_len);
wpa_printf(MSG_DEBUG, "FT: S0KH-ID: " MACSTR, MAC2STR(s0kh_id));
pos = buf;
*pos++ = ssid_len;
os_memcpy(pos, ssid, ssid_len);
pos += ssid_len;
os_memcpy(pos, mdid, MOBILITY_DOMAIN_ID_LEN);
pos += MOBILITY_DOMAIN_ID_LEN;
*pos++ = r0kh_id_len;
os_memcpy(pos, r0kh_id, r0kh_id_len);
pos += r0kh_id_len;
os_memcpy(pos, s0kh_id, ETH_ALEN);
pos += ETH_ALEN;
#ifdef CONFIG_SHA384
if (use_sha384) {
if (xxkey_len != SHA384_MAC_LEN) {
wpa_printf(MSG_ERROR,
"FT: Unexpected XXKey length %d (expected %d)",
(int) xxkey_len, SHA384_MAC_LEN);
return -1;
}
if (sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
r0_key_data, r0_key_data_len) < 0)
return -1;
}
#endif /* CONFIG_SHA384 */
if (!use_sha384) {
if (xxkey_len != PMK_LEN) {
wpa_printf(MSG_ERROR,
"FT: Unexpected XXKey length %d (expected %d)",
(int) xxkey_len, PMK_LEN);
return -1;
}
if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
r0_key_data, r0_key_data_len) < 0)
return -1;
}
os_memcpy(pmk_r0, r0_key_data, q);
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, q);
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0Name-Salt", &r0_key_data[q], 16);
/*
* PMKR0Name = Truncate-128(Hash("FT-R0N" || PMK-R0Name-Salt)
*/
addr[0] = (const u8 *) "FT-R0N";
len[0] = 6;
addr[1] = &r0_key_data[q];
len[1] = 16;
#ifdef CONFIG_SHA384
if (use_sha384 && sha384_vector(2, addr, len, hash) < 0)
return -1;
#endif /* CONFIG_SHA384 */
if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0)
return -1;
os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
forced_memzero(r0_key_data, sizeof(r0_key_data));
return 0;
}
/**
* wpa_derive_pmk_r1_name - Derive PMKR1Name
*
* IEEE Std 802.11r-2008 - 8.5.1.5.4
*/
int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384)
{
u8 hash[48];
const u8 *addr[4];
size_t len[4];
/*
* PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name ||
* R1KH-ID || S1KH-ID))
*/
addr[0] = (const u8 *) "FT-R1N";
len[0] = 6;
addr[1] = pmk_r0_name;
len[1] = WPA_PMK_NAME_LEN;
addr[2] = r1kh_id;
len[2] = FT_R1KH_ID_LEN;
addr[3] = s1kh_id;
len[3] = ETH_ALEN;
#ifdef CONFIG_SHA384
if (use_sha384 && sha384_vector(4, addr, len, hash) < 0)
return -1;
#endif /* CONFIG_SHA384 */
if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0)
return -1;
os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
return 0;
}
/**
* wpa_derive_pmk_r1 - Derive PMK-R1 and PMKR1Name from PMK-R0
*
* IEEE Std 802.11r-2008 - 8.5.1.5.4
*/
int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len,
const u8 *pmk_r0_name,
const u8 *r1kh_id, const u8 *s1kh_id,
u8 *pmk_r1, u8 *pmk_r1_name)
{
u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
u8 *pos;
/* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-%s",
pmk_r0_len == SHA384_MAC_LEN ? "SHA384" : "SHA256");
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len);
wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN);
wpa_printf(MSG_DEBUG, "FT: S1KH-ID: " MACSTR, MAC2STR(s1kh_id));
pos = buf;
os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN);
pos += FT_R1KH_ID_LEN;
os_memcpy(pos, s1kh_id, ETH_ALEN);
pos += ETH_ALEN;
#ifdef CONFIG_SHA384
if (pmk_r0_len == SHA384_MAC_LEN &&
sha384_prf(pmk_r0, pmk_r0_len, "FT-R1",
buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
return -1;
#endif /* CONFIG_SHA384 */
if (pmk_r0_len == PMK_LEN &&
sha256_prf(pmk_r0, pmk_r0_len, "FT-R1",
buf, pos - buf, pmk_r1, pmk_r0_len) < 0)
return -1;
if (pmk_r0_len != SHA384_MAC_LEN && pmk_r0_len != PMK_LEN) {
wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R0 length %d",
(int) pmk_r0_len);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r0_len);
return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id,
pmk_r1_name,
pmk_r0_len == SHA384_MAC_LEN);
}
/**
* wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1
*
* IEEE Std 802.11r-2008 - 8.5.1.5.5
*/
int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len,
const u8 *snonce, const u8 *anonce,
const u8 *sta_addr, const u8 *bssid,
const u8 *pmk_r1_name,
struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher,
size_t kdk_len)
{
u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN];
u8 *pos, hash[32];
const u8 *addr[6];
size_t len[6];
u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
WPA_KDK_MAX_LEN];
size_t ptk_len, offset;
int use_sha384 = wpa_key_mgmt_sha384(akmp);
if (kdk_len > WPA_KDK_MAX_LEN) {
wpa_printf(MSG_ERROR,
"FT: KDK len=%zu exceeds max supported len",
kdk_len);
return -1;
}
/*
* PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
* BSSID || STA-ADDR)
*/
wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-%s",
use_sha384 ? "SHA384" : "SHA256");
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len);
wpa_hexdump(MSG_DEBUG, "FT: SNonce", snonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN);
wpa_printf(MSG_DEBUG, "FT: BSSID=" MACSTR " STA-ADDR=" MACSTR,
MAC2STR(bssid), MAC2STR(sta_addr));
pos = buf;
os_memcpy(pos, snonce, WPA_NONCE_LEN);
pos += WPA_NONCE_LEN;
os_memcpy(pos, anonce, WPA_NONCE_LEN);
pos += WPA_NONCE_LEN;
os_memcpy(pos, bssid, ETH_ALEN);
pos += ETH_ALEN;
os_memcpy(pos, sta_addr, ETH_ALEN);
pos += ETH_ALEN;
ptk->kck_len = wpa_kck_len(akmp, PMK_LEN);
ptk->kck2_len = wpa_kck2_len(akmp);
ptk->kek_len = wpa_kek_len(akmp, PMK_LEN);
ptk->kek2_len = wpa_kek2_len(akmp);
ptk->tk_len = wpa_cipher_key_len(cipher);
ptk->kdk_len = kdk_len;
ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len +
ptk->kck2_len + ptk->kek2_len + ptk->kdk_len;
#ifdef CONFIG_SHA384
if (use_sha384) {
if (pmk_r1_len != SHA384_MAC_LEN) {
wpa_printf(MSG_ERROR,
"FT: Unexpected PMK-R1 length %d (expected %d)",
(int) pmk_r1_len, SHA384_MAC_LEN);
return -1;
}
if (sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK",
buf, pos - buf, tmp, ptk_len) < 0)
return -1;
}
#endif /* CONFIG_SHA384 */
if (!use_sha384) {
if (pmk_r1_len != PMK_LEN) {
wpa_printf(MSG_ERROR,
"FT: Unexpected PMK-R1 length %d (expected %d)",
(int) pmk_r1_len, PMK_LEN);
return -1;
}
if (sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK",
buf, pos - buf, tmp, ptk_len) < 0)
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "FT: PTK", tmp, ptk_len);
/*
* PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
* ANonce || BSSID || STA-ADDR))
*/
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
addr[0] = pmk_r1_name;
len[0] = WPA_PMK_NAME_LEN;
addr[1] = (const u8 *) "FT-PTKN";
len[1] = 7;
addr[2] = snonce;
len[2] = WPA_NONCE_LEN;
addr[3] = anonce;
len[3] = WPA_NONCE_LEN;
addr[4] = bssid;
len[4] = ETH_ALEN;
addr[5] = sta_addr;
len[5] = ETH_ALEN;
if (sha256_vector(6, addr, len, hash) < 0)
return -1;
os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
os_memcpy(ptk->kck, tmp, ptk->kck_len);
offset = ptk->kck_len;
os_memcpy(ptk->kek, tmp + offset, ptk->kek_len);
offset += ptk->kek_len;
os_memcpy(ptk->tk, tmp + offset, ptk->tk_len);
offset += ptk->tk_len;
os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len);
offset += ptk->kck2_len;
os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len);
offset += ptk->kek2_len;
os_memcpy(ptk->kdk, tmp + offset, ptk->kdk_len);
wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len);
if (ptk->kck2_len)
wpa_hexdump_key(MSG_DEBUG, "FT: KCK2",
ptk->kck2, ptk->kck2_len);
if (ptk->kek2_len)
wpa_hexdump_key(MSG_DEBUG, "FT: KEK2",
ptk->kek2, ptk->kek2_len);
if (ptk->kdk_len)
wpa_hexdump_key(MSG_DEBUG, "FT: KDK", ptk->kdk, ptk->kdk_len);
wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
forced_memzero(tmp, sizeof(tmp));
return 0;
}
#endif /* CONFIG_IEEE80211R */
/**
* rsn_pmkid - Calculate PMK identifier
* @pmk: Pairwise master key
* @pmk_len: Length of pmk in bytes
* @aa: Authenticator address
* @spa: Supplicant address
* @pmkid: Buffer for PMKID
* @akmp: Negotiated key management protocol
*
* IEEE Std 802.11-2016 - 12.7.1.3 Pairwise key hierarchy
* AKM: 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16
* PMKID = Truncate-128(HMAC-SHA-256(PMK, "PMK Name" || AA || SPA))
* AKM: 00-0F-AC:11
* See rsn_pmkid_suite_b()
* AKM: 00-0F-AC:12
* See rsn_pmkid_suite_b_192()
* AKM: 00-0F-AC:13, 00-0F-AC:15, 00-0F-AC:17
* PMKID = Truncate-128(HMAC-SHA-384(PMK, "PMK Name" || AA || SPA))
* Otherwise:
* PMKID = Truncate-128(HMAC-SHA-1(PMK, "PMK Name" || AA || SPA))
*/
void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
u8 *pmkid, int akmp)
{
char *title = "PMK Name";
const u8 *addr[3];
const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
unsigned char hash[SHA384_MAC_LEN];
addr[0] = (u8 *) title;
addr[1] = aa;
addr[2] = spa;
if (0) {
#if defined(CONFIG_FILS) || defined(CONFIG_SHA384)
} else if (wpa_key_mgmt_sha384(akmp)) {
wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-384");
hmac_sha384_vector(pmk, pmk_len, 3, addr, len, hash);
#endif /* CONFIG_FILS || CONFIG_SHA384 */
} else if (wpa_key_mgmt_sha256(akmp)) {
wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-256");
hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
} else {
wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-1");
hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
}
wpa_hexdump(MSG_DEBUG, "RSN: Derived PMKID", hash, PMKID_LEN);
os_memcpy(pmkid, hash, PMKID_LEN);
}
#ifdef CONFIG_SUITEB
/**
* rsn_pmkid_suite_b - Calculate PMK identifier for Suite B AKM
* @kck: Key confirmation key
* @kck_len: Length of kck in bytes
* @aa: Authenticator address
* @spa: Supplicant address
* @pmkid: Buffer for PMKID
* Returns: 0 on success, -1 on failure
*
* IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
* PMKID = Truncate(HMAC-SHA-256(KCK, "PMK Name" || AA || SPA))
*/
int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
const u8 *spa, u8 *pmkid)
{
char *title = "PMK Name";
const u8 *addr[3];
const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
unsigned char hash[SHA256_MAC_LEN];
addr[0] = (u8 *) title;
addr[1] = aa;
addr[2] = spa;
if (hmac_sha256_vector(kck, kck_len, 3, addr, len, hash) < 0)
return -1;
os_memcpy(pmkid, hash, PMKID_LEN);
return 0;
}
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_SUITEB192
/**
* rsn_pmkid_suite_b_192 - Calculate PMK identifier for Suite B AKM
* @kck: Key confirmation key
* @kck_len: Length of kck in bytes
* @aa: Authenticator address
* @spa: Supplicant address
* @pmkid: Buffer for PMKID
* Returns: 0 on success, -1 on failure
*
* IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
* PMKID = Truncate(HMAC-SHA-384(KCK, "PMK Name" || AA || SPA))
*/
int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
const u8 *spa, u8 *pmkid)
{
char *title = "PMK Name";
const u8 *addr[3];
const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
unsigned char hash[SHA384_MAC_LEN];
addr[0] = (u8 *) title;
addr[1] = aa;
addr[2] = spa;
if (hmac_sha384_vector(kck, kck_len, 3, addr, len, hash) < 0)
return -1;
os_memcpy(pmkid, hash, PMKID_LEN);
return 0;
}
#endif /* CONFIG_SUITEB192 */
/**
* wpa_cipher_txt - Convert cipher suite to a text string
* @cipher: Cipher suite (WPA_CIPHER_* enum)
* Returns: Pointer to a text string of the cipher suite name
*/
const char * wpa_cipher_txt(int cipher)
{
switch (cipher) {
case WPA_CIPHER_NONE:
return "NONE";
#ifdef CONFIG_WEP
case WPA_CIPHER_WEP40:
return "WEP-40";
case WPA_CIPHER_WEP104:
return "WEP-104";
#endif /* CONFIG_WEP */
case WPA_CIPHER_TKIP:
return "TKIP";
case WPA_CIPHER_CCMP:
return "CCMP";
case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP:
return "CCMP+TKIP";
case WPA_CIPHER_GCMP:
return "GCMP";
case WPA_CIPHER_GCMP_256:
return "GCMP-256";
case WPA_CIPHER_CCMP_256:
return "CCMP-256";
case WPA_CIPHER_AES_128_CMAC:
return "BIP";
case WPA_CIPHER_BIP_GMAC_128:
return "BIP-GMAC-128";
case WPA_CIPHER_BIP_GMAC_256:
return "BIP-GMAC-256";
case WPA_CIPHER_BIP_CMAC_256:
return "BIP-CMAC-256";
case WPA_CIPHER_GTK_NOT_USED:
return "GTK_NOT_USED";
default:
return "UNKNOWN";
}
}
/**
* wpa_key_mgmt_txt - Convert key management suite to a text string
* @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum)
* @proto: WPA/WPA2 version (WPA_PROTO_*)
* Returns: Pointer to a text string of the key management suite name
*/
const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
{
switch (key_mgmt) {
case WPA_KEY_MGMT_IEEE8021X:
if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
return "WPA2+WPA/IEEE 802.1X/EAP";
return proto == WPA_PROTO_RSN ?
"WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP";
case WPA_KEY_MGMT_PSK:
if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
return "WPA2-PSK+WPA-PSK";
return proto == WPA_PROTO_RSN ?
"WPA2-PSK" : "WPA-PSK";
case WPA_KEY_MGMT_NONE:
return "NONE";
case WPA_KEY_MGMT_WPA_NONE:
return "WPA-NONE";
case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
return "IEEE 802.1X (no WPA)";
#ifdef CONFIG_IEEE80211R
case WPA_KEY_MGMT_FT_IEEE8021X:
return "FT-EAP";
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
return "FT-EAP-SHA384";
case WPA_KEY_MGMT_FT_PSK:
return "FT-PSK";
#endif /* CONFIG_IEEE80211R */
case WPA_KEY_MGMT_IEEE8021X_SHA256:
return "WPA2-EAP-SHA256";
case WPA_KEY_MGMT_PSK_SHA256:
return "WPA2-PSK-SHA256";
case WPA_KEY_MGMT_WPS:
return "WPS";
case WPA_KEY_MGMT_SAE:
return "SAE";
case WPA_KEY_MGMT_FT_SAE:
return "FT-SAE";
case WPA_KEY_MGMT_OSEN:
return "OSEN";
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
return "WPA2-EAP-SUITE-B";
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
return "WPA2-EAP-SUITE-B-192";
case WPA_KEY_MGMT_FILS_SHA256:
return "FILS-SHA256";
case WPA_KEY_MGMT_FILS_SHA384:
return "FILS-SHA384";
case WPA_KEY_MGMT_FT_FILS_SHA256:
return "FT-FILS-SHA256";
case WPA_KEY_MGMT_FT_FILS_SHA384:
return "FT-FILS-SHA384";
case WPA_KEY_MGMT_OWE:
return "OWE";
case WPA_KEY_MGMT_DPP:
return "DPP";
case WPA_KEY_MGMT_PASN:
return "PASN";
default:
return "UNKNOWN";
}
}
u32 wpa_akm_to_suite(int akm)
{
if (akm & WPA_KEY_MGMT_FT_IEEE8021X_SHA384)
return RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
if (akm & WPA_KEY_MGMT_FT_IEEE8021X)
return RSN_AUTH_KEY_MGMT_FT_802_1X;
if (akm & WPA_KEY_MGMT_FT_PSK)
return RSN_AUTH_KEY_MGMT_FT_PSK;
if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256)
return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
if (akm & WPA_KEY_MGMT_IEEE8021X)
return RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
if (akm & WPA_KEY_MGMT_PSK_SHA256)
return RSN_AUTH_KEY_MGMT_PSK_SHA256;
if (akm & WPA_KEY_MGMT_PSK)
return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
if (akm & WPA_KEY_MGMT_CCKM)
return RSN_AUTH_KEY_MGMT_CCKM;
if (akm & WPA_KEY_MGMT_OSEN)
return RSN_AUTH_KEY_MGMT_OSEN;
if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
if (akm & WPA_KEY_MGMT_FILS_SHA256)
return RSN_AUTH_KEY_MGMT_FILS_SHA256;
if (akm & WPA_KEY_MGMT_FILS_SHA384)
return RSN_AUTH_KEY_MGMT_FILS_SHA384;
if (akm & WPA_KEY_MGMT_FT_FILS_SHA256)
return RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
if (akm & WPA_KEY_MGMT_FT_FILS_SHA384)
return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
if (akm & WPA_KEY_MGMT_SAE)
return RSN_AUTH_KEY_MGMT_SAE;
if (akm & WPA_KEY_MGMT_FT_SAE)
return RSN_AUTH_KEY_MGMT_FT_SAE;
if (akm & WPA_KEY_MGMT_OWE)
return RSN_AUTH_KEY_MGMT_OWE;
if (akm & WPA_KEY_MGMT_DPP)
return RSN_AUTH_KEY_MGMT_DPP;
return 0;
}
int wpa_compare_rsn_ie(int ft_initial_assoc,
const u8 *ie1, size_t ie1len,
const u8 *ie2, size_t ie2len)
{
if (ie1 == NULL || ie2 == NULL)
return -1;
if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
return 0; /* identical IEs */
#ifdef CONFIG_IEEE80211R
if (ft_initial_assoc) {
struct wpa_ie_data ie1d, ie2d;
/*
* The PMKID-List in RSN IE is different between Beacon/Probe
* Response/(Re)Association Request frames and EAPOL-Key
* messages in FT initial mobility domain association. Allow
* for this, but verify that other parts of the RSN IEs are
* identical.
*/
if (wpa_parse_wpa_ie_rsn(ie1, ie1len, &ie1d) < 0 ||
wpa_parse_wpa_ie_rsn(ie2, ie2len, &ie2d) < 0)
return -1;
if (ie1d.proto == ie2d.proto &&
ie1d.pairwise_cipher == ie2d.pairwise_cipher &&
ie1d.group_cipher == ie2d.group_cipher &&
ie1d.key_mgmt == ie2d.key_mgmt &&
ie1d.capabilities == ie2d.capabilities &&
ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher)
return 0;
}
#endif /* CONFIG_IEEE80211R */
return -1;
}
int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid)
{
u8 *start, *end, *rpos, *rend;
int added = 0;
start = ies;
end = ies + *ies_len;
while (start < end) {
if (*start == WLAN_EID_RSN)
break;
start += 2 + start[1];
}
if (start >= end) {
wpa_printf(MSG_ERROR, "RSN: Could not find RSNE in IEs data");
return -1;
}
wpa_hexdump(MSG_DEBUG, "RSN: RSNE before modification",
start, 2 + start[1]);
/* Find start of PMKID-Count */
rpos = start + 2;
rend = rpos + start[1];
/* Skip Version and Group Data Cipher Suite */
rpos += 2 + 4;
/* Skip Pairwise Cipher Suite Count and List */
rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
/* Skip AKM Suite Count and List */
rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
if (rpos == rend) {
/* Add RSN Capabilities */
os_memmove(rpos + 2, rpos, end - rpos);
*rpos++ = 0;
*rpos++ = 0;
added += 2;
start[1] += 2;
rend = rpos;
} else {
/* Skip RSN Capabilities */
rpos += 2;
if (rpos > rend) {
wpa_printf(MSG_ERROR,
"RSN: Could not parse RSNE in IEs data");
return -1;
}
}
if (rpos == rend) {
/* No PMKID-Count field included; add it */
os_memmove(rpos + 2 + PMKID_LEN, rpos, end + added - rpos);
WPA_PUT_LE16(rpos, 1);
rpos += 2;
os_memcpy(rpos, pmkid, PMKID_LEN);
added += 2 + PMKID_LEN;
start[1] += 2 + PMKID_LEN;
} else {
u16 num_pmkid;
if (rend - rpos < 2)
return -1;
num_pmkid = WPA_GET_LE16(rpos);
/* PMKID-Count was included; use it */
if (num_pmkid != 0) {
u8 *after;
if (num_pmkid * PMKID_LEN > rend - rpos - 2)
return -1;
/*
* PMKID may have been included in RSN IE in
* (Re)Association Request frame, so remove the old
* PMKID(s) first before adding the new one.
*/
wpa_printf(MSG_DEBUG,
"RSN: Remove %u old PMKID(s) from RSNE",
num_pmkid);
after = rpos + 2 + num_pmkid * PMKID_LEN;
os_memmove(rpos + 2, after, end - after);
start[1] -= num_pmkid * PMKID_LEN;
added -= num_pmkid * PMKID_LEN;
}
WPA_PUT_LE16(rpos, 1);
rpos += 2;
os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos);
os_memcpy(rpos, pmkid, PMKID_LEN);
added += PMKID_LEN;
start[1] += PMKID_LEN;
}
wpa_hexdump(MSG_DEBUG, "RSN: RSNE after modification (PMKID inserted)",
start, 2 + start[1]);
*ies_len += added;
return 0;
}
int wpa_cipher_key_len(int cipher)
{
switch (cipher) {
case WPA_CIPHER_CCMP_256:
case WPA_CIPHER_GCMP_256:
case WPA_CIPHER_BIP_GMAC_256:
case WPA_CIPHER_BIP_CMAC_256:
return 32;
case WPA_CIPHER_CCMP:
case WPA_CIPHER_GCMP:
case WPA_CIPHER_AES_128_CMAC:
case WPA_CIPHER_BIP_GMAC_128:
return 16;
case WPA_CIPHER_TKIP:
return 32;
}
return 0;
}
int wpa_cipher_rsc_len(int cipher)
{
switch (cipher) {
case WPA_CIPHER_CCMP_256:
case WPA_CIPHER_GCMP_256:
case WPA_CIPHER_CCMP:
case WPA_CIPHER_GCMP:
case WPA_CIPHER_TKIP:
return 6;
}
return 0;
}
enum wpa_alg wpa_cipher_to_alg(int cipher)
{
switch (cipher) {
case WPA_CIPHER_CCMP_256:
return WPA_ALG_CCMP_256;
case WPA_CIPHER_GCMP_256:
return WPA_ALG_GCMP_256;
case WPA_CIPHER_CCMP:
return WPA_ALG_CCMP;
case WPA_CIPHER_GCMP:
return WPA_ALG_GCMP;
case WPA_CIPHER_TKIP:
return WPA_ALG_TKIP;
case WPA_CIPHER_AES_128_CMAC:
return WPA_ALG_BIP_CMAC_128;
case WPA_CIPHER_BIP_GMAC_128:
return WPA_ALG_BIP_GMAC_128;
case WPA_CIPHER_BIP_GMAC_256:
return WPA_ALG_BIP_GMAC_256;
case WPA_CIPHER_BIP_CMAC_256:
return WPA_ALG_BIP_CMAC_256;
}
return WPA_ALG_NONE;
}
int wpa_cipher_valid_pairwise(int cipher)
{
#ifdef CONFIG_NO_TKIP
return cipher == WPA_CIPHER_CCMP_256 ||
cipher == WPA_CIPHER_GCMP_256 ||
cipher == WPA_CIPHER_CCMP ||
cipher == WPA_CIPHER_GCMP;
#else /* CONFIG_NO_TKIP */
return cipher == WPA_CIPHER_CCMP_256 ||
cipher == WPA_CIPHER_GCMP_256 ||
cipher == WPA_CIPHER_CCMP ||
cipher == WPA_CIPHER_GCMP ||
cipher == WPA_CIPHER_TKIP;
#endif /* CONFIG_NO_TKIP */
}
u32 wpa_cipher_to_suite(int proto, int cipher)
{
if (cipher & WPA_CIPHER_CCMP_256)
return RSN_CIPHER_SUITE_CCMP_256;
if (cipher & WPA_CIPHER_GCMP_256)
return RSN_CIPHER_SUITE_GCMP_256;
if (cipher & WPA_CIPHER_CCMP)
return (proto == WPA_PROTO_RSN ?
RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
if (cipher & WPA_CIPHER_GCMP)
return RSN_CIPHER_SUITE_GCMP;
if (cipher & WPA_CIPHER_TKIP)
return (proto == WPA_PROTO_RSN ?
RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
if (cipher & WPA_CIPHER_NONE)
return (proto == WPA_PROTO_RSN ?
RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
if (cipher & WPA_CIPHER_GTK_NOT_USED)
return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
if (cipher & WPA_CIPHER_AES_128_CMAC)
return RSN_CIPHER_SUITE_AES_128_CMAC;
if (cipher & WPA_CIPHER_BIP_GMAC_128)
return RSN_CIPHER_SUITE_BIP_GMAC_128;
if (cipher & WPA_CIPHER_BIP_GMAC_256)
return RSN_CIPHER_SUITE_BIP_GMAC_256;
if (cipher & WPA_CIPHER_BIP_CMAC_256)
return RSN_CIPHER_SUITE_BIP_CMAC_256;
return 0;
}
int rsn_cipher_put_suites(u8 *start, int ciphers)
{
u8 *pos = start;
if (ciphers & WPA_CIPHER_CCMP_256) {
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP_256);
pos += RSN_SELECTOR_LEN;
}
if (ciphers & WPA_CIPHER_GCMP_256) {
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP_256);
pos += RSN_SELECTOR_LEN;
}
if (ciphers & WPA_CIPHER_CCMP) {
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
pos += RSN_SELECTOR_LEN;
}
if (ciphers & WPA_CIPHER_GCMP) {
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
pos += RSN_SELECTOR_LEN;
}
if (ciphers & WPA_CIPHER_TKIP) {
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
pos += RSN_SELECTOR_LEN;
}
if (ciphers & WPA_CIPHER_NONE) {
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
pos += RSN_SELECTOR_LEN;
}
return (pos - start) / RSN_SELECTOR_LEN;
}
int wpa_cipher_put_suites(u8 *start, int ciphers)
{
u8 *pos = start;
if (ciphers & WPA_CIPHER_CCMP) {
RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
pos += WPA_SELECTOR_LEN;
}
if (ciphers & WPA_CIPHER_TKIP) {
RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
pos += WPA_SELECTOR_LEN;
}
if (ciphers & WPA_CIPHER_NONE) {
RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
pos += WPA_SELECTOR_LEN;
}
return (pos - start) / RSN_SELECTOR_LEN;
}
int wpa_pick_pairwise_cipher(int ciphers, int none_allowed)
{
if (ciphers & WPA_CIPHER_CCMP_256)
return WPA_CIPHER_CCMP_256;
if (ciphers & WPA_CIPHER_GCMP_256)
return WPA_CIPHER_GCMP_256;
if (ciphers & WPA_CIPHER_CCMP)
return WPA_CIPHER_CCMP;
if (ciphers & WPA_CIPHER_GCMP)
return WPA_CIPHER_GCMP;
if (ciphers & WPA_CIPHER_TKIP)
return WPA_CIPHER_TKIP;
if (none_allowed && (ciphers & WPA_CIPHER_NONE))
return WPA_CIPHER_NONE;
return -1;
}
int wpa_pick_group_cipher(int ciphers)
{
if (ciphers & WPA_CIPHER_CCMP_256)
return WPA_CIPHER_CCMP_256;
if (ciphers & WPA_CIPHER_GCMP_256)
return WPA_CIPHER_GCMP_256;
if (ciphers & WPA_CIPHER_CCMP)
return WPA_CIPHER_CCMP;
if (ciphers & WPA_CIPHER_GCMP)
return WPA_CIPHER_GCMP;
if (ciphers & WPA_CIPHER_GTK_NOT_USED)
return WPA_CIPHER_GTK_NOT_USED;
if (ciphers & WPA_CIPHER_TKIP)
return WPA_CIPHER_TKIP;
return -1;
}
int wpa_parse_cipher(const char *value)
{
int val = 0, last;
char *start, *end, *buf;
buf = os_strdup(value);
if (buf == NULL)
return -1;
start = buf;
while (*start != '\0') {
while (*start == ' ' || *start == '\t')
start++;
if (*start == '\0')
break;
end = start;
while (*end != ' ' && *end != '\t' && *end != '\0')
end++;
last = *end == '\0';
*end = '\0';
if (os_strcmp(start, "CCMP-256") == 0)
val |= WPA_CIPHER_CCMP_256;
else if (os_strcmp(start, "GCMP-256") == 0)
val |= WPA_CIPHER_GCMP_256;
else if (os_strcmp(start, "CCMP") == 0)
val |= WPA_CIPHER_CCMP;
else if (os_strcmp(start, "GCMP") == 0)
val |= WPA_CIPHER_GCMP;
#ifndef CONFIG_NO_TKIP
else if (os_strcmp(start, "TKIP") == 0)
val |= WPA_CIPHER_TKIP;
#endif /* CONFIG_NO_TKIP */
#ifdef CONFIG_WEP
else if (os_strcmp(start, "WEP104") == 0)
val |= WPA_CIPHER_WEP104;
else if (os_strcmp(start, "WEP40") == 0)
val |= WPA_CIPHER_WEP40;
#endif /* CONFIG_WEP */
else if (os_strcmp(start, "NONE") == 0)
val |= WPA_CIPHER_NONE;
else if (os_strcmp(start, "GTK_NOT_USED") == 0)
val |= WPA_CIPHER_GTK_NOT_USED;
else if (os_strcmp(start, "AES-128-CMAC") == 0)
val |= WPA_CIPHER_AES_128_CMAC;
else if (os_strcmp(start, "BIP-GMAC-128") == 0)
val |= WPA_CIPHER_BIP_GMAC_128;
else if (os_strcmp(start, "BIP-GMAC-256") == 0)
val |= WPA_CIPHER_BIP_GMAC_256;
else if (os_strcmp(start, "BIP-CMAC-256") == 0)
val |= WPA_CIPHER_BIP_CMAC_256;
else {
os_free(buf);
return -1;
}
if (last)
break;
start = end + 1;
}
os_free(buf);
return val;
}
int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim)
{
char *pos = start;
int ret;
if (ciphers & WPA_CIPHER_CCMP_256) {
ret = os_snprintf(pos, end - pos, "%sCCMP-256",
pos == start ? "" : delim);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (ciphers & WPA_CIPHER_GCMP_256) {
ret = os_snprintf(pos, end - pos, "%sGCMP-256",
pos == start ? "" : delim);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (ciphers & WPA_CIPHER_CCMP) {
ret = os_snprintf(pos, end - pos, "%sCCMP",
pos == start ? "" : delim);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (ciphers & WPA_CIPHER_GCMP) {
ret = os_snprintf(pos, end - pos, "%sGCMP",
pos == start ? "" : delim);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (ciphers & WPA_CIPHER_TKIP) {
ret = os_snprintf(pos, end - pos, "%sTKIP",
pos == start ? "" : delim);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (ciphers & WPA_CIPHER_AES_128_CMAC) {
ret = os_snprintf(pos, end - pos, "%sAES-128-CMAC",
pos == start ? "" : delim);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (ciphers & WPA_CIPHER_BIP_GMAC_128) {
ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-128",
pos == start ? "" : delim);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (ciphers & WPA_CIPHER_BIP_GMAC_256) {
ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-256",
pos == start ? "" : delim);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (ciphers & WPA_CIPHER_BIP_CMAC_256) {
ret = os_snprintf(pos, end - pos, "%sBIP-CMAC-256",
pos == start ? "" : delim);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (ciphers & WPA_CIPHER_NONE) {
ret = os_snprintf(pos, end - pos, "%sNONE",
pos == start ? "" : delim);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
return pos - start;
}
int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise)
{
int pairwise = 0;
/* Select group cipher based on the enabled pairwise cipher suites */
if (wpa & 1)
pairwise |= wpa_pairwise;
if (wpa & 2)
pairwise |= rsn_pairwise;
if (pairwise & WPA_CIPHER_TKIP)
return WPA_CIPHER_TKIP;
if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP)
return WPA_CIPHER_GCMP;
if ((pairwise & (WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP |
WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP_256)
return WPA_CIPHER_GCMP_256;
if ((pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
WPA_CIPHER_GCMP)) == WPA_CIPHER_CCMP_256)
return WPA_CIPHER_CCMP_256;
return WPA_CIPHER_CCMP;
}
#ifdef CONFIG_FILS
int fils_domain_name_hash(const char *domain, u8 *hash)
{
char buf[255], *wpos = buf;
const char *pos = domain;
size_t len;
const u8 *addr[1];
u8 mac[SHA256_MAC_LEN];
for (len = 0; len < sizeof(buf) && *pos; len++) {
if (isalpha(*pos) && isupper(*pos))
*wpos++ = tolower(*pos);
else
*wpos++ = *pos;
pos++;
}
addr[0] = (const u8 *) buf;
if (sha256_vector(1, addr, &len, mac) < 0)
return -1;
os_memcpy(hash, mac, 2);
return 0;
}
#endif /* CONFIG_FILS */
/**
* wpa_parse_vendor_specific - Parse Vendor Specific IEs
* @pos: Pointer to the IE header
* @end: Pointer to the end of the Key Data buffer
* @ie: Pointer to parsed IE data
*/
static void wpa_parse_vendor_specific(const u8 *pos, const u8 *end,
struct wpa_eapol_ie_parse *ie)
{
unsigned int oui;
if (pos[1] < 4) {
wpa_printf(MSG_MSGDUMP,
"Too short vendor specific IE ignored (len=%u)",
pos[1]);
return;
}
oui = WPA_GET_BE24(&pos[2]);
if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) {
if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) {
ie->wmm = &pos[2];
ie->wmm_len = pos[1];
wpa_hexdump(MSG_DEBUG, "WPA: WMM IE",
ie->wmm, ie->wmm_len);
} else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) {
ie->wmm = &pos[2];
ie->wmm_len = pos[1];
wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element",
ie->wmm, ie->wmm_len);
}
}
}
/**
* wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
* @pos: Pointer to the IE header
* @ie: Pointer to parsed IE data
* Returns: 0 on success, 1 if end mark is found, 2 if KDE is not recognized
*/
static int wpa_parse_generic(const u8 *pos, struct wpa_eapol_ie_parse *ie)
{
if (pos[1] == 0)
return 1;
if (pos[1] >= 6 &&
RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
pos[2 + WPA_SELECTOR_LEN] == 1 &&
pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
ie->wpa_ie = pos;
ie->wpa_ie_len = pos[1] + 2;
wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
ie->wpa_ie, ie->wpa_ie_len);
return 0;
}
if (pos[1] >= 4 && WPA_GET_BE32(pos + 2) == OSEN_IE_VENDOR_TYPE) {
ie->osen = pos;
ie->osen_len = pos[1] + 2;
return 0;
}
if (pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
pos, pos[1] + 2);
return 0;
}
if (pos[1] >= RSN_SELECTOR_LEN + 2 &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_KEYID) {
ie->key_id = pos + 2 + RSN_SELECTOR_LEN;
wpa_hexdump(MSG_DEBUG, "WPA: KeyID in EAPOL-Key",
pos, pos[1] + 2);
return 0;
}
if (pos[1] > RSN_SELECTOR_LEN + 2 &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
pos, pos[1] + 2);
return 0;
}
if (pos[1] > RSN_SELECTOR_LEN + 2 &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
pos, pos[1] + 2);
return 0;
}
if (pos[1] > RSN_SELECTOR_LEN + 2 &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
pos, pos[1] + 2);
return 0;
}
if (pos[1] > RSN_SELECTOR_LEN + 2 &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_BIGTK) {
ie->bigtk = pos + 2 + RSN_SELECTOR_LEN;
ie->bigtk_len = pos[1] - RSN_SELECTOR_LEN;
wpa_hexdump_key(MSG_DEBUG, "WPA: BIGTK in EAPOL-Key",
pos, pos[1] + 2);
return 0;
}
if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
return 0;
}
if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
wpa_hexdump(MSG_DEBUG,
"WPA: IP Address Allocation in EAPOL-Key",
ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
return 0;
}
if (pos[1] > RSN_SELECTOR_LEN + 2 &&
RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) {
ie->oci = pos + 2 + RSN_SELECTOR_LEN;
ie->oci_len = pos[1] - RSN_SELECTOR_LEN;
wpa_hexdump(MSG_DEBUG, "WPA: OCI KDE in EAPOL-Key",
pos, pos[1] + 2);
return 0;
}
if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_TRANSITION_DISABLE) {
ie->transition_disable = pos + 2 + RSN_SELECTOR_LEN;
ie->transition_disable_len = pos[1] - RSN_SELECTOR_LEN;
wpa_hexdump(MSG_DEBUG,
"WPA: Transition Disable KDE in EAPOL-Key",
pos, pos[1] + 2);
return 0;
}
if (pos[1] >= RSN_SELECTOR_LEN + 2 &&
RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_DPP) {
ie->dpp_kde = pos + 2 + RSN_SELECTOR_LEN;
ie->dpp_kde_len = pos[1] - RSN_SELECTOR_LEN;
wpa_hexdump(MSG_DEBUG, "WPA: DPP KDE in EAPOL-Key",
pos, pos[1] + 2);
return 0;
}
return 2;
}
/**
* wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs
* @buf: Pointer to the Key Data buffer
* @len: Key Data Length
* @ie: Pointer to parsed IE data
* Returns: 0 on success, -1 on failure
*/
int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
{
const u8 *pos, *end;
int ret = 0;
os_memset(ie, 0, sizeof(*ie));
for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
if (pos[0] == 0xdd &&
((pos == buf + len - 1) || pos[1] == 0)) {
/* Ignore padding */
break;
}
if (2 + pos[1] > end - pos) {
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key Key Data underflow (ie=%d len=%d pos=%d)",
pos[0], pos[1], (int) (pos - buf));
wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", buf, len);
ret = -1;
break;
}
if (*pos == WLAN_EID_RSN) {
ie->rsn_ie = pos;
ie->rsn_ie_len = pos[1] + 2;
wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
ie->rsn_ie, ie->rsn_ie_len);
} else if (*pos == WLAN_EID_RSNX) {
ie->rsnxe = pos;
ie->rsnxe_len = pos[1] + 2;
wpa_hexdump(MSG_DEBUG, "WPA: RSNXE in EAPOL-Key",
ie->rsnxe, ie->rsnxe_len);
} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
ie->mdie = pos;
ie->mdie_len = pos[1] + 2;
wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
ie->mdie, ie->mdie_len);
} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
ie->ftie = pos;
ie->ftie_len = pos[1] + 2;
wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
ie->ftie, ie->ftie_len);
} else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) {
if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) {
ie->reassoc_deadline = pos;
wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline "
"in EAPOL-Key",
ie->reassoc_deadline, pos[1] + 2);
} else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) {
ie->key_lifetime = pos;
wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime "
"in EAPOL-Key",
ie->key_lifetime, pos[1] + 2);
} else {
wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized "
"EAPOL-Key Key Data IE",
pos, 2 + pos[1]);
}
} else if (*pos == WLAN_EID_LINK_ID) {
if (pos[1] >= 18) {
ie->lnkid = pos;
ie->lnkid_len = pos[1] + 2;
}
} else if (*pos == WLAN_EID_EXT_CAPAB) {
ie->ext_capab = pos;
ie->ext_capab_len = pos[1] + 2;
} else if (*pos == WLAN_EID_SUPP_RATES) {
ie->supp_rates = pos;
ie->supp_rates_len = pos[1] + 2;
} else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
ie->ext_supp_rates = pos;
ie->ext_supp_rates_len = pos[1] + 2;
} else if (*pos == WLAN_EID_HT_CAP &&
pos[1] >= sizeof(struct ieee80211_ht_capabilities)) {
ie->ht_capabilities = pos + 2;
} else if (*pos == WLAN_EID_VHT_AID) {
if (pos[1] >= 2)
ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff;
} else if (*pos == WLAN_EID_VHT_CAP &&
pos[1] >= sizeof(struct ieee80211_vht_capabilities))
{
ie->vht_capabilities = pos + 2;
} else if (*pos == WLAN_EID_EXTENSION &&
pos[1] >= 1 + IEEE80211_HE_CAPAB_MIN_LEN &&
pos[2] == WLAN_EID_EXT_HE_CAPABILITIES) {
ie->he_capabilities = pos + 3;
ie->he_capab_len = pos[1] - 1;
+ } else if (*pos == WLAN_EID_EXTENSION &&
+ pos[1] >= 1 +
+ sizeof(struct ieee80211_he_6ghz_band_cap) &&
+ pos[2] == WLAN_EID_EXT_HE_6GHZ_BAND_CAP) {
+ ie->he_6ghz_capabilities = pos + 3;
} else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
ie->qosinfo = pos[2];
} else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) {
ie->supp_channels = pos + 2;
ie->supp_channels_len = pos[1];
} else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) {
/*
* The value of the Length field of the Supported
* Operating Classes element is between 2 and 253.
* Silently skip invalid elements to avoid interop
* issues when trying to use the value.
*/
if (pos[1] >= 2 && pos[1] <= 253) {
ie->supp_oper_classes = pos + 2;
ie->supp_oper_classes_len = pos[1];
}
} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
ret = wpa_parse_generic(pos, ie);
if (ret == 1) {
/* end mark found */
ret = 0;
break;
}
if (ret == 2) {
/* not a known KDE */
wpa_parse_vendor_specific(pos, end, ie);
}
ret = 0;
} else {
wpa_hexdump(MSG_DEBUG,
"WPA: Unrecognized EAPOL-Key Key Data IE",
pos, 2 + pos[1]);
}
}
return ret;
}
#ifdef CONFIG_PASN
/*
* wpa_pasn_build_auth_header - Add the MAC header and initialize Authentication
* frame for PASN
*
* @buf: Buffer in which the header will be added
* @bssid: The BSSID of the AP
* @src: Source address
* @dst: Destination address
* @trans_seq: Authentication transaction sequence number
* @status: Authentication status
*/
void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid,
const u8 *src, const u8 *dst,
u8 trans_seq, u16 status)
{
struct ieee80211_mgmt *auth;
wpa_printf(MSG_DEBUG, "PASN: Add authentication header. trans_seq=%u",
trans_seq);
auth = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
u.auth.variable));
auth->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_AUTH << 4));
os_memcpy(auth->da, dst, ETH_ALEN);
os_memcpy(auth->sa, src, ETH_ALEN);
os_memcpy(auth->bssid, bssid, ETH_ALEN);
auth->seq_ctrl = 0;
auth->u.auth.auth_alg = host_to_le16(WLAN_AUTH_PASN);
auth->u.auth.auth_transaction = host_to_le16(trans_seq);
auth->u.auth.status_code = host_to_le16(status);
}
/*
* wpa_pasn_add_rsne - Add an RSNE for PASN authentication
* @buf: Buffer in which the IE will be added
* @pmkid: Optional PMKID. Can be NULL.
* @akmp: Authentication and key management protocol
* @cipher: The cipher suite
*/
int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid, int akmp, int cipher)
{
struct rsn_ie_hdr *hdr;
u32 suite;
u16 capab;
u8 *pos;
u8 rsne_len;
wpa_printf(MSG_DEBUG, "PASN: Add RSNE");
rsne_len = sizeof(*hdr) + RSN_SELECTOR_LEN +
2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN +
2 + RSN_SELECTOR_LEN + 2 + (pmkid ? PMKID_LEN : 0);
if (wpabuf_tailroom(buf) < rsne_len)
return -1;
hdr = wpabuf_put(buf, rsne_len);
hdr->elem_id = WLAN_EID_RSN;
hdr->len = rsne_len - 2;
WPA_PUT_LE16(hdr->version, RSN_VERSION);
pos = (u8 *) (hdr + 1);
/* Group addressed data is not allowed */
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
pos += RSN_SELECTOR_LEN;
/* Add the pairwise cipher */
WPA_PUT_LE16(pos, 1);
pos += 2;
suite = wpa_cipher_to_suite(WPA_PROTO_RSN, cipher);
RSN_SELECTOR_PUT(pos, suite);
pos += RSN_SELECTOR_LEN;
/* Add the AKM suite */
WPA_PUT_LE16(pos, 1);
pos += 2;
switch (akmp) {
case WPA_KEY_MGMT_PASN:
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PASN);
break;
#ifdef CONFIG_SAE
case WPA_KEY_MGMT_SAE:
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
break;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
case WPA_KEY_MGMT_FILS_SHA256:
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
break;
case WPA_KEY_MGMT_FILS_SHA384:
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
break;
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
case WPA_KEY_MGMT_FT_PSK:
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
break;
case WPA_KEY_MGMT_FT_IEEE8021X:
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
break;
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
break;
#endif /* CONFIG_IEEE80211R */
default:
wpa_printf(MSG_ERROR, "PASN: Invalid AKMP=0x%x", akmp);
return -1;
}
pos += RSN_SELECTOR_LEN;
/* RSN Capabilities: PASN mandates both MFP capable and required */
capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR;
WPA_PUT_LE16(pos, capab);
pos += 2;
if (pmkid) {
wpa_printf(MSG_DEBUG, "PASN: Adding PMKID");
WPA_PUT_LE16(pos, 1);
pos += 2;
os_memcpy(pos, pmkid, PMKID_LEN);
pos += PMKID_LEN;
} else {
WPA_PUT_LE16(pos, 0);
pos += 2;
}
/* Group addressed management is not allowed */
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
return 0;
}
/*
* wpa_pasn_add_parameter_ie - Add PASN Parameters IE for PASN authentication
* @buf: Buffer in which the IE will be added
* @pasn_group: Finite Cyclic Group ID for PASN authentication
* @wrapped_data_format: Format of the data in the Wrapped Data IE
* @pubkey: A buffer holding the local public key. Can be NULL
* @compressed: In case pubkey is included, indicates if the public key is
* compressed (only x coordinate is included) or not (both x and y
* coordinates are included)
* @comeback: A buffer holding the comeback token. Can be NULL
* @after: If comeback is set, defined the comeback time in seconds. -1 to not
* include the Comeback After field (frames from non-AP STA).
*/
void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group,
u8 wrapped_data_format,
const struct wpabuf *pubkey, bool compressed,
const struct wpabuf *comeback, int after)
{
struct pasn_parameter_ie *params;
wpa_printf(MSG_DEBUG, "PASN: Add PASN Parameters element");
params = wpabuf_put(buf, sizeof(*params));
params->id = WLAN_EID_EXTENSION;
params->len = sizeof(*params) - 2;
params->id_ext = WLAN_EID_EXT_PASN_PARAMS;
params->control = 0;
params->wrapped_data_format = wrapped_data_format;
if (comeback) {
wpa_printf(MSG_DEBUG, "PASN: Adding comeback data");
/*
* 2 octets for the 'after' field + 1 octet for the length +
* actual cookie data
*/
if (after >= 0)
params->len += 2;
params->len += 1 + wpabuf_len(comeback);
params->control |= WPA_PASN_CTRL_COMEBACK_INFO_PRESENT;
if (after >= 0)
wpabuf_put_le16(buf, after);
wpabuf_put_u8(buf, wpabuf_len(comeback));
wpabuf_put_buf(buf, comeback);
}
if (pubkey) {
wpa_printf(MSG_DEBUG,
"PASN: Adding public key and group ID %u",
pasn_group);
/*
* 2 octets for the finite cyclic group + 2 octets public key
* length + 1 octet for the compressed/uncompressed indication +
* the actual key.
*/
params->len += 2 + 1 + 1 + wpabuf_len(pubkey);
params->control |= WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT;
wpabuf_put_le16(buf, pasn_group);
/*
* The first octet indicates whether the public key is
* compressed, as defined in RFC 5480 section 2.2.
*/
wpabuf_put_u8(buf, wpabuf_len(pubkey) + 1);
wpabuf_put_u8(buf, compressed ? WPA_PASN_PUBKEY_COMPRESSED_0 :
WPA_PASN_PUBKEY_UNCOMPRESSED);
wpabuf_put_buf(buf, pubkey);
}
}
/*
* wpa_pasn_add_wrapped_data - Add a Wrapped Data IE to PASN Authentication
* frame. If needed, the Wrapped Data IE would be fragmented.
*
* @buf: Buffer in which the IE will be added
* @wrapped_data_buf: Buffer holding the wrapped data
*/
int wpa_pasn_add_wrapped_data(struct wpabuf *buf,
struct wpabuf *wrapped_data_buf)
{
const u8 *data;
size_t data_len;
u8 len;
if (!wrapped_data_buf)
return 0;
wpa_printf(MSG_DEBUG, "PASN: Add wrapped data");
data = wpabuf_head_u8(wrapped_data_buf);
data_len = wpabuf_len(wrapped_data_buf);
/* nothing to add */
if (!data_len)
return 0;
if (data_len <= 254)
len = 1 + data_len;
else
len = 255;
if (wpabuf_tailroom(buf) < 3 + data_len)
return -1;
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, len);
wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
wpabuf_put_data(buf, data, len - 1);
data += len - 1;
data_len -= len - 1;
while (data_len) {
if (wpabuf_tailroom(buf) < 1 + data_len)
return -1;
wpabuf_put_u8(buf, WLAN_EID_FRAGMENT);
len = data_len > 255 ? 255 : data_len;
wpabuf_put_u8(buf, len);
wpabuf_put_data(buf, data, len);
data += len;
data_len -= len;
}
return 0;
}
/*
* wpa_pasn_validate_rsne - Validate PSAN specific data of RSNE
* @data: Parsed representation of an RSNE
* Returns -1 for invalid data; otherwise 0
*/
int wpa_pasn_validate_rsne(const struct wpa_ie_data *data)
{
u16 capab = WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR;
if (data->proto != WPA_PROTO_RSN)
return -1;
if ((data->capabilities & capab) != capab) {
wpa_printf(MSG_DEBUG, "PASN: Invalid RSNE capabilities");
return -1;
}
if (!data->has_group || data->group_cipher != WPA_CIPHER_GTK_NOT_USED) {
wpa_printf(MSG_DEBUG, "PASN: Invalid group data cipher");
return -1;
}
if (!data->has_pairwise || !data->pairwise_cipher ||
(data->pairwise_cipher & (data->pairwise_cipher - 1))) {
wpa_printf(MSG_DEBUG, "PASN: No valid pairwise suite");
return -1;
}
switch (data->key_mgmt) {
#ifdef CONFIG_SAE
case WPA_KEY_MGMT_SAE:
/* fall through */
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
case WPA_KEY_MGMT_FILS_SHA256:
case WPA_KEY_MGMT_FILS_SHA384:
/* fall through */
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
case WPA_KEY_MGMT_FT_PSK:
case WPA_KEY_MGMT_FT_IEEE8021X:
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
/* fall through */
#endif /* CONFIG_IEEE80211R */
case WPA_KEY_MGMT_PASN:
break;
default:
wpa_printf(MSG_ERROR, "PASN: invalid key_mgmt: 0x%0x",
data->key_mgmt);
return -1;
}
if (data->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED) {
wpa_printf(MSG_DEBUG, "PASN: Invalid group mgmt cipher");
return -1;
}
if (data->num_pmkid > 1) {
wpa_printf(MSG_DEBUG, "PASN: Invalid number of PMKIDs");
return -1;
}
return 0;
}
/*
* wpa_pasn_parse_parameter_ie - Validates PASN Parameters IE
* @data: Pointer to the PASN Parameters IE (starting with the EID).
* @len: Length of the data in the PASN Parameters IE
* @from_ap: Whether this was received from an AP
* @pasn_params: On successful return would hold the parsed PASN parameters.
* Returns: -1 for invalid data; otherwise 0
*
* Note: On successful return, the pointers in &pasn_params point to the data in
* the IE and are not locally allocated (so they should not be freed etc.).
*/
int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap,
struct wpa_pasn_params_data *pasn_params)
{
struct pasn_parameter_ie *params = (struct pasn_parameter_ie *) data;
const u8 *pos = (const u8 *) (params + 1);
if (!pasn_params) {
wpa_printf(MSG_DEBUG, "PASN: Invalid params");
return -1;
}
if (!params || ((size_t) (params->len + 2) < sizeof(*params)) ||
len < sizeof(*params) || params->len + 2 != len) {
wpa_printf(MSG_DEBUG,
"PASN: Invalid parameters IE. len=(%u, %u)",
params ? params->len : 0, len);
return -1;
}
os_memset(pasn_params, 0, sizeof(*pasn_params));
switch (params->wrapped_data_format) {
case WPA_PASN_WRAPPED_DATA_NO:
case WPA_PASN_WRAPPED_DATA_SAE:
case WPA_PASN_WRAPPED_DATA_FILS_SK:
case WPA_PASN_WRAPPED_DATA_FT:
break;
default:
wpa_printf(MSG_DEBUG, "PASN: Invalid wrapped data format");
return -1;
}
pasn_params->wrapped_data_format = params->wrapped_data_format;
len -= sizeof(*params);
if (params->control & WPA_PASN_CTRL_COMEBACK_INFO_PRESENT) {
if (from_ap) {
if (len < 2) {
wpa_printf(MSG_DEBUG,
"PASN: Invalid Parameters IE: Truncated Comeback After");
return -1;
}
pasn_params->after = WPA_GET_LE16(pos);
pos += 2;
len -= 2;
}
if (len < 1 || len < 1 + *pos) {
wpa_printf(MSG_DEBUG,
"PASN: Invalid Parameters IE: comeback len");
return -1;
}
pasn_params->comeback_len = *pos++;
len--;
pasn_params->comeback = pos;
len -= pasn_params->comeback_len;
pos += pasn_params->comeback_len;
}
if (params->control & WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT) {
if (len < 3 || len < 3 + pos[2]) {
wpa_printf(MSG_DEBUG,
"PASN: Invalid Parameters IE: group and key");
return -1;
}
pasn_params->group = WPA_GET_LE16(pos);
pos += 2;
len -= 2;
pasn_params->pubkey_len = *pos++;
len--;
pasn_params->pubkey = pos;
len -= pasn_params->pubkey_len;
pos += pasn_params->pubkey_len;
}
if (len) {
wpa_printf(MSG_DEBUG,
"PASN: Invalid Parameters IE. Bytes left=%u", len);
return -1;
}
return 0;
}
void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab)
{
size_t flen;
flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
return; /* no supported extended RSN capabilities */
if (wpabuf_tailroom(buf) < 2 + flen)
return;
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
wpabuf_put_u8(buf, WLAN_EID_RSNX);
wpabuf_put_u8(buf, flen);
wpabuf_put_u8(buf, capab & 0x00ff);
capab >>= 8;
if (capab)
wpabuf_put_u8(buf, capab);
}
#endif /* CONFIG_PASN */
diff --git a/contrib/wpa/src/common/wpa_common.h b/contrib/wpa/src/common/wpa_common.h
index a1ff895659cb..c28c55d3aab6 100644
--- a/contrib/wpa/src/common/wpa_common.h
+++ b/contrib/wpa/src/common/wpa_common.h
@@ -1,677 +1,678 @@
/*
* WPA definitions shared between hostapd and wpa_supplicant
* Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPA_COMMON_H
#define WPA_COMMON_H
/* IEEE 802.11i */
#define PMKID_LEN 16
#define PMK_LEN 32
#define PMK_LEN_SUITE_B_192 48
#define PMK_LEN_MAX 64
#define WPA_REPLAY_COUNTER_LEN 8
#define WPA_NONCE_LEN 32
#define WPA_KEY_RSC_LEN 8
#define WPA_GMK_LEN 32
#define WPA_GTK_MAX_LEN 32
#define WPA_PASN_PMK_LEN 32
#define WPA_PASN_MAX_MIC_LEN 24
#define WPA_MAX_RSNXE_LEN 4
#define OWE_DH_GROUP 19
#ifdef CONFIG_NO_TKIP
#define WPA_ALLOWED_PAIRWISE_CIPHERS \
(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_NONE | \
WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
#define WPA_ALLOWED_GROUP_CIPHERS \
(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | \
WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
WPA_CIPHER_GTK_NOT_USED)
#else /* CONFIG_NO_TKIP */
#define WPA_ALLOWED_PAIRWISE_CIPHERS \
(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \
WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
#define WPA_ALLOWED_GROUP_CIPHERS \
(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \
WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
WPA_CIPHER_GTK_NOT_USED)
#endif /* CONFIG_NO_TKIP */
#define WPA_ALLOWED_GROUP_MGMT_CIPHERS \
(WPA_CIPHER_AES_128_CMAC | WPA_CIPHER_BIP_GMAC_128 | WPA_CIPHER_BIP_GMAC_256 | \
WPA_CIPHER_BIP_CMAC_256)
#define WPA_SELECTOR_LEN 4
#define WPA_VERSION 1
#define RSN_SELECTOR_LEN 4
#define RSN_VERSION 1
#define RSN_SELECTOR(a, b, c, d) \
((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \
(u32) (d))
#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
#define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0)
#define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
#define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
#define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4)
#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
#define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
#define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
#define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
#define RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
#define RSN_AUTH_KEY_MGMT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 14)
#define RSN_AUTH_KEY_MGMT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 15)
#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
#define RSN_AUTH_KEY_MGMT_OWE RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
#define RSN_AUTH_KEY_MGMT_PASN RSN_SELECTOR(0x00, 0x0f, 0xac, 21)
#define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
#define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
#define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02)
#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
#define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
#if 0
#define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
#endif
#define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
#define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
#define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
#define RSN_CIPHER_SUITE_GCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
#define RSN_CIPHER_SUITE_CCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
#define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
#define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
#define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
#define RSN_CIPHER_SUITE_SMS4 RSN_SELECTOR(0x00, 0x14, 0x72, 1)
#define RSN_CIPHER_SUITE_CKIP RSN_SELECTOR(0x00, 0x40, 0x96, 0)
#define RSN_CIPHER_SUITE_CKIP_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 1)
#define RSN_CIPHER_SUITE_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 2)
/* KRK is defined for nl80211 use only */
#define RSN_CIPHER_SUITE_KRK RSN_SELECTOR(0x00, 0x40, 0x96, 255)
/* EAPOL-Key Key Data Encapsulation
* GroupKey and PeerKey require encryption, otherwise, encryption is optional.
*/
#define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
#if 0
#define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
#endif
#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
#define RSN_KEY_DATA_OCI RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
#define RSN_KEY_DATA_BIGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 14)
#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
#define WFA_KEY_DATA_TRANSITION_DISABLE RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x20)
#define WFA_KEY_DATA_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x21)
#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
#define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val))
#define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a))
#define RSN_NUM_REPLAY_COUNTERS_1 0
#define RSN_NUM_REPLAY_COUNTERS_2 1
#define RSN_NUM_REPLAY_COUNTERS_4 2
#define RSN_NUM_REPLAY_COUNTERS_16 3
#ifdef _MSC_VER
#pragma pack(push, 1)
#endif /* _MSC_VER */
#define WPA_IGTK_LEN 16
#define WPA_IGTK_MAX_LEN 32
#define WPA_BIGTK_LEN 16
#define WPA_BIGTK_MAX_LEN 32
/* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */
#define WPA_CAPABILITY_PREAUTH BIT(0)
#define WPA_CAPABILITY_NO_PAIRWISE BIT(1)
/* B2-B3: PTKSA Replay Counter */
/* B4-B5: GTKSA Replay Counter */
#define WPA_CAPABILITY_MFPR BIT(6)
#define WPA_CAPABILITY_MFPC BIT(7)
/* B8: Reserved */
#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9)
#define WPA_CAPABILITY_SPP_A_MSDU_CAPABLE BIT(10)
#define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11)
#define WPA_CAPABILITY_PBAC BIT(12)
#define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13)
#define WPA_CAPABILITY_OCVC BIT(14)
/* B15: Reserved */
/* IEEE 802.11r */
#define MOBILITY_DOMAIN_ID_LEN 2
#define FT_R0KH_ID_MAX_LEN 48
#define FT_R1KH_ID_LEN 6
#define WPA_PMK_NAME_LEN 16
/* IEEE 802.11, 8.5.2 EAPOL-Key frames */
#define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2)))
#define WPA_KEY_INFO_TYPE_AKM_DEFINED 0
#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0)
#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1)
#define WPA_KEY_INFO_TYPE_AES_128_CMAC 3
#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */
/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5))
#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4
#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */
#define WPA_KEY_INFO_TXRX BIT(6) /* group */
#define WPA_KEY_INFO_ACK BIT(7)
#define WPA_KEY_INFO_MIC BIT(8)
#define WPA_KEY_INFO_SECURE BIT(9)
#define WPA_KEY_INFO_ERROR BIT(10)
#define WPA_KEY_INFO_REQUEST BIT(11)
#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */
#define WPA_KEY_INFO_SMK_MESSAGE BIT(13)
struct wpa_eapol_key {
u8 type;
/* Note: key_info, key_length, and key_data_length are unaligned */
u8 key_info[2]; /* big endian */
u8 key_length[2]; /* big endian */
u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
u8 key_nonce[WPA_NONCE_LEN];
u8 key_iv[16];
u8 key_rsc[WPA_KEY_RSC_LEN];
u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
/* variable length Key MIC field */
/* big endian 2-octet Key Data Length field */
/* followed by Key Data Length bytes of Key Data */
} STRUCT_PACKED;
#define WPA_EAPOL_KEY_MIC_MAX_LEN 32
#define WPA_KCK_MAX_LEN 32
#define WPA_KEK_MAX_LEN 64
#define WPA_TK_MAX_LEN 32
#define WPA_KDK_MAX_LEN 32
#define FILS_ICK_MAX_LEN 48
#define FILS_FT_MAX_LEN 48
#define WPA_PASN_KCK_LEN 32
#define WPA_PASN_MIC_MAX_LEN 24
/**
* struct wpa_ptk - WPA Pairwise Transient Key
* IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
*/
struct wpa_ptk {
u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */
u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */
u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */
u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */
u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */
u8 kdk[WPA_KDK_MAX_LEN]; /* Key Derivation Key */
size_t kck_len;
size_t kek_len;
size_t tk_len;
size_t kck2_len;
size_t kek2_len;
size_t kdk_len;
int installed; /* 1 if key has already been installed to driver */
};
struct wpa_gtk {
u8 gtk[WPA_GTK_MAX_LEN];
size_t gtk_len;
};
struct wpa_igtk {
u8 igtk[WPA_IGTK_MAX_LEN];
size_t igtk_len;
};
struct wpa_bigtk {
u8 bigtk[WPA_BIGTK_MAX_LEN];
size_t bigtk_len;
};
/* WPA IE version 1
* 00-50-f2:1 (OUI:OUI type)
* 0x01 0x00 (version; little endian)
* (all following fields are optional:)
* Group Suite Selector (4 octets) (default: TKIP)
* Pairwise Suite Count (2 octets, little endian) (default: 1)
* Pairwise Suite List (4 * n octets) (default: TKIP)
* Authenticated Key Management Suite Count (2 octets, little endian)
* (default: 1)
* Authenticated Key Management Suite List (4 * n octets)
* (default: unspec 802.1X)
* WPA Capabilities (2 octets, little endian) (default: 0)
*/
struct wpa_ie_hdr {
u8 elem_id;
u8 len;
u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */
u8 version[2]; /* little endian */
} STRUCT_PACKED;
/* 1/4: PMKID
* 2/4: RSN IE
* 3/4: one or two RSN IEs + GTK IE (encrypted)
* 4/4: empty
* 1/2: GTK IE (encrypted)
* 2/2: empty
*/
/* RSN IE version 1
* 0x01 0x00 (version; little endian)
* (all following fields are optional:)
* Group Suite Selector (4 octets) (default: CCMP)
* Pairwise Suite Count (2 octets, little endian) (default: 1)
* Pairwise Suite List (4 * n octets) (default: CCMP)
* Authenticated Key Management Suite Count (2 octets, little endian)
* (default: 1)
* Authenticated Key Management Suite List (4 * n octets)
* (default: unspec 802.1X)
* RSN Capabilities (2 octets, little endian) (default: 0)
* PMKID Count (2 octets) (default: 0)
* PMKID List (16 * n octets)
* Management Group Cipher Suite (4 octets) (default: AES-128-CMAC)
*/
struct rsn_ie_hdr {
u8 elem_id; /* WLAN_EID_RSN */
u8 len;
u8 version[2]; /* little endian */
} STRUCT_PACKED;
struct rsn_error_kde {
be16 mui;
be16 error_type;
} STRUCT_PACKED;
#define WPA_IGTK_KDE_PREFIX_LEN (2 + 6)
struct wpa_igtk_kde {
u8 keyid[2];
u8 pn[6];
u8 igtk[WPA_IGTK_MAX_LEN];
} STRUCT_PACKED;
#define WPA_BIGTK_KDE_PREFIX_LEN (2 + 6)
struct wpa_bigtk_kde {
u8 keyid[2];
u8 pn[6];
u8 bigtk[WPA_BIGTK_MAX_LEN];
} STRUCT_PACKED;
struct rsn_mdie {
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 ft_capab;
} STRUCT_PACKED;
#define RSN_FT_CAPAB_FT_OVER_DS BIT(0)
#define RSN_FT_CAPAB_FT_RESOURCE_REQ_SUPP BIT(1)
struct rsn_ftie {
u8 mic_control[2];
u8 mic[16];
u8 anonce[WPA_NONCE_LEN];
u8 snonce[WPA_NONCE_LEN];
/* followed by optional parameters */
} STRUCT_PACKED;
struct rsn_ftie_sha384 {
u8 mic_control[2];
u8 mic[24];
u8 anonce[WPA_NONCE_LEN];
u8 snonce[WPA_NONCE_LEN];
/* followed by optional parameters */
} STRUCT_PACKED;
#define FTIE_SUBELEM_R1KH_ID 1
#define FTIE_SUBELEM_GTK 2
#define FTIE_SUBELEM_R0KH_ID 3
#define FTIE_SUBELEM_IGTK 4
#define FTIE_SUBELEM_OCI 5
#define FTIE_SUBELEM_BIGTK 6
struct rsn_rdie {
u8 id;
u8 descr_count;
le16 status_code;
} STRUCT_PACKED;
/* WFA Transition Disable KDE (using OUI_WFA) */
/* Transition Disable Bitmap bits */
#define TRANSITION_DISABLE_WPA3_PERSONAL BIT(0)
#define TRANSITION_DISABLE_SAE_PK BIT(1)
#define TRANSITION_DISABLE_WPA3_ENTERPRISE BIT(2)
#define TRANSITION_DISABLE_ENHANCED_OPEN BIT(3)
/* DPP KDE Flags */
#define DPP_KDE_PFS_ALLOWED BIT(0)
#define DPP_KDE_PFS_REQUIRED BIT(1)
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
const u8 *buf, size_t len, u8 *mic);
int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
const u8 *addr1, const u8 *addr2,
const u8 *nonce1, const u8 *nonce2,
struct wpa_ptk *ptk, int akmp, int cipher,
const u8 *z, size_t z_len, size_t kdk_len);
int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
size_t dh_ss_len, u8 *pmk, size_t *pmk_len);
int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
u8 *pmkid);
int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
const u8 *snonce, const u8 *anonce, const u8 *dhss,
size_t dhss_len, struct wpa_ptk *ptk,
u8 *ick, size_t *ick_len, int akmp, int cipher,
u8 *fils_ft, size_t *fils_ft_len, size_t kdk_len);
int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
const u8 *g_sta, size_t g_sta_len,
const u8 *g_ap, size_t g_ap_len,
int akmp, u8 *key_auth_sta, u8 *key_auth_ap,
size_t *key_auth_len);
#ifdef CONFIG_IEEE80211R
int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
const u8 *ap_addr, u8 transaction_seqnum,
const u8 *mdie, size_t mdie_len,
const u8 *ftie, size_t ftie_len,
const u8 *rsnie, size_t rsnie_len,
const u8 *ric, size_t ric_len,
const u8 *rsnxe, size_t rsnxe_len,
u8 *mic);
int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
const u8 *ssid, size_t ssid_len,
const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name,
int use_sha384);
int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384);
int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len,
const u8 *pmk_r0_name,
const u8 *r1kh_id, const u8 *s1kh_id,
u8 *pmk_r1, u8 *pmk_r1_name);
int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce,
const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
const u8 *pmk_r1_name,
struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher,
size_t kdk_len);
#endif /* CONFIG_IEEE80211R */
struct wpa_ie_data {
int proto;
int pairwise_cipher;
int has_pairwise;
int group_cipher;
int has_group;
int key_mgmt;
int capabilities;
size_t num_pmkid;
const u8 *pmkid;
int mgmt_group_cipher;
};
int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
struct wpa_ie_data *data);
int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ie_data *data);
int wpa_default_rsn_cipher(int freq);
void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
u8 *pmkid, int akmp);
#ifdef CONFIG_SUITEB
int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
const u8 *spa, u8 *pmkid);
#else /* CONFIG_SUITEB */
static inline int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
const u8 *spa, u8 *pmkid)
{
return -1;
}
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_SUITEB192
int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
const u8 *spa, u8 *pmkid);
#else /* CONFIG_SUITEB192 */
static inline int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len,
const u8 *aa, const u8 *spa, u8 *pmkid)
{
return -1;
}
#endif /* CONFIG_SUITEB192 */
const char * wpa_cipher_txt(int cipher);
const char * wpa_key_mgmt_txt(int key_mgmt, int proto);
u32 wpa_akm_to_suite(int akm);
int wpa_compare_rsn_ie(int ft_initial_assoc,
const u8 *ie1, size_t ie1len,
const u8 *ie2, size_t ie2len);
int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid);
struct wpa_ft_ies {
const u8 *mdie;
size_t mdie_len;
const u8 *ftie;
size_t ftie_len;
const u8 *r1kh_id;
const u8 *gtk;
size_t gtk_len;
const u8 *r0kh_id;
size_t r0kh_id_len;
const u8 *fte_anonce;
const u8 *fte_snonce;
const u8 *rsn;
size_t rsn_len;
u16 rsn_capab;
const u8 *rsn_pmkid;
const u8 *tie;
size_t tie_len;
const u8 *igtk;
size_t igtk_len;
const u8 *bigtk;
size_t bigtk_len;
#ifdef CONFIG_OCV
const u8 *oci;
size_t oci_len;
#endif /* CONFIG_OCV */
const u8 *ric;
size_t ric_len;
int key_mgmt;
int pairwise_cipher;
const u8 *rsnxe;
size_t rsnxe_len;
};
/* IEEE P802.11az/D2.6 - 9.4.2.303 PASN Parameters element */
#define WPA_PASN_CTRL_COMEBACK_INFO_PRESENT BIT(0)
#define WPA_PASN_CTRL_GROUP_AND_KEY_PRESENT BIT(1)
#define WPA_PASN_WRAPPED_DATA_NO 0
#define WPA_PASN_WRAPPED_DATA_FT 1
#define WPA_PASN_WRAPPED_DATA_FILS_SK 2
#define WPA_PASN_WRAPPED_DATA_SAE 3
struct pasn_parameter_ie {
u8 id;
u8 len;
u8 id_ext;
u8 control; /* WPA_PASN_CTRL_* */
u8 wrapped_data_format; /* WPA_PASN_WRAPPED_DATA_* */
} STRUCT_PACKED;
struct wpa_pasn_params_data {
u8 wrapped_data_format;
u16 after;
u8 comeback_len;
const u8 *comeback;
u16 group;
u8 pubkey_len;
const u8 *pubkey;
};
/* See RFC 5480 section 2.2 */
#define WPA_PASN_PUBKEY_COMPRESSED_0 0x02
#define WPA_PASN_PUBKEY_COMPRESSED_1 0x03
#define WPA_PASN_PUBKEY_UNCOMPRESSED 0x04
int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse,
int use_sha384);
struct wpa_eapol_ie_parse {
const u8 *wpa_ie;
size_t wpa_ie_len;
const u8 *rsn_ie;
size_t rsn_ie_len;
const u8 *pmkid;
const u8 *key_id;
const u8 *gtk;
size_t gtk_len;
const u8 *mac_addr;
size_t mac_addr_len;
const u8 *igtk;
size_t igtk_len;
const u8 *bigtk;
size_t bigtk_len;
const u8 *mdie;
size_t mdie_len;
const u8 *ftie;
size_t ftie_len;
const u8 *ip_addr_req;
const u8 *ip_addr_alloc;
const u8 *transition_disable;
size_t transition_disable_len;
const u8 *dpp_kde;
size_t dpp_kde_len;
const u8 *oci;
size_t oci_len;
const u8 *osen;
size_t osen_len;
const u8 *rsnxe;
size_t rsnxe_len;
const u8 *reassoc_deadline;
const u8 *key_lifetime;
const u8 *lnkid;
size_t lnkid_len;
const u8 *ext_capab;
size_t ext_capab_len;
const u8 *supp_rates;
size_t supp_rates_len;
const u8 *ext_supp_rates;
size_t ext_supp_rates_len;
const u8 *ht_capabilities;
const u8 *vht_capabilities;
const u8 *he_capabilities;
size_t he_capab_len;
+ const u8 *he_6ghz_capabilities;
const u8 *supp_channels;
size_t supp_channels_len;
const u8 *supp_oper_classes;
size_t supp_oper_classes_len;
u8 qosinfo;
u16 aid;
const u8 *wmm;
size_t wmm_len;
};
int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie);
static inline int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
struct wpa_eapol_ie_parse *ie)
{
return wpa_parse_kde_ies(buf, len, ie);
}
int wpa_cipher_key_len(int cipher);
int wpa_cipher_rsc_len(int cipher);
enum wpa_alg wpa_cipher_to_alg(int cipher);
int wpa_cipher_valid_group(int cipher);
int wpa_cipher_valid_pairwise(int cipher);
int wpa_cipher_valid_mgmt_group(int cipher);
u32 wpa_cipher_to_suite(int proto, int cipher);
int rsn_cipher_put_suites(u8 *pos, int ciphers);
int wpa_cipher_put_suites(u8 *pos, int ciphers);
int wpa_pick_pairwise_cipher(int ciphers, int none_allowed);
int wpa_pick_group_cipher(int ciphers);
int wpa_parse_cipher(const char *value);
int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
unsigned int wpa_mic_len(int akmp, size_t pmk_len);
int wpa_use_akm_defined(int akmp);
int wpa_use_cmac(int akmp);
int wpa_use_aes_key_wrap(int akmp);
int fils_domain_name_hash(const char *domain, u8 *hash);
int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
const u8 *spa, const u8 *bssid,
const u8 *dhss, size_t dhss_len,
struct wpa_ptk *ptk, int akmp, int cipher,
size_t kdk_len);
u8 pasn_mic_len(int akmp, int cipher);
int pasn_mic(const u8 *kck, int akmp, int cipher,
const u8 *addr1, const u8 *addr2,
const u8 *data, size_t data_len,
const u8 *frame, size_t frame_len, u8 *mic);
int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len,
u8 *hash);
void wpa_pasn_build_auth_header(struct wpabuf *buf, const u8 *bssid,
const u8 *src, const u8 *dst,
u8 trans_seq, u16 status);
int wpa_pasn_add_rsne(struct wpabuf *buf, const u8 *pmkid,
int akmp, int cipher);
void wpa_pasn_add_parameter_ie(struct wpabuf *buf, u16 pasn_group,
u8 wrapped_data_format,
const struct wpabuf *pubkey, bool compressed,
const struct wpabuf *comeback, int after);
int wpa_pasn_add_wrapped_data(struct wpabuf *buf,
struct wpabuf *wrapped_data_buf);
int wpa_pasn_validate_rsne(const struct wpa_ie_data *data);
int wpa_pasn_parse_parameter_ie(const u8 *data, u8 len, bool from_ap,
struct wpa_pasn_params_data *pasn_params);
void wpa_pasn_add_rsnxe(struct wpabuf *buf, u16 capab);
#endif /* WPA_COMMON_H */
diff --git a/contrib/wpa/src/common/wpa_ctrl.h b/contrib/wpa/src/common/wpa_ctrl.h
index 126a7892c8a8..2c7ec043d409 100644
--- a/contrib/wpa/src/common/wpa_ctrl.h
+++ b/contrib/wpa/src/common/wpa_ctrl.h
@@ -1,633 +1,641 @@
/*
* wpa_supplicant/hostapd control interface library
* Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPA_CTRL_H
#define WPA_CTRL_H
#ifdef __cplusplus
extern "C" {
#endif
/* wpa_supplicant control interface - fixed message prefixes */
/** Interactive request for identity/password/pin */
#define WPA_CTRL_REQ "CTRL-REQ-"
/** Response to identity/password/pin request */
#define WPA_CTRL_RSP "CTRL-RSP-"
/* Event messages with fixed prefix */
/** Authentication completed successfully and data connection enabled */
#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "
/** Disconnected, data connection is not available */
#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
/** Association rejected during connection attempt */
#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT "
/** Authentication rejected during connection attempt */
#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT "
/** wpa_supplicant is exiting */
#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
/** Password change was completed successfully */
#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED "
/** EAP-Request/Notification received */
#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION "
/** EAP authentication started (EAP-Request/Identity received) */
#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED "
/** EAP method proposed by the server */
#define WPA_EVENT_EAP_PROPOSED_METHOD "CTRL-EVENT-EAP-PROPOSED-METHOD "
/** EAP method selected */
#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD "
/** EAP peer certificate from TLS */
#define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT "
/** EAP peer certificate alternative subject name component from TLS */
#define WPA_EVENT_EAP_PEER_ALT "CTRL-EVENT-EAP-PEER-ALT "
/** EAP TLS certificate chain validation error */
#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
/** EAP status */
#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS "
/** Retransmit the previous request packet */
#define WPA_EVENT_EAP_RETRANSMIT "CTRL-EVENT-EAP-RETRANSMIT "
#define WPA_EVENT_EAP_RETRANSMIT2 "CTRL-EVENT-EAP-RETRANSMIT2 "
/** EAP authentication completed successfully */
#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
#define WPA_EVENT_EAP_SUCCESS2 "CTRL-EVENT-EAP-SUCCESS2 "
/** EAP authentication failed (EAP-Failure received) */
#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
#define WPA_EVENT_EAP_FAILURE2 "CTRL-EVENT-EAP-FAILURE2 "
/** EAP authentication failed due to no response received */
#define WPA_EVENT_EAP_TIMEOUT_FAILURE "CTRL-EVENT-EAP-TIMEOUT-FAILURE "
#define WPA_EVENT_EAP_TIMEOUT_FAILURE2 "CTRL-EVENT-EAP-TIMEOUT-FAILURE2 "
#define WPA_EVENT_EAP_ERROR_CODE "EAP-ERROR-CODE "
/** Network block temporarily disabled (e.g., due to authentication failure) */
#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED "
/** Temporarily disabled network block re-enabled */
#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED "
/** New scan started */
#define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED "
/** New scan results available */
#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
/** Scan command failed */
#define WPA_EVENT_SCAN_FAILED "CTRL-EVENT-SCAN-FAILED "
/** wpa_supplicant state change */
#define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE "
/** A new BSS entry was added (followed by BSS entry id and BSSID) */
#define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED "
/** A BSS entry was removed (followed by BSS entry id and BSSID) */
#define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED "
/** No suitable network was found */
#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND "
/** Change in the signal level was reported by the driver */
#define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
/** Beacon loss reported by the driver */
#define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS "
/** Regulatory domain channel */
#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
/** Channel switch started (followed by freq=<MHz> and other channel parameters)
*/
#define WPA_EVENT_CHANNEL_SWITCH_STARTED "CTRL-EVENT-STARTED-CHANNEL-SWITCH "
/** Channel switch (followed by freq=<MHz> and other channel parameters) */
#define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH "
/** SAE authentication failed due to unknown password identifier */
#define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \
"CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
/** Unprotected Beacon frame dropped */
#define WPA_EVENT_UNPROT_BEACON "CTRL-EVENT-UNPROT-BEACON "
/** Decision made to do a within-ESS roam */
#define WPA_EVENT_DO_ROAM "CTRL-EVENT-DO-ROAM "
/** Decision made to skip a within-ESS roam */
#define WPA_EVENT_SKIP_ROAM "CTRL-EVENT-SKIP-ROAM "
/** IP subnet status change notification
*
* When using an offloaded roaming mechanism where driver/firmware takes care
* of roaming and IP subnet validation checks post-roaming, this event can
* indicate whether IP subnet has changed.
*
* The event has a status=<0/1/2> parameter where
* 0 = unknown
* 1 = IP subnet unchanged (can continue to use the old IP address)
* 2 = IP subnet changed (need to get a new IP address)
*/
#define WPA_EVENT_SUBNET_STATUS_UPDATE "CTRL-EVENT-SUBNET-STATUS-UPDATE "
/** RSN IBSS 4-way handshakes completed with specified peer */
#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "
/** Notification of frequency conflict due to a concurrent operation.
*
* The indicated network is disabled and needs to be re-enabled before it can
* be used again.
*/
#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
/** Frequency ranges that the driver recommends to avoid */
#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
+/** A new network profile was added (followed by network entry id) */
+#define WPA_EVENT_NETWORK_ADDED "CTRL-EVENT-NETWORK-ADDED "
+/** A network profile was removed (followed by prior network entry id) */
+#define WPA_EVENT_NETWORK_REMOVED "CTRL-EVENT-NETWORK-REMOVED "
/** Result of MSCS setup */
#define WPA_EVENT_MSCS_RESULT "CTRL-EVENT-MSCS-RESULT "
/** WPS overlap detected in PBC mode */
#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
/** Available WPS AP with active PBC found in scan results */
#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC "
/** Available WPS AP with our address as authorized in scan results */
#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH "
/** Available WPS AP with recently selected PIN registrar found in scan results
*/
#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN "
/** Available WPS AP found in scan results */
#define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE "
/** A new credential received */
#define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED "
/** M2D received */
#define WPS_EVENT_M2D "WPS-M2D "
/** WPS registration failed after M2/M2D */
#define WPS_EVENT_FAIL "WPS-FAIL "
/** WPS registration completed successfully */
#define WPS_EVENT_SUCCESS "WPS-SUCCESS "
/** WPS enrollment attempt timed out and was terminated */
#define WPS_EVENT_TIMEOUT "WPS-TIMEOUT "
/* PBC mode was activated */
#define WPS_EVENT_ACTIVE "WPS-PBC-ACTIVE "
/* PBC mode was disabled */
#define WPS_EVENT_DISABLE "WPS-PBC-DISABLE "
#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN "
#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK "
+/** Result of SCS setup */
+#define WPA_EVENT_SCS_RESULT "CTRL-EVENT-SCS-RESULT "
+/* Event indicating DSCP policy */
+#define WPA_EVENT_DSCP_POLICY "CTRL-EVENT-DSCP-POLICY "
/* WPS ER events */
#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD "
#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE "
#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD "
#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE "
#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
/* DPP events */
#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS "
#define DPP_EVENT_AUTH_INIT_FAILED "DPP-AUTH-INIT-FAILED "
#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE "
#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING "
#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE "
#define DPP_EVENT_AUTH_DIRECTION "DPP-AUTH-DIRECTION "
#define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED "
#define DPP_EVENT_CONF_SENT "DPP-CONF-SENT "
#define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED "
#define DPP_EVENT_CONN_STATUS_RESULT "DPP-CONN-STATUS-RESULT "
#define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM "
#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID "
#define DPP_EVENT_CONFOBJ_SSID_CHARSET "DPP-CONFOBJ-SSID-CHARSET "
#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS "
#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK "
#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR "
#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY "
#define DPP_EVENT_PP_KEY "DPP-PP-KEY "
#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY "
#define DPP_EVENT_SERVER_NAME "DPP-SERVER-NAME "
#define DPP_EVENT_CERTBAG "DPP-CERTBAG "
#define DPP_EVENT_CACERT "DPP-CACERT "
#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR "
#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID "
#define DPP_EVENT_CONFIGURATOR_ID "DPP-CONFIGURATOR-ID "
#define DPP_EVENT_RX "DPP-RX "
#define DPP_EVENT_TX "DPP-TX "
#define DPP_EVENT_TX_STATUS "DPP-TX-STATUS "
#define DPP_EVENT_FAIL "DPP-FAIL "
#define DPP_EVENT_PKEX_T_LIMIT "DPP-PKEX-T-LIMIT "
#define DPP_EVENT_INTRO "DPP-INTRO "
#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX "
#define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED "
#define DPP_EVENT_MUD_URL "DPP-MUD-URL "
#define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT "
#define DPP_EVENT_CSR "DPP-CSR "
#define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX "
/* MESH events */
#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
#define MESH_PEER_CONNECTED "MESH-PEER-CONNECTED "
#define MESH_PEER_DISCONNECTED "MESH-PEER-DISCONNECTED "
/** Mesh SAE authentication failure. Wrong password suspected. */
#define MESH_SAE_AUTH_FAILURE "MESH-SAE-AUTH-FAILURE "
#define MESH_SAE_AUTH_BLOCKED "MESH-SAE-AUTH-BLOCKED "
/* WMM AC events */
#define WMM_AC_EVENT_TSPEC_ADDED "TSPEC-ADDED "
#define WMM_AC_EVENT_TSPEC_REMOVED "TSPEC-REMOVED "
#define WMM_AC_EVENT_TSPEC_REQ_FAILED "TSPEC-REQ-FAILED "
/** P2P device found */
#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND "
/** P2P device lost */
#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST "
/** A P2P device requested GO negotiation, but we were not ready to start the
* negotiation */
#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST "
#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS "
#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE "
#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS "
#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE "
#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED "
#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED "
#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE "
#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE "
/* parameters: <peer address> <PIN> */
#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN "
/* parameters: <peer address> */
#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN "
/* parameters: <peer address> */
#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ "
/* parameters: <peer address> */
#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP "
/* parameters: <peer address> <status> */
#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE"
/* parameters: <freq> <src addr> <dialog token> <update indicator> <TLVs> */
#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
/* parameters: <src addr> <update indicator> <TLVs> */
#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
#define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP "
#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
#define P2P_EVENT_INVITATION_ACCEPTED "P2P-INVITATION-ACCEPTED "
#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
#define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
#define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE "
#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO "
#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT "
#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT "
#define P2P_EVENT_FALLBACK_TO_GO_NEG "P2P-FALLBACK-TO-GO-NEG "
#define P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED "P2P-FALLBACK-TO-GO-NEG-ENABLED "
/* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
#define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START "
#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE "
#define INTERWORKING_AP "INTERWORKING-AP "
#define INTERWORKING_EXCLUDED "INTERWORKING-BLACKLISTED "
#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED "
#define INTERWORKING_SELECTED "INTERWORKING-SELECTED "
/* Credential block added; parameters: <id> */
#define CRED_ADDED "CRED-ADDED "
/* Credential block modified; parameters: <id> <field> */
#define CRED_MODIFIED "CRED-MODIFIED "
/* Credential block removed; parameters: <id> */
#define CRED_REMOVED "CRED-REMOVED "
#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO "
/* parameters: <addr> <dialog_token> <freq> */
#define GAS_QUERY_START "GAS-QUERY-START "
/* parameters: <addr> <dialog_token> <freq> <status_code> <result> */
#define GAS_QUERY_DONE "GAS-QUERY-DONE "
/* parameters: <addr> <result> */
#define ANQP_QUERY_DONE "ANQP-QUERY-DONE "
#define RX_ANQP "RX-ANQP "
#define RX_HS20_ANQP "RX-HS20-ANQP "
#define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON "
#define RX_HS20_ICON "RX-HS20-ICON "
#define RX_MBO_ANQP "RX-MBO-ANQP "
/* parameters: <Venue Number> <Venue URL> */
#define RX_VENUE_URL "RX-VENUE-URL "
#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
#define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE "
#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START "
#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT "
#define RRM_EVENT_NEIGHBOR_REP_RXED "RRM-NEIGHBOR-REP-RECEIVED "
#define RRM_EVENT_NEIGHBOR_REP_FAILED "RRM-NEIGHBOR-REP-REQUEST-FAILED "
/* hostapd control interface - fixed message prefixes */
#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS "
#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED "
#define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED "
#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
#define WPS_EVENT_PIN_ACTIVE "WPS-PIN-ACTIVE "
#define WPS_EVENT_CANCEL "WPS-CANCEL "
#define AP_STA_CONNECTED "AP-STA-CONNECTED "
#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
#define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH "
#define AP_STA_POLL_OK "AP-STA-POLL-OK "
#define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
#define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
#define HS20_T_C_FILTERING_ADD "HS20-T-C-FILTERING-ADD "
#define HS20_T_C_FILTERING_REMOVE "HS20-T-C-FILTERING-REMOVE "
#define AP_EVENT_ENABLED "AP-ENABLED "
#define AP_EVENT_DISABLED "AP-DISABLED "
#define INTERFACE_ENABLED "INTERFACE-ENABLED "
#define INTERFACE_DISABLED "INTERFACE-DISABLED "
#define ACS_EVENT_STARTED "ACS-STARTED "
#define ACS_EVENT_COMPLETED "ACS-COMPLETED "
#define ACS_EVENT_FAILED "ACS-FAILED "
#define DFS_EVENT_RADAR_DETECTED "DFS-RADAR-DETECTED "
#define DFS_EVENT_NEW_CHANNEL "DFS-NEW-CHANNEL "
#define DFS_EVENT_CAC_START "DFS-CAC-START "
#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
#define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED "
#define AP_CSA_FINISHED "AP-CSA-FINISHED "
#define P2P_EVENT_LISTEN_OFFLOAD_STOP "P2P-LISTEN-OFFLOAD-STOPPED "
#define P2P_LISTEN_OFFLOAD_STOP_REASON "P2P-LISTEN-OFFLOAD-STOP-REASON "
/* BSS Transition Management Response frame received */
#define BSS_TM_RESP "BSS-TM-RESP "
/* Collocated Interference Request frame received;
* parameters: <dialog token> <automatic report enabled> <report timeout> */
#define COLOC_INTF_REQ "COLOC-INTF-REQ "
/* Collocated Interference Report frame received;
* parameters: <STA address> <dialog token> <hexdump of report elements> */
#define COLOC_INTF_REPORT "COLOC-INTF-REPORT "
/* MBO IE with cellular data connection preference received */
#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE "
/* BSS Transition Management Request received with MBO transition reason */
#define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON "
/* parameters: <STA address> <dialog token> <ack=0/1> */
#define BEACON_REQ_TX_STATUS "BEACON-REQ-TX-STATUS "
/* parameters: <STA address> <dialog token> <report mode> <beacon report> */
#define BEACON_RESP_RX "BEACON-RESP-RX "
/* PMKSA cache entry added; parameters: <BSSID> <network_id> */
#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED "
/* PMKSA cache entry removed; parameters: <BSSID> <network_id> */
#define PMKSA_CACHE_REMOVED "PMKSA-CACHE-REMOVED "
/* FILS HLP Container receive; parameters: dst=<addr> src=<addr> frame=<hexdump>
*/
#define FILS_HLP_RX "FILS-HLP-RX "
/* Event to indicate Probe Request frame;
* parameters: sa=<STA MAC address> signal=<signal> */
#define RX_PROBE_REQUEST "RX-PROBE-REQUEST "
/* Event to indicate station's HT/VHT operation mode change information */
#define STA_OPMODE_MAX_BW_CHANGED "STA-OPMODE-MAX-BW-CHANGED "
#define STA_OPMODE_SMPS_MODE_CHANGED "STA-OPMODE-SMPS-MODE-CHANGED "
#define STA_OPMODE_N_SS_CHANGED "STA-OPMODE-N_SS-CHANGED "
/* New interface addition or removal for 4addr WDS SDA */
#define WDS_STA_INTERFACE_ADDED "WDS-STA-INTERFACE-ADDED "
#define WDS_STA_INTERFACE_REMOVED "WDS-STA-INTERFACE-REMOVED "
/* Transition mode disabled indication - followed by bitmap */
#define TRANSITION_DISABLE "TRANSITION-DISABLE "
/* OCV validation failure; parameters: addr=<src addr>
* frame=<saqueryreq/saqueryresp> error=<error string> */
#define OCV_FAILURE "OCV-FAILURE "
/* Event triggered for received management frame */
#define AP_MGMT_FRAME_RECEIVED "AP-MGMT-FRAME-RECEIVED "
#ifndef BIT
#define BIT(x) (1U << (x))
#endif
/* PASN authentication status */
#define PASN_AUTH_STATUS "PASN-AUTH-STATUS "
/* BSS command information masks */
#define WPA_BSS_MASK_ALL 0xFFFDFFFF
#define WPA_BSS_MASK_ID BIT(0)
#define WPA_BSS_MASK_BSSID BIT(1)
#define WPA_BSS_MASK_FREQ BIT(2)
#define WPA_BSS_MASK_BEACON_INT BIT(3)
#define WPA_BSS_MASK_CAPABILITIES BIT(4)
#define WPA_BSS_MASK_QUAL BIT(5)
#define WPA_BSS_MASK_NOISE BIT(6)
#define WPA_BSS_MASK_LEVEL BIT(7)
#define WPA_BSS_MASK_TSF BIT(8)
#define WPA_BSS_MASK_AGE BIT(9)
#define WPA_BSS_MASK_IE BIT(10)
#define WPA_BSS_MASK_FLAGS BIT(11)
#define WPA_BSS_MASK_SSID BIT(12)
#define WPA_BSS_MASK_WPS_SCAN BIT(13)
#define WPA_BSS_MASK_P2P_SCAN BIT(14)
#define WPA_BSS_MASK_INTERNETW BIT(15)
#define WPA_BSS_MASK_WIFI_DISPLAY BIT(16)
#define WPA_BSS_MASK_DELIM BIT(17)
#define WPA_BSS_MASK_MESH_SCAN BIT(18)
#define WPA_BSS_MASK_SNR BIT(19)
#define WPA_BSS_MASK_EST_THROUGHPUT BIT(20)
#define WPA_BSS_MASK_FST BIT(21)
#define WPA_BSS_MASK_UPDATE_IDX BIT(22)
#define WPA_BSS_MASK_BEACON_IE BIT(23)
#define WPA_BSS_MASK_FILS_INDICATION BIT(24)
/* VENDOR_ELEM_* frame id values */
enum wpa_vendor_elem_frame {
VENDOR_ELEM_PROBE_REQ_P2P = 0,
VENDOR_ELEM_PROBE_RESP_P2P = 1,
VENDOR_ELEM_PROBE_RESP_P2P_GO = 2,
VENDOR_ELEM_BEACON_P2P_GO = 3,
VENDOR_ELEM_P2P_PD_REQ = 4,
VENDOR_ELEM_P2P_PD_RESP = 5,
VENDOR_ELEM_P2P_GO_NEG_REQ = 6,
VENDOR_ELEM_P2P_GO_NEG_RESP = 7,
VENDOR_ELEM_P2P_GO_NEG_CONF = 8,
VENDOR_ELEM_P2P_INV_REQ = 9,
VENDOR_ELEM_P2P_INV_RESP = 10,
VENDOR_ELEM_P2P_ASSOC_REQ = 11,
VENDOR_ELEM_P2P_ASSOC_RESP = 12,
VENDOR_ELEM_ASSOC_REQ = 13,
VENDOR_ELEM_PROBE_REQ = 14,
NUM_VENDOR_ELEM_FRAMES
};
/* wpa_supplicant/hostapd control interface access */
/**
* wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd
* @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
* Returns: Pointer to abstract control interface data or %NULL on failure
*
* This function is used to open a control interface to wpa_supplicant/hostapd.
* ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path
* is configured in wpa_supplicant/hostapd and other programs using the control
* interface need to use matching path configuration.
*/
struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
/**
* wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd
* @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
* @cli_path: Path for client UNIX domain sockets; ignored if UDP socket
* is used.
* Returns: Pointer to abstract control interface data or %NULL on failure
*
* This function is used to open a control interface to wpa_supplicant/hostapd
* when the socket path for client need to be specified explicitly. Default
* ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client
* socket path is /tmp.
*/
struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path);
/**
* wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd
* @ctrl: Control interface data from wpa_ctrl_open()
*
* This function is used to close a control interface.
*/
void wpa_ctrl_close(struct wpa_ctrl *ctrl);
/**
* wpa_ctrl_request - Send a command to wpa_supplicant/hostapd
* @ctrl: Control interface data from wpa_ctrl_open()
* @cmd: Command; usually, ASCII text, e.g., "PING"
* @cmd_len: Length of the cmd in bytes
* @reply: Buffer for the response
* @reply_len: Reply buffer length
* @msg_cb: Callback function for unsolicited messages or %NULL if not used
* Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout
*
* This function is used to send commands to wpa_supplicant/hostapd. Received
* response will be written to reply and reply_len is set to the actual length
* of the reply. This function will block for up to 10 seconds while waiting
* for the reply. If unsolicited messages are received, the blocking time may
* be longer.
*
* msg_cb can be used to register a callback function that will be called for
* unsolicited messages received while waiting for the command response. These
* messages may be received if wpa_ctrl_request() is called at the same time as
* wpa_supplicant/hostapd is sending such a message. This can happen only if
* the program has used wpa_ctrl_attach() to register itself as a monitor for
* event messages. Alternatively to msg_cb, programs can register two control
* interface connections and use one of them for commands and the other one for
* receiving event messages, in other words, call wpa_ctrl_attach() only for
* the control interface connection that will be used for event messages.
*/
int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
char *reply, size_t *reply_len,
void (*msg_cb)(char *msg, size_t len));
/**
* wpa_ctrl_attach - Register as an event monitor for the control interface
* @ctrl: Control interface data from wpa_ctrl_open()
* Returns: 0 on success, -1 on failure, -2 on timeout
*
* This function registers the control interface connection as a monitor for
* wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the
* control interface connection starts receiving event messages that can be
* read with wpa_ctrl_recv().
*/
int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
/**
* wpa_ctrl_detach - Unregister event monitor from the control interface
* @ctrl: Control interface data from wpa_ctrl_open()
* Returns: 0 on success, -1 on failure, -2 on timeout
*
* This function unregisters the control interface connection as a monitor for
* wpa_supplicant/hostapd events, i.e., cancels the registration done with
* wpa_ctrl_attach().
*/
int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
/**
* wpa_ctrl_recv - Receive a pending control interface message
* @ctrl: Control interface data from wpa_ctrl_open()
* @reply: Buffer for the message data
* @reply_len: Length of the reply buffer
* Returns: 0 on success, -1 on failure
*
* This function will receive a pending control interface message. The received
* response will be written to reply and reply_len is set to the actual length
* of the reply.
* wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach()
* must have been used to register the control interface as an event monitor.
*/
int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
/**
* wpa_ctrl_pending - Check whether there are pending event messages
* @ctrl: Control interface data from wpa_ctrl_open()
* Returns: 1 if there are pending messages, 0 if no, or -1 on error
*
* This function will check whether there are any pending control interface
* message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is
* only used for event messages, i.e., wpa_ctrl_attach() must have been used to
* register the control interface as an event monitor.
*/
int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
/**
* wpa_ctrl_get_fd - Get file descriptor used by the control interface
* @ctrl: Control interface data from wpa_ctrl_open()
* Returns: File descriptor used for the connection
*
* This function can be used to get the file descriptor that is used for the
* control interface connection. The returned value can be used, e.g., with
* select() while waiting for multiple events.
*
* The returned file descriptor must not be used directly for sending or
* receiving packets; instead, the library functions wpa_ctrl_request() and
* wpa_ctrl_recv() must be used for this.
*/
int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
#ifdef ANDROID
/**
* wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
* may be left over from clients that were previously connected to
* wpa_supplicant. This keeps these files from being orphaned in the
* event of crashes that prevented them from being removed as part
* of the normal orderly shutdown.
*/
void wpa_ctrl_cleanup(void);
#endif /* ANDROID */
#ifdef CONFIG_CTRL_IFACE_UDP
/* Port range for multiple wpa_supplicant instances and multiple VIFs */
#define WPA_CTRL_IFACE_PORT 9877
#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */
#define WPA_GLOBAL_CTRL_IFACE_PORT 9878
#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */
char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
#endif /* CONFIG_CTRL_IFACE_UDP */
#ifdef __cplusplus
}
#endif
#endif /* WPA_CTRL_H */
diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h
index 7d2ebd61caea..eb600699d3d0 100644
--- a/contrib/wpa/src/crypto/crypto.h
+++ b/contrib/wpa/src/crypto/crypto.h
@@ -1,933 +1,1290 @@
/*
* Wrapper functions for crypto libraries
* Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This file defines the cryptographic functions that need to be implemented
* for wpa_supplicant and hostapd. When TLS is not used, internal
* implementation of MD5, SHA1, and AES is used and no external libraries are
* required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the
* crypto library used by the TLS implementation is expected to be used for
* non-TLS needs, too, in order to save space by not implementing these
* functions twice.
*
* Wrapper code for using each crypto library is in its own file (crypto*.c)
* and one of these files is build and linked in to provide the functions
* defined here.
*/
#ifndef CRYPTO_H
#define CRYPTO_H
/**
* md4_vector - MD4 hash for data vector
* @num_elem: Number of elements in the data vector
* @addr: Pointers to the data areas
* @len: Lengths of the data blocks
* @mac: Buffer for the hash
* Returns: 0 on success, -1 on failure
*/
int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
/**
* md5_vector - MD5 hash for data vector
* @num_elem: Number of elements in the data vector
* @addr: Pointers to the data areas
* @len: Lengths of the data blocks
* @mac: Buffer for the hash
* Returns: 0 on success, -1 on failure
*/
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
/**
* sha1_vector - SHA-1 hash for data vector
* @num_elem: Number of elements in the data vector
* @addr: Pointers to the data areas
* @len: Lengths of the data blocks
* @mac: Buffer for the hash
* Returns: 0 on success, -1 on failure
*/
int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac);
/**
* fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
* @seed: Seed/key for the PRF
* @seed_len: Seed length in bytes
* @x: Buffer for PRF output
* @xlen: Output length in bytes
* Returns: 0 on success, -1 on failure
*
* This function implements random number generation specified in NIST FIPS
* Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to
* SHA-1, but has different message padding.
*/
int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
size_t xlen);
/**
* sha256_vector - SHA256 hash for data vector
* @num_elem: Number of elements in the data vector
* @addr: Pointers to the data areas
* @len: Lengths of the data blocks
* @mac: Buffer for the hash
* Returns: 0 on success, -1 on failure
*/
int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac);
/**
* sha384_vector - SHA384 hash for data vector
* @num_elem: Number of elements in the data vector
* @addr: Pointers to the data areas
* @len: Lengths of the data blocks
* @mac: Buffer for the hash
* Returns: 0 on success, -1 on failure
*/
int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac);
/**
* sha512_vector - SHA512 hash for data vector
* @num_elem: Number of elements in the data vector
* @addr: Pointers to the data areas
* @len: Lengths of the data blocks
* @mac: Buffer for the hash
* Returns: 0 on success, -1 on failure
*/
int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac);
/**
* des_encrypt - Encrypt one block with DES
* @clear: 8 octets (in)
* @key: 7 octets (in) (no parity bits included)
* @cypher: 8 octets (out)
* Returns: 0 on success, -1 on failure
*/
int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
/**
* aes_encrypt_init - Initialize AES for encryption
* @key: Encryption key
* @len: Key length in bytes (usually 16, i.e., 128 bits)
* Returns: Pointer to context data or %NULL on failure
*/
void * aes_encrypt_init(const u8 *key, size_t len);
/**
* aes_encrypt - Encrypt one AES block
* @ctx: Context pointer from aes_encrypt_init()
* @plain: Plaintext data to be encrypted (16 bytes)
* @crypt: Buffer for the encrypted data (16 bytes)
* Returns: 0 on success, -1 on failure
*/
int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
/**
* aes_encrypt_deinit - Deinitialize AES encryption
* @ctx: Context pointer from aes_encrypt_init()
*/
void aes_encrypt_deinit(void *ctx);
/**
* aes_decrypt_init - Initialize AES for decryption
* @key: Decryption key
* @len: Key length in bytes (usually 16, i.e., 128 bits)
* Returns: Pointer to context data or %NULL on failure
*/
void * aes_decrypt_init(const u8 *key, size_t len);
/**
* aes_decrypt - Decrypt one AES block
* @ctx: Context pointer from aes_encrypt_init()
* @crypt: Encrypted data (16 bytes)
* @plain: Buffer for the decrypted data (16 bytes)
* Returns: 0 on success, -1 on failure
*/
int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
/**
* aes_decrypt_deinit - Deinitialize AES decryption
* @ctx: Context pointer from aes_encrypt_init()
*/
void aes_decrypt_deinit(void *ctx);
enum crypto_hash_alg {
CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256,
CRYPTO_HASH_ALG_SHA384, CRYPTO_HASH_ALG_SHA512
};
struct crypto_hash;
/**
* crypto_hash_init - Initialize hash/HMAC function
* @alg: Hash algorithm
* @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed
* @key_len: Length of the key in bytes
* Returns: Pointer to hash context to use with other hash functions or %NULL
* on failure
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
size_t key_len);
/**
* crypto_hash_update - Add data to hash calculation
* @ctx: Context pointer from crypto_hash_init()
* @data: Data buffer to add
* @len: Length of the buffer
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len);
/**
* crypto_hash_finish - Complete hash calculation
* @ctx: Context pointer from crypto_hash_init()
* @hash: Buffer for hash value or %NULL if caller is just freeing the hash
* context
* @len: Pointer to length of the buffer or %NULL if caller is just freeing the
* hash context; on return, this is set to the actual length of the hash value
* Returns: 0 on success, -1 if buffer is too small (len set to needed length),
* or -2 on other failures (including failed crypto_hash_update() operations)
*
* This function calculates the hash value and frees the context buffer that
* was used for hash calculation.
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
enum crypto_cipher_alg {
CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
};
struct crypto_cipher;
/**
* crypto_cipher_init - Initialize block/stream cipher function
* @alg: Cipher algorithm
* @iv: Initialization vector for block ciphers or %NULL for stream ciphers
* @key: Cipher key
* @key_len: Length of key in bytes
* Returns: Pointer to cipher context to use with other cipher functions or
* %NULL on failure
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
const u8 *iv, const u8 *key,
size_t key_len);
/**
* crypto_cipher_encrypt - Cipher encrypt
* @ctx: Context pointer from crypto_cipher_init()
* @plain: Plaintext to cipher
* @crypt: Resulting ciphertext
* @len: Length of the plaintext
* Returns: 0 on success, -1 on failure
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx,
const u8 *plain, u8 *crypt, size_t len);
/**
* crypto_cipher_decrypt - Cipher decrypt
* @ctx: Context pointer from crypto_cipher_init()
* @crypt: Ciphertext to decrypt
* @plain: Resulting plaintext
* @len: Length of the cipher text
* Returns: 0 on success, -1 on failure
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx,
const u8 *crypt, u8 *plain, size_t len);
/**
* crypto_cipher_decrypt - Free cipher context
* @ctx: Context pointer from crypto_cipher_init()
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
void crypto_cipher_deinit(struct crypto_cipher *ctx);
struct crypto_public_key;
struct crypto_private_key;
/**
* crypto_public_key_import - Import an RSA public key
* @key: Key buffer (DER encoded RSA public key)
* @len: Key buffer length in bytes
* Returns: Pointer to the public key or %NULL on failure
*
* This function can just return %NULL if the crypto library supports X.509
* parsing. In that case, crypto_public_key_from_cert() is used to import the
* public key from a certificate.
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
struct crypto_public_key *
crypto_public_key_import_parts(const u8 *n, size_t n_len,
const u8 *e, size_t e_len);
/**
* crypto_private_key_import - Import an RSA private key
* @key: Key buffer (DER encoded RSA private key)
* @len: Key buffer length in bytes
* @passwd: Key encryption password or %NULL if key is not encrypted
* Returns: Pointer to the private key or %NULL on failure
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
struct crypto_private_key * crypto_private_key_import(const u8 *key,
size_t len,
const char *passwd);
/**
* crypto_public_key_from_cert - Import an RSA public key from a certificate
* @buf: DER encoded X.509 certificate
* @len: Certificate buffer length in bytes
* Returns: Pointer to public key or %NULL on failure
*
* This function can just return %NULL if the crypto library does not support
* X.509 parsing. In that case, internal code will be used to parse the
* certificate and public key is imported using crypto_public_key_import().
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
size_t len);
/**
* crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5)
* @key: Public key
* @in: Plaintext buffer
* @inlen: Length of plaintext buffer in bytes
* @out: Output buffer for encrypted data
* @outlen: Length of output buffer in bytes; set to used length on success
* Returns: 0 on success, -1 on failure
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
int __must_check crypto_public_key_encrypt_pkcs1_v15(
struct crypto_public_key *key, const u8 *in, size_t inlen,
u8 *out, size_t *outlen);
/**
* crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5)
* @key: Private key
* @in: Encrypted buffer
* @inlen: Length of encrypted buffer in bytes
* @out: Output buffer for encrypted data
* @outlen: Length of output buffer in bytes; set to used length on success
* Returns: 0 on success, -1 on failure
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
int __must_check crypto_private_key_decrypt_pkcs1_v15(
struct crypto_private_key *key, const u8 *in, size_t inlen,
u8 *out, size_t *outlen);
/**
* crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1)
* @key: Private key from crypto_private_key_import()
* @in: Plaintext buffer
* @inlen: Length of plaintext buffer in bytes
* @out: Output buffer for encrypted (signed) data
* @outlen: Length of output buffer in bytes; set to used length on success
* Returns: 0 on success, -1 on failure
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
const u8 *in, size_t inlen,
u8 *out, size_t *outlen);
/**
* crypto_public_key_free - Free public key
* @key: Public key
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
void crypto_public_key_free(struct crypto_public_key *key);
/**
* crypto_private_key_free - Free private key
* @key: Private key from crypto_private_key_import()
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
void crypto_private_key_free(struct crypto_private_key *key);
/**
* crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature
* @key: Public key
* @crypt: Encrypted signature data (using the private key)
* @crypt_len: Encrypted signature data length
* @plain: Buffer for plaintext (at least crypt_len bytes)
* @plain_len: Plaintext length (max buffer size on input, real len on output);
* Returns: 0 on success, -1 on failure
*/
int __must_check crypto_public_key_decrypt_pkcs1(
struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
u8 *plain, size_t *plain_len);
int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
u8 *pubkey);
int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
const u8 *order, size_t order_len,
const u8 *privkey, size_t privkey_len,
const u8 *pubkey, size_t pubkey_len,
u8 *secret, size_t *len);
/**
* crypto_global_init - Initialize crypto wrapper
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
int __must_check crypto_global_init(void);
/**
* crypto_global_deinit - Deinitialize crypto wrapper
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
void crypto_global_deinit(void);
/**
* crypto_mod_exp - Modular exponentiation of large integers
* @base: Base integer (big endian byte array)
* @base_len: Length of base integer in bytes
* @power: Power integer (big endian byte array)
* @power_len: Length of power integer in bytes
* @modulus: Modulus integer (big endian byte array)
* @modulus_len: Length of modulus integer in bytes
* @result: Buffer for the result
* @result_len: Result length (max buffer size on input, real len on output)
* Returns: 0 on success, -1 on failure
*
* This function calculates result = base ^ power mod modulus. modules_len is
* used as the maximum size of modulus buffer. It is set to the used size on
* success.
*
* This function is only used with internal TLSv1 implementation
* (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
* to implement this.
*/
int __must_check crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
u8 *result, size_t *result_len);
/**
* rc4_skip - XOR RC4 stream to given data with skip-stream-start
* @key: RC4 key
* @keylen: RC4 key length
* @skip: number of bytes to skip from the beginning of the RC4 stream
* @data: data to be XOR'ed with RC4 stream
* @data_len: buf length
* Returns: 0 on success, -1 on failure
*
* Generate RC4 pseudo random stream for the given key, skip beginning of the
* stream, and XOR the end result with the data buffer to perform RC4
* encryption/decryption.
*/
int rc4_skip(const u8 *key, size_t keylen, size_t skip,
u8 *data, size_t data_len);
/**
* crypto_get_random - Generate cryptographically strong pseudo-random bytes
* @buf: Buffer for data
* @len: Number of bytes to generate
* Returns: 0 on success, -1 on failure
*
* If the PRNG does not have enough entropy to ensure unpredictable byte
* sequence, this functions must return -1.
*/
int crypto_get_random(void *buf, size_t len);
+/**
+ * crypto_pkcs7_get_certificates - Extract X.509 certificates from PKCS#7 data
+ * @pkcs7: DER encoded PKCS#7 data
+ * Returns: Buffer of the extracted PEM X.509 certificates or %NULL on failure
+ */
+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7);
+
/**
* struct crypto_bignum - bignum
*
* Internal data structure for bignum implementation. The contents is specific
* to the used crypto library.
*/
struct crypto_bignum;
/**
* crypto_bignum_init - Allocate memory for bignum
* Returns: Pointer to allocated bignum or %NULL on failure
*/
struct crypto_bignum * crypto_bignum_init(void);
/**
* crypto_bignum_init_set - Allocate memory for bignum and set the value
* @buf: Buffer with unsigned binary value
* @len: Length of buf in octets
* Returns: Pointer to allocated bignum or %NULL on failure
*/
struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len);
/**
* crypto_bignum_init_set - Allocate memory for bignum and set the value (uint)
* @val: Value to set
* Returns: Pointer to allocated bignum or %NULL on failure
*/
struct crypto_bignum * crypto_bignum_init_uint(unsigned int val);
/**
* crypto_bignum_deinit - Free bignum
* @n: Bignum from crypto_bignum_init() or crypto_bignum_init_set()
* @clear: Whether to clear the value from memory
*/
void crypto_bignum_deinit(struct crypto_bignum *n, int clear);
/**
* crypto_bignum_to_bin - Set binary buffer to unsigned bignum
* @a: Bignum
* @buf: Buffer for the binary number
* @len: Length of @buf in octets
* @padlen: Length in octets to pad the result to or 0 to indicate no padding
* Returns: Number of octets written on success, -1 on failure
*/
int crypto_bignum_to_bin(const struct crypto_bignum *a,
u8 *buf, size_t buflen, size_t padlen);
/**
* crypto_bignum_rand - Create a random number in range of modulus
* @r: Bignum; set to a random value
* @m: Bignum; modulus
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m);
/**
* crypto_bignum_add - c = a + b
* @a: Bignum
* @b: Bignum
* @c: Bignum; used to store the result of a + b
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_add(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c);
/**
* crypto_bignum_mod - c = a % b
* @a: Bignum
* @b: Bignum
* @c: Bignum; used to store the result of a % b
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_mod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c);
/**
* crypto_bignum_exptmod - Modular exponentiation: d = a^b (mod c)
* @a: Bignum; base
* @b: Bignum; exponent
* @c: Bignum; modulus
* @d: Bignum; used to store the result of a^b (mod c)
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_exptmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
const struct crypto_bignum *c,
struct crypto_bignum *d);
/**
* crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b)
* @a: Bignum
* @b: Bignum
* @c: Bignum; used to store the result
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_inverse(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c);
/**
* crypto_bignum_sub - c = a - b
* @a: Bignum
* @b: Bignum
* @c: Bignum; used to store the result of a - b
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_sub(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c);
/**
* crypto_bignum_div - c = a / b
* @a: Bignum
* @b: Bignum
* @c: Bignum; used to store the result of a / b
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_div(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c);
/**
* crypto_bignum_addmod - d = a + b (mod c)
* @a: Bignum
* @b: Bignum
* @c: Bignum
* @d: Bignum; used to store the result of (a + b) % c
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_addmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
const struct crypto_bignum *c,
struct crypto_bignum *d);
/**
* crypto_bignum_mulmod - d = a * b (mod c)
* @a: Bignum
* @b: Bignum
* @c: Bignum
* @d: Bignum; used to store the result of (a * b) % c
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_mulmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
const struct crypto_bignum *c,
struct crypto_bignum *d);
/**
* crypto_bignum_sqrmod - c = a^2 (mod b)
* @a: Bignum
* @b: Bignum
* @c: Bignum; used to store the result of a^2 % b
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_sqrmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c);
/**
* crypto_bignum_rshift - r = a >> n
* @a: Bignum
* @n: Number of bits
* @r: Bignum; used to store the result of a >> n
* Returns: 0 on success, -1 on failure
*/
int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
struct crypto_bignum *r);
/**
* crypto_bignum_cmp - Compare two bignums
* @a: Bignum
* @b: Bignum
* Returns: -1 if a < b, 0 if a == b, or 1 if a > b
*/
int crypto_bignum_cmp(const struct crypto_bignum *a,
const struct crypto_bignum *b);
/**
* crypto_bignum_is_zero - Is the given bignum zero
* @a: Bignum
* Returns: 1 if @a is zero or 0 if not
*/
int crypto_bignum_is_zero(const struct crypto_bignum *a);
/**
* crypto_bignum_is_one - Is the given bignum one
* @a: Bignum
* Returns: 1 if @a is one or 0 if not
*/
int crypto_bignum_is_one(const struct crypto_bignum *a);
/**
* crypto_bignum_is_odd - Is the given bignum odd
* @a: Bignum
* Returns: 1 if @a is odd or 0 if not
*/
int crypto_bignum_is_odd(const struct crypto_bignum *a);
/**
* crypto_bignum_legendre - Compute the Legendre symbol (a/p)
* @a: Bignum
* @p: Bignum
* Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure
*/
int crypto_bignum_legendre(const struct crypto_bignum *a,
const struct crypto_bignum *p);
/**
* struct crypto_ec - Elliptic curve context
*
* Internal data structure for EC implementation. The contents is specific
* to the used crypto library.
*/
struct crypto_ec;
+/**
+ * struct crypto_ec_point - Elliptic curve point
+ *
+ * Internal data structure for EC implementation to represent a point. The
+ * contents is specific to the used crypto library.
+ */
+struct crypto_ec_point;
+
/**
* crypto_ec_init - Initialize elliptic curve context
* @group: Identifying number for the ECC group (IANA "Group Description"
* attribute registrty for RFC 2409)
* Returns: Pointer to EC context or %NULL on failure
*/
struct crypto_ec * crypto_ec_init(int group);
/**
* crypto_ec_deinit - Deinitialize elliptic curve context
* @e: EC context from crypto_ec_init()
*/
void crypto_ec_deinit(struct crypto_ec *e);
/**
* crypto_ec_prime_len - Get length of the prime in octets
* @e: EC context from crypto_ec_init()
* Returns: Length of the prime defining the group
*/
size_t crypto_ec_prime_len(struct crypto_ec *e);
/**
* crypto_ec_prime_len_bits - Get length of the prime in bits
* @e: EC context from crypto_ec_init()
* Returns: Length of the prime defining the group in bits
*/
size_t crypto_ec_prime_len_bits(struct crypto_ec *e);
/**
* crypto_ec_order_len - Get length of the order in octets
* @e: EC context from crypto_ec_init()
* Returns: Length of the order defining the group
*/
size_t crypto_ec_order_len(struct crypto_ec *e);
/**
* crypto_ec_get_prime - Get prime defining an EC group
* @e: EC context from crypto_ec_init()
* Returns: Prime (bignum) defining the group
*/
const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e);
/**
* crypto_ec_get_order - Get order of an EC group
* @e: EC context from crypto_ec_init()
* Returns: Order (bignum) of the group
*/
const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e);
+/**
+ * crypto_ec_get_a - Get 'a' coefficient of an EC group's curve
+ * @e: EC context from crypto_ec_init()
+ * Returns: 'a' coefficient (bignum) of the group
+ */
const struct crypto_bignum * crypto_ec_get_a(struct crypto_ec *e);
+
+/**
+ * crypto_ec_get_b - Get 'b' coeffiecient of an EC group's curve
+ * @e: EC context from crypto_ec_init()
+ * Returns: 'b' coefficient (bignum) of the group
+ */
const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e);
/**
- * struct crypto_ec_point - Elliptic curve point
- *
- * Internal data structure for EC implementation to represent a point. The
- * contents is specific to the used crypto library.
+ * crypto_ec_get_generator - Get generator point of the EC group's curve
+ * @e: EC context from crypto_ec_init()
+ * Returns: Pointer to generator point
*/
-struct crypto_ec_point;
+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e);
/**
* crypto_ec_point_init - Initialize data for an EC point
* @e: EC context from crypto_ec_init()
* Returns: Pointer to EC point data or %NULL on failure
*/
struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e);
/**
* crypto_ec_point_deinit - Deinitialize EC point data
* @p: EC point data from crypto_ec_point_init()
* @clear: Whether to clear the EC point value from memory
*/
void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear);
/**
* crypto_ec_point_x - Copies the x-ordinate point into big number
* @e: EC context from crypto_ec_init()
* @p: EC point data
* @x: Big number to set to the copy of x-ordinate
* Returns: 0 on success, -1 on failure
*/
int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
struct crypto_bignum *x);
/**
* crypto_ec_point_to_bin - Write EC point value as binary data
* @e: EC context from crypto_ec_init()
* @p: EC point data from crypto_ec_point_init()
* @x: Buffer for writing the binary data for x coordinate or %NULL if not used
* @y: Buffer for writing the binary data for y coordinate or %NULL if not used
* Returns: 0 on success, -1 on failure
*
* This function can be used to write an EC point as binary data in a format
* that has the x and y coordinates in big endian byte order fields padded to
* the length of the prime defining the group.
*/
int crypto_ec_point_to_bin(struct crypto_ec *e,
const struct crypto_ec_point *point, u8 *x, u8 *y);
/**
* crypto_ec_point_from_bin - Create EC point from binary data
* @e: EC context from crypto_ec_init()
* @val: Binary data to read the EC point from
* Returns: Pointer to EC point data or %NULL on failure
*
* This function readers x and y coordinates of the EC point from the provided
* buffer assuming the values are in big endian byte order with fields padded to
* the length of the prime defining the group.
*/
struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
const u8 *val);
/**
* crypto_ec_point_add - c = a + b
* @e: EC context from crypto_ec_init()
* @a: Bignum
* @b: Bignum
* @c: Bignum; used to store the result of a + b
* Returns: 0 on success, -1 on failure
*/
int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
const struct crypto_ec_point *b,
struct crypto_ec_point *c);
/**
* crypto_ec_point_mul - res = b * p
* @e: EC context from crypto_ec_init()
* @p: EC point
* @b: Bignum
* @res: EC point; used to store the result of b * p
* Returns: 0 on success, -1 on failure
*/
int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
const struct crypto_bignum *b,
struct crypto_ec_point *res);
/**
* crypto_ec_point_invert - Compute inverse of an EC point
* @e: EC context from crypto_ec_init()
* @p: EC point to invert (and result of the operation)
* Returns: 0 on success, -1 on failure
*/
int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p);
/**
* crypto_ec_point_solve_y_coord - Solve y coordinate for an x coordinate
* @e: EC context from crypto_ec_init()
* @p: EC point to use for the returning the result
* @x: x coordinate
* @y_bit: y-bit (0 or 1) for selecting the y value to use
* Returns: 0 on success, -1 on failure
*/
int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
struct crypto_ec_point *p,
const struct crypto_bignum *x, int y_bit);
/**
* crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b
* @e: EC context from crypto_ec_init()
* @x: x coordinate
* Returns: y^2 on success, %NULL failure
*/
struct crypto_bignum *
crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
const struct crypto_bignum *x);
/**
* crypto_ec_point_is_at_infinity - Check whether EC point is neutral element
* @e: EC context from crypto_ec_init()
* @p: EC point
* Returns: 1 if the specified EC point is the neutral element of the group or
* 0 if not
*/
int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
const struct crypto_ec_point *p);
/**
* crypto_ec_point_is_on_curve - Check whether EC point is on curve
* @e: EC context from crypto_ec_init()
* @p: EC point
* Returns: 1 if the specified EC point is on the curve or 0 if not
*/
int crypto_ec_point_is_on_curve(struct crypto_ec *e,
const struct crypto_ec_point *p);
/**
* crypto_ec_point_cmp - Compare two EC points
* @e: EC context from crypto_ec_init()
* @a: EC point
* @b: EC point
* Returns: 0 on equal, non-zero otherwise
*/
int crypto_ec_point_cmp(const struct crypto_ec *e,
const struct crypto_ec_point *a,
const struct crypto_ec_point *b);
+/**
+ * crypto_ec_point_debug_print - Dump EC point to debug log
+ * @e: EC context from crypto_ec_init()
+ * @p: EC point
+ * @title: Name of the EC point in the trace
+ */
+void crypto_ec_point_debug_print(const struct crypto_ec *e,
+ const struct crypto_ec_point *p,
+ const char *title);
+
+/**
+ * struct crypto_ec_key - Elliptic curve key pair
+ *
+ * Internal data structure for EC key pair. The contents is specific to the used
+ * crypto library.
+ */
+struct crypto_ec_key;
+
+/**
+ * struct crypto_ecdh - Elliptic Curve Diffie–Hellman context
+ *
+ * Internal data structure for ECDH. The contents is specific to the used
+ * crypto library.
+ */
struct crypto_ecdh;
+/**
+ * crypto_ecdh_init - Initialize elliptic curve Diffie–Hellman context
+ * @group: Identifying number for the ECC group (IANA "Group Description"
+ * attribute registry for RFC 2409)
+ * This function generates an ephemeral key pair.
+ * Returns: Pointer to ECDH context or %NULL on failure
+ */
struct crypto_ecdh * crypto_ecdh_init(int group);
+
+/**
+ * crypto_ecdh_init2 - Initialize elliptic curve Diffie–Hellman context with a
+ * given EC key
+ * @group: Identifying number for the ECC group (IANA "Group Description"
+ * attribute registry for RFC 2409)
+ * @own_key: Our own EC Key
+ * Returns: Pointer to ECDH context or %NULL on failure
+ */
+struct crypto_ecdh * crypto_ecdh_init2(int group,
+ struct crypto_ec_key *own_key);
+
+/**
+ * crypto_ecdh_get_pubkey - Retrieve public key from ECDH context
+ * @ecdh: ECDH context from crypto_ecdh_init() or crypto_ecdh_init2()
+ * @inc_y: Whether public key should include y coordinate (explicit form)
+ * or not (compressed form)
+ * Returns: Binary data f the public key or %NULL on failure
+ */
struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y);
+
+/**
+ * crypto_ecdh_set_peerkey - Compute ECDH secret
+ * @ecdh: ECDH context from crypto_ecdh_init() or crypto_ecdh_init2()
+ * @inc_y: Whether peer's public key includes y coordinate (explicit form)
+ * or not (compressed form)
+ * @key: Binary data of the peer's public key
+ * @len: Length of the @key buffer
+ * Returns: Binary data with the EDCH secret or %NULL on failure
+ */
struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
const u8 *key, size_t len);
+
+/**
+ * crypto_ecdh_deinit - Free ECDH context
+ * @ecdh: ECDH context from crypto_ecdh_init() or crypto_ecdh_init2()
+ */
void crypto_ecdh_deinit(struct crypto_ecdh *ecdh);
-size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh);
-struct crypto_ec_key;
+/**
+ * crypto_ecdh_prime_len - Get length of the prime in octets
+ * @e: ECDH context from crypto_ecdh_init()
+ * Returns: Length of the prime defining the group
+ */
+size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh);
+/**
+ * crypto_ec_key_parse_priv - Initialize EC key pair from ECPrivateKey ASN.1
+ * @der: DER encoding of ASN.1 ECPrivateKey
+ * @der_len: Length of @der buffer
+ * Returns: EC key or %NULL on failure
+ */
struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len);
+
+/**
+ * crypto_ec_key_parse_pub - Initialize EC key pair from SubjectPublicKeyInfo ASN.1
+ * @der: DER encoding of ASN.1 SubjectPublicKeyInfo
+ * @der_len: Length of @der buffer
+ * Returns: EC key or %NULL on failure
+ */
struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len);
+
+/**
+ * crypto_ec_key_set_pub - Initialize an EC public key from EC point coordinates
+ * @group: Identifying number for the ECC group
+ * @x: X coordinate of the public key
+ * @y: Y coordinate of the public key
+ * @len: Length of @x and @y buffer
+ * Returns: EC key or %NULL on failure
+ *
+ * This function initialize an EC key from public key coordinates, in big endian
+ * byte order padded to the length of the prime defining the group.
+ */
+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *x,
+ const u8 *y, size_t len);
+
+/**
+ * crypto_ec_key_set_pub_point - Initialize an EC public key from EC point
+ * @e: EC context from crypto_ec_init()
+ * @pub: Public key point
+ * Returns: EC key or %NULL on failure
+ */
+struct crypto_ec_key *
+crypto_ec_key_set_pub_point(struct crypto_ec *e,
+ const struct crypto_ec_point *pub);
+
+/**
+ * crypto_ec_key_gen - Generate EC key pair
+ * @group: Identifying number for the ECC group
+ * Returns: EC key or %NULL on failure
+ */
+struct crypto_ec_key * crypto_ec_key_gen(int group);
+
+/**
+ * crypto_ec_key_deinit - Free EC key
+ * @key: EC key from crypto_ec_key_parse_pub/priv() or crypto_ec_key_gen()
+ */
void crypto_ec_key_deinit(struct crypto_ec_key *key);
+
+/**
+ * crypto_ec_key_get_subject_public_key - Get SubjectPublicKeyInfo ASN.1 for an EC key
+ * @key: EC key from crypto_ec_key_parse/set_pub/priv() or crypto_ec_key_gen()
+ * Returns: Buffer with DER encoding of ASN.1 SubjectPublicKeyInfo or %NULL on failure
+ */
struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key);
+
+/**
+ * crypto_ec_key_get_ecprivate_key - Get ECPrivateKey ASN.1 for a EC key
+ * @key: EC key from crypto_ec_key_parse_priv() or crypto_ec_key_gen()
+ * @include_pub: Whether to include public key in the ASN.1 sequence
+ * Returns: Buffer with DER encoding of ASN.1 ECPrivateKey or %NULL on failure
+ */
+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
+ bool include_pub);
+
+/**
+ * crypto_ec_key_get_pubkey_point - Get public key point coordinates
+ * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_parse_priv()
+ * @prefix: Whether output buffer should include the octet to indicate
+ * coordinate form (as defined for SubjectPublicKeyInfo)
+ * Returns: Buffer with coordinates of public key in uncompressed form or %NULL
+ * on failure
+ */
+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
+ int prefix);
+
+/**
+ * crypto_ec_key_get_public_key - Get EC public key as an EC point
+ * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_parse_priv()
+ * Returns: Public key as an EC point or %NULL on failure
+ */
+const struct crypto_ec_point *
+crypto_ec_key_get_public_key(struct crypto_ec_key *key);
+
+/**
+ * crypto_ec_key_get_private_key - Get EC private key as a bignum
+ * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_parse_priv()
+ * Returns: Private key as a bignum or %NULL on failure
+ */
+const struct crypto_bignum *
+crypto_ec_key_get_private_key(struct crypto_ec_key *key);
+
+/**
+ * crypto_ec_key_sign - Sign a buffer with an EC key
+ * @key: EC key from crypto_ec_key_parse_priv() or crypto_ec_key_gen()
+ * @data: Data to sign
+ * @len: Length of @data buffer
+ * Returns: Buffer with DER encoding of ASN.1 Ecdsa-Sig-Value or %NULL on failure
+ */
struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
size_t len);
+
+/**
+ * crypto_ec_key_sign_r_s - Sign a buffer with an EC key
+ * @key: EC key from crypto_ec_key_parse_priv() or crypto_ec_key_gen()
+ * @data: Data to sign
+ * @len: Length of @data buffer
+ * Returns: Buffer with the concatenated r and s values. Each value is in big
+ * endian byte order padded to the length of the prime defining the group of
+ * the key.
+ */
+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
+ const u8 *data, size_t len);
+
+/**
+ * crypto_ec_key_verify_signature - Verify ECDSA signature
+ * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_gen()
+ * @data: Data to be signed
+ * @len: Length of @data buffer
+ * @sig: DER encoding of ASN.1 Ecdsa-Sig-Value
+ * @sig_len: Length of @sig buffer
+ * Returns: 1 if signature is valid, 0 if signature is invalid and -1 on failure
+ */
int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
size_t len, const u8 *sig, size_t sig_len);
+
+/**
+ * crypto_ec_key_verify_signature_r_s - Verify signature
+ * @key: EC key from crypto_ec_key_parse/set_pub() or crypto_ec_key_gen()
+ * @data: Data to signed
+ * @len: Length of @data buffer
+ * @r: Binary data, in big endian byte order, of the 'r' field of the ECDSA
+ * signature.
+ * @s: Binary data, in big endian byte order, of the 's' field of the ECDSA
+ * signature.
+ * @r_len: Length of @r buffer
+ * @s_len: Length of @s buffer
+ * Returns: 1 if signature is valid, 0 if signature is invalid, or -1 on failure
+ */
+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
+ const u8 *data, size_t len,
+ const u8 *r, size_t r_len,
+ const u8 *s, size_t s_len);
+
+/**
+ * crypto_ec_key_group - Get IANA group identifier for an EC key
+ * @key: EC key from crypto_ec_key_parse/set_pub/priv() or crypto_ec_key_gen()
+ * Returns: IANA group identifier and -1 on failure
+ */
int crypto_ec_key_group(struct crypto_ec_key *key);
+/**
+ * crypto_ec_key_cmp - Compare two EC public keys
+ * @key1: Key 1
+ * @key2: Key 2
+ * Returns: 0 if public keys are identical, -1 otherwise
+ */
+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2);
+
+/**
+ * crypto_ec_key_debug_print - Dump EC key to debug log
+ * @key: EC key from crypto_ec_key_parse/set_pub/priv() or crypto_ec_key_gen()
+ * @title: Name of the EC point in the trace
+ */
+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
+ const char *title);
+
+/**
+ * struct crypto_csr - Certification Signing Request
+ *
+ * Internal data structure for CSR. The contents is specific to the used
+ * crypto library.
+ * For now it is assumed that only an EC public key can be used
+ */
+struct crypto_csr;
+
+/**
+ * enum crypto_csr_name - CSR name type
+ */
+enum crypto_csr_name {
+ CSR_NAME_CN,
+ CSR_NAME_SN,
+ CSR_NAME_C,
+ CSR_NAME_O,
+ CSR_NAME_OU,
+};
+
+/**
+ * enum crypto_csr_attr - CSR attribute
+ */
+enum crypto_csr_attr {
+ CSR_ATTR_CHALLENGE_PASSWORD,
+};
+
+/**
+ * crypto_csr_init - Initialize empty CSR
+ * Returns: Pointer to CSR data or %NULL on failure
+ */
+struct crypto_csr * crypto_csr_init(void);
+
+/**
+ * crypto_csr_verify - Initialize CSR from CertificationRequest
+ * @req: DER encoding of ASN.1 CertificationRequest
+ *
+ * Returns: Pointer to CSR data or %NULL on failure or if signature is invalid
+ */
+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req);
+
+/**
+ * crypto_csr_deinit - Free CSR structure
+ * @csr: CSR structure from @crypto_csr_init() or crypto_csr_verify()
+ */
+void crypto_csr_deinit(struct crypto_csr *csr);
+
+/**
+ * crypto_csr_set_ec_public_key - Set public key in CSR
+ * @csr: CSR structure from @crypto_csr_init()
+ * @key: EC public key to set as public key in the CSR
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_csr_set_ec_public_key(struct crypto_csr *csr,
+ struct crypto_ec_key *key);
+
+/**
+ * crypto_csr_set_name - Set name entry in CSR SubjectName
+ * @csr: CSR structure from @crypto_csr_init()
+ * @type: Name type to add into the CSR SubjectName
+ * @name: UTF-8 string to write in the CSR SubjectName
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
+ const char *name);
+
+/**
+ * crypto_csr_set_attribute - Set attribute in CSR
+ * @csr: CSR structure from @crypto_csr_init()
+ * @attr: Attribute identifier
+ * @attr_type: ASN.1 type of @value buffer
+ * @value: Attribute value
+ * @len: length of @value buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
+ int attr_type, const u8 *value, size_t len);
+
+/**
+ * crypto_csr_get_attribute - Get attribute from CSR
+ * @csr: CSR structure from @crypto_csr_verify()
+ * @attr: Updated with atribute identifier
+ * @len: Updated with length of returned buffer
+ * @type: ASN.1 type of the attribute buffer
+ * Returns: Type, length, and pointer on attribute value or %NULL on failure
+ */
+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
+ enum crypto_csr_attr attr,
+ size_t *len, int *type);
+
+/**
+ * crypto_csr_sign - Sign CSR and return ASN.1 CertificationRequest
+ * @csr: CSR structure from @crypto_csr_init()
+ * @key: Private key to sign the CSR (for now ony EC key are supported)
+ * @algo: Hash algorithm to use for the signature
+ * Returns: DER encoding of ASN.1 CertificationRequest for the CSR or %NULL on
+ * failure
+ */
+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
+ struct crypto_ec_key *key,
+ enum crypto_hash_alg algo);
+
#endif /* CRYPTO_H */
diff --git a/contrib/wpa/src/crypto/crypto_internal-rsa.c b/contrib/wpa/src/crypto/crypto_internal-rsa.c
index dc7f350af057..0c5ceadb805a 100644
--- a/contrib/wpa/src/crypto/crypto_internal-rsa.c
+++ b/contrib/wpa/src/crypto/crypto_internal-rsa.c
@@ -1,117 +1,117 @@
/*
* Crypto wrapper for internal crypto implementation - RSA parts
* Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto.h"
#include "tls/rsa.h"
#include "tls/pkcs1.h"
#include "tls/pkcs8.h"
-/* Dummy structures; these are just typecast to struct crypto_rsa_key */
+/* Stub structures; these are just typecast to struct crypto_rsa_key */
struct crypto_public_key;
struct crypto_private_key;
struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
{
return (struct crypto_public_key *)
crypto_rsa_import_public_key(key, len);
}
struct crypto_public_key *
crypto_public_key_import_parts(const u8 *n, size_t n_len,
const u8 *e, size_t e_len)
{
return (struct crypto_public_key *)
crypto_rsa_import_public_key_parts(n, n_len, e, e_len);
}
struct crypto_private_key * crypto_private_key_import(const u8 *key,
size_t len,
const char *passwd)
{
struct crypto_private_key *res;
/* First, check for possible PKCS #8 encoding */
res = pkcs8_key_import(key, len);
if (res)
return res;
if (passwd) {
/* Try to parse as encrypted PKCS #8 */
res = pkcs8_enc_key_import(key, len, passwd);
if (res)
return res;
}
/* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */
wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private "
"key");
return (struct crypto_private_key *)
crypto_rsa_import_private_key(key, len);
}
struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
size_t len)
{
/* No X.509 support in crypto_internal.c */
return NULL;
}
int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
const u8 *in, size_t inlen,
u8 *out, size_t *outlen)
{
return pkcs1_encrypt(2, (struct crypto_rsa_key *) key,
0, in, inlen, out, outlen);
}
int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key,
const u8 *in, size_t inlen,
u8 *out, size_t *outlen)
{
return pkcs1_v15_private_key_decrypt((struct crypto_rsa_key *) key,
in, inlen, out, outlen);
}
int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
const u8 *in, size_t inlen,
u8 *out, size_t *outlen)
{
return pkcs1_encrypt(1, (struct crypto_rsa_key *) key,
1, in, inlen, out, outlen);
}
void crypto_public_key_free(struct crypto_public_key *key)
{
crypto_rsa_free((struct crypto_rsa_key *) key);
}
void crypto_private_key_free(struct crypto_private_key *key)
{
crypto_rsa_free((struct crypto_rsa_key *) key);
}
int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key,
const u8 *crypt, size_t crypt_len,
u8 *plain, size_t *plain_len)
{
return pkcs1_decrypt_public_key((struct crypto_rsa_key *) key,
crypt, crypt_len, plain, plain_len);
}
diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c
index a4b1083bb4c1..ef669c408474 100644
--- a/contrib/wpa/src/crypto/crypto_openssl.c
+++ b/contrib/wpa/src/crypto/crypto_openssl.c
@@ -1,2348 +1,3213 @@
/*
* Wrapper functions for OpenSSL libcrypto
* Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <openssl/opensslv.h>
#include <openssl/err.h>
#include <openssl/des.h>
#include <openssl/aes.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/dh.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#ifdef CONFIG_OPENSSL_CMAC
#include <openssl/cmac.h>
#endif /* CONFIG_OPENSSL_CMAC */
#ifdef CONFIG_ECC
#include <openssl/ec.h>
#include <openssl/x509.h>
+#include <openssl/pem.h>
#endif /* CONFIG_ECC */
#include "common.h"
#include "utils/const_time.h"
#include "wpabuf.h"
#include "dh_group5.h"
#include "sha1.h"
#include "sha256.h"
#include "sha384.h"
#include "sha512.h"
#include "md5.h"
#include "aes_wrap.h"
#include "crypto.h"
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)
/* Compatibility wrappers for older versions. */
static HMAC_CTX * HMAC_CTX_new(void)
{
HMAC_CTX *ctx;
ctx = os_zalloc(sizeof(*ctx));
if (ctx)
HMAC_CTX_init(ctx);
return ctx;
}
static void HMAC_CTX_free(HMAC_CTX *ctx)
{
if (!ctx)
return;
HMAC_CTX_cleanup(ctx);
bin_clear_free(ctx, sizeof(*ctx));
}
static EVP_MD_CTX * EVP_MD_CTX_new(void)
{
EVP_MD_CTX *ctx;
ctx = os_zalloc(sizeof(*ctx));
if (ctx)
EVP_MD_CTX_init(ctx);
return ctx;
}
static void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
{
if (!ctx)
return;
EVP_MD_CTX_cleanup(ctx);
bin_clear_free(ctx, sizeof(*ctx));
}
#ifdef CONFIG_ECC
+
static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
{
if (pkey->type != EVP_PKEY_EC)
return NULL;
return pkey->pkey.ec;
}
+
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+ sig->r = r;
+ sig->s = s;
+ return 1;
+}
+
+
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+ const BIGNUM **ps)
+{
+ if (pr)
+ *pr = sig->r;
+ if (ps)
+ *ps = sig->s;
+}
+
#endif /* CONFIG_ECC */
+static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+ return ASN1_STRING_data((ASN1_STRING *) x);
+}
#endif /* OpenSSL version < 1.1.0 */
static BIGNUM * get_group5_prime(void)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
!(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)
return BN_get_rfc3526_prime_1536(NULL);
#elif !defined(OPENSSL_IS_BORINGSSL)
return get_rfc3526_prime_1536(NULL);
#else
static const unsigned char RFC3526_PRIME_1536[] = {
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6,
0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,
0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,
0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9,
0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED,
0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,
0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,
0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36,
0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F,
0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,
0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,
0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08,
0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
#endif
}
static BIGNUM * get_group5_order(void)
{
static const unsigned char RFC3526_ORDER_1536[] = {
0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE4,0x87,0xED,0x51,
0x10,0xB4,0x61,0x1A,0x62,0x63,0x31,0x45,0xC0,0x6E,0x0E,0x68,
0x94,0x81,0x27,0x04,0x45,0x33,0xE6,0x3A,0x01,0x05,0xDF,0x53,
0x1D,0x89,0xCD,0x91,0x28,0xA5,0x04,0x3C,0xC7,0x1A,0x02,0x6E,
0xF7,0xCA,0x8C,0xD9,0xE6,0x9D,0x21,0x8D,0x98,0x15,0x85,0x36,
0xF9,0x2F,0x8A,0x1B,0xA7,0xF0,0x9A,0xB6,0xB6,0xA8,0xE1,0x22,
0xF2,0x42,0xDA,0xBB,0x31,0x2F,0x3F,0x63,0x7A,0x26,0x21,0x74,
0xD3,0x1B,0xF6,0xB5,0x85,0xFF,0xAE,0x5B,0x7A,0x03,0x5B,0xF6,
0xF7,0x1C,0x35,0xFD,0xAD,0x44,0xCF,0xD2,0xD7,0x4F,0x92,0x08,
0xBE,0x25,0x8F,0xF3,0x24,0x94,0x33,0x28,0xF6,0x72,0x2D,0x9E,
0xE1,0x00,0x3E,0x5C,0x50,0xB1,0xDF,0x82,0xCC,0x6D,0x24,0x1B,
0x0E,0x2A,0xE9,0xCD,0x34,0x8B,0x1F,0xD4,0x7E,0x92,0x67,0xAF,
0xC1,0xB2,0xAE,0x91,0xEE,0x51,0xD6,0xCB,0x0E,0x31,0x79,0xAB,
0x10,0x42,0xA9,0x5D,0xCF,0x6A,0x94,0x83,0xB8,0x4B,0x4B,0x36,
0xB3,0x86,0x1A,0xA7,0x25,0x5E,0x4C,0x02,0x78,0xBA,0x36,0x04,
0x65,0x11,0xB9,0x93,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
};
return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL);
}
#ifdef OPENSSL_NO_SHA256
#define NO_SHA256_WRAPPER
#endif
#ifdef OPENSSL_NO_SHA512
#define NO_SHA384_WRAPPER
#endif
static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
EVP_MD_CTX *ctx;
size_t i;
unsigned int mac_len;
if (TEST_FAIL())
return -1;
ctx = EVP_MD_CTX_new();
if (!ctx)
return -1;
if (!EVP_DigestInit_ex(ctx, type, NULL)) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_MD_CTX_free(ctx);
return -1;
}
for (i = 0; i < num_elem; i++) {
if (!EVP_DigestUpdate(ctx, addr[i], len[i])) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate "
"failed: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_MD_CTX_free(ctx);
return -1;
}
}
if (!EVP_DigestFinal(ctx, mac, &mac_len)) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s",
ERR_error_string(ERR_get_error(), NULL));
EVP_MD_CTX_free(ctx);
return -1;
}
EVP_MD_CTX_free(ctx);
return 0;
}
#ifndef CONFIG_FIPS
int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
}
#endif /* CONFIG_FIPS */
int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
{
u8 pkey[8], next, tmp;
- int i;
- DES_key_schedule ks;
+ int i, plen, ret = -1;
+ EVP_CIPHER_CTX *ctx;
/* Add parity bits to the key */
next = 0;
for (i = 0; i < 7; i++) {
tmp = key[i];
pkey[i] = (tmp >> i) | next | 1;
next = tmp << (7 - i);
}
pkey[i] = next | 1;
- DES_set_key((DES_cblock *) &pkey, &ks);
- DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
- DES_ENCRYPT);
- return 0;
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx &&
+ EVP_EncryptInit_ex(ctx, EVP_des_ecb(), NULL, pkey, NULL) == 1 &&
+ EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
+ EVP_EncryptUpdate(ctx, cypher, &plen, clear, 8) == 1 &&
+ EVP_EncryptFinal_ex(ctx, &cypher[plen], &plen) == 1)
+ ret = 0;
+ else
+ wpa_printf(MSG_ERROR, "OpenSSL: DES encrypt failed");
+
+ if (ctx)
+ EVP_CIPHER_CTX_free(ctx);
+ return ret;
}
#ifndef CONFIG_NO_RC4
int rc4_skip(const u8 *key, size_t keylen, size_t skip,
u8 *data, size_t data_len)
{
#ifdef OPENSSL_NO_RC4
return -1;
#else /* OPENSSL_NO_RC4 */
EVP_CIPHER_CTX *ctx;
int outl;
int res = -1;
unsigned char skip_buf[16];
ctx = EVP_CIPHER_CTX_new();
if (!ctx ||
- !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
!EVP_CipherInit_ex(ctx, EVP_rc4(), NULL, NULL, NULL, 1) ||
+ !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
!EVP_CIPHER_CTX_set_key_length(ctx, keylen) ||
!EVP_CipherInit_ex(ctx, NULL, NULL, key, NULL, 1))
goto out;
while (skip >= sizeof(skip_buf)) {
size_t len = skip;
if (len > sizeof(skip_buf))
len = sizeof(skip_buf);
if (!EVP_CipherUpdate(ctx, skip_buf, &outl, skip_buf, len))
goto out;
skip -= len;
}
if (EVP_CipherUpdate(ctx, data, &outl, data, data_len))
res = 0;
out:
if (ctx)
EVP_CIPHER_CTX_free(ctx);
return res;
#endif /* OPENSSL_NO_RC4 */
}
#endif /* CONFIG_NO_RC4 */
#ifndef CONFIG_FIPS
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
}
#endif /* CONFIG_FIPS */
int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_digest_vector(EVP_sha1(), num_elem, addr, len, mac);
}
#ifndef NO_SHA256_WRAPPER
int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac)
{
return openssl_digest_vector(EVP_sha256(), num_elem, addr, len, mac);
}
#endif /* NO_SHA256_WRAPPER */
#ifndef NO_SHA384_WRAPPER
int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac)
{
return openssl_digest_vector(EVP_sha384(), num_elem, addr, len, mac);
}
#endif /* NO_SHA384_WRAPPER */
#ifndef NO_SHA512_WRAPPER
int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac)
{
return openssl_digest_vector(EVP_sha512(), num_elem, addr, len, mac);
}
#endif /* NO_SHA512_WRAPPER */
static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
{
switch (keylen) {
case 16:
return EVP_aes_128_ecb();
case 24:
return EVP_aes_192_ecb();
case 32:
return EVP_aes_256_ecb();
}
return NULL;
}
void * aes_encrypt_init(const u8 *key, size_t len)
{
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *type;
if (TEST_FAIL())
return NULL;
type = aes_get_evp_cipher(len);
if (!type) {
wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
__func__, (unsigned int) len);
return NULL;
}
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return NULL;
if (EVP_EncryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
os_free(ctx);
return NULL;
}
EVP_CIPHER_CTX_set_padding(ctx, 0);
return ctx;
}
int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
{
EVP_CIPHER_CTX *c = ctx;
int clen = 16;
if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s",
ERR_error_string(ERR_get_error(), NULL));
return -1;
}
return 0;
}
void aes_encrypt_deinit(void *ctx)
{
EVP_CIPHER_CTX *c = ctx;
u8 buf[16];
int len = sizeof(buf);
if (EVP_EncryptFinal_ex(c, buf, &len) != 1) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptFinal_ex failed: "
"%s", ERR_error_string(ERR_get_error(), NULL));
}
if (len != 0) {
wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
"in AES encrypt", len);
}
EVP_CIPHER_CTX_free(c);
}
void * aes_decrypt_init(const u8 *key, size_t len)
{
EVP_CIPHER_CTX *ctx;
const EVP_CIPHER *type;
if (TEST_FAIL())
return NULL;
type = aes_get_evp_cipher(len);
if (!type) {
wpa_printf(MSG_INFO, "%s: Unsupported len=%u",
__func__, (unsigned int) len);
return NULL;
}
ctx = EVP_CIPHER_CTX_new();
if (ctx == NULL)
return NULL;
if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
EVP_CIPHER_CTX_free(ctx);
return NULL;
}
EVP_CIPHER_CTX_set_padding(ctx, 0);
return ctx;
}
int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
{
EVP_CIPHER_CTX *c = ctx;
int plen = 16;
if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s",
ERR_error_string(ERR_get_error(), NULL));
return -1;
}
return 0;
}
void aes_decrypt_deinit(void *ctx)
{
EVP_CIPHER_CTX *c = ctx;
u8 buf[16];
int len = sizeof(buf);
if (EVP_DecryptFinal_ex(c, buf, &len) != 1) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptFinal_ex failed: "
"%s", ERR_error_string(ERR_get_error(), NULL));
}
if (len != 0) {
wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
"in AES decrypt", len);
}
EVP_CIPHER_CTX_free(c);
}
#ifndef CONFIG_FIPS
#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP
int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
{
AES_KEY actx;
int res;
if (TEST_FAIL())
return -1;
if (AES_set_encrypt_key(kek, kek_len << 3, &actx))
return -1;
res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
OPENSSL_cleanse(&actx, sizeof(actx));
return res <= 0 ? -1 : 0;
}
int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
u8 *plain)
{
AES_KEY actx;
int res;
if (TEST_FAIL())
return -1;
if (AES_set_decrypt_key(kek, kek_len << 3, &actx))
return -1;
res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
OPENSSL_cleanse(&actx, sizeof(actx));
return res <= 0 ? -1 : 0;
}
#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */
#endif /* CONFIG_FIPS */
int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
{
EVP_CIPHER_CTX *ctx;
int clen, len;
u8 buf[16];
int res = -1;
if (TEST_FAIL())
return -1;
ctx = EVP_CIPHER_CTX_new();
if (!ctx)
return -1;
clen = data_len;
len = sizeof(buf);
if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 &&
EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
EVP_EncryptUpdate(ctx, data, &clen, data, data_len) == 1 &&
clen == (int) data_len &&
EVP_EncryptFinal_ex(ctx, buf, &len) == 1 && len == 0)
res = 0;
EVP_CIPHER_CTX_free(ctx);
return res;
}
int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
{
EVP_CIPHER_CTX *ctx;
int plen, len;
u8 buf[16];
int res = -1;
if (TEST_FAIL())
return -1;
ctx = EVP_CIPHER_CTX_new();
if (!ctx)
return -1;
plen = data_len;
len = sizeof(buf);
if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 &&
EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
EVP_DecryptUpdate(ctx, data, &plen, data, data_len) == 1 &&
plen == (int) data_len &&
EVP_DecryptFinal_ex(ctx, buf, &len) == 1 && len == 0)
res = 0;
EVP_CIPHER_CTX_free(ctx);
return res;
}
int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
u8 *pubkey)
{
size_t pubkey_len, pad;
if (os_get_random(privkey, prime_len) < 0)
return -1;
if (os_memcmp(privkey, prime, prime_len) > 0) {
/* Make sure private value is smaller than prime */
privkey[0] = 0;
}
pubkey_len = prime_len;
if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len,
pubkey, &pubkey_len) < 0)
return -1;
if (pubkey_len < prime_len) {
pad = prime_len - pubkey_len;
os_memmove(pubkey + pad, pubkey, pubkey_len);
os_memset(pubkey, 0, pad);
}
return 0;
}
int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
const u8 *order, size_t order_len,
const u8 *privkey, size_t privkey_len,
const u8 *pubkey, size_t pubkey_len,
u8 *secret, size_t *len)
{
BIGNUM *pub, *p;
int res = -1;
pub = BN_bin2bn(pubkey, pubkey_len, NULL);
p = BN_bin2bn(prime, prime_len, NULL);
if (!pub || !p || BN_is_zero(pub) || BN_is_one(pub) ||
BN_cmp(pub, p) >= 0)
goto fail;
if (order) {
BN_CTX *ctx;
BIGNUM *q, *tmp;
int failed;
/* verify: pubkey^q == 1 mod p */
q = BN_bin2bn(order, order_len, NULL);
ctx = BN_CTX_new();
tmp = BN_new();
failed = !q || !ctx || !tmp ||
!BN_mod_exp(tmp, pub, q, p, ctx) ||
!BN_is_one(tmp);
BN_clear_free(q);
BN_clear_free(tmp);
BN_CTX_free(ctx);
if (failed)
goto fail;
}
res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
prime, prime_len, secret, len);
fail:
BN_clear_free(pub);
BN_clear_free(p);
return res;
}
int crypto_mod_exp(const u8 *base, size_t base_len,
const u8 *power, size_t power_len,
const u8 *modulus, size_t modulus_len,
u8 *result, size_t *result_len)
{
BIGNUM *bn_base, *bn_exp, *bn_modulus, *bn_result;
int ret = -1;
BN_CTX *ctx;
ctx = BN_CTX_new();
if (ctx == NULL)
return -1;
bn_base = BN_bin2bn(base, base_len, NULL);
bn_exp = BN_bin2bn(power, power_len, NULL);
bn_modulus = BN_bin2bn(modulus, modulus_len, NULL);
bn_result = BN_new();
if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
bn_result == NULL)
goto error;
if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus,
ctx, NULL) != 1)
goto error;
*result_len = BN_bn2bin(bn_result, result);
ret = 0;
error:
BN_clear_free(bn_base);
BN_clear_free(bn_exp);
BN_clear_free(bn_modulus);
BN_clear_free(bn_result);
BN_CTX_free(ctx);
return ret;
}
struct crypto_cipher {
EVP_CIPHER_CTX *enc;
EVP_CIPHER_CTX *dec;
};
struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
const u8 *iv, const u8 *key,
size_t key_len)
{
struct crypto_cipher *ctx;
const EVP_CIPHER *cipher;
ctx = os_zalloc(sizeof(*ctx));
if (ctx == NULL)
return NULL;
switch (alg) {
#ifndef CONFIG_NO_RC4
#ifndef OPENSSL_NO_RC4
case CRYPTO_CIPHER_ALG_RC4:
cipher = EVP_rc4();
break;
#endif /* OPENSSL_NO_RC4 */
#endif /* CONFIG_NO_RC4 */
#ifndef OPENSSL_NO_AES
case CRYPTO_CIPHER_ALG_AES:
switch (key_len) {
case 16:
cipher = EVP_aes_128_cbc();
break;
#ifndef OPENSSL_IS_BORINGSSL
case 24:
cipher = EVP_aes_192_cbc();
break;
#endif /* OPENSSL_IS_BORINGSSL */
case 32:
cipher = EVP_aes_256_cbc();
break;
default:
os_free(ctx);
return NULL;
}
break;
#endif /* OPENSSL_NO_AES */
#ifndef OPENSSL_NO_DES
case CRYPTO_CIPHER_ALG_3DES:
cipher = EVP_des_ede3_cbc();
break;
case CRYPTO_CIPHER_ALG_DES:
cipher = EVP_des_cbc();
break;
#endif /* OPENSSL_NO_DES */
#ifndef OPENSSL_NO_RC2
case CRYPTO_CIPHER_ALG_RC2:
cipher = EVP_rc2_ecb();
break;
#endif /* OPENSSL_NO_RC2 */
default:
os_free(ctx);
return NULL;
}
if (!(ctx->enc = EVP_CIPHER_CTX_new()) ||
- !EVP_CIPHER_CTX_set_padding(ctx->enc, 0) ||
!EVP_EncryptInit_ex(ctx->enc, cipher, NULL, NULL, NULL) ||
+ !EVP_CIPHER_CTX_set_padding(ctx->enc, 0) ||
!EVP_CIPHER_CTX_set_key_length(ctx->enc, key_len) ||
!EVP_EncryptInit_ex(ctx->enc, NULL, NULL, key, iv)) {
if (ctx->enc)
EVP_CIPHER_CTX_free(ctx->enc);
os_free(ctx);
return NULL;
}
if (!(ctx->dec = EVP_CIPHER_CTX_new()) ||
- !EVP_CIPHER_CTX_set_padding(ctx->dec, 0) ||
!EVP_DecryptInit_ex(ctx->dec, cipher, NULL, NULL, NULL) ||
+ !EVP_CIPHER_CTX_set_padding(ctx->dec, 0) ||
!EVP_CIPHER_CTX_set_key_length(ctx->dec, key_len) ||
!EVP_DecryptInit_ex(ctx->dec, NULL, NULL, key, iv)) {
EVP_CIPHER_CTX_free(ctx->enc);
if (ctx->dec)
EVP_CIPHER_CTX_free(ctx->dec);
os_free(ctx);
return NULL;
}
return ctx;
}
int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
u8 *crypt, size_t len)
{
int outl;
if (!EVP_EncryptUpdate(ctx->enc, crypt, &outl, plain, len))
return -1;
return 0;
}
int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
u8 *plain, size_t len)
{
int outl;
outl = len;
if (!EVP_DecryptUpdate(ctx->dec, plain, &outl, crypt, len))
return -1;
return 0;
}
void crypto_cipher_deinit(struct crypto_cipher *ctx)
{
EVP_CIPHER_CTX_free(ctx->enc);
EVP_CIPHER_CTX_free(ctx->dec);
os_free(ctx);
}
void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)
DH *dh;
struct wpabuf *pubkey = NULL, *privkey = NULL;
size_t publen, privlen;
*priv = NULL;
wpabuf_free(*publ);
*publ = NULL;
dh = DH_new();
if (dh == NULL)
return NULL;
dh->g = BN_new();
if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
goto err;
dh->p = get_group5_prime();
if (dh->p == NULL)
goto err;
dh->q = get_group5_order();
if (!dh->q)
goto err;
if (DH_generate_key(dh) != 1)
goto err;
publen = BN_num_bytes(dh->pub_key);
pubkey = wpabuf_alloc(publen);
if (pubkey == NULL)
goto err;
privlen = BN_num_bytes(dh->priv_key);
privkey = wpabuf_alloc(privlen);
if (privkey == NULL)
goto err;
BN_bn2bin(dh->pub_key, wpabuf_put(pubkey, publen));
BN_bn2bin(dh->priv_key, wpabuf_put(privkey, privlen));
*priv = privkey;
*publ = pubkey;
return dh;
err:
wpabuf_clear_free(pubkey);
wpabuf_clear_free(privkey);
DH_free(dh);
return NULL;
#else
DH *dh;
struct wpabuf *pubkey = NULL, *privkey = NULL;
size_t publen, privlen;
BIGNUM *p, *g, *q;
const BIGNUM *priv_key = NULL, *pub_key = NULL;
*priv = NULL;
wpabuf_free(*publ);
*publ = NULL;
dh = DH_new();
if (dh == NULL)
return NULL;
g = BN_new();
p = get_group5_prime();
q = get_group5_order();
if (!g || BN_set_word(g, 2) != 1 || !p || !q ||
DH_set0_pqg(dh, p, q, g) != 1)
goto err;
p = NULL;
q = NULL;
g = NULL;
if (DH_generate_key(dh) != 1)
goto err;
DH_get0_key(dh, &pub_key, &priv_key);
publen = BN_num_bytes(pub_key);
pubkey = wpabuf_alloc(publen);
if (!pubkey)
goto err;
privlen = BN_num_bytes(priv_key);
privkey = wpabuf_alloc(privlen);
if (!privkey)
goto err;
BN_bn2bin(pub_key, wpabuf_put(pubkey, publen));
BN_bn2bin(priv_key, wpabuf_put(privkey, privlen));
*priv = privkey;
*publ = pubkey;
return dh;
err:
BN_free(p);
BN_free(q);
BN_free(g);
wpabuf_clear_free(pubkey);
wpabuf_clear_free(privkey);
DH_free(dh);
return NULL;
#endif
}
void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)
DH *dh;
dh = DH_new();
if (dh == NULL)
return NULL;
dh->g = BN_new();
if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
goto err;
dh->p = get_group5_prime();
if (dh->p == NULL)
goto err;
dh->priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
if (dh->priv_key == NULL)
goto err;
dh->pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
if (dh->pub_key == NULL)
goto err;
if (DH_generate_key(dh) != 1)
goto err;
return dh;
err:
DH_free(dh);
return NULL;
#else
DH *dh;
BIGNUM *p = NULL, *g, *priv_key = NULL, *pub_key = NULL;
dh = DH_new();
if (dh == NULL)
return NULL;
g = BN_new();
p = get_group5_prime();
if (!g || BN_set_word(g, 2) != 1 || !p ||
DH_set0_pqg(dh, p, NULL, g) != 1)
goto err;
p = NULL;
g = NULL;
priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
if (!priv_key || !pub_key || DH_set0_key(dh, pub_key, priv_key) != 1)
goto err;
pub_key = NULL;
priv_key = NULL;
if (DH_generate_key(dh) != 1)
goto err;
return dh;
err:
BN_free(p);
BN_free(g);
BN_free(pub_key);
BN_clear_free(priv_key);
DH_free(dh);
return NULL;
#endif
}
struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
const struct wpabuf *own_private)
{
BIGNUM *pub_key;
struct wpabuf *res = NULL;
size_t rlen;
DH *dh = ctx;
int keylen;
if (ctx == NULL)
return NULL;
pub_key = BN_bin2bn(wpabuf_head(peer_public), wpabuf_len(peer_public),
NULL);
if (pub_key == NULL)
return NULL;
rlen = DH_size(dh);
res = wpabuf_alloc(rlen);
if (res == NULL)
goto err;
keylen = DH_compute_key(wpabuf_mhead(res), pub_key, dh);
if (keylen < 0)
goto err;
wpabuf_put(res, keylen);
BN_clear_free(pub_key);
return res;
err:
BN_clear_free(pub_key);
wpabuf_clear_free(res);
return NULL;
}
void dh5_free(void *ctx)
{
DH *dh;
if (ctx == NULL)
return;
dh = ctx;
DH_free(dh);
}
struct crypto_hash {
HMAC_CTX *ctx;
};
struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
size_t key_len)
{
struct crypto_hash *ctx;
const EVP_MD *md;
switch (alg) {
#ifndef OPENSSL_NO_MD5
case CRYPTO_HASH_ALG_HMAC_MD5:
md = EVP_md5();
break;
#endif /* OPENSSL_NO_MD5 */
#ifndef OPENSSL_NO_SHA
case CRYPTO_HASH_ALG_HMAC_SHA1:
md = EVP_sha1();
break;
#endif /* OPENSSL_NO_SHA */
#ifndef OPENSSL_NO_SHA256
#ifdef CONFIG_SHA256
case CRYPTO_HASH_ALG_HMAC_SHA256:
md = EVP_sha256();
break;
#endif /* CONFIG_SHA256 */
#endif /* OPENSSL_NO_SHA256 */
default:
return NULL;
}
ctx = os_zalloc(sizeof(*ctx));
if (ctx == NULL)
return NULL;
ctx->ctx = HMAC_CTX_new();
if (!ctx->ctx) {
os_free(ctx);
return NULL;
}
if (HMAC_Init_ex(ctx->ctx, key, key_len, md, NULL) != 1) {
HMAC_CTX_free(ctx->ctx);
bin_clear_free(ctx, sizeof(*ctx));
return NULL;
}
return ctx;
}
void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
{
if (ctx == NULL)
return;
HMAC_Update(ctx->ctx, data, len);
}
int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
{
unsigned int mdlen;
int res;
if (ctx == NULL)
return -2;
if (mac == NULL || len == NULL) {
HMAC_CTX_free(ctx->ctx);
bin_clear_free(ctx, sizeof(*ctx));
return 0;
}
mdlen = *len;
res = HMAC_Final(ctx->ctx, mac, &mdlen);
HMAC_CTX_free(ctx->ctx);
bin_clear_free(ctx, sizeof(*ctx));
if (TEST_FAIL())
return -1;
if (res == 1) {
*len = mdlen;
return 0;
}
return -1;
}
static int openssl_hmac_vector(const EVP_MD *type, const u8 *key,
size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac,
unsigned int mdlen)
{
HMAC_CTX *ctx;
size_t i;
int res;
if (TEST_FAIL())
return -1;
ctx = HMAC_CTX_new();
if (!ctx)
return -1;
res = HMAC_Init_ex(ctx, key, key_len, type, NULL);
if (res != 1)
goto done;
for (i = 0; i < num_elem; i++)
HMAC_Update(ctx, addr[i], len[i]);
res = HMAC_Final(ctx, mac, &mdlen);
done:
HMAC_CTX_free(ctx);
return res == 1 ? 0 : -1;
}
#ifndef CONFIG_FIPS
int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_hmac_vector(EVP_md5(), key ,key_len, num_elem, addr, len,
mac, 16);
}
int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
u8 *mac)
{
return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
}
#endif /* CONFIG_FIPS */
int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
int iterations, u8 *buf, size_t buflen)
{
if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
ssid_len, iterations, buflen, buf) != 1)
return -1;
return 0;
}
int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_hmac_vector(EVP_sha1(), key, key_len, num_elem, addr,
len, mac, 20);
}
int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
u8 *mac)
{
return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
}
#ifdef CONFIG_SHA256
int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_hmac_vector(EVP_sha256(), key, key_len, num_elem, addr,
len, mac, 32);
}
int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
size_t data_len, u8 *mac)
{
return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
}
#endif /* CONFIG_SHA256 */
#ifdef CONFIG_SHA384
int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr,
len, mac, 48);
}
int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
size_t data_len, u8 *mac)
{
return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
}
#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SHA512
int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return openssl_hmac_vector(EVP_sha512(), key, key_len, num_elem, addr,
len, mac, 64);
}
int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
size_t data_len, u8 *mac)
{
return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
}
#endif /* CONFIG_SHA512 */
int crypto_get_random(void *buf, size_t len)
{
if (RAND_bytes(buf, len) != 1)
return -1;
return 0;
}
#ifdef CONFIG_OPENSSL_CMAC
int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
CMAC_CTX *ctx;
int ret = -1;
size_t outlen, i;
if (TEST_FAIL())
return -1;
ctx = CMAC_CTX_new();
if (ctx == NULL)
return -1;
if (key_len == 32) {
if (!CMAC_Init(ctx, key, 32, EVP_aes_256_cbc(), NULL))
goto fail;
} else if (key_len == 16) {
if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
goto fail;
} else {
goto fail;
}
for (i = 0; i < num_elem; i++) {
if (!CMAC_Update(ctx, addr[i], len[i]))
goto fail;
}
if (!CMAC_Final(ctx, mac, &outlen) || outlen != 16)
goto fail;
ret = 0;
fail:
CMAC_CTX_free(ctx);
return ret;
}
int omac1_aes_128_vector(const u8 *key, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
}
int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
{
return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
}
int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
{
return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
}
#endif /* CONFIG_OPENSSL_CMAC */
struct crypto_bignum * crypto_bignum_init(void)
{
if (TEST_FAIL())
return NULL;
return (struct crypto_bignum *) BN_new();
}
struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
{
BIGNUM *bn;
if (TEST_FAIL())
return NULL;
bn = BN_bin2bn(buf, len, NULL);
return (struct crypto_bignum *) bn;
}
struct crypto_bignum * crypto_bignum_init_uint(unsigned int val)
{
BIGNUM *bn;
if (TEST_FAIL())
return NULL;
bn = BN_new();
if (!bn)
return NULL;
if (BN_set_word(bn, val) != 1) {
BN_free(bn);
return NULL;
}
return (struct crypto_bignum *) bn;
}
void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
{
if (clear)
BN_clear_free((BIGNUM *) n);
else
BN_free((BIGNUM *) n);
}
int crypto_bignum_to_bin(const struct crypto_bignum *a,
u8 *buf, size_t buflen, size_t padlen)
{
int num_bytes, offset;
if (TEST_FAIL())
return -1;
if (padlen > buflen)
return -1;
if (padlen) {
#ifdef OPENSSL_IS_BORINGSSL
if (BN_bn2bin_padded(buf, padlen, (const BIGNUM *) a) == 0)
return -1;
return padlen;
#else /* OPENSSL_IS_BORINGSSL */
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
return BN_bn2binpad((const BIGNUM *) a, buf, padlen);
#endif
#endif
}
num_bytes = BN_num_bytes((const BIGNUM *) a);
if ((size_t) num_bytes > buflen)
return -1;
if (padlen > (size_t) num_bytes)
offset = padlen - num_bytes;
else
offset = 0;
os_memset(buf, 0, offset);
BN_bn2bin((const BIGNUM *) a, buf + offset);
return num_bytes + offset;
}
int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
{
if (TEST_FAIL())
return -1;
return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1;
}
int crypto_bignum_add(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c)
{
return BN_add((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
0 : -1;
}
int crypto_bignum_mod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c)
{
int res;
BN_CTX *bnctx;
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
res = BN_mod((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b,
bnctx);
BN_CTX_free(bnctx);
return res ? 0 : -1;
}
int crypto_bignum_exptmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
const struct crypto_bignum *c,
struct crypto_bignum *d)
{
int res;
BN_CTX *bnctx;
if (TEST_FAIL())
return -1;
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a,
(const BIGNUM *) b, (const BIGNUM *) c,
bnctx, NULL);
BN_CTX_free(bnctx);
return res ? 0 : -1;
}
int crypto_bignum_inverse(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c)
{
BIGNUM *res;
BN_CTX *bnctx;
if (TEST_FAIL())
return -1;
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
#ifdef OPENSSL_IS_BORINGSSL
/* TODO: use BN_mod_inverse_blinded() ? */
#else /* OPENSSL_IS_BORINGSSL */
BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
#endif /* OPENSSL_IS_BORINGSSL */
res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
(const BIGNUM *) b, bnctx);
BN_CTX_free(bnctx);
return res ? 0 : -1;
}
int crypto_bignum_sub(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c)
{
if (TEST_FAIL())
return -1;
return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
0 : -1;
}
int crypto_bignum_div(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c)
{
int res;
BN_CTX *bnctx;
if (TEST_FAIL())
return -1;
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
#ifndef OPENSSL_IS_BORINGSSL
BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
#endif /* OPENSSL_IS_BORINGSSL */
res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
(const BIGNUM *) b, bnctx);
BN_CTX_free(bnctx);
return res ? 0 : -1;
}
int crypto_bignum_addmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
const struct crypto_bignum *c,
struct crypto_bignum *d)
{
int res;
BN_CTX *bnctx;
if (TEST_FAIL())
return -1;
bnctx = BN_CTX_new();
if (!bnctx)
return -1;
res = BN_mod_add((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
(const BIGNUM *) c, bnctx);
BN_CTX_free(bnctx);
return res ? 0 : -1;
}
int crypto_bignum_mulmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
const struct crypto_bignum *c,
struct crypto_bignum *d)
{
int res;
BN_CTX *bnctx;
if (TEST_FAIL())
return -1;
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
res = BN_mod_mul((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
(const BIGNUM *) c, bnctx);
BN_CTX_free(bnctx);
return res ? 0 : -1;
}
int crypto_bignum_sqrmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c)
{
int res;
BN_CTX *bnctx;
if (TEST_FAIL())
return -1;
bnctx = BN_CTX_new();
if (!bnctx)
return -1;
res = BN_mod_sqr((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b,
bnctx);
BN_CTX_free(bnctx);
return res ? 0 : -1;
}
int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
struct crypto_bignum *r)
{
/* Note: BN_rshift() does not modify the first argument even though it
* has not been marked const. */
return BN_rshift((BIGNUM *) a, (BIGNUM *) r, n) == 1 ? 0 : -1;
}
int crypto_bignum_cmp(const struct crypto_bignum *a,
const struct crypto_bignum *b)
{
return BN_cmp((const BIGNUM *) a, (const BIGNUM *) b);
}
int crypto_bignum_is_zero(const struct crypto_bignum *a)
{
return BN_is_zero((const BIGNUM *) a);
}
int crypto_bignum_is_one(const struct crypto_bignum *a)
{
return BN_is_one((const BIGNUM *) a);
}
int crypto_bignum_is_odd(const struct crypto_bignum *a)
{
return BN_is_odd((const BIGNUM *) a);
}
int crypto_bignum_legendre(const struct crypto_bignum *a,
const struct crypto_bignum *p)
{
BN_CTX *bnctx;
BIGNUM *exp = NULL, *tmp = NULL;
int res = -2;
unsigned int mask;
if (TEST_FAIL())
return -2;
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -2;
exp = BN_new();
tmp = BN_new();
if (!exp || !tmp ||
/* exp = (p-1) / 2 */
!BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
!BN_rshift1(exp, exp) ||
!BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp,
(const BIGNUM *) p, bnctx, NULL))
goto fail;
/* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use
* constant time selection to avoid branches here. */
res = -1;
mask = const_time_eq(BN_is_word(tmp, 1), 1);
res = const_time_select_int(mask, 1, res);
mask = const_time_eq(BN_is_zero(tmp), 1);
res = const_time_select_int(mask, 0, res);
fail:
BN_clear_free(tmp);
BN_clear_free(exp);
BN_CTX_free(bnctx);
return res;
}
#ifdef CONFIG_ECC
struct crypto_ec {
EC_GROUP *group;
int nid;
BN_CTX *bnctx;
BIGNUM *prime;
BIGNUM *order;
BIGNUM *a;
BIGNUM *b;
};
-struct crypto_ec * crypto_ec_init(int group)
-{
- struct crypto_ec *e;
- int nid;
+static int crypto_ec_group_2_nid(int group)
+{
/* Map from IANA registry for IKE D-H groups to OpenSSL NID */
switch (group) {
case 19:
- nid = NID_X9_62_prime256v1;
- break;
+ return NID_X9_62_prime256v1;
case 20:
- nid = NID_secp384r1;
- break;
+ return NID_secp384r1;
case 21:
- nid = NID_secp521r1;
- break;
+ return NID_secp521r1;
case 25:
- nid = NID_X9_62_prime192v1;
- break;
+ return NID_X9_62_prime192v1;
case 26:
- nid = NID_secp224r1;
- break;
+ return NID_secp224r1;
#ifdef NID_brainpoolP224r1
case 27:
- nid = NID_brainpoolP224r1;
- break;
+ return NID_brainpoolP224r1;
#endif /* NID_brainpoolP224r1 */
#ifdef NID_brainpoolP256r1
case 28:
- nid = NID_brainpoolP256r1;
- break;
+ return NID_brainpoolP256r1;
#endif /* NID_brainpoolP256r1 */
#ifdef NID_brainpoolP384r1
case 29:
- nid = NID_brainpoolP384r1;
- break;
+ return NID_brainpoolP384r1;
#endif /* NID_brainpoolP384r1 */
#ifdef NID_brainpoolP512r1
case 30:
- nid = NID_brainpoolP512r1;
- break;
+ return NID_brainpoolP512r1;
#endif /* NID_brainpoolP512r1 */
default:
- return NULL;
+ return -1;
}
+}
+
+
+struct crypto_ec * crypto_ec_init(int group)
+{
+ struct crypto_ec *e;
+ int nid;
+
+ nid = crypto_ec_group_2_nid(group);
+ if (nid < 0)
+ return NULL;
e = os_zalloc(sizeof(*e));
if (e == NULL)
return NULL;
e->nid = nid;
e->bnctx = BN_CTX_new();
e->group = EC_GROUP_new_by_curve_name(nid);
e->prime = BN_new();
e->order = BN_new();
e->a = BN_new();
e->b = BN_new();
if (e->group == NULL || e->bnctx == NULL || e->prime == NULL ||
e->order == NULL || e->a == NULL || e->b == NULL ||
!EC_GROUP_get_curve_GFp(e->group, e->prime, e->a, e->b, e->bnctx) ||
!EC_GROUP_get_order(e->group, e->order, e->bnctx)) {
crypto_ec_deinit(e);
e = NULL;
}
return e;
}
void crypto_ec_deinit(struct crypto_ec *e)
{
if (e == NULL)
return;
BN_clear_free(e->b);
BN_clear_free(e->a);
BN_clear_free(e->order);
BN_clear_free(e->prime);
EC_GROUP_free(e->group);
BN_CTX_free(e->bnctx);
os_free(e);
}
struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
{
if (TEST_FAIL())
return NULL;
if (e == NULL)
return NULL;
return (struct crypto_ec_point *) EC_POINT_new(e->group);
}
size_t crypto_ec_prime_len(struct crypto_ec *e)
{
return BN_num_bytes(e->prime);
}
size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
{
return BN_num_bits(e->prime);
}
size_t crypto_ec_order_len(struct crypto_ec *e)
{
return BN_num_bytes(e->order);
}
const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
{
return (const struct crypto_bignum *) e->prime;
}
const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
{
return (const struct crypto_bignum *) e->order;
}
const struct crypto_bignum * crypto_ec_get_a(struct crypto_ec *e)
{
return (const struct crypto_bignum *) e->a;
}
const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e)
{
return (const struct crypto_bignum *) e->b;
}
+const struct crypto_ec_point * crypto_ec_get_generator(struct crypto_ec *e)
+{
+ return (const struct crypto_ec_point *)
+ EC_GROUP_get0_generator(e->group);
+}
+
+
void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
{
if (clear)
EC_POINT_clear_free((EC_POINT *) p);
else
EC_POINT_free((EC_POINT *) p);
}
int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
struct crypto_bignum *x)
{
return EC_POINT_get_affine_coordinates_GFp(e->group,
(const EC_POINT *) p,
(BIGNUM *) x, NULL,
e->bnctx) == 1 ? 0 : -1;
}
int crypto_ec_point_to_bin(struct crypto_ec *e,
const struct crypto_ec_point *point, u8 *x, u8 *y)
{
BIGNUM *x_bn, *y_bn;
int ret = -1;
int len = BN_num_bytes(e->prime);
if (TEST_FAIL())
return -1;
x_bn = BN_new();
y_bn = BN_new();
if (x_bn && y_bn &&
EC_POINT_get_affine_coordinates_GFp(e->group, (EC_POINT *) point,
x_bn, y_bn, e->bnctx)) {
if (x) {
crypto_bignum_to_bin((struct crypto_bignum *) x_bn,
x, len, len);
}
if (y) {
crypto_bignum_to_bin((struct crypto_bignum *) y_bn,
y, len, len);
}
ret = 0;
}
BN_clear_free(x_bn);
BN_clear_free(y_bn);
return ret;
}
struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
const u8 *val)
{
BIGNUM *x, *y;
EC_POINT *elem;
int len = BN_num_bytes(e->prime);
if (TEST_FAIL())
return NULL;
x = BN_bin2bn(val, len, NULL);
y = BN_bin2bn(val + len, len, NULL);
elem = EC_POINT_new(e->group);
if (x == NULL || y == NULL || elem == NULL) {
BN_clear_free(x);
BN_clear_free(y);
EC_POINT_clear_free(elem);
return NULL;
}
if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y,
e->bnctx)) {
EC_POINT_clear_free(elem);
elem = NULL;
}
BN_clear_free(x);
BN_clear_free(y);
return (struct crypto_ec_point *) elem;
}
int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
const struct crypto_ec_point *b,
struct crypto_ec_point *c)
{
if (TEST_FAIL())
return -1;
return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a,
(const EC_POINT *) b, e->bnctx) ? 0 : -1;
}
int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
const struct crypto_bignum *b,
struct crypto_ec_point *res)
{
if (TEST_FAIL())
return -1;
return EC_POINT_mul(e->group, (EC_POINT *) res, NULL,
(const EC_POINT *) p, (const BIGNUM *) b, e->bnctx)
? 0 : -1;
}
int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
{
if (TEST_FAIL())
return -1;
return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1;
}
int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
struct crypto_ec_point *p,
const struct crypto_bignum *x, int y_bit)
{
if (TEST_FAIL())
return -1;
if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p,
(const BIGNUM *) x, y_bit,
e->bnctx) ||
!EC_POINT_is_on_curve(e->group, (EC_POINT *) p, e->bnctx))
return -1;
return 0;
}
struct crypto_bignum *
crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
const struct crypto_bignum *x)
{
BIGNUM *tmp, *tmp2, *y_sqr = NULL;
if (TEST_FAIL())
return NULL;
tmp = BN_new();
tmp2 = BN_new();
/* y^2 = x^3 + ax + b */
if (tmp && tmp2 &&
BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
BN_mod_mul(tmp2, e->a, (const BIGNUM *) x, e->prime, e->bnctx) &&
BN_mod_add_quick(tmp2, tmp2, tmp, e->prime) &&
BN_mod_add_quick(tmp2, tmp2, e->b, e->prime)) {
y_sqr = tmp2;
tmp2 = NULL;
}
BN_clear_free(tmp);
BN_clear_free(tmp2);
return (struct crypto_bignum *) y_sqr;
}
int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
const struct crypto_ec_point *p)
{
return EC_POINT_is_at_infinity(e->group, (const EC_POINT *) p);
}
int crypto_ec_point_is_on_curve(struct crypto_ec *e,
const struct crypto_ec_point *p)
{
return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p,
e->bnctx) == 1;
}
int crypto_ec_point_cmp(const struct crypto_ec *e,
const struct crypto_ec_point *a,
const struct crypto_ec_point *b)
{
return EC_POINT_cmp(e->group, (const EC_POINT *) a,
(const EC_POINT *) b, e->bnctx);
}
+void crypto_ec_point_debug_print(const struct crypto_ec *e,
+ const struct crypto_ec_point *p,
+ const char *title)
+{
+ BIGNUM *x, *y;
+ char *x_str = NULL, *y_str = NULL;
+
+ x = BN_new();
+ y = BN_new();
+ if (!x || !y ||
+ EC_POINT_get_affine_coordinates_GFp(e->group, (const EC_POINT *) p,
+ x, y, e->bnctx) != 1)
+ goto fail;
+
+ x_str = BN_bn2hex(x);
+ y_str = BN_bn2hex(y);
+ if (!x_str || !y_str)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
+
+fail:
+ OPENSSL_free(x_str);
+ OPENSSL_free(y_str);
+ BN_free(x);
+ BN_free(y);
+}
+
+
struct crypto_ecdh {
struct crypto_ec *ec;
EVP_PKEY *pkey;
};
struct crypto_ecdh * crypto_ecdh_init(int group)
{
struct crypto_ecdh *ecdh;
EVP_PKEY *params = NULL;
EC_KEY *ec_params = NULL;
EVP_PKEY_CTX *kctx = NULL;
ecdh = os_zalloc(sizeof(*ecdh));
if (!ecdh)
goto fail;
ecdh->ec = crypto_ec_init(group);
if (!ecdh->ec)
goto fail;
ec_params = EC_KEY_new_by_curve_name(ecdh->ec->nid);
if (!ec_params) {
wpa_printf(MSG_ERROR,
"OpenSSL: Failed to generate EC_KEY parameters");
goto fail;
}
EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
params = EVP_PKEY_new();
if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
wpa_printf(MSG_ERROR,
"OpenSSL: Failed to generate EVP_PKEY parameters");
goto fail;
}
kctx = EVP_PKEY_CTX_new(params, NULL);
if (!kctx)
goto fail;
if (EVP_PKEY_keygen_init(kctx) != 1) {
wpa_printf(MSG_ERROR,
"OpenSSL: EVP_PKEY_keygen_init failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
if (EVP_PKEY_keygen(kctx, &ecdh->pkey) != 1) {
wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_keygen failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
done:
EC_KEY_free(ec_params);
EVP_PKEY_free(params);
EVP_PKEY_CTX_free(kctx);
return ecdh;
fail:
crypto_ecdh_deinit(ecdh);
ecdh = NULL;
goto done;
}
+struct crypto_ecdh * crypto_ecdh_init2(int group, struct crypto_ec_key *own_key)
+{
+ struct crypto_ecdh *ecdh;
+
+ ecdh = os_zalloc(sizeof(*ecdh));
+ if (!ecdh)
+ goto fail;
+
+ ecdh->ec = crypto_ec_init(group);
+ if (!ecdh->ec)
+ goto fail;
+
+ ecdh->pkey = EVP_PKEY_new();
+ if (!ecdh->pkey ||
+ EVP_PKEY_assign_EC_KEY(ecdh->pkey,
+ EVP_PKEY_get1_EC_KEY((EVP_PKEY *) own_key))
+ != 1)
+ goto fail;
+
+ return ecdh;
+fail:
+ crypto_ecdh_deinit(ecdh);
+ return NULL;
+}
+
+
struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
{
struct wpabuf *buf = NULL;
EC_KEY *eckey;
const EC_POINT *pubkey;
BIGNUM *x, *y = NULL;
int len = BN_num_bytes(ecdh->ec->prime);
int res;
eckey = EVP_PKEY_get1_EC_KEY(ecdh->pkey);
if (!eckey)
return NULL;
pubkey = EC_KEY_get0_public_key(eckey);
if (!pubkey)
return NULL;
x = BN_new();
if (inc_y) {
y = BN_new();
if (!y)
goto fail;
}
buf = wpabuf_alloc(inc_y ? 2 * len : len);
if (!x || !buf)
goto fail;
if (EC_POINT_get_affine_coordinates_GFp(ecdh->ec->group, pubkey,
x, y, ecdh->ec->bnctx) != 1) {
wpa_printf(MSG_ERROR,
"OpenSSL: EC_POINT_get_affine_coordinates_GFp failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
res = crypto_bignum_to_bin((struct crypto_bignum *) x,
wpabuf_put(buf, len), len, len);
if (res < 0)
goto fail;
if (inc_y) {
res = crypto_bignum_to_bin((struct crypto_bignum *) y,
wpabuf_put(buf, len), len, len);
if (res < 0)
goto fail;
}
done:
BN_clear_free(x);
BN_clear_free(y);
EC_KEY_free(eckey);
return buf;
fail:
wpabuf_free(buf);
buf = NULL;
goto done;
}
struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
const u8 *key, size_t len)
{
BIGNUM *x, *y = NULL;
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *peerkey = NULL;
struct wpabuf *secret = NULL;
size_t secret_len;
EC_POINT *pub;
EC_KEY *eckey = NULL;
x = BN_bin2bn(key, inc_y ? len / 2 : len, NULL);
pub = EC_POINT_new(ecdh->ec->group);
if (!x || !pub)
goto fail;
if (inc_y) {
y = BN_bin2bn(key + len / 2, len / 2, NULL);
if (!y)
goto fail;
if (!EC_POINT_set_affine_coordinates_GFp(ecdh->ec->group, pub,
x, y,
ecdh->ec->bnctx)) {
wpa_printf(MSG_ERROR,
"OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
} else if (!EC_POINT_set_compressed_coordinates_GFp(ecdh->ec->group,
pub, x, 0,
ecdh->ec->bnctx)) {
wpa_printf(MSG_ERROR,
"OpenSSL: EC_POINT_set_compressed_coordinates_GFp failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
if (!EC_POINT_is_on_curve(ecdh->ec->group, pub, ecdh->ec->bnctx)) {
wpa_printf(MSG_ERROR,
"OpenSSL: ECDH peer public key is not on curve");
goto fail;
}
eckey = EC_KEY_new_by_curve_name(ecdh->ec->nid);
if (!eckey || EC_KEY_set_public_key(eckey, pub) != 1) {
wpa_printf(MSG_ERROR,
"OpenSSL: EC_KEY_set_public_key failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
peerkey = EVP_PKEY_new();
if (!peerkey || EVP_PKEY_set1_EC_KEY(peerkey, eckey) != 1)
goto fail;
ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL);
if (!ctx || EVP_PKEY_derive_init(ctx) != 1 ||
EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 ||
EVP_PKEY_derive(ctx, NULL, &secret_len) != 1) {
wpa_printf(MSG_ERROR,
"OpenSSL: EVP_PKEY_derive(1) failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
secret = wpabuf_alloc(secret_len);
if (!secret)
goto fail;
if (EVP_PKEY_derive(ctx, wpabuf_put(secret, 0), &secret_len) != 1) {
wpa_printf(MSG_ERROR,
"OpenSSL: EVP_PKEY_derive(2) failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
if (secret->size != secret_len)
wpa_printf(MSG_DEBUG,
"OpenSSL: EVP_PKEY_derive(2) changed secret_len %d -> %d",
(int) secret->size, (int) secret_len);
wpabuf_put(secret, secret_len);
done:
BN_free(x);
BN_free(y);
EC_KEY_free(eckey);
EC_POINT_free(pub);
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(peerkey);
return secret;
fail:
wpabuf_free(secret);
secret = NULL;
goto done;
}
void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
{
if (ecdh) {
crypto_ec_deinit(ecdh->ec);
EVP_PKEY_free(ecdh->pkey);
os_free(ecdh);
}
}
size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
{
return crypto_ec_prime_len(ecdh->ec);
}
-struct crypto_ec_key {
- EVP_PKEY *pkey;
- EC_KEY *eckey;
-};
-
-
struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len)
{
- struct crypto_ec_key *key;
-
- key = os_zalloc(sizeof(*key));
- if (!key)
- return NULL;
+ EVP_PKEY *pkey = NULL;
+ EC_KEY *eckey;
- key->eckey = d2i_ECPrivateKey(NULL, &der, der_len);
- if (!key->eckey) {
+ eckey = d2i_ECPrivateKey(NULL, &der, der_len);
+ if (!eckey) {
wpa_printf(MSG_INFO, "OpenSSL: d2i_ECPrivateKey() failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
- EC_KEY_set_conv_form(key->eckey, POINT_CONVERSION_COMPRESSED);
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
- key->pkey = EVP_PKEY_new();
- if (!key->pkey || EVP_PKEY_assign_EC_KEY(key->pkey, key->eckey) != 1) {
- EC_KEY_free(key->eckey);
- key->eckey = NULL;
+ pkey = EVP_PKEY_new();
+ if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+ EC_KEY_free(eckey);
goto fail;
}
- return key;
+ return (struct crypto_ec_key *) pkey;
fail:
- crypto_ec_key_deinit(key);
+ crypto_ec_key_deinit((struct crypto_ec_key *) pkey);
return NULL;
}
struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
{
- struct crypto_ec_key *key;
-
- key = os_zalloc(sizeof(*key));
- if (!key)
- return NULL;
+ EVP_PKEY *pkey;
- key->pkey = d2i_PUBKEY(NULL, &der, der_len);
- if (!key->pkey) {
+ pkey = d2i_PUBKEY(NULL, &der, der_len);
+ if (!pkey) {
wpa_printf(MSG_INFO, "OpenSSL: d2i_PUBKEY() failed: %s",
ERR_error_string(ERR_get_error(), NULL));
goto fail;
}
- key->eckey = EVP_PKEY_get0_EC_KEY(key->pkey);
- if (!key->eckey)
+ /* Ensure this is an EC key */
+ if (!EVP_PKEY_get0_EC_KEY(pkey))
goto fail;
- return key;
+ return (struct crypto_ec_key *) pkey;
fail:
- crypto_ec_key_deinit(key);
+ crypto_ec_key_deinit((struct crypto_ec_key *) pkey);
return NULL;
}
-void crypto_ec_key_deinit(struct crypto_ec_key *key)
+struct crypto_ec_key * crypto_ec_key_set_pub(int group, const u8 *buf_x,
+ const u8 *buf_y, size_t len)
{
- if (key) {
- EVP_PKEY_free(key->pkey);
- os_free(key);
+ EC_KEY *eckey = NULL;
+ EVP_PKEY *pkey = NULL;
+ EC_GROUP *ec_group = NULL;
+ BN_CTX *ctx;
+ EC_POINT *point = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ int nid;
+
+ if (!buf_x || !buf_y)
+ return NULL;
+
+ nid = crypto_ec_group_2_nid(group);
+ if (nid < 0) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Unsupported group %d", group);
+ return NULL;
+ }
+
+ ctx = BN_CTX_new();
+ if (!ctx)
+ goto fail;
+
+ ec_group = EC_GROUP_new_by_curve_name(nid);
+ if (!ec_group)
+ goto fail;
+
+ x = BN_bin2bn(buf_x, len, NULL);
+ y = BN_bin2bn(buf_y, len, NULL);
+ point = EC_POINT_new(ec_group);
+ if (!x || !y || !point)
+ goto fail;
+
+ if (!EC_POINT_set_affine_coordinates_GFp(ec_group, point, x, y, ctx)) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (!EC_POINT_is_on_curve(ec_group, point, ctx) ||
+ EC_POINT_is_at_infinity(ec_group, point)) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Invalid point");
+ goto fail;
+ }
+
+ eckey = EC_KEY_new();
+ if (!eckey ||
+ EC_KEY_set_group(eckey, ec_group) != 1 ||
+ EC_KEY_set_public_key(eckey, point) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to set EC_KEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
}
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+ pkey = EVP_PKEY_new();
+ if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Could not create EVP_PKEY");
+ goto fail;
+ }
+
+out:
+ EC_GROUP_free(ec_group);
+ BN_free(x);
+ BN_free(y);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+ return (struct crypto_ec_key *) pkey;
+
+fail:
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto out;
}
-struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
+struct crypto_ec_key *
+crypto_ec_key_set_pub_point(struct crypto_ec *ec,
+ const struct crypto_ec_point *pub)
{
- unsigned char *der = NULL;
- int der_len;
- struct wpabuf *buf;
+ EC_KEY *eckey;
+ EVP_PKEY *pkey = NULL;
- der_len = i2d_PUBKEY(key->pkey, &der);
- if (der_len <= 0) {
- wpa_printf(MSG_INFO, "OpenSSL: i2d_PUBKEY() failed: %s",
+ eckey = EC_KEY_new();
+ if (!eckey ||
+ EC_KEY_set_group(eckey, ec->group) != 1 ||
+ EC_KEY_set_public_key(eckey, (const EC_POINT *) pub) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to set EC_KEY: %s",
ERR_error_string(ERR_get_error(), NULL));
- return NULL;
+ goto fail;
}
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
- buf = wpabuf_alloc_copy(der, der_len);
- OPENSSL_free(der);
- return buf;
+ pkey = EVP_PKEY_new();
+ if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Could not create EVP_PKEY");
+ goto fail;
+ }
+
+out:
+ return (struct crypto_ec_key *) pkey;
+
+fail:
+ EVP_PKEY_free(pkey);
+ EC_KEY_free(eckey);
+ pkey = NULL;
+ goto out;
}
-struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
- size_t len)
+struct crypto_ec_key * crypto_ec_key_gen(int group)
{
- EVP_PKEY_CTX *pkctx;
- struct wpabuf *sig_der;
- size_t sig_len;
+ EVP_PKEY_CTX *kctx = NULL;
+ EC_KEY *ec_params = NULL, *eckey;
+ EVP_PKEY *params = NULL, *key = NULL;
+ int nid;
- sig_len = EVP_PKEY_size(key->pkey);
- sig_der = wpabuf_alloc(sig_len);
- if (!sig_der)
+ nid = crypto_ec_group_2_nid(group);
+ if (nid < 0) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Unsupported group %d", group);
return NULL;
+ }
- pkctx = EVP_PKEY_CTX_new(key->pkey, NULL);
- if (!pkctx ||
- EVP_PKEY_sign_init(pkctx) <= 0 ||
- EVP_PKEY_sign(pkctx, wpabuf_put(sig_der, 0), &sig_len,
- data, len) <= 0) {
- wpabuf_free(sig_der);
- sig_der = NULL;
- } else {
- wpabuf_put(sig_der, sig_len);
+ ec_params = EC_KEY_new_by_curve_name(nid);
+ if (!ec_params) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to generate EC_KEY parameters");
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+ params = EVP_PKEY_new();
+ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to generate EVP_PKEY parameters");
+ goto fail;
}
- EVP_PKEY_CTX_free(pkctx);
- return sig_der;
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (!kctx ||
+ EVP_PKEY_keygen_init(kctx) != 1 ||
+ EVP_PKEY_keygen(kctx, &key) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Failed to generate EC key");
+ key = NULL;
+ goto fail;
+ }
+
+ eckey = EVP_PKEY_get0_EC_KEY(key);
+ if (!eckey) {
+ key = NULL;
+ goto fail;
+ }
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
+
+fail:
+ EC_KEY_free(ec_params);
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(kctx);
+ return (struct crypto_ec_key *) key;
}
-int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
- size_t len, const u8 *sig, size_t sig_len)
+void crypto_ec_key_deinit(struct crypto_ec_key *key)
{
- EVP_PKEY_CTX *pkctx;
- int ret;
+ EVP_PKEY_free((EVP_PKEY *) key);
+}
- pkctx = EVP_PKEY_CTX_new(key->pkey, NULL);
- if (!pkctx || EVP_PKEY_verify_init(pkctx) <= 0) {
- EVP_PKEY_CTX_free(pkctx);
- return -1;
- }
- ret = EVP_PKEY_verify(pkctx, sig, sig_len, data, len);
- EVP_PKEY_CTX_free(pkctx);
- if (ret == 1)
- return 1; /* signature ok */
- if (ret == 0)
- return 0; /* incorrect signature */
- return -1;
-}
+#ifdef OPENSSL_IS_BORINGSSL
+/* BoringSSL version of i2d_PUBKEY() always outputs public EC key using
+ * uncompressed form so define a custom function to export EC pubkey using
+ * the compressed format that is explicitly required for some protocols. */
-int crypto_ec_key_group(struct crypto_ec_key *key)
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+
+typedef struct {
+ /* AlgorithmIdentifier ecPublicKey with optional parameters present
+ * as an OID identifying the curve */
+ X509_ALGOR *alg;
+ /* Compressed format public key per ANSI X9.63 */
+ ASN1_BIT_STRING *pub_key;
+} EC_COMP_PUBKEY;
+
+ASN1_SEQUENCE(EC_COMP_PUBKEY) = {
+ ASN1_SIMPLE(EC_COMP_PUBKEY, alg, X509_ALGOR),
+ ASN1_SIMPLE(EC_COMP_PUBKEY, pub_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(EC_COMP_PUBKEY);
+
+IMPLEMENT_ASN1_FUNCTIONS(EC_COMP_PUBKEY);
+
+#endif /* OPENSSL_IS_BORINGSSL */
+
+
+struct wpabuf * crypto_ec_key_get_subject_public_key(struct crypto_ec_key *key)
{
+#ifdef OPENSSL_IS_BORINGSSL
+ unsigned char *der = NULL;
+ int der_len;
+ const EC_KEY *eckey;
+ struct wpabuf *ret = NULL;
+ size_t len;
const EC_GROUP *group;
+ const EC_POINT *point;
+ BN_CTX *ctx;
+ EC_COMP_PUBKEY *pubkey = NULL;
int nid;
- group = EC_KEY_get0_group(key->eckey);
- if (!group)
- return -1;
+ ctx = BN_CTX_new();
+ eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+ if (!ctx || !eckey)
+ goto fail;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (!group || !point)
+ goto fail;
nid = EC_GROUP_get_curve_name(group);
- switch (nid) {
- case NID_X9_62_prime256v1:
- return 19;
- case NID_secp384r1:
- return 20;
- case NID_secp521r1:
- return 21;
- }
- return -1;
+
+ pubkey = EC_COMP_PUBKEY_new();
+ if (!pubkey ||
+ X509_ALGOR_set0(pubkey->alg, OBJ_nid2obj(EVP_PKEY_EC),
+ V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
+ goto fail;
+
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ NULL, 0, ctx);
+ if (len == 0)
+ goto fail;
+
+ der = OPENSSL_malloc(len);
+ if (!der)
+ goto fail;
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ der, len, ctx);
+
+ OPENSSL_free(pubkey->pub_key->data);
+ pubkey->pub_key->data = der;
+ der = NULL;
+ pubkey->pub_key->length = len;
+ /* No unused bits */
+ pubkey->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ pubkey->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+ der_len = i2d_EC_COMP_PUBKEY(pubkey, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "BoringSSL: Failed to build DER encoded public key");
+ goto fail;
+ }
+
+ ret = wpabuf_alloc_copy(der, der_len);
+fail:
+ EC_COMP_PUBKEY_free(pubkey);
+ OPENSSL_free(der);
+ BN_CTX_free(ctx);
+ return ret;
+#else /* OPENSSL_IS_BORINGSSL */
+ unsigned char *der = NULL;
+ int der_len;
+ struct wpabuf *buf;
+
+ /* For now, all users expect COMPRESSED form */
+ EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key),
+ POINT_CONVERSION_COMPRESSED);
+
+ der_len = i2d_PUBKEY((EVP_PKEY *) key, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_INFO, "OpenSSL: i2d_PUBKEY() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return NULL;
+ }
+
+ buf = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+ return buf;
+#endif /* OPENSSL_IS_BORINGSSL */
+}
+
+
+struct wpabuf * crypto_ec_key_get_ecprivate_key(struct crypto_ec_key *key,
+ bool include_pub)
+{
+ EC_KEY *eckey;
+ unsigned char *der = NULL;
+ int der_len;
+ struct wpabuf *buf;
+ unsigned int key_flags;
+
+ eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+ if (!eckey)
+ return NULL;
+
+ key_flags = EC_KEY_get_enc_flags(eckey);
+ if (include_pub)
+ key_flags &= ~EC_PKEY_NO_PUBKEY;
+ else
+ key_flags |= EC_PKEY_NO_PUBKEY;
+ EC_KEY_set_enc_flags(eckey, key_flags);
+
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len <= 0)
+ return NULL;
+ buf = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+
+ return buf;
+}
+
+
+struct wpabuf * crypto_ec_key_get_pubkey_point(struct crypto_ec_key *key,
+ int prefix)
+{
+ int len, res;
+ EC_KEY *eckey;
+ struct wpabuf *buf;
+ unsigned char *pos;
+
+ eckey = EVP_PKEY_get1_EC_KEY((EVP_PKEY *) key);
+ if (!eckey)
+ return NULL;
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+ len = i2o_ECPublicKey(eckey, NULL);
+ if (len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to determine public key encoding length");
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ buf = wpabuf_alloc(len);
+ if (!buf) {
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ pos = wpabuf_put(buf, len);
+ res = i2o_ECPublicKey(eckey, &pos);
+ EC_KEY_free(eckey);
+ if (res != len) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to encode public key (res=%d/%d)",
+ res, len);
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ if (!prefix) {
+ /* Remove 0x04 prefix if requested */
+ pos = wpabuf_mhead(buf);
+ os_memmove(pos, pos + 1, len - 1);
+ buf->used--;
+ }
+
+ return buf;
+}
+
+
+const struct crypto_ec_point *
+crypto_ec_key_get_public_key(struct crypto_ec_key *key)
+{
+ EC_KEY *eckey;
+
+ eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+ if (!eckey)
+ return NULL;
+ return (const struct crypto_ec_point *) EC_KEY_get0_public_key(eckey);
+}
+
+
+const struct crypto_bignum *
+crypto_ec_key_get_private_key(struct crypto_ec_key *key)
+{
+ EC_KEY *eckey;
+
+ eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+ if (!eckey)
+ return NULL;
+ return (const struct crypto_bignum *) EC_KEY_get0_private_key(eckey);
+}
+
+
+struct wpabuf * crypto_ec_key_sign(struct crypto_ec_key *key, const u8 *data,
+ size_t len)
+{
+ EVP_PKEY_CTX *pkctx;
+ struct wpabuf *sig_der;
+ size_t sig_len;
+
+ sig_len = EVP_PKEY_size((EVP_PKEY *) key);
+ sig_der = wpabuf_alloc(sig_len);
+ if (!sig_der)
+ return NULL;
+
+ pkctx = EVP_PKEY_CTX_new((EVP_PKEY *) key, NULL);
+ if (!pkctx ||
+ EVP_PKEY_sign_init(pkctx) <= 0 ||
+ EVP_PKEY_sign(pkctx, wpabuf_put(sig_der, 0), &sig_len,
+ data, len) <= 0) {
+ wpabuf_free(sig_der);
+ sig_der = NULL;
+ } else {
+ wpabuf_put(sig_der, sig_len);
+ }
+
+ EVP_PKEY_CTX_free(pkctx);
+ return sig_der;
+}
+
+
+struct wpabuf * crypto_ec_key_sign_r_s(struct crypto_ec_key *key,
+ const u8 *data, size_t len)
+{
+ const EC_GROUP *group;
+ const EC_KEY *eckey;
+ BIGNUM *prime = NULL;
+ ECDSA_SIG *sig = NULL;
+ const BIGNUM *r, *s;
+ u8 *r_buf, *s_buf;
+ struct wpabuf *buf;
+ const unsigned char *p;
+ int prime_len;
+
+ buf = crypto_ec_key_sign(key, data, len);
+ if (!buf)
+ return NULL;
+
+ /* Extract (r,s) from Ecdsa-Sig-Value */
+ eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+ if (!eckey)
+ goto fail;
+ group = EC_KEY_get0_group(eckey);
+ prime = BN_new();
+ if (!prime || !group ||
+ !EC_GROUP_get_curve_GFp(group, prime, NULL, NULL, NULL))
+ goto fail;
+ prime_len = BN_num_bytes(prime);
+
+ p = wpabuf_head(buf);
+ sig = d2i_ECDSA_SIG(NULL, &p, wpabuf_len(buf));
+ if (!sig)
+ goto fail;
+ ECDSA_SIG_get0(sig, &r, &s);
+
+ /* Re-use wpabuf returned by crypto_ec_key_sign() */
+ buf->used = 0;
+ r_buf = wpabuf_put(buf, prime_len);
+ s_buf = wpabuf_put(buf, prime_len);
+ if (crypto_bignum_to_bin((const struct crypto_bignum *) r, r_buf,
+ prime_len, prime_len) < 0 ||
+ crypto_bignum_to_bin((const struct crypto_bignum *) s, s_buf,
+ prime_len, prime_len) < 0)
+ goto fail;
+
+out:
+ BN_free(prime);
+ ECDSA_SIG_free(sig);
+ return buf;
+fail:
+ wpabuf_clear_free(buf);
+ buf = NULL;
+ goto out;
+}
+
+
+int crypto_ec_key_verify_signature(struct crypto_ec_key *key, const u8 *data,
+ size_t len, const u8 *sig, size_t sig_len)
+{
+ EVP_PKEY_CTX *pkctx;
+ int ret;
+
+ pkctx = EVP_PKEY_CTX_new((EVP_PKEY *) key, NULL);
+ if (!pkctx || EVP_PKEY_verify_init(pkctx) <= 0) {
+ EVP_PKEY_CTX_free(pkctx);
+ return -1;
+ }
+
+ ret = EVP_PKEY_verify(pkctx, sig, sig_len, data, len);
+ EVP_PKEY_CTX_free(pkctx);
+ if (ret == 1)
+ return 1; /* signature ok */
+ if (ret == 0)
+ return 0; /* incorrect signature */
+ return -1;
+}
+
+
+int crypto_ec_key_verify_signature_r_s(struct crypto_ec_key *key,
+ const u8 *data, size_t len,
+ const u8 *r, size_t r_len,
+ const u8 *s, size_t s_len)
+{
+ ECDSA_SIG *sig;
+ BIGNUM *r_bn, *s_bn;
+ unsigned char *der = NULL;
+ int der_len;
+ int ret = -1;
+
+ r_bn = BN_bin2bn(r, r_len, NULL);
+ s_bn = BN_bin2bn(s, s_len, NULL);
+ sig = ECDSA_SIG_new();
+ if (!r_bn || !s_bn || !sig || ECDSA_SIG_set0(sig, r_bn, s_bn) != 1)
+ goto fail;
+ r_bn = NULL;
+ s_bn = NULL;
+
+ der_len = i2d_ECDSA_SIG(sig, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Could not DER encode signature");
+ goto fail;
+ }
+
+ ret = crypto_ec_key_verify_signature(key, data, len, der, der_len);
+
+fail:
+ OPENSSL_free(der);
+ BN_free(r_bn);
+ BN_free(s_bn);
+ ECDSA_SIG_free(sig);
+ return ret;
+}
+
+
+int crypto_ec_key_group(struct crypto_ec_key *key)
+{
+ const EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY *) key);
+ if (!eckey)
+ return -1;
+ group = EC_KEY_get0_group(eckey);
+ if (!group)
+ return -1;
+ nid = EC_GROUP_get_curve_name(group);
+ switch (nid) {
+ case NID_X9_62_prime256v1:
+ return 19;
+ case NID_secp384r1:
+ return 20;
+ case NID_secp521r1:
+ return 21;
+#ifdef NID_brainpoolP256r1
+ case NID_brainpoolP256r1:
+ return 28;
+#endif /* NID_brainpoolP256r1 */
+#ifdef NID_brainpoolP384r1
+ case NID_brainpoolP384r1:
+ return 29;
+#endif /* NID_brainpoolP384r1 */
+#ifdef NID_brainpoolP512r1
+ case NID_brainpoolP512r1:
+ return 30;
+#endif /* NID_brainpoolP512r1 */
+ }
+ wpa_printf(MSG_ERROR, "OpenSSL: Unsupported curve (nid=%d) in EC key",
+ nid);
+ return -1;
+}
+
+
+int crypto_ec_key_cmp(struct crypto_ec_key *key1, struct crypto_ec_key *key2)
+{
+ if (EVP_PKEY_cmp((EVP_PKEY *) key1, (EVP_PKEY *) key2) != 1)
+ return -1;
+ return 0;
+}
+
+
+void crypto_ec_key_debug_print(const struct crypto_ec_key *key,
+ const char *title)
+{
+ BIO *out;
+ size_t rlen;
+ char *txt;
+ int res;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return;
+
+ EVP_PKEY_print_private(out, (EVP_PKEY *) key, 0, NULL);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+}
+
+
+struct wpabuf * crypto_pkcs7_get_certificates(const struct wpabuf *pkcs7)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+ CBS pkcs7_cbs;
+#else /* OPENSSL_IS_BORINGSSL */
+ PKCS7 *p7 = NULL;
+ const unsigned char *p = wpabuf_head(pkcs7);
+#endif /* OPENSSL_IS_BORINGSSL */
+ STACK_OF(X509) *certs;
+ int i, num;
+ BIO *out = NULL;
+ size_t rlen;
+ struct wpabuf *pem = NULL;
+ int res;
+
+#ifdef OPENSSL_IS_BORINGSSL
+ certs = sk_X509_new_null();
+ if (!certs)
+ goto fail;
+ CBS_init(&pkcs7_cbs, wpabuf_head(pkcs7), wpabuf_len(pkcs7));
+ if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Could not parse PKCS#7 object: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+#else /* OPENSSL_IS_BORINGSSL */
+ p7 = d2i_PKCS7(NULL, &p, wpabuf_len(pkcs7));
+ if (!p7) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Could not parse PKCS#7 object: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ switch (OBJ_obj2nid(p7->type)) {
+ case NID_pkcs7_signed:
+ certs = p7->d.sign->cert;
+ break;
+ case NID_pkcs7_signedAndEnveloped:
+ certs = p7->d.signed_and_enveloped->cert;
+ break;
+ default:
+ certs = NULL;
+ break;
+ }
+#endif /* OPENSSL_IS_BORINGSSL */
+
+ if (!certs || ((num = sk_X509_num(certs)) == 0)) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: No certificates found in PKCS#7 object");
+ goto fail;
+ }
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ goto fail;
+
+ for (i = 0; i < num; i++) {
+ X509 *cert = sk_X509_value(certs, i);
+
+ PEM_write_bio_X509(out, cert);
+ }
+
+ rlen = BIO_ctrl_pending(out);
+ pem = wpabuf_alloc(rlen);
+ if (!pem)
+ goto fail;
+ res = BIO_read(out, wpabuf_put(pem, 0), rlen);
+ if (res <= 0) {
+ wpabuf_free(pem);
+ pem = NULL;
+ goto fail;
+ }
+ wpabuf_put(pem, res);
+
+fail:
+#ifdef OPENSSL_IS_BORINGSSL
+ if (certs)
+ sk_X509_pop_free(certs, X509_free);
+#else /* OPENSSL_IS_BORINGSSL */
+ PKCS7_free(p7);
+#endif /* OPENSSL_IS_BORINGSSL */
+ if (out)
+ BIO_free_all(out);
+
+ return pem;
+}
+
+
+struct crypto_csr * crypto_csr_init()
+{
+ return (struct crypto_csr *)X509_REQ_new();
+}
+
+
+struct crypto_csr * crypto_csr_verify(const struct wpabuf *req)
+{
+ X509_REQ *csr;
+ EVP_PKEY *pkey = NULL;
+ const u8 *der = wpabuf_head(req);
+
+ csr = d2i_X509_REQ(NULL, &der, wpabuf_len(req));
+ if (!csr)
+ return NULL;
+
+ pkey = X509_REQ_get_pubkey((X509_REQ *)csr);
+ if (!pkey)
+ goto fail;
+
+ if (X509_REQ_verify((X509_REQ *)csr, pkey) != 1)
+ goto fail;
+
+ return (struct crypto_csr *)csr;
+fail:
+ X509_REQ_free(csr);
+ return NULL;
+}
+
+
+void crypto_csr_deinit(struct crypto_csr *csr)
+{
+ X509_REQ_free((X509_REQ *)csr);
+}
+
+
+int crypto_csr_set_ec_public_key(struct crypto_csr *csr, struct crypto_ec_key *key)
+{
+ if (!X509_REQ_set_pubkey((X509_REQ *)csr, (EVP_PKEY *)key))
+ return -1;
+
+ return 0;
+}
+
+
+int crypto_csr_set_name(struct crypto_csr *csr, enum crypto_csr_name type,
+ const char *name)
+{
+ X509_NAME *n;
+ int nid;
+
+ switch (type) {
+ case CSR_NAME_CN:
+ nid = NID_commonName;
+ break;
+ case CSR_NAME_SN:
+ nid = NID_surname;
+ break;
+ case CSR_NAME_C:
+ nid = NID_countryName;
+ break;
+ case CSR_NAME_O:
+ nid = NID_organizationName;
+ break;
+ case CSR_NAME_OU:
+ nid = NID_organizationalUnitName;
+ break;
+ default:
+ return -1;
+ }
+
+ n = X509_REQ_get_subject_name((X509_REQ *) csr);
+ if (!n)
+ return -1;
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_UTF8,
+ (unsigned char *) name,
+ os_strlen(name), -1, 0))
+ return -1;
+#else
+ if (!X509_NAME_add_entry_by_NID(n, nid, MBSTRING_UTF8,
+ (const unsigned char *) name,
+ os_strlen(name), -1, 0))
+ return -1;
+#endif
+
+ return 0;
+}
+
+
+int crypto_csr_set_attribute(struct crypto_csr *csr, enum crypto_csr_attr attr,
+ int attr_type, const u8 *value, size_t len)
+{
+ int nid;
+
+ switch (attr) {
+ case CSR_ATTR_CHALLENGE_PASSWORD:
+ nid = NID_pkcs9_challengePassword;
+ break;
+ default:
+ return -1;
+ }
+
+ if (!X509_REQ_add1_attr_by_NID((X509_REQ *) csr, nid, attr_type, value,
+ len))
+ return -1;
+
+ return 0;
+}
+
+
+const u8 * crypto_csr_get_attribute(struct crypto_csr *csr,
+ enum crypto_csr_attr attr,
+ size_t *len, int *type)
+{
+ X509_ATTRIBUTE *attrib;
+ ASN1_TYPE *attrib_type;
+ ASN1_STRING *data;
+ int loc;
+ int nid;
+
+ switch (attr) {
+ case CSR_ATTR_CHALLENGE_PASSWORD:
+ nid = NID_pkcs9_challengePassword;
+ break;
+ default:
+ return NULL;
+ }
+
+ loc = X509_REQ_get_attr_by_NID((X509_REQ *) csr, nid, -1);
+ if (loc < 0)
+ return NULL;
+
+ attrib = X509_REQ_get_attr((X509_REQ *) csr, loc);
+ if (!attrib)
+ return NULL;
+
+ attrib_type = X509_ATTRIBUTE_get0_type(attrib, 0);
+ if (!attrib_type)
+ return NULL;
+ *type = ASN1_TYPE_get(attrib_type);
+ data = X509_ATTRIBUTE_get0_data(attrib, 0, *type, NULL);
+ if (!data)
+ return NULL;
+ *len = ASN1_STRING_length(data);
+ return ASN1_STRING_get0_data(data);
+}
+
+
+struct wpabuf * crypto_csr_sign(struct crypto_csr *csr,
+ struct crypto_ec_key *key,
+ enum crypto_hash_alg algo)
+{
+ const EVP_MD *sign_md;
+ struct wpabuf *buf;
+ unsigned char *der = NULL;
+ int der_len;
+
+ switch (algo) {
+ case CRYPTO_HASH_ALG_SHA256:
+ sign_md = EVP_sha256();
+ break;
+ case CRYPTO_HASH_ALG_SHA384:
+ sign_md = EVP_sha384();
+ break;
+ case CRYPTO_HASH_ALG_SHA512:
+ sign_md = EVP_sha512();
+ break;
+ default:
+ return NULL;
+ }
+
+ if (!X509_REQ_sign((X509_REQ *) csr, (EVP_PKEY *) key, sign_md))
+ return NULL;
+
+ der_len = i2d_X509_REQ((X509_REQ *) csr, &der);
+ if (der_len < 0)
+ return NULL;
+
+ buf = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+
+ return buf;
}
#endif /* CONFIG_ECC */
diff --git a/contrib/wpa/src/crypto/crypto_wolfssl.c b/contrib/wpa/src/crypto/crypto_wolfssl.c
index 2e4bf8962b55..6f116eb62f53 100644
--- a/contrib/wpa/src/crypto/crypto_wolfssl.c
+++ b/contrib/wpa/src/crypto/crypto_wolfssl.c
@@ -1,1845 +1,1849 @@
/*
* Wrapper functions for libwolfssl
* Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto.h"
/* wolfSSL headers */
#include <wolfssl/options.h>
#include <wolfssl/wolfcrypt/md4.h>
#include <wolfssl/wolfcrypt/md5.h>
#include <wolfssl/wolfcrypt/sha.h>
#include <wolfssl/wolfcrypt/sha256.h>
#include <wolfssl/wolfcrypt/sha512.h>
#include <wolfssl/wolfcrypt/hmac.h>
#include <wolfssl/wolfcrypt/pwdbased.h>
#include <wolfssl/wolfcrypt/arc4.h>
#include <wolfssl/wolfcrypt/des3.h>
#include <wolfssl/wolfcrypt/aes.h>
#include <wolfssl/wolfcrypt/dh.h>
#include <wolfssl/wolfcrypt/cmac.h>
#include <wolfssl/wolfcrypt/ecc.h>
#include <wolfssl/openssl/bn.h>
#ifndef CONFIG_FIPS
int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
Md4 md4;
size_t i;
if (TEST_FAIL())
return -1;
wc_InitMd4(&md4);
for (i = 0; i < num_elem; i++)
wc_Md4Update(&md4, addr[i], len[i]);
wc_Md4Final(&md4, mac);
return 0;
}
int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
wc_Md5 md5;
size_t i;
if (TEST_FAIL())
return -1;
wc_InitMd5(&md5);
for (i = 0; i < num_elem; i++)
wc_Md5Update(&md5, addr[i], len[i]);
wc_Md5Final(&md5, mac);
return 0;
}
#endif /* CONFIG_FIPS */
int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
{
wc_Sha sha;
size_t i;
if (TEST_FAIL())
return -1;
wc_InitSha(&sha);
for (i = 0; i < num_elem; i++)
wc_ShaUpdate(&sha, addr[i], len[i]);
wc_ShaFinal(&sha, mac);
return 0;
}
#ifndef NO_SHA256_WRAPPER
int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac)
{
wc_Sha256 sha256;
size_t i;
if (TEST_FAIL())
return -1;
wc_InitSha256(&sha256);
for (i = 0; i < num_elem; i++)
wc_Sha256Update(&sha256, addr[i], len[i]);
wc_Sha256Final(&sha256, mac);
return 0;
}
#endif /* NO_SHA256_WRAPPER */
#ifdef CONFIG_SHA384
int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac)
{
wc_Sha384 sha384;
size_t i;
if (TEST_FAIL())
return -1;
wc_InitSha384(&sha384);
for (i = 0; i < num_elem; i++)
wc_Sha384Update(&sha384, addr[i], len[i]);
wc_Sha384Final(&sha384, mac);
return 0;
}
#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SHA512
int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
u8 *mac)
{
wc_Sha512 sha512;
size_t i;
if (TEST_FAIL())
return -1;
wc_InitSha512(&sha512);
for (i = 0; i < num_elem; i++)
wc_Sha512Update(&sha512, addr[i], len[i]);
wc_Sha512Final(&sha512, mac);
return 0;
}
#endif /* CONFIG_SHA512 */
static int wolfssl_hmac_vector(int type, const u8 *key,
size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac,
unsigned int mdlen)
{
Hmac hmac;
size_t i;
(void) mdlen;
if (TEST_FAIL())
return -1;
if (wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0)
return -1;
for (i = 0; i < num_elem; i++)
if (wc_HmacUpdate(&hmac, addr[i], len[i]) != 0)
return -1;
if (wc_HmacFinal(&hmac, mac) != 0)
return -1;
return 0;
}
#ifndef CONFIG_FIPS
int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return wolfssl_hmac_vector(WC_MD5, key, key_len, num_elem, addr, len,
mac, 16);
}
int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
u8 *mac)
{
return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
}
#endif /* CONFIG_FIPS */
int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return wolfssl_hmac_vector(WC_SHA, key, key_len, num_elem, addr, len,
mac, 20);
}
int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
u8 *mac)
{
return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
}
#ifdef CONFIG_SHA256
int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return wolfssl_hmac_vector(WC_SHA256, key, key_len, num_elem, addr, len,
mac, 32);
}
int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
size_t data_len, u8 *mac)
{
return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
}
#endif /* CONFIG_SHA256 */
#ifdef CONFIG_SHA384
int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return wolfssl_hmac_vector(WC_SHA384, key, key_len, num_elem, addr, len,
mac, 48);
}
int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
size_t data_len, u8 *mac)
{
return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
}
#endif /* CONFIG_SHA384 */
#ifdef CONFIG_SHA512
int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return wolfssl_hmac_vector(WC_SHA512, key, key_len, num_elem, addr, len,
mac, 64);
}
int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
size_t data_len, u8 *mac)
{
return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
}
#endif /* CONFIG_SHA512 */
int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
int iterations, u8 *buf, size_t buflen)
{
if (wc_PBKDF2(buf, (const byte*)passphrase, os_strlen(passphrase), ssid,
ssid_len, iterations, buflen, WC_SHA) != 0)
return -1;
return 0;
}
#ifdef CONFIG_DES
int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
{
Des des;
u8 pkey[8], next, tmp;
int i;
/* Add parity bits to the key */
next = 0;
for (i = 0; i < 7; i++) {
tmp = key[i];
pkey[i] = (tmp >> i) | next | 1;
next = tmp << (7 - i);
}
pkey[i] = next | 1;
wc_Des_SetKey(&des, pkey, NULL, DES_ENCRYPTION);
wc_Des_EcbEncrypt(&des, cypher, clear, DES_BLOCK_SIZE);
return 0;
}
#endif /* CONFIG_DES */
void * aes_encrypt_init(const u8 *key, size_t len)
{
Aes *aes;
if (TEST_FAIL())
return NULL;
aes = os_malloc(sizeof(Aes));
if (!aes)
return NULL;
if (wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION) < 0) {
os_free(aes);
return NULL;
}
return aes;
}
int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
{
wc_AesEncryptDirect(ctx, crypt, plain);
return 0;
}
void aes_encrypt_deinit(void *ctx)
{
os_free(ctx);
}
void * aes_decrypt_init(const u8 *key, size_t len)
{
Aes *aes;
if (TEST_FAIL())
return NULL;
aes = os_malloc(sizeof(Aes));
if (!aes)
return NULL;
if (wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION) < 0) {
os_free(aes);
return NULL;
}
return aes;
}
int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
{
wc_AesDecryptDirect(ctx, plain, crypt);
return 0;
}
void aes_decrypt_deinit(void *ctx)
{
os_free(ctx);
}
int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
{
Aes aes;
int ret;
if (TEST_FAIL())
return -1;
ret = wc_AesSetKey(&aes, key, 16, iv, AES_ENCRYPTION);
if (ret != 0)
return -1;
ret = wc_AesCbcEncrypt(&aes, data, data, data_len);
if (ret != 0)
return -1;
return 0;
}
int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
{
Aes aes;
int ret;
if (TEST_FAIL())
return -1;
ret = wc_AesSetKey(&aes, key, 16, iv, AES_DECRYPTION);
if (ret != 0)
return -1;
ret = wc_AesCbcDecrypt(&aes, data, data, data_len);
if (ret != 0)
return -1;
return 0;
}
int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
{
int ret;
if (TEST_FAIL())
return -1;
ret = wc_AesKeyWrap(kek, kek_len, plain, n * 8, cipher, (n + 1) * 8,
NULL);
return ret != (n + 1) * 8 ? -1 : 0;
}
int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
u8 *plain)
{
int ret;
if (TEST_FAIL())
return -1;
ret = wc_AesKeyUnWrap(kek, kek_len, cipher, (n + 1) * 8, plain, n * 8,
NULL);
return ret != n * 8 ? -1 : 0;
}
#ifndef CONFIG_NO_RC4
int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data,
size_t data_len)
{
#ifndef NO_RC4
Arc4 arc4;
unsigned char skip_buf[16];
wc_Arc4SetKey(&arc4, key, keylen);
while (skip >= sizeof(skip_buf)) {
size_t len = skip;
if (len > sizeof(skip_buf))
len = sizeof(skip_buf);
wc_Arc4Process(&arc4, skip_buf, skip_buf, len);
skip -= len;
}
wc_Arc4Process(&arc4, data, data, data_len);
return 0;
#else /* NO_RC4 */
return -1;
#endif /* NO_RC4 */
}
#endif /* CONFIG_NO_RC4 */
#if defined(EAP_IKEV2) || defined(EAP_IKEV2_DYNAMIC) \
|| defined(EAP_SERVER_IKEV2)
union wolfssl_cipher {
Aes aes;
Des3 des3;
Arc4 arc4;
};
struct crypto_cipher {
enum crypto_cipher_alg alg;
union wolfssl_cipher enc;
union wolfssl_cipher dec;
};
struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
const u8 *iv, const u8 *key,
size_t key_len)
{
struct crypto_cipher *ctx;
ctx = os_zalloc(sizeof(*ctx));
if (!ctx)
return NULL;
switch (alg) {
#ifndef CONFIG_NO_RC4
#ifndef NO_RC4
case CRYPTO_CIPHER_ALG_RC4:
wc_Arc4SetKey(&ctx->enc.arc4, key, key_len);
wc_Arc4SetKey(&ctx->dec.arc4, key, key_len);
break;
#endif /* NO_RC4 */
#endif /* CONFIG_NO_RC4 */
#ifndef NO_AES
case CRYPTO_CIPHER_ALG_AES:
switch (key_len) {
case 16:
case 24:
case 32:
break;
default:
os_free(ctx);
return NULL;
}
if (wc_AesSetKey(&ctx->enc.aes, key, key_len, iv,
AES_ENCRYPTION) ||
wc_AesSetKey(&ctx->dec.aes, key, key_len, iv,
AES_DECRYPTION)) {
os_free(ctx);
return NULL;
}
break;
#endif /* NO_AES */
#ifndef NO_DES3
case CRYPTO_CIPHER_ALG_3DES:
if (key_len != DES3_KEYLEN ||
wc_Des3_SetKey(&ctx->enc.des3, key, iv, DES_ENCRYPTION) ||
wc_Des3_SetKey(&ctx->dec.des3, key, iv, DES_DECRYPTION)) {
os_free(ctx);
return NULL;
}
break;
#endif /* NO_DES3 */
case CRYPTO_CIPHER_ALG_RC2:
case CRYPTO_CIPHER_ALG_DES:
default:
os_free(ctx);
return NULL;
}
ctx->alg = alg;
return ctx;
}
int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
u8 *crypt, size_t len)
{
switch (ctx->alg) {
#ifndef CONFIG_NO_RC4
#ifndef NO_RC4
case CRYPTO_CIPHER_ALG_RC4:
wc_Arc4Process(&ctx->enc.arc4, crypt, plain, len);
return 0;
#endif /* NO_RC4 */
#endif /* CONFIG_NO_RC4 */
#ifndef NO_AES
case CRYPTO_CIPHER_ALG_AES:
if (wc_AesCbcEncrypt(&ctx->enc.aes, crypt, plain, len) != 0)
return -1;
return 0;
#endif /* NO_AES */
#ifndef NO_DES3
case CRYPTO_CIPHER_ALG_3DES:
if (wc_Des3_CbcEncrypt(&ctx->enc.des3, crypt, plain, len) != 0)
return -1;
return 0;
#endif /* NO_DES3 */
default:
return -1;
}
return -1;
}
int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
u8 *plain, size_t len)
{
switch (ctx->alg) {
#ifndef CONFIG_NO_RC4
#ifndef NO_RC4
case CRYPTO_CIPHER_ALG_RC4:
wc_Arc4Process(&ctx->dec.arc4, plain, crypt, len);
return 0;
#endif /* NO_RC4 */
#endif /* CONFIG_NO_RC4 */
#ifndef NO_AES
case CRYPTO_CIPHER_ALG_AES:
if (wc_AesCbcDecrypt(&ctx->dec.aes, plain, crypt, len) != 0)
return -1;
return 0;
#endif /* NO_AES */
#ifndef NO_DES3
case CRYPTO_CIPHER_ALG_3DES:
if (wc_Des3_CbcDecrypt(&ctx->dec.des3, plain, crypt, len) != 0)
return -1;
return 0;
#endif /* NO_DES3 */
default:
return -1;
}
return -1;
}
void crypto_cipher_deinit(struct crypto_cipher *ctx)
{
os_free(ctx);
}
#endif
-#ifdef CONFIG_WPS_NFC
+#ifdef CONFIG_WPS
static const unsigned char RFC3526_PRIME_1536[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
static const unsigned char RFC3526_GENERATOR_1536[] = {
0x02
};
#define RFC3526_LEN sizeof(RFC3526_PRIME_1536)
void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
{
WC_RNG rng;
DhKey *ret = NULL;
DhKey *dh = NULL;
struct wpabuf *privkey = NULL;
struct wpabuf *pubkey = NULL;
word32 priv_sz, pub_sz;
*priv = NULL;
wpabuf_free(*publ);
*publ = NULL;
dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (!dh)
return NULL;
wc_InitDhKey(dh);
if (wc_InitRng(&rng) != 0) {
XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return NULL;
}
privkey = wpabuf_alloc(RFC3526_LEN);
pubkey = wpabuf_alloc(RFC3526_LEN);
if (!privkey || !pubkey)
goto done;
if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
!= 0)
goto done;
if (wc_DhGenerateKeyPair(dh, &rng, wpabuf_mhead(privkey), &priv_sz,
wpabuf_mhead(pubkey), &pub_sz) != 0)
goto done;
wpabuf_put(privkey, priv_sz);
wpabuf_put(pubkey, pub_sz);
ret = dh;
*priv = privkey;
*publ = pubkey;
dh = NULL;
privkey = NULL;
pubkey = NULL;
done:
wpabuf_clear_free(pubkey);
wpabuf_clear_free(privkey);
if (dh) {
wc_FreeDhKey(dh);
XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
wc_FreeRng(&rng);
return ret;
}
+#ifdef CONFIG_WPS_NFC
+
void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
{
DhKey *ret = NULL;
DhKey *dh;
byte *secret;
word32 secret_sz;
dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (!dh)
return NULL;
wc_InitDhKey(dh);
secret = XMALLOC(RFC3526_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (!secret)
goto done;
if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536),
RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536))
!= 0)
goto done;
if (wc_DhAgree(dh, secret, &secret_sz, wpabuf_head(priv),
wpabuf_len(priv), RFC3526_GENERATOR_1536,
sizeof(RFC3526_GENERATOR_1536)) != 0)
goto done;
if (secret_sz != wpabuf_len(publ) ||
os_memcmp(secret, wpabuf_head(publ), secret_sz) != 0)
goto done;
ret = dh;
dh = NULL;
done:
if (dh) {
wc_FreeDhKey(dh);
XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return ret;
}
+#endif /* CONFIG_WPS_NFC */
+
struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
const struct wpabuf *own_private)
{
struct wpabuf *ret = NULL;
struct wpabuf *secret;
word32 secret_sz;
secret = wpabuf_alloc(RFC3526_LEN);
if (!secret)
goto done;
if (wc_DhAgree(ctx, wpabuf_mhead(secret), &secret_sz,
wpabuf_head(own_private), wpabuf_len(own_private),
wpabuf_head(peer_public), wpabuf_len(peer_public)) != 0)
goto done;
wpabuf_put(secret, secret_sz);
ret = secret;
secret = NULL;
done:
wpabuf_clear_free(secret);
return ret;
}
void dh5_free(void *ctx)
{
if (!ctx)
return;
wc_FreeDhKey(ctx);
XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
-#endif /* CONFIG_WPS_NFC */
+#endif /* CONFIG_WPS */
int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
u8 *pubkey)
{
int ret = -1;
WC_RNG rng;
DhKey *dh = NULL;
word32 priv_sz, pub_sz;
if (TEST_FAIL())
return -1;
dh = os_malloc(sizeof(DhKey));
if (!dh)
return -1;
wc_InitDhKey(dh);
if (wc_InitRng(&rng) != 0) {
os_free(dh);
return -1;
}
if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
goto done;
if (wc_DhGenerateKeyPair(dh, &rng, privkey, &priv_sz, pubkey, &pub_sz)
!= 0)
goto done;
if (priv_sz < prime_len) {
size_t pad_sz = prime_len - priv_sz;
os_memmove(privkey + pad_sz, privkey, priv_sz);
os_memset(privkey, 0, pad_sz);
}
if (pub_sz < prime_len) {
size_t pad_sz = prime_len - pub_sz;
os_memmove(pubkey + pad_sz, pubkey, pub_sz);
os_memset(pubkey, 0, pad_sz);
}
ret = 0;
done:
wc_FreeDhKey(dh);
os_free(dh);
wc_FreeRng(&rng);
return ret;
}
int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
const u8 *order, size_t order_len,
const u8 *privkey, size_t privkey_len,
const u8 *pubkey, size_t pubkey_len,
u8 *secret, size_t *len)
{
int ret = -1;
DhKey *dh;
word32 secret_sz;
dh = os_malloc(sizeof(DhKey));
if (!dh)
return -1;
wc_InitDhKey(dh);
if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0)
goto done;
if (wc_DhAgree(dh, secret, &secret_sz, privkey, privkey_len, pubkey,
pubkey_len) != 0)
goto done;
*len = secret_sz;
ret = 0;
done:
wc_FreeDhKey(dh);
os_free(dh);
return ret;
}
#ifdef CONFIG_FIPS
int crypto_get_random(void *buf, size_t len)
{
int ret = 0;
WC_RNG rng;
if (wc_InitRng(&rng) != 0)
return -1;
if (wc_RNG_GenerateBlock(&rng, buf, len) != 0)
ret = -1;
wc_FreeRng(&rng);
return ret;
}
#endif /* CONFIG_FIPS */
#if defined(EAP_PWD) || defined(EAP_SERVER_PWD)
struct crypto_hash {
Hmac hmac;
int size;
};
struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
size_t key_len)
{
struct crypto_hash *ret = NULL;
struct crypto_hash *hash;
int type;
hash = os_zalloc(sizeof(*hash));
if (!hash)
goto done;
switch (alg) {
#ifndef NO_MD5
case CRYPTO_HASH_ALG_HMAC_MD5:
hash->size = 16;
type = WC_MD5;
break;
#endif /* NO_MD5 */
#ifndef NO_SHA
case CRYPTO_HASH_ALG_HMAC_SHA1:
type = WC_SHA;
hash->size = 20;
break;
#endif /* NO_SHA */
#ifdef CONFIG_SHA256
#ifndef NO_SHA256
case CRYPTO_HASH_ALG_HMAC_SHA256:
type = WC_SHA256;
hash->size = 32;
break;
#endif /* NO_SHA256 */
#endif /* CONFIG_SHA256 */
default:
goto done;
}
if (wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0)
goto done;
ret = hash;
hash = NULL;
done:
os_free(hash);
return ret;
}
void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
{
if (!ctx)
return;
wc_HmacUpdate(&ctx->hmac, data, len);
}
int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
{
int ret = 0;
if (!ctx)
return -2;
if (!mac || !len)
goto done;
if (wc_HmacFinal(&ctx->hmac, mac) != 0) {
ret = -1;
goto done;
}
*len = ctx->size;
ret = 0;
done:
bin_clear_free(ctx, sizeof(*ctx));
if (TEST_FAIL())
return -1;
return ret;
}
#endif
int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
Cmac cmac;
size_t i;
word32 sz;
if (TEST_FAIL())
return -1;
if (wc_InitCmac(&cmac, key, key_len, WC_CMAC_AES, NULL) != 0)
return -1;
for (i = 0; i < num_elem; i++)
if (wc_CmacUpdate(&cmac, addr[i], len[i]) != 0)
return -1;
sz = AES_BLOCK_SIZE;
if (wc_CmacFinal(&cmac, mac, &sz) != 0 || sz != AES_BLOCK_SIZE)
return -1;
return 0;
}
int omac1_aes_128_vector(const u8 *key, size_t num_elem,
const u8 *addr[], const size_t *len, u8 *mac)
{
return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
}
int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
{
return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
}
int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
{
return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
}
struct crypto_bignum * crypto_bignum_init(void)
{
mp_int *a;
if (TEST_FAIL())
return NULL;
a = os_malloc(sizeof(*a));
if (!a || mp_init(a) != MP_OKAY) {
os_free(a);
a = NULL;
}
return (struct crypto_bignum *) a;
}
struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
{
mp_int *a;
if (TEST_FAIL())
return NULL;
a = (mp_int *) crypto_bignum_init();
if (!a)
return NULL;
if (mp_read_unsigned_bin(a, buf, len) != MP_OKAY) {
os_free(a);
a = NULL;
}
return (struct crypto_bignum *) a;
}
struct crypto_bignum * crypto_bignum_init_uint(unsigned int val)
{
mp_int *a;
if (TEST_FAIL())
return NULL;
a = (mp_int *) crypto_bignum_init();
if (!a)
return NULL;
if (mp_set_int(a, val) != MP_OKAY) {
os_free(a);
a = NULL;
}
return (struct crypto_bignum *) a;
}
void crypto_bignum_deinit(struct crypto_bignum *n, int clear)
{
if (!n)
return;
if (clear)
mp_forcezero((mp_int *) n);
mp_clear((mp_int *) n);
os_free((mp_int *) n);
}
int crypto_bignum_to_bin(const struct crypto_bignum *a,
u8 *buf, size_t buflen, size_t padlen)
{
int num_bytes, offset;
if (TEST_FAIL())
return -1;
if (padlen > buflen)
return -1;
num_bytes = (mp_count_bits((mp_int *) a) + 7) / 8;
if ((size_t) num_bytes > buflen)
return -1;
if (padlen > (size_t) num_bytes)
offset = padlen - num_bytes;
else
offset = 0;
os_memset(buf, 0, offset);
mp_to_unsigned_bin((mp_int *) a, buf + offset);
return num_bytes + offset;
}
int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
{
int ret = 0;
WC_RNG rng;
size_t len;
u8 *buf;
if (TEST_FAIL())
return -1;
if (wc_InitRng(&rng) != 0)
return -1;
len = (mp_count_bits((mp_int *) m) + 7) / 8;
buf = os_malloc(len);
if (!buf || wc_RNG_GenerateBlock(&rng, buf, len) != 0 ||
mp_read_unsigned_bin((mp_int *) r, buf, len) != MP_OKAY ||
mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0)
ret = -1;
wc_FreeRng(&rng);
bin_clear_free(buf, len);
return ret;
}
int crypto_bignum_add(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *r)
{
return mp_add((mp_int *) a, (mp_int *) b,
(mp_int *) r) == MP_OKAY ? 0 : -1;
}
int crypto_bignum_mod(const struct crypto_bignum *a,
const struct crypto_bignum *m,
struct crypto_bignum *r)
{
return mp_mod((mp_int *) a, (mp_int *) m,
(mp_int *) r) == MP_OKAY ? 0 : -1;
}
int crypto_bignum_exptmod(const struct crypto_bignum *b,
const struct crypto_bignum *e,
const struct crypto_bignum *m,
struct crypto_bignum *r)
{
if (TEST_FAIL())
return -1;
return mp_exptmod((mp_int *) b, (mp_int *) e, (mp_int *) m,
(mp_int *) r) == MP_OKAY ? 0 : -1;
}
int crypto_bignum_inverse(const struct crypto_bignum *a,
const struct crypto_bignum *m,
struct crypto_bignum *r)
{
if (TEST_FAIL())
return -1;
return mp_invmod((mp_int *) a, (mp_int *) m,
(mp_int *) r) == MP_OKAY ? 0 : -1;
}
int crypto_bignum_sub(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *r)
{
if (TEST_FAIL())
return -1;
return mp_sub((mp_int *) a, (mp_int *) b,
(mp_int *) r) == MP_OKAY ? 0 : -1;
}
int crypto_bignum_div(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *d)
{
if (TEST_FAIL())
return -1;
return mp_div((mp_int *) a, (mp_int *) b, (mp_int *) d,
NULL) == MP_OKAY ? 0 : -1;
}
int crypto_bignum_addmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
const struct crypto_bignum *c,
struct crypto_bignum *d)
{
if (TEST_FAIL())
return -1;
return mp_addmod((mp_int *) a, (mp_int *) b, (mp_int *) c,
(mp_int *) d) == MP_OKAY ? 0 : -1;
}
int crypto_bignum_mulmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
const struct crypto_bignum *m,
struct crypto_bignum *d)
{
if (TEST_FAIL())
return -1;
return mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) m,
(mp_int *) d) == MP_OKAY ? 0 : -1;
}
int crypto_bignum_sqrmod(const struct crypto_bignum *a,
const struct crypto_bignum *b,
struct crypto_bignum *c)
{
if (TEST_FAIL())
return -1;
return mp_sqrmod((mp_int *) a, (mp_int *) b,
(mp_int *) c) == MP_OKAY ? 0 : -1;
}
int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
struct crypto_bignum *r)
{
if (mp_copy((mp_int *) a, (mp_int *) r) != MP_OKAY)
return -1;
mp_rshb((mp_int *) r, n);
return 0;
}
int crypto_bignum_cmp(const struct crypto_bignum *a,
const struct crypto_bignum *b)
{
return mp_cmp((mp_int *) a, (mp_int *) b);
}
int crypto_bignum_is_zero(const struct crypto_bignum *a)
{
return mp_iszero((mp_int *) a);
}
int crypto_bignum_is_one(const struct crypto_bignum *a)
{
return mp_isone((const mp_int *) a);
}
int crypto_bignum_is_odd(const struct crypto_bignum *a)
{
return mp_isodd((mp_int *) a);
}
int crypto_bignum_legendre(const struct crypto_bignum *a,
const struct crypto_bignum *p)
{
mp_int t;
int ret;
int res = -2;
if (TEST_FAIL())
return -2;
if (mp_init(&t) != MP_OKAY)
return -2;
/* t = (p-1) / 2 */
ret = mp_sub_d((mp_int *) p, 1, &t);
if (ret == MP_OKAY)
mp_rshb(&t, 1);
if (ret == MP_OKAY)
ret = mp_exptmod((mp_int *) a, &t, (mp_int *) p, &t);
if (ret == MP_OKAY) {
if (mp_isone(&t))
res = 1;
else if (mp_iszero(&t))
res = 0;
else
res = -1;
}
mp_clear(&t);
return res;
}
#ifdef CONFIG_ECC
int ecc_map(ecc_point *, mp_int *, mp_digit);
int ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R,
mp_int *a, mp_int *modulus, mp_digit mp);
struct crypto_ec {
ecc_key key;
mp_int a;
mp_int prime;
mp_int order;
mp_digit mont_b;
mp_int b;
};
struct crypto_ec * crypto_ec_init(int group)
{
int built = 0;
struct crypto_ec *e;
int curve_id;
/* Map from IANA registry for IKE D-H groups to OpenSSL NID */
switch (group) {
case 19:
curve_id = ECC_SECP256R1;
break;
case 20:
curve_id = ECC_SECP384R1;
break;
case 21:
curve_id = ECC_SECP521R1;
break;
case 25:
curve_id = ECC_SECP192R1;
break;
case 26:
curve_id = ECC_SECP224R1;
break;
#ifdef HAVE_ECC_BRAINPOOL
case 27:
curve_id = ECC_BRAINPOOLP224R1;
break;
case 28:
curve_id = ECC_BRAINPOOLP256R1;
break;
case 29:
curve_id = ECC_BRAINPOOLP384R1;
break;
case 30:
curve_id = ECC_BRAINPOOLP512R1;
break;
#endif /* HAVE_ECC_BRAINPOOL */
default:
return NULL;
}
e = os_zalloc(sizeof(*e));
if (!e)
return NULL;
if (wc_ecc_init(&e->key) != 0 ||
wc_ecc_set_curve(&e->key, 0, curve_id) != 0 ||
mp_init(&e->a) != MP_OKAY ||
mp_init(&e->prime) != MP_OKAY ||
mp_init(&e->order) != MP_OKAY ||
mp_init(&e->b) != MP_OKAY ||
mp_read_radix(&e->a, e->key.dp->Af, 16) != MP_OKAY ||
mp_read_radix(&e->b, e->key.dp->Bf, 16) != MP_OKAY ||
mp_read_radix(&e->prime, e->key.dp->prime, 16) != MP_OKAY ||
mp_read_radix(&e->order, e->key.dp->order, 16) != MP_OKAY ||
mp_montgomery_setup(&e->prime, &e->mont_b) != MP_OKAY)
goto done;
built = 1;
done:
if (!built) {
crypto_ec_deinit(e);
e = NULL;
}
return e;
}
void crypto_ec_deinit(struct crypto_ec* e)
{
if (!e)
return;
mp_clear(&e->b);
mp_clear(&e->order);
mp_clear(&e->prime);
mp_clear(&e->a);
wc_ecc_free(&e->key);
os_free(e);
}
struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
{
if (TEST_FAIL())
return NULL;
if (!e)
return NULL;
return (struct crypto_ec_point *) wc_ecc_new_point();
}
size_t crypto_ec_prime_len(struct crypto_ec *e)
{
return (mp_count_bits(&e->prime) + 7) / 8;
}
size_t crypto_ec_prime_len_bits(struct crypto_ec *e)
{
return mp_count_bits(&e->prime);
}
size_t crypto_ec_order_len(struct crypto_ec *e)
{
return (mp_count_bits(&e->order) + 7) / 8;
}
const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e)
{
return (const struct crypto_bignum *) &e->prime;
}
const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e)
{
return (const struct crypto_bignum *) &e->order;
}
const struct crypto_bignum * crypto_ec_get_a(struct crypto_ec *e)
{
return (const struct crypto_bignum *) &e->a;
}
const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e)
{
return (const struct crypto_bignum *) &e->b;
}
void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear)
{
ecc_point *point = (ecc_point *) p;
if (!p)
return;
if (clear) {
mp_forcezero(point->x);
mp_forcezero(point->y);
mp_forcezero(point->z);
}
wc_ecc_del_point(point);
}
int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p,
struct crypto_bignum *x)
{
return mp_copy(((ecc_point *) p)->x, (mp_int *) x) == MP_OKAY ? 0 : -1;
}
int crypto_ec_point_to_bin(struct crypto_ec *e,
const struct crypto_ec_point *point, u8 *x, u8 *y)
{
ecc_point *p = (ecc_point *) point;
if (TEST_FAIL())
return -1;
if (!mp_isone(p->z)) {
if (ecc_map(p, &e->prime, e->mont_b) != MP_OKAY)
return -1;
}
if (x) {
if (crypto_bignum_to_bin((struct crypto_bignum *)p->x, x,
e->key.dp->size,
e->key.dp->size) <= 0)
return -1;
}
if (y) {
if (crypto_bignum_to_bin((struct crypto_bignum *) p->y, y,
e->key.dp->size,
e->key.dp->size) <= 0)
return -1;
}
return 0;
}
struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
const u8 *val)
{
ecc_point *point = NULL;
int loaded = 0;
if (TEST_FAIL())
return NULL;
point = wc_ecc_new_point();
if (!point)
goto done;
if (mp_read_unsigned_bin(point->x, val, e->key.dp->size) != MP_OKAY)
goto done;
val += e->key.dp->size;
if (mp_read_unsigned_bin(point->y, val, e->key.dp->size) != MP_OKAY)
goto done;
mp_set(point->z, 1);
loaded = 1;
done:
if (!loaded) {
wc_ecc_del_point(point);
point = NULL;
}
return (struct crypto_ec_point *) point;
}
int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a,
const struct crypto_ec_point *b,
struct crypto_ec_point *c)
{
mp_int mu;
ecc_point *ta = NULL, *tb = NULL;
ecc_point *pa = (ecc_point *) a, *pb = (ecc_point *) b;
mp_int *modulus = &e->prime;
int ret;
if (TEST_FAIL())
return -1;
ret = mp_init(&mu);
if (ret != MP_OKAY)
return -1;
ret = mp_montgomery_calc_normalization(&mu, modulus);
if (ret != MP_OKAY) {
mp_clear(&mu);
return -1;
}
if (!mp_isone(&mu)) {
ta = wc_ecc_new_point();
if (!ta) {
mp_clear(&mu);
return -1;
}
tb = wc_ecc_new_point();
if (!tb) {
wc_ecc_del_point(ta);
mp_clear(&mu);
return -1;
}
if (mp_mulmod(pa->x, &mu, modulus, ta->x) != MP_OKAY ||
mp_mulmod(pa->y, &mu, modulus, ta->y) != MP_OKAY ||
mp_mulmod(pa->z, &mu, modulus, ta->z) != MP_OKAY ||
mp_mulmod(pb->x, &mu, modulus, tb->x) != MP_OKAY ||
mp_mulmod(pb->y, &mu, modulus, tb->y) != MP_OKAY ||
mp_mulmod(pb->z, &mu, modulus, tb->z) != MP_OKAY) {
ret = -1;
goto end;
}
pa = ta;
pb = tb;
}
ret = ecc_projective_add_point(pa, pb, (ecc_point *) c, &e->a,
&e->prime, e->mont_b);
if (ret != 0) {
ret = -1;
goto end;
}
if (ecc_map((ecc_point *) c, &e->prime, e->mont_b) != MP_OKAY)
ret = -1;
else
ret = 0;
end:
wc_ecc_del_point(tb);
wc_ecc_del_point(ta);
mp_clear(&mu);
return ret;
}
int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p,
const struct crypto_bignum *b,
struct crypto_ec_point *res)
{
int ret;
if (TEST_FAIL())
return -1;
ret = wc_ecc_mulmod((mp_int *) b, (ecc_point *) p, (ecc_point *) res,
&e->a, &e->prime, 1);
return ret == 0 ? 0 : -1;
}
int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
{
ecc_point *point = (ecc_point *) p;
if (TEST_FAIL())
return -1;
if (mp_sub(&e->prime, point->y, point->y) != MP_OKAY)
return -1;
return 0;
}
int crypto_ec_point_solve_y_coord(struct crypto_ec *e,
struct crypto_ec_point *p,
const struct crypto_bignum *x, int y_bit)
{
byte buf[1 + 2 * MAX_ECC_BYTES];
int ret;
int prime_len = crypto_ec_prime_len(e);
if (TEST_FAIL())
return -1;
buf[0] = y_bit ? ECC_POINT_COMP_ODD : ECC_POINT_COMP_EVEN;
ret = crypto_bignum_to_bin(x, buf + 1, prime_len, prime_len);
if (ret <= 0)
return -1;
ret = wc_ecc_import_point_der(buf, 1 + 2 * ret, e->key.idx,
(ecc_point *) p);
if (ret != 0)
return -1;
return 0;
}
struct crypto_bignum *
crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
const struct crypto_bignum *x)
{
mp_int *y2 = NULL;
mp_int t;
int calced = 0;
if (TEST_FAIL())
return NULL;
if (mp_init(&t) != MP_OKAY)
return NULL;
y2 = (mp_int *) crypto_bignum_init();
if (!y2)
goto done;
if (mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 ||
mp_mulmod((mp_int *) x, y2, &e->prime, y2) != 0 ||
mp_mulmod((mp_int *) x, &e->a, &e->prime, &t) != 0 ||
mp_addmod(y2, &t, &e->prime, y2) != 0 ||
mp_addmod(y2, &e->b, &e->prime, y2) != 0)
goto done;
calced = 1;
done:
if (!calced) {
if (y2) {
mp_clear(y2);
os_free(y2);
}
mp_clear(&t);
}
return (struct crypto_bignum *) y2;
}
int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
const struct crypto_ec_point *p)
{
return wc_ecc_point_is_at_infinity((ecc_point *) p);
}
int crypto_ec_point_is_on_curve(struct crypto_ec *e,
const struct crypto_ec_point *p)
{
return wc_ecc_is_point((ecc_point *) p, &e->a, &e->b, &e->prime) ==
MP_OKAY;
}
int crypto_ec_point_cmp(const struct crypto_ec *e,
const struct crypto_ec_point *a,
const struct crypto_ec_point *b)
{
return wc_ecc_cmp_point((ecc_point *) a, (ecc_point *) b);
}
struct crypto_ecdh {
struct crypto_ec *ec;
};
struct crypto_ecdh * crypto_ecdh_init(int group)
{
struct crypto_ecdh *ecdh = NULL;
WC_RNG rng;
int ret;
if (wc_InitRng(&rng) != 0)
goto fail;
ecdh = os_zalloc(sizeof(*ecdh));
if (!ecdh)
goto fail;
ecdh->ec = crypto_ec_init(group);
if (!ecdh->ec)
goto fail;
ret = wc_ecc_make_key_ex(&rng, ecdh->ec->key.dp->size, &ecdh->ec->key,
ecdh->ec->key.dp->id);
if (ret < 0)
goto fail;
done:
wc_FreeRng(&rng);
return ecdh;
fail:
crypto_ecdh_deinit(ecdh);
ecdh = NULL;
goto done;
}
void crypto_ecdh_deinit(struct crypto_ecdh *ecdh)
{
if (ecdh) {
crypto_ec_deinit(ecdh->ec);
os_free(ecdh);
}
}
struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y)
{
struct wpabuf *buf = NULL;
int ret;
int len = ecdh->ec->key.dp->size;
buf = wpabuf_alloc(inc_y ? 2 * len : len);
if (!buf)
goto fail;
ret = crypto_bignum_to_bin((struct crypto_bignum *)
ecdh->ec->key.pubkey.x, wpabuf_put(buf, len),
len, len);
if (ret < 0)
goto fail;
if (inc_y) {
ret = crypto_bignum_to_bin((struct crypto_bignum *)
ecdh->ec->key.pubkey.y,
wpabuf_put(buf, len), len, len);
if (ret < 0)
goto fail;
}
done:
return buf;
fail:
wpabuf_free(buf);
buf = NULL;
goto done;
}
struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y,
const u8 *key, size_t len)
{
int ret;
struct wpabuf *pubkey = NULL;
struct wpabuf *secret = NULL;
word32 key_len = ecdh->ec->key.dp->size;
ecc_point *point = NULL;
size_t need_key_len = inc_y ? 2 * key_len : key_len;
if (len < need_key_len)
goto fail;
pubkey = wpabuf_alloc(1 + 2 * key_len);
if (!pubkey)
goto fail;
wpabuf_put_u8(pubkey, inc_y ? ECC_POINT_UNCOMP : ECC_POINT_COMP_EVEN);
wpabuf_put_data(pubkey, key, need_key_len);
point = wc_ecc_new_point();
if (!point)
goto fail;
ret = wc_ecc_import_point_der(wpabuf_mhead(pubkey), 1 + 2 * key_len,
ecdh->ec->key.idx, point);
if (ret != MP_OKAY)
goto fail;
secret = wpabuf_alloc(key_len);
if (!secret)
goto fail;
ret = wc_ecc_shared_secret_ex(&ecdh->ec->key, point,
wpabuf_put(secret, key_len), &key_len);
if (ret != MP_OKAY)
goto fail;
done:
wc_ecc_del_point(point);
wpabuf_free(pubkey);
return secret;
fail:
wpabuf_free(secret);
secret = NULL;
goto done;
}
size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh)
{
return crypto_ec_prime_len(ecdh->ec);
}
#endif /* CONFIG_ECC */
diff --git a/contrib/wpa/src/crypto/random.c b/contrib/wpa/src/crypto/random.c
index 1cabf3f4b9a4..548b60dba267 100644
--- a/contrib/wpa/src/crypto/random.c
+++ b/contrib/wpa/src/crypto/random.c
@@ -1,478 +1,478 @@
/*
* Random number generator
* Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This random number generator is used to provide additional entropy to the
* one provided by the operating system (os_get_random()) for session key
* generation. The os_get_random() output is expected to be secure and the
* implementation here is expected to provide only limited protection against
* cases where os_get_random() cannot provide strong randomness. This
* implementation shall not be assumed to be secure as the sole source of
* randomness. The random_get_bytes() function mixes in randomness from
* os_get_random() and as such, calls to os_get_random() can be replaced with
* calls to random_get_bytes() without reducing security.
*
* The design here follows partially the design used in the Linux
* drivers/char/random.c, but the implementation here is simpler and not as
* strong. This is a compromise to reduce duplicated CPU effort and to avoid
* extra code/memory size. As pointed out above, os_get_random() needs to be
* guaranteed to be secure for any of the security assumptions to hold.
*/
#include "utils/includes.h"
#ifdef __linux__
#include <fcntl.h>
#ifdef CONFIG_GETRANDOM
#include <sys/random.h>
#endif /* CONFIG_GETRANDOM */
#endif /* __linux__ */
#include "utils/common.h"
#include "utils/eloop.h"
#include "crypto/crypto.h"
#include "sha1.h"
#include "random.h"
#define POOL_WORDS 32
#define POOL_WORDS_MASK (POOL_WORDS - 1)
#define POOL_TAP1 26
#define POOL_TAP2 20
#define POOL_TAP3 14
#define POOL_TAP4 7
#define POOL_TAP5 1
#define EXTRACT_LEN 16
#define MIN_READY_MARK 2
static u32 pool[POOL_WORDS];
static unsigned int input_rotate = 0;
static unsigned int pool_pos = 0;
-static u8 dummy_key[20];
+static u8 stub_key[20];
#ifdef __linux__
-static size_t dummy_key_avail = 0;
+static size_t stub_key_avail = 0;
static int random_fd = -1;
#endif /* __linux__ */
static unsigned int own_pool_ready = 0;
#define RANDOM_ENTROPY_SIZE 20
static char *random_entropy_file = NULL;
#define MIN_COLLECT_ENTROPY 1000
static unsigned int entropy = 0;
static unsigned int total_collected = 0;
static void random_write_entropy(void);
static u32 __ROL32(u32 x, u32 y)
{
if (y == 0)
return x;
return (x << (y & 31)) | (x >> (32 - (y & 31)));
}
static void random_mix_pool(const void *buf, size_t len)
{
static const u32 twist[8] = {
0x00000000, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278
};
const u8 *pos = buf;
u32 w;
wpa_hexdump_key(MSG_EXCESSIVE, "random_mix_pool", buf, len);
while (len--) {
w = __ROL32(*pos++, input_rotate & 31);
input_rotate += pool_pos ? 7 : 14;
pool_pos = (pool_pos - 1) & POOL_WORDS_MASK;
w ^= pool[pool_pos];
w ^= pool[(pool_pos + POOL_TAP1) & POOL_WORDS_MASK];
w ^= pool[(pool_pos + POOL_TAP2) & POOL_WORDS_MASK];
w ^= pool[(pool_pos + POOL_TAP3) & POOL_WORDS_MASK];
w ^= pool[(pool_pos + POOL_TAP4) & POOL_WORDS_MASK];
w ^= pool[(pool_pos + POOL_TAP5) & POOL_WORDS_MASK];
pool[pool_pos] = (w >> 3) ^ twist[w & 7];
}
}
static void random_extract(u8 *out)
{
unsigned int i;
u8 hash[SHA1_MAC_LEN];
u32 *hash_ptr;
u32 buf[POOL_WORDS / 2];
/* First, add hash back to pool to make backtracking more difficult. */
- hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) pool,
+ hmac_sha1(stub_key, sizeof(stub_key), (const u8 *) pool,
sizeof(pool), hash);
random_mix_pool(hash, sizeof(hash));
/* Hash half the pool to extra data */
for (i = 0; i < POOL_WORDS / 2; i++)
buf[i] = pool[(pool_pos - i) & POOL_WORDS_MASK];
- hmac_sha1(dummy_key, sizeof(dummy_key), (const u8 *) buf,
+ hmac_sha1(stub_key, sizeof(stub_key), (const u8 *) buf,
sizeof(buf), hash);
/*
* Fold the hash to further reduce any potential output pattern.
* Though, compromise this to reduce CPU use for the most common output
* length (32) and return 16 bytes from instead of only half.
*/
hash_ptr = (u32 *) hash;
hash_ptr[0] ^= hash_ptr[4];
os_memcpy(out, hash, EXTRACT_LEN);
}
void random_add_randomness(const void *buf, size_t len)
{
struct os_time t;
static unsigned int count = 0;
count++;
if (entropy > MIN_COLLECT_ENTROPY && (count & 0x3ff) != 0) {
/*
* No need to add more entropy at this point, so save CPU and
* skip the update.
*/
return;
}
wpa_printf(MSG_EXCESSIVE, "Add randomness: count=%u entropy=%u",
count, entropy);
os_get_time(&t);
wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
(const u8 *) pool, sizeof(pool));
random_mix_pool(&t, sizeof(t));
random_mix_pool(buf, len);
wpa_hexdump_key(MSG_EXCESSIVE, "random pool",
(const u8 *) pool, sizeof(pool));
entropy++;
total_collected++;
}
int random_get_bytes(void *buf, size_t len)
{
int ret;
u8 *bytes = buf;
size_t left;
wpa_printf(MSG_MSGDUMP, "Get randomness: len=%u entropy=%u",
(unsigned int) len, entropy);
/* Start with assumed strong randomness from OS */
ret = os_get_random(buf, len);
wpa_hexdump_key(MSG_EXCESSIVE, "random from os_get_random",
buf, len);
/* Mix in additional entropy extracted from the internal pool */
left = len;
while (left) {
size_t siz, i;
u8 tmp[EXTRACT_LEN];
random_extract(tmp);
wpa_hexdump_key(MSG_EXCESSIVE, "random from internal pool",
tmp, sizeof(tmp));
siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
for (i = 0; i < siz; i++)
*bytes++ ^= tmp[i];
left -= siz;
}
#ifdef CONFIG_FIPS
/* Mix in additional entropy from the crypto module */
bytes = buf;
left = len;
while (left) {
size_t siz, i;
u8 tmp[EXTRACT_LEN];
if (crypto_get_random(tmp, sizeof(tmp)) < 0) {
wpa_printf(MSG_ERROR, "random: No entropy available "
"for generating strong random bytes");
return -1;
}
wpa_hexdump_key(MSG_EXCESSIVE, "random from crypto module",
tmp, sizeof(tmp));
siz = left > EXTRACT_LEN ? EXTRACT_LEN : left;
for (i = 0; i < siz; i++)
*bytes++ ^= tmp[i];
left -= siz;
}
#endif /* CONFIG_FIPS */
wpa_hexdump_key(MSG_EXCESSIVE, "mixed random", buf, len);
if (entropy < len)
entropy = 0;
else
entropy -= len;
return ret;
}
int random_pool_ready(void)
{
#ifdef __linux__
int fd;
ssize_t res;
/*
* Make sure that there is reasonable entropy available before allowing
* some key derivation operations to proceed.
*/
- if (dummy_key_avail == sizeof(dummy_key))
+ if (stub_key_avail == sizeof(stub_key))
return 1; /* Already initialized - good to continue */
/*
* Try to fetch some more data from the kernel high quality RNG.
* There may not be enough data available at this point,
* so use non-blocking read to avoid blocking the application
* completely.
*/
#ifdef CONFIG_GETRANDOM
- res = getrandom(dummy_key + dummy_key_avail,
- sizeof(dummy_key) - dummy_key_avail, GRND_NONBLOCK);
+ res = getrandom(stub_key + stub_key_avail,
+ sizeof(stub_key) - stub_key_avail, GRND_NONBLOCK);
if (res < 0) {
if (errno == ENOSYS) {
wpa_printf(MSG_DEBUG,
"random: getrandom() not supported, falling back to /dev/random");
} else {
wpa_printf(MSG_INFO,
"random: no data from getrandom(): %s",
strerror(errno));
res = 0;
}
}
#else /* CONFIG_GETRANDOM */
res = -1;
#endif /* CONFIG_GETRANDOM */
if (res < 0) {
fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
if (fd < 0) {
wpa_printf(MSG_ERROR,
"random: Cannot open /dev/random: %s",
strerror(errno));
return -1;
}
- res = read(fd, dummy_key + dummy_key_avail,
- sizeof(dummy_key) - dummy_key_avail);
+ res = read(fd, stub_key + stub_key_avail,
+ sizeof(stub_key) - stub_key_avail);
if (res < 0) {
wpa_printf(MSG_ERROR,
"random: Cannot read from /dev/random: %s",
strerror(errno));
res = 0;
}
close(fd);
}
wpa_printf(MSG_DEBUG, "random: Got %u/%u random bytes", (unsigned) res,
- (unsigned) (sizeof(dummy_key) - dummy_key_avail));
- dummy_key_avail += res;
+ (unsigned) (sizeof(stub_key) - stub_key_avail));
+ stub_key_avail += res;
- if (dummy_key_avail == sizeof(dummy_key)) {
+ if (stub_key_avail == sizeof(stub_key)) {
if (own_pool_ready < MIN_READY_MARK)
own_pool_ready = MIN_READY_MARK;
random_write_entropy();
return 1;
}
wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
"random data available",
- (unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key));
+ (unsigned) stub_key_avail, (unsigned) sizeof(stub_key));
if (own_pool_ready >= MIN_READY_MARK ||
total_collected + 10 * own_pool_ready > MIN_COLLECT_ENTROPY) {
wpa_printf(MSG_INFO, "random: Allow operation to proceed "
"based on internal entropy");
return 1;
}
wpa_printf(MSG_INFO, "random: Not enough entropy pool available for "
"secure operations");
return 0;
#else /* __linux__ */
/* TODO: could do similar checks on non-Linux platforms */
return 1;
#endif /* __linux__ */
}
void random_mark_pool_ready(void)
{
own_pool_ready++;
wpa_printf(MSG_DEBUG, "random: Mark internal entropy pool to be "
"ready (count=%u/%u)", own_pool_ready, MIN_READY_MARK);
random_write_entropy();
}
#ifdef __linux__
static void random_close_fd(void)
{
if (random_fd >= 0) {
eloop_unregister_read_sock(random_fd);
close(random_fd);
random_fd = -1;
}
}
static void random_read_fd(int sock, void *eloop_ctx, void *sock_ctx)
{
ssize_t res;
- if (dummy_key_avail == sizeof(dummy_key)) {
+ if (stub_key_avail == sizeof(stub_key)) {
random_close_fd();
return;
}
- res = read(sock, dummy_key + dummy_key_avail,
- sizeof(dummy_key) - dummy_key_avail);
+ res = read(sock, stub_key + stub_key_avail,
+ sizeof(stub_key) - stub_key_avail);
if (res < 0) {
wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
"%s", strerror(errno));
return;
}
wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from /dev/random",
(unsigned) res,
- (unsigned) (sizeof(dummy_key) - dummy_key_avail));
- dummy_key_avail += res;
+ (unsigned) (sizeof(stub_key) - stub_key_avail));
+ stub_key_avail += res;
- if (dummy_key_avail == sizeof(dummy_key)) {
+ if (stub_key_avail == sizeof(stub_key)) {
random_close_fd();
if (own_pool_ready < MIN_READY_MARK)
own_pool_ready = MIN_READY_MARK;
random_write_entropy();
}
}
#endif /* __linux__ */
static void random_read_entropy(void)
{
char *buf;
size_t len;
if (!random_entropy_file)
return;
buf = os_readfile(random_entropy_file, &len);
if (buf == NULL)
return; /* entropy file not yet available */
if (len != 1 + RANDOM_ENTROPY_SIZE) {
wpa_printf(MSG_DEBUG, "random: Invalid entropy file %s",
random_entropy_file);
os_free(buf);
return;
}
own_pool_ready = (u8) buf[0];
random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE);
os_free(buf);
wpa_printf(MSG_DEBUG, "random: Added entropy from %s "
"(own_pool_ready=%u)",
random_entropy_file, own_pool_ready);
}
static void random_write_entropy(void)
{
char buf[RANDOM_ENTROPY_SIZE];
FILE *f;
u8 opr;
int fail = 0;
if (!random_entropy_file)
return;
if (random_get_bytes(buf, RANDOM_ENTROPY_SIZE) < 0)
return;
f = fopen(random_entropy_file, "wb");
if (f == NULL) {
wpa_printf(MSG_ERROR, "random: Could not open entropy file %s "
"for writing", random_entropy_file);
return;
}
opr = own_pool_ready > 0xff ? 0xff : own_pool_ready;
if (fwrite(&opr, 1, 1, f) != 1 ||
fwrite(buf, RANDOM_ENTROPY_SIZE, 1, f) != 1)
fail = 1;
fclose(f);
if (fail) {
wpa_printf(MSG_ERROR, "random: Could not write entropy data "
"to %s", random_entropy_file);
return;
}
wpa_printf(MSG_DEBUG, "random: Updated entropy file %s "
"(own_pool_ready=%u)",
random_entropy_file, own_pool_ready);
}
void random_init(const char *entropy_file)
{
os_free(random_entropy_file);
if (entropy_file)
random_entropy_file = os_strdup(entropy_file);
else
random_entropy_file = NULL;
random_read_entropy();
#ifdef __linux__
if (random_fd >= 0)
return;
#ifdef CONFIG_GETRANDOM
{
- u8 dummy;
+ u8 stub;
- if (getrandom(&dummy, 0, GRND_NONBLOCK) == 0 ||
+ if (getrandom(&stub, 0, GRND_NONBLOCK) == 0 ||
errno != ENOSYS) {
wpa_printf(MSG_DEBUG,
"random: getrandom() support available");
return;
}
}
#endif /* CONFIG_GETRANDOM */
random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
if (random_fd < 0) {
wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
strerror(errno));
return;
}
wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
"/dev/random");
eloop_register_read_sock(random_fd, random_read_fd, NULL, NULL);
#endif /* __linux__ */
random_write_entropy();
}
void random_deinit(void)
{
#ifdef __linux__
random_close_fd();
#endif /* __linux__ */
random_write_entropy();
os_free(random_entropy_file);
random_entropy_file = NULL;
}
diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c
index 345a35ee16f4..203b0f781ff5 100644
--- a/contrib/wpa/src/crypto/tls_openssl.c
+++ b/contrib/wpa/src/crypto/tls_openssl.c
@@ -1,5738 +1,5761 @@
/*
* SSL/TLS interface functions for OpenSSL
* Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#ifndef CONFIG_SMARTCARD
#ifndef OPENSSL_NO_ENGINE
#ifndef ANDROID
#define OPENSSL_NO_ENGINE
#endif
#endif
#endif
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/opensslv.h>
#include <openssl/pkcs12.h>
#include <openssl/x509v3.h>
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif /* OPENSSL_NO_ENGINE */
#ifndef OPENSSL_NO_DSA
#include <openssl/dsa.h>
#endif
#ifndef OPENSSL_NO_DH
#include <openssl/dh.h>
#endif
#include "common.h"
#include "crypto.h"
#include "sha1.h"
#include "sha256.h"
#include "tls.h"
#include "tls_openssl.h"
#if !defined(CONFIG_FIPS) && \
(defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
defined(EAP_SERVER_FAST))
#define OPENSSL_NEED_EAP_FAST_PRF
#endif
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \
defined(EAP_SERVER_FAST) || defined(EAP_TEAP) || \
defined(EAP_SERVER_TEAP)
#define EAP_FAST_OR_TEAP
#endif
#if defined(OPENSSL_IS_BORINGSSL)
/* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */
typedef size_t stack_index_t;
#else
typedef int stack_index_t;
#endif
#ifdef SSL_set_tlsext_status_type
#ifndef OPENSSL_NO_TLSEXT
#define HAVE_OCSP
#include <openssl/ocsp.h>
#endif /* OPENSSL_NO_TLSEXT */
#endif /* SSL_set_tlsext_status_type */
#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)) && \
!defined(BORINGSSL_API_VERSION)
/*
* SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
* 1.1.0 and newer BoringSSL revisions. Provide compatibility wrappers for
* older versions.
*/
static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out,
size_t outlen)
{
if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
return 0;
os_memcpy(out, ssl->s3->client_random, SSL3_RANDOM_SIZE);
return SSL3_RANDOM_SIZE;
}
static size_t SSL_get_server_random(const SSL *ssl, unsigned char *out,
size_t outlen)
{
if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
return 0;
os_memcpy(out, ssl->s3->server_random, SSL3_RANDOM_SIZE);
return SSL3_RANDOM_SIZE;
}
#ifdef OPENSSL_NEED_EAP_FAST_PRF
static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session,
unsigned char *out, size_t outlen)
{
if (!session || session->master_key_length < 0 ||
(size_t) session->master_key_length > outlen)
return 0;
if ((size_t) session->master_key_length < outlen)
outlen = session->master_key_length;
os_memcpy(out, session->master_key, outlen);
return outlen;
}
#endif /* OPENSSL_NEED_EAP_FAST_PRF */
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)
#ifdef CONFIG_SUITEB
static int RSA_bits(const RSA *r)
{
return BN_num_bits(r->n);
}
#endif /* CONFIG_SUITEB */
static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
{
return ASN1_STRING_data((ASN1_STRING *) x);
}
#endif
#ifdef ANDROID
#include <openssl/pem.h>
#include <keystore/keystore_get.h>
static BIO * BIO_from_keystore(const char *key)
{
BIO *bio = NULL;
uint8_t *value = NULL;
int length = keystore_get(key, strlen(key), &value);
if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
BIO_write(bio, value, length);
free(value);
return bio;
}
static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias)
{
BIO *bio = BIO_from_keystore(key_alias);
STACK_OF(X509_INFO) *stack = NULL;
stack_index_t i;
if (bio) {
stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
BIO_free(bio);
}
if (!stack) {
wpa_printf(MSG_WARNING, "TLS: Failed to parse certificate: %s",
key_alias);
return -1;
}
for (i = 0; i < sk_X509_INFO_num(stack); ++i) {
X509_INFO *info = sk_X509_INFO_value(stack, i);
if (info->x509)
X509_STORE_add_cert(ctx, info->x509);
if (info->crl)
X509_STORE_add_crl(ctx, info->crl);
}
sk_X509_INFO_pop_free(stack, X509_INFO_free);
return 0;
}
static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx,
const char *encoded_key_alias)
{
int rc = -1;
int len = os_strlen(encoded_key_alias);
unsigned char *decoded_alias;
if (len & 1) {
wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s",
encoded_key_alias);
return rc;
}
decoded_alias = os_malloc(len / 2 + 1);
if (decoded_alias) {
if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) {
decoded_alias[len / 2] = '\0';
rc = tls_add_ca_from_keystore(
ctx, (const char *) decoded_alias);
}
os_free(decoded_alias);
}
return rc;
}
#endif /* ANDROID */
static int tls_openssl_ref_count = 0;
static int tls_ex_idx_session = -1;
struct tls_context {
void (*event_cb)(void *ctx, enum tls_event ev,
union tls_event_data *data);
void *cb_ctx;
int cert_in_cb;
char *ocsp_stapling_response;
};
static struct tls_context *tls_global = NULL;
struct tls_data {
SSL_CTX *ssl;
unsigned int tls_session_lifetime;
int check_crl;
int check_crl_strict;
char *ca_cert;
unsigned int crl_reload_interval;
struct os_reltime crl_last_reload;
char *check_cert_subject;
};
struct tls_connection {
struct tls_context *context;
struct tls_data *data;
SSL_CTX *ssl_ctx;
SSL *ssl;
BIO *ssl_in, *ssl_out;
#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
ENGINE *engine; /* functional reference to the engine */
EVP_PKEY *private_key; /* the private key if using engine */
#endif /* OPENSSL_NO_ENGINE */
char *subject_match, *altsubject_match, *suffix_match, *domain_match;
char *check_cert_subject;
int read_alerts, write_alerts, failed;
tls_session_ticket_cb session_ticket_cb;
void *session_ticket_cb_ctx;
/* SessionTicket received from OpenSSL hello_extension_cb (server) */
u8 *session_ticket;
size_t session_ticket_len;
unsigned int ca_cert_verify:1;
unsigned int cert_probe:1;
unsigned int server_cert_only:1;
unsigned int invalid_hb_used:1;
unsigned int success_data:1;
unsigned int client_hello_generated:1;
unsigned int server:1;
u8 srv_cert_hash[32];
unsigned int flags;
X509 *peer_cert;
X509 *peer_issuer;
X509 *peer_issuer_issuer;
char *peer_subject; /* peer subject info for authenticated peer */
unsigned char client_random[SSL3_RANDOM_SIZE];
unsigned char server_random[SSL3_RANDOM_SIZE];
u16 cipher_suite;
int server_dh_prime_len;
};
static struct tls_context * tls_context_new(const struct tls_config *conf)
{
struct tls_context *context = os_zalloc(sizeof(*context));
if (context == NULL)
return NULL;
if (conf) {
context->event_cb = conf->event_cb;
context->cb_ctx = conf->cb_ctx;
context->cert_in_cb = conf->cert_in_cb;
}
return context;
}
#ifdef CONFIG_NO_STDOUT_DEBUG
static void _tls_show_errors(void)
{
unsigned long err;
while ((err = ERR_get_error())) {
/* Just ignore the errors, since stdout is disabled */
}
}
#define tls_show_errors(l, f, t) _tls_show_errors()
#else /* CONFIG_NO_STDOUT_DEBUG */
static void tls_show_errors(int level, const char *func, const char *txt)
{
unsigned long err;
wpa_printf(level, "OpenSSL: %s - %s %s",
func, txt, ERR_error_string(ERR_get_error(), NULL));
while ((err = ERR_get_error())) {
wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
ERR_error_string(err, NULL));
}
}
#endif /* CONFIG_NO_STDOUT_DEBUG */
static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl)
{
int flags;
X509_STORE *store;
store = X509_STORE_new();
if (!store) {
wpa_printf(MSG_DEBUG,
"OpenSSL: %s - failed to allocate new certificate store",
__func__);
return NULL;
}
if (ca_cert && X509_STORE_load_locations(store, ca_cert, NULL) != 1) {
tls_show_errors(MSG_WARNING, __func__,
"Failed to load root certificates");
X509_STORE_free(store);
return NULL;
}
flags = check_crl ? X509_V_FLAG_CRL_CHECK : 0;
if (check_crl == 2)
flags |= X509_V_FLAG_CRL_CHECK_ALL;
X509_STORE_set_flags(store, flags);
return store;
}
#ifdef CONFIG_NATIVE_WINDOWS
/* Windows CryptoAPI and access to certificate stores */
#include <wincrypt.h>
#ifdef __MINGW32_VERSION
/*
* MinGW does not yet include all the needed definitions for CryptoAPI, so
* define here whatever extra is needed.
*/
#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16)
#define CERT_STORE_READONLY_FLAG 0x00008000
#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
#endif /* __MINGW32_VERSION */
struct cryptoapi_rsa_data {
const CERT_CONTEXT *cert;
HCRYPTPROV crypt_prov;
DWORD key_spec;
BOOL free_crypt_prov;
};
static void cryptoapi_error(const char *msg)
{
wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u",
msg, (unsigned int) GetLastError());
}
static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding)
{
wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
return 0;
}
static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding)
{
wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
return 0;
}
static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding)
{
struct cryptoapi_rsa_data *priv =
(struct cryptoapi_rsa_data *) rsa->meth->app_data;
HCRYPTHASH hash;
DWORD hash_size, len, i;
unsigned char *buf = NULL;
int ret = 0;
if (priv == NULL) {
RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (padding != RSA_PKCS1_PADDING) {
RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
RSA_R_UNKNOWN_PADDING_TYPE);
return 0;
}
if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) {
wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported",
__func__);
RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
RSA_R_INVALID_MESSAGE_LENGTH);
return 0;
}
if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash))
{
cryptoapi_error("CryptCreateHash failed");
return 0;
}
len = sizeof(hash_size);
if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len,
0)) {
cryptoapi_error("CryptGetHashParam failed");
goto err;
}
if ((int) hash_size != flen) {
wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)",
(unsigned) hash_size, flen);
RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
RSA_R_INVALID_MESSAGE_LENGTH);
goto err;
}
if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) {
cryptoapi_error("CryptSetHashParam failed");
goto err;
}
len = RSA_size(rsa);
buf = os_malloc(len);
if (buf == NULL) {
RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
goto err;
}
if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) {
cryptoapi_error("CryptSignHash failed");
goto err;
}
for (i = 0; i < len; i++)
to[i] = buf[len - i - 1];
ret = len;
err:
os_free(buf);
CryptDestroyHash(hash);
return ret;
}
static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding)
{
wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
return 0;
}
static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv)
{
if (priv == NULL)
return;
if (priv->crypt_prov && priv->free_crypt_prov)
CryptReleaseContext(priv->crypt_prov, 0);
if (priv->cert)
CertFreeCertificateContext(priv->cert);
os_free(priv);
}
static int cryptoapi_finish(RSA *rsa)
{
cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data);
os_free((void *) rsa->meth);
rsa->meth = NULL;
return 1;
}
static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store)
{
HCERTSTORE cs;
const CERT_CONTEXT *ret = NULL;
cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0,
store | CERT_STORE_OPEN_EXISTING_FLAG |
CERT_STORE_READONLY_FLAG, L"MY");
if (cs == NULL) {
cryptoapi_error("Failed to open 'My system store'");
return NULL;
}
if (strncmp(name, "cert://", 7) == 0) {
unsigned short wbuf[255];
MultiByteToWideChar(CP_ACP, 0, name + 7, -1, wbuf, 255);
ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING |
PKCS_7_ASN_ENCODING,
0, CERT_FIND_SUBJECT_STR,
wbuf, NULL);
} else if (strncmp(name, "hash://", 7) == 0) {
CRYPT_HASH_BLOB blob;
int len;
const char *hash = name + 7;
unsigned char *buf;
len = os_strlen(hash) / 2;
buf = os_malloc(len);
if (buf && hexstr2bin(hash, buf, len) == 0) {
blob.cbData = len;
blob.pbData = buf;
ret = CertFindCertificateInStore(cs,
X509_ASN_ENCODING |
PKCS_7_ASN_ENCODING,
0, CERT_FIND_HASH,
&blob, NULL);
}
os_free(buf);
}
CertCloseStore(cs, 0);
return ret;
}
static int tls_cryptoapi_cert(SSL *ssl, const char *name)
{
X509 *cert = NULL;
RSA *rsa = NULL, *pub_rsa;
struct cryptoapi_rsa_data *priv;
RSA_METHOD *rsa_meth;
if (name == NULL ||
(strncmp(name, "cert://", 7) != 0 &&
strncmp(name, "hash://", 7) != 0))
return -1;
priv = os_zalloc(sizeof(*priv));
rsa_meth = os_zalloc(sizeof(*rsa_meth));
if (priv == NULL || rsa_meth == NULL) {
wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory "
"for CryptoAPI RSA method");
os_free(priv);
os_free(rsa_meth);
return -1;
}
priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER);
if (priv->cert == NULL) {
priv->cert = cryptoapi_find_cert(
name, CERT_SYSTEM_STORE_LOCAL_MACHINE);
}
if (priv->cert == NULL) {
wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate "
"'%s'", name);
goto err;
}
cert = d2i_X509(NULL,
(const unsigned char **) &priv->cert->pbCertEncoded,
priv->cert->cbCertEncoded);
if (cert == NULL) {
wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER "
"encoding");
goto err;
}
if (!CryptAcquireCertificatePrivateKey(priv->cert,
CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
NULL, &priv->crypt_prov,
&priv->key_spec,
&priv->free_crypt_prov)) {
cryptoapi_error("Failed to acquire a private key for the "
"certificate");
goto err;
}
rsa_meth->name = "Microsoft CryptoAPI RSA Method";
rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc;
rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec;
rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc;
rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec;
rsa_meth->finish = cryptoapi_finish;
rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK;
rsa_meth->app_data = (char *) priv;
rsa = RSA_new();
if (rsa == NULL) {
SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,
ERR_R_MALLOC_FAILURE);
goto err;
}
if (!SSL_use_certificate(ssl, cert)) {
RSA_free(rsa);
rsa = NULL;
goto err;
}
pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
X509_free(cert);
cert = NULL;
rsa->n = BN_dup(pub_rsa->n);
rsa->e = BN_dup(pub_rsa->e);
if (!RSA_set_method(rsa, rsa_meth))
goto err;
if (!SSL_use_RSAPrivateKey(ssl, rsa))
goto err;
RSA_free(rsa);
return 0;
err:
if (cert)
X509_free(cert);
if (rsa)
RSA_free(rsa);
else {
os_free(rsa_meth);
cryptoapi_free_data(priv);
}
return -1;
}
static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name)
{
HCERTSTORE cs;
PCCERT_CONTEXT ctx = NULL;
X509 *cert;
char buf[128];
const char *store;
#ifdef UNICODE
WCHAR *wstore;
#endif /* UNICODE */
if (name == NULL || strncmp(name, "cert_store://", 13) != 0)
return -1;
store = name + 13;
#ifdef UNICODE
wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR));
if (wstore == NULL)
return -1;
wsprintf(wstore, L"%S", store);
cs = CertOpenSystemStore(0, wstore);
os_free(wstore);
#else /* UNICODE */
cs = CertOpenSystemStore(0, store);
#endif /* UNICODE */
if (cs == NULL) {
wpa_printf(MSG_DEBUG, "%s: failed to open system cert store "
"'%s': error=%d", __func__, store,
(int) GetLastError());
return -1;
}
while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
cert = d2i_X509(NULL,
(const unsigned char **) &ctx->pbCertEncoded,
ctx->cbCertEncoded);
if (cert == NULL) {
wpa_printf(MSG_INFO, "CryptoAPI: Could not process "
"X509 DER encoding for CA cert");
continue;
}
X509_NAME_oneline(X509_get_subject_name(cert), buf,
sizeof(buf));
wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for "
"system certificate store: subject='%s'", buf);
if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
cert)) {
tls_show_errors(MSG_WARNING, __func__,
"Failed to add ca_cert to OpenSSL "
"certificate store");
}
X509_free(cert);
}
if (!CertCloseStore(cs, 0)) {
wpa_printf(MSG_DEBUG, "%s: failed to close system cert store "
"'%s': error=%d", __func__, name + 13,
(int) GetLastError());
}
return 0;
}
#else /* CONFIG_NATIVE_WINDOWS */
static int tls_cryptoapi_cert(SSL *ssl, const char *name)
{
return -1;
}
#endif /* CONFIG_NATIVE_WINDOWS */
static void ssl_info_cb(const SSL *ssl, int where, int ret)
{
const char *str;
int w;
wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret);
w = where & ~SSL_ST_MASK;
if (w & SSL_ST_CONNECT)
str = "SSL_connect";
else if (w & SSL_ST_ACCEPT)
str = "SSL_accept";
else
str = "undefined";
if (where & SSL_CB_LOOP) {
wpa_printf(MSG_DEBUG, "SSL: %s:%s",
str, SSL_state_string_long(ssl));
} else if (where & SSL_CB_ALERT) {
struct tls_connection *conn = SSL_get_app_data((SSL *) ssl);
wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s",
where & SSL_CB_READ ?
"read (remote end reported an error)" :
"write (local SSL3 detected an error)",
SSL_alert_type_string_long(ret),
SSL_alert_desc_string_long(ret));
if ((ret >> 8) == SSL3_AL_FATAL) {
if (where & SSL_CB_READ)
conn->read_alerts++;
else
conn->write_alerts++;
}
if (conn->context->event_cb != NULL) {
union tls_event_data ev;
struct tls_context *context = conn->context;
os_memset(&ev, 0, sizeof(ev));
ev.alert.is_local = !(where & SSL_CB_READ);
ev.alert.type = SSL_alert_type_string_long(ret);
ev.alert.description = SSL_alert_desc_string_long(ret);
context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
}
} else if (where & SSL_CB_EXIT && ret <= 0) {
wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
str, ret == 0 ? "failed" : "error",
SSL_state_string_long(ssl));
}
}
#ifndef OPENSSL_NO_ENGINE
/**
* tls_engine_load_dynamic_generic - load any openssl engine
* @pre: an array of commands and values that load an engine initialized
* in the engine specific function
* @post: an array of commands and values that initialize an already loaded
* engine (or %NULL if not required)
* @id: the engine id of the engine to load (only required if post is not %NULL
*
* This function is a generic function that loads any openssl engine.
*
* Returns: 0 on success, -1 on failure
*/
static int tls_engine_load_dynamic_generic(const char *pre[],
const char *post[], const char *id)
{
ENGINE *engine;
const char *dynamic_id = "dynamic";
engine = ENGINE_by_id(id);
if (engine) {
wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
"available", id);
/*
* If it was auto-loaded by ENGINE_by_id() we might still
* need to tell it which PKCS#11 module to use in legacy
* (non-p11-kit) environments. Do so now; even if it was
* properly initialised before, setting it again will be
* harmless.
*/
goto found;
}
ERR_clear_error();
engine = ENGINE_by_id(dynamic_id);
if (engine == NULL) {
wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
dynamic_id,
ERR_error_string(ERR_get_error(), NULL));
return -1;
}
/* Perform the pre commands. This will load the engine. */
while (pre && pre[0]) {
wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]);
if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) {
wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: "
"%s %s [%s]", pre[0], pre[1],
ERR_error_string(ERR_get_error(), NULL));
ENGINE_free(engine);
return -1;
}
pre += 2;
}
/*
* Free the reference to the "dynamic" engine. The loaded engine can
* now be looked up using ENGINE_by_id().
*/
ENGINE_free(engine);
engine = ENGINE_by_id(id);
if (engine == NULL) {
wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
id, ERR_error_string(ERR_get_error(), NULL));
return -1;
}
found:
while (post && post[0]) {
wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]);
if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) {
wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:"
" %s %s [%s]", post[0], post[1],
ERR_error_string(ERR_get_error(), NULL));
ENGINE_remove(engine);
ENGINE_free(engine);
return -1;
}
post += 2;
}
ENGINE_free(engine);
return 0;
}
/**
* tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc
* @pkcs11_so_path: pksc11_so_path from the configuration
* @pcks11_module_path: pkcs11_module_path from the configuration
*/
static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
const char *pkcs11_module_path)
{
char *engine_id = "pkcs11";
const char *pre_cmd[] = {
"SO_PATH", NULL /* pkcs11_so_path */,
"ID", NULL /* engine_id */,
"LIST_ADD", "1",
/* "NO_VCHECK", "1", */
"LOAD", NULL,
NULL, NULL
};
const char *post_cmd[] = {
"MODULE_PATH", NULL /* pkcs11_module_path */,
NULL, NULL
};
if (!pkcs11_so_path)
return 0;
pre_cmd[1] = pkcs11_so_path;
pre_cmd[3] = engine_id;
if (pkcs11_module_path)
post_cmd[1] = pkcs11_module_path;
else
post_cmd[0] = NULL;
wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s",
pkcs11_so_path);
return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id);
}
/**
* tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc
* @opensc_so_path: opensc_so_path from the configuration
*/
static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
{
char *engine_id = "opensc";
const char *pre_cmd[] = {
"SO_PATH", NULL /* opensc_so_path */,
"ID", NULL /* engine_id */,
"LIST_ADD", "1",
"LOAD", NULL,
NULL, NULL
};
if (!opensc_so_path)
return 0;
pre_cmd[1] = opensc_so_path;
pre_cmd[3] = engine_id;
wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s",
opensc_so_path);
return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id);
}
#endif /* OPENSSL_NO_ENGINE */
static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess)
{
struct wpabuf *buf;
if (tls_ex_idx_session < 0)
return;
buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
if (!buf)
return;
wpa_printf(MSG_DEBUG,
"OpenSSL: Free application session data %p (sess %p)",
buf, sess);
wpabuf_free(buf);
SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
}
void * tls_init(const struct tls_config *conf)
{
struct tls_data *data;
SSL_CTX *ssl;
struct tls_context *context;
const char *ciphers;
if (tls_openssl_ref_count == 0) {
tls_global = context = tls_context_new(conf);
if (context == NULL)
return NULL;
#ifdef CONFIG_FIPS
#ifdef OPENSSL_FIPS
if (conf && conf->fips_mode) {
static int fips_enabled = 0;
if (!fips_enabled && !FIPS_mode_set(1)) {
wpa_printf(MSG_ERROR, "Failed to enable FIPS "
"mode");
ERR_load_crypto_strings();
ERR_print_errors_fp(stderr);
os_free(tls_global);
tls_global = NULL;
return NULL;
} else {
wpa_printf(MSG_INFO, "Running in FIPS mode");
fips_enabled = 1;
}
}
#else /* OPENSSL_FIPS */
if (conf && conf->fips_mode) {
wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
"supported");
os_free(tls_global);
tls_global = NULL;
return NULL;
}
#endif /* OPENSSL_FIPS */
#endif /* CONFIG_FIPS */
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)
SSL_load_error_strings();
SSL_library_init();
#ifndef OPENSSL_NO_SHA256
EVP_add_digest(EVP_sha256());
#endif /* OPENSSL_NO_SHA256 */
/* TODO: if /dev/urandom is available, PRNG is seeded
* automatically. If this is not the case, random data should
* be added here. */
#ifdef PKCS12_FUNCS
#ifndef OPENSSL_NO_RC2
/*
* 40-bit RC2 is commonly used in PKCS#12 files, so enable it.
* This is enabled by PKCS12_PBE_add() in OpenSSL 0.9.8
* versions, but it looks like OpenSSL 1.0.0 does not do that
* anymore.
*/
EVP_add_cipher(EVP_rc2_40_cbc());
#endif /* OPENSSL_NO_RC2 */
PKCS12_PBE_add();
#endif /* PKCS12_FUNCS */
#endif /* < 1.1.0 */
} else {
context = tls_context_new(conf);
if (context == NULL)
return NULL;
}
tls_openssl_ref_count++;
data = os_zalloc(sizeof(*data));
if (data)
ssl = SSL_CTX_new(SSLv23_method());
else
ssl = NULL;
if (ssl == NULL) {
tls_openssl_ref_count--;
if (context != tls_global)
os_free(context);
if (tls_openssl_ref_count == 0) {
os_free(tls_global);
tls_global = NULL;
}
os_free(data);
return NULL;
}
data->ssl = ssl;
if (conf) {
data->tls_session_lifetime = conf->tls_session_lifetime;
data->crl_reload_interval = conf->crl_reload_interval;
}
SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
SSL_CTX_set_mode(ssl, SSL_MODE_AUTO_RETRY);
#ifdef SSL_MODE_NO_AUTO_CHAIN
/* Number of deployed use cases assume the default OpenSSL behavior of
* auto chaining the local certificate is in use. BoringSSL removed this
* functionality by default, so we need to restore it here to avoid
* breaking existing use cases. */
SSL_CTX_clear_mode(ssl, SSL_MODE_NO_AUTO_CHAIN);
#endif /* SSL_MODE_NO_AUTO_CHAIN */
SSL_CTX_set_info_callback(ssl, ssl_info_cb);
SSL_CTX_set_app_data(ssl, context);
if (data->tls_session_lifetime > 0) {
SSL_CTX_set_quiet_shutdown(ssl, 1);
/*
* Set default context here. In practice, this will be replaced
* by the per-EAP method context in tls_connection_set_verify().
*/
SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7);
SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER);
SSL_CTX_set_timeout(ssl, data->tls_session_lifetime);
SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb);
} else {
SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF);
}
if (tls_ex_idx_session < 0) {
tls_ex_idx_session = SSL_SESSION_get_ex_new_index(
0, NULL, NULL, NULL, NULL);
if (tls_ex_idx_session < 0) {
tls_deinit(data);
return NULL;
}
}
#ifndef OPENSSL_NO_ENGINE
wpa_printf(MSG_DEBUG, "ENGINE: Loading builtin engines");
ENGINE_load_builtin_engines();
if (conf &&
(conf->opensc_engine_path || conf->pkcs11_engine_path ||
conf->pkcs11_module_path)) {
if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
conf->pkcs11_module_path)) {
tls_deinit(data);
return NULL;
}
}
#endif /* OPENSSL_NO_ENGINE */
if (conf && conf->openssl_ciphers)
ciphers = conf->openssl_ciphers;
else
ciphers = TLS_DEFAULT_CIPHERS;
if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) {
wpa_printf(MSG_ERROR,
"OpenSSL: Failed to set cipher string '%s'",
ciphers);
tls_deinit(data);
return NULL;
}
return data;
}
void tls_deinit(void *ssl_ctx)
{
struct tls_data *data = ssl_ctx;
SSL_CTX *ssl = data->ssl;
struct tls_context *context = SSL_CTX_get_app_data(ssl);
if (context != tls_global)
os_free(context);
if (data->tls_session_lifetime > 0)
SSL_CTX_flush_sessions(ssl, 0);
os_free(data->ca_cert);
SSL_CTX_free(ssl);
tls_openssl_ref_count--;
if (tls_openssl_ref_count == 0) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)
#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
#endif /* OPENSSL_NO_ENGINE */
CRYPTO_cleanup_all_ex_data();
ERR_remove_thread_state(NULL);
ERR_free_strings();
EVP_cleanup();
#endif /* < 1.1.0 */
os_free(tls_global->ocsp_stapling_response);
tls_global->ocsp_stapling_response = NULL;
os_free(tls_global);
tls_global = NULL;
}
os_free(data->check_cert_subject);
os_free(data);
}
#ifndef OPENSSL_NO_ENGINE
/* Cryptoki return values */
#define CKR_PIN_INCORRECT 0x000000a0
#define CKR_PIN_INVALID 0x000000a1
#define CKR_PIN_LEN_RANGE 0x000000a2
/* libp11 */
#define ERR_LIB_PKCS11 ERR_LIB_USER
static int tls_is_pin_error(unsigned int err)
{
return ERR_GET_LIB(err) == ERR_LIB_PKCS11 &&
(ERR_GET_REASON(err) == CKR_PIN_INCORRECT ||
ERR_GET_REASON(err) == CKR_PIN_INVALID ||
ERR_GET_REASON(err) == CKR_PIN_LEN_RANGE);
}
#endif /* OPENSSL_NO_ENGINE */
#ifdef ANDROID
/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id);
#endif /* ANDROID */
static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
const char *pin, const char *key_id,
const char *cert_id, const char *ca_cert_id)
{
#if defined(ANDROID) && defined(OPENSSL_IS_BORINGSSL)
#if !defined(OPENSSL_NO_ENGINE)
#error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL."
#endif
if (!key_id)
return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
conn->engine = NULL;
conn->private_key = EVP_PKEY_from_keystore(key_id);
if (!conn->private_key) {
wpa_printf(MSG_ERROR,
"ENGINE: cannot load private key with id '%s' [%s]",
key_id,
ERR_error_string(ERR_get_error(), NULL));
return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
}
#endif /* ANDROID && OPENSSL_IS_BORINGSSL */
#ifndef OPENSSL_NO_ENGINE
int ret = -1;
if (engine_id == NULL) {
wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set");
return -1;
}
ERR_clear_error();
#ifdef ANDROID
ENGINE_load_dynamic();
#endif
conn->engine = ENGINE_by_id(engine_id);
if (!conn->engine) {
wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]",
engine_id, ERR_error_string(ERR_get_error(), NULL));
goto err;
}
if (ENGINE_init(conn->engine) != 1) {
wpa_printf(MSG_ERROR, "ENGINE: engine init failed "
"(engine: %s) [%s]", engine_id,
ERR_error_string(ERR_get_error(), NULL));
goto err;
}
wpa_printf(MSG_DEBUG, "ENGINE: engine initialized");
#ifndef ANDROID
if (pin && ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]",
ERR_error_string(ERR_get_error(), NULL));
goto err;
}
#endif
if (key_id) {
/*
* Ensure that the ENGINE does not attempt to use the OpenSSL
* UI system to obtain a PIN, if we didn't provide one.
*/
struct {
const void *password;
const char *prompt_info;
} key_cb = { "", NULL };
/* load private key first in-case PIN is required for cert */
conn->private_key = ENGINE_load_private_key(conn->engine,
key_id, NULL,
&key_cb);
if (!conn->private_key) {
unsigned long err = ERR_get_error();
wpa_printf(MSG_ERROR,
"ENGINE: cannot load private key with id '%s' [%s]",
key_id,
ERR_error_string(err, NULL));
if (tls_is_pin_error(err))
ret = TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
else
ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
goto err;
}
}
/* handle a certificate and/or CA certificate */
if (cert_id || ca_cert_id) {
const char *cmd_name = "LOAD_CERT_CTRL";
/* test if the engine supports a LOAD_CERT_CTRL */
if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
0, (void *)cmd_name, NULL)) {
wpa_printf(MSG_ERROR, "ENGINE: engine does not support"
" loading certificates");
ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
goto err;
}
}
return 0;
err:
if (conn->engine) {
ENGINE_free(conn->engine);
conn->engine = NULL;
}
if (conn->private_key) {
EVP_PKEY_free(conn->private_key);
conn->private_key = NULL;
}
return ret;
#else /* OPENSSL_NO_ENGINE */
return 0;
#endif /* OPENSSL_NO_ENGINE */
}
static void tls_engine_deinit(struct tls_connection *conn)
{
#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
if (conn->private_key) {
EVP_PKEY_free(conn->private_key);
conn->private_key = NULL;
}
if (conn->engine) {
#if !defined(OPENSSL_IS_BORINGSSL)
ENGINE_finish(conn->engine);
#endif /* !OPENSSL_IS_BORINGSSL */
conn->engine = NULL;
}
#endif /* ANDROID || !OPENSSL_NO_ENGINE */
}
int tls_get_errors(void *ssl_ctx)
{
int count = 0;
unsigned long err;
while ((err = ERR_get_error())) {
wpa_printf(MSG_INFO, "TLS - SSL error: %s",
ERR_error_string(err, NULL));
count++;
}
return count;
}
static const char * openssl_content_type(int content_type)
{
switch (content_type) {
case 20:
return "change cipher spec";
case 21:
return "alert";
case 22:
return "handshake";
case 23:
return "application data";
case 24:
return "heartbeat";
case 256:
return "TLS header info"; /* pseudo content type */
case 257:
return "inner content type"; /* pseudo content type */
default:
return "?";
}
}
static const char * openssl_handshake_type(int content_type, const u8 *buf,
size_t len)
{
if (content_type == 257 && buf && len == 1)
return openssl_content_type(buf[0]);
if (content_type != 22 || !buf || len == 0)
return "";
switch (buf[0]) {
case 0:
return "hello request";
case 1:
return "client hello";
case 2:
return "server hello";
case 3:
return "hello verify request";
case 4:
return "new session ticket";
case 5:
return "end of early data";
case 6:
return "hello retry request";
case 8:
return "encrypted extensions";
case 11:
return "certificate";
case 12:
return "server key exchange";
case 13:
return "certificate request";
case 14:
return "server hello done";
case 15:
return "certificate verify";
case 16:
return "client key exchange";
case 20:
return "finished";
case 21:
return "certificate url";
case 22:
return "certificate status";
case 23:
return "supplemental data";
case 24:
return "key update";
case 254:
return "message hash";
default:
return "?";
}
}
#ifdef CONFIG_SUITEB
static void check_server_hello(struct tls_connection *conn,
const u8 *pos, const u8 *end)
{
size_t payload_len, id_len;
/*
* Parse ServerHello to get the selected cipher suite since OpenSSL does
* not make it cleanly available during handshake and we need to know
* whether DHE was selected.
*/
if (end - pos < 3)
return;
payload_len = WPA_GET_BE24(pos);
pos += 3;
if ((size_t) (end - pos) < payload_len)
return;
end = pos + payload_len;
/* Skip Version and Random */
if (end - pos < 2 + SSL3_RANDOM_SIZE)
return;
pos += 2 + SSL3_RANDOM_SIZE;
/* Skip Session ID */
if (end - pos < 1)
return;
id_len = *pos++;
if ((size_t) (end - pos) < id_len)
return;
pos += id_len;
if (end - pos < 2)
return;
conn->cipher_suite = WPA_GET_BE16(pos);
wpa_printf(MSG_DEBUG, "OpenSSL: Server selected cipher suite 0x%x",
conn->cipher_suite);
}
static void check_server_key_exchange(SSL *ssl, struct tls_connection *conn,
const u8 *pos, const u8 *end)
{
size_t payload_len;
u16 dh_len;
BIGNUM *p;
int bits;
if (!(conn->flags & TLS_CONN_SUITEB))
return;
/* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
if (conn->cipher_suite != 0x9f)
return;
if (end - pos < 3)
return;
payload_len = WPA_GET_BE24(pos);
pos += 3;
if ((size_t) (end - pos) < payload_len)
return;
end = pos + payload_len;
if (end - pos < 2)
return;
dh_len = WPA_GET_BE16(pos);
pos += 2;
if ((size_t) (end - pos) < dh_len)
return;
p = BN_bin2bn(pos, dh_len, NULL);
if (!p)
return;
bits = BN_num_bits(p);
BN_free(p);
conn->server_dh_prime_len = bits;
wpa_printf(MSG_DEBUG, "OpenSSL: Server DH prime length: %d bits",
conn->server_dh_prime_len);
}
#endif /* CONFIG_SUITEB */
static void tls_msg_cb(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl, void *arg)
{
struct tls_connection *conn = arg;
const u8 *pos = buf;
if (write_p == 2) {
wpa_printf(MSG_DEBUG,
"OpenSSL: session ver=0x%x content_type=%d",
version, content_type);
wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len);
return;
}
wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)",
write_p ? "TX" : "RX", version, content_type,
openssl_content_type(content_type),
openssl_handshake_type(content_type, buf, len));
wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len);
if (content_type == 24 && len >= 3 && pos[0] == 1) {
size_t payload_len = WPA_GET_BE16(pos + 1);
if (payload_len + 3 > len) {
wpa_printf(MSG_ERROR, "OpenSSL: Heartbeat attack detected");
conn->invalid_hb_used = 1;
}
}
#ifdef CONFIG_SUITEB
/*
* Need to parse these handshake messages to be able to check DH prime
* length since OpenSSL does not expose the new cipher suite and DH
* parameters during handshake (e.g., for cert_cb() callback).
*/
if (content_type == 22 && pos && len > 0 && pos[0] == 2)
check_server_hello(conn, pos + 1, pos + len);
if (content_type == 22 && pos && len > 0 && pos[0] == 12)
check_server_key_exchange(ssl, conn, pos + 1, pos + len);
#endif /* CONFIG_SUITEB */
}
struct tls_connection * tls_connection_init(void *ssl_ctx)
{
struct tls_data *data = ssl_ctx;
SSL_CTX *ssl = data->ssl;
struct tls_connection *conn;
long options;
X509_STORE *new_cert_store;
struct os_reltime now;
struct tls_context *context = SSL_CTX_get_app_data(ssl);
/* Replace X509 store if it is time to update CRL. */
if (data->crl_reload_interval > 0 && os_get_reltime(&now) == 0 &&
os_reltime_expired(&now, &data->crl_last_reload,
data->crl_reload_interval)) {
wpa_printf(MSG_INFO,
"OpenSSL: Flushing X509 store with ca_cert file");
new_cert_store = tls_crl_cert_reload(data->ca_cert,
data->check_crl);
if (!new_cert_store) {
wpa_printf(MSG_ERROR,
"OpenSSL: Error replacing X509 store with ca_cert file");
} else {
/* Replace old store */
SSL_CTX_set_cert_store(ssl, new_cert_store);
data->crl_last_reload = now;
}
}
conn = os_zalloc(sizeof(*conn));
if (conn == NULL)
return NULL;
conn->data = data;
conn->ssl_ctx = ssl;
conn->ssl = SSL_new(ssl);
if (conn->ssl == NULL) {
tls_show_errors(MSG_INFO, __func__,
"Failed to initialize new SSL connection");
os_free(conn);
return NULL;
}
conn->context = context;
SSL_set_app_data(conn->ssl, conn);
SSL_set_msg_callback(conn->ssl, tls_msg_cb);
SSL_set_msg_callback_arg(conn->ssl, conn);
options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
SSL_OP_SINGLE_DH_USE;
#ifdef SSL_OP_NO_COMPRESSION
options |= SSL_OP_NO_COMPRESSION;
#endif /* SSL_OP_NO_COMPRESSION */
SSL_set_options(conn->ssl, options);
#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
/* Hopefully there is no need for middlebox compatibility mechanisms
* when going through EAP authentication. */
SSL_clear_options(conn->ssl, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
#endif
conn->ssl_in = BIO_new(BIO_s_mem());
if (!conn->ssl_in) {
tls_show_errors(MSG_INFO, __func__,
"Failed to create a new BIO for ssl_in");
SSL_free(conn->ssl);
os_free(conn);
return NULL;
}
conn->ssl_out = BIO_new(BIO_s_mem());
if (!conn->ssl_out) {
tls_show_errors(MSG_INFO, __func__,
"Failed to create a new BIO for ssl_out");
SSL_free(conn->ssl);
BIO_free(conn->ssl_in);
os_free(conn);
return NULL;
}
SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out);
return conn;
}
void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
{
if (conn == NULL)
return;
if (conn->success_data) {
/*
* Make sure ssl_clear_bad_session() does not remove this
* session.
*/
SSL_set_quiet_shutdown(conn->ssl, 1);
SSL_shutdown(conn->ssl);
}
SSL_free(conn->ssl);
tls_engine_deinit(conn);
os_free(conn->subject_match);
os_free(conn->altsubject_match);
os_free(conn->suffix_match);
os_free(conn->domain_match);
os_free(conn->check_cert_subject);
os_free(conn->session_ticket);
os_free(conn->peer_subject);
os_free(conn);
}
int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
{
return conn ? SSL_is_init_finished(conn->ssl) : 0;
}
char * tls_connection_peer_serial_num(void *tls_ctx,
struct tls_connection *conn)
{
ASN1_INTEGER *ser;
char *serial_num;
size_t len;
if (!conn->peer_cert)
return NULL;
ser = X509_get_serialNumber(conn->peer_cert);
if (!ser)
return NULL;
len = ASN1_STRING_length(ser) * 2 + 1;
serial_num = os_malloc(len);
if (!serial_num)
return NULL;
wpa_snprintf_hex_uppercase(serial_num, len,
ASN1_STRING_get0_data(ser),
ASN1_STRING_length(ser));
return serial_num;
}
int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
{
if (conn == NULL)
return -1;
/* Shutdown previous TLS connection without notifying the peer
* because the connection was already terminated in practice
* and "close notify" shutdown alert would confuse AS. */
SSL_set_quiet_shutdown(conn->ssl, 1);
SSL_shutdown(conn->ssl);
return SSL_clear(conn->ssl) == 1 ? 0 : -1;
}
static int tls_match_altsubject_component(X509 *cert, int type,
const char *value, size_t len)
{
GENERAL_NAME *gen;
void *ext;
int found = 0;
stack_index_t i;
ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
gen = sk_GENERAL_NAME_value(ext, i);
if (gen->type != type)
continue;
if (os_strlen((char *) gen->d.ia5->data) == len &&
os_memcmp(value, gen->d.ia5->data, len) == 0)
found++;
}
sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
return found;
}
static int tls_match_altsubject(X509 *cert, const char *match)
{
int type;
const char *pos, *end;
size_t len;
pos = match;
do {
if (os_strncmp(pos, "EMAIL:", 6) == 0) {
type = GEN_EMAIL;
pos += 6;
} else if (os_strncmp(pos, "DNS:", 4) == 0) {
type = GEN_DNS;
pos += 4;
} else if (os_strncmp(pos, "URI:", 4) == 0) {
type = GEN_URI;
pos += 4;
} else {
wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName "
"match '%s'", pos);
return 0;
}
end = os_strchr(pos, ';');
while (end) {
if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
os_strncmp(end + 1, "DNS:", 4) == 0 ||
os_strncmp(end + 1, "URI:", 4) == 0)
break;
end = os_strchr(end + 1, ';');
}
if (end)
len = end - pos;
else
len = os_strlen(pos);
if (tls_match_altsubject_component(cert, type, pos, len) > 0)
return 1;
pos = end + 1;
} while (end);
return 0;
}
#ifndef CONFIG_NATIVE_WINDOWS
static int domain_suffix_match(const u8 *val, size_t len, const char *match,
size_t match_len, int full)
{
size_t i;
/* Check for embedded nuls that could mess up suffix matching */
for (i = 0; i < len; i++) {
if (val[i] == '\0') {
wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject");
return 0;
}
}
if (match_len > len || (full && match_len != len))
return 0;
if (os_strncasecmp((const char *) val + len - match_len, match,
match_len) != 0)
return 0; /* no match */
if (match_len == len)
return 1; /* exact match */
if (val[len - match_len - 1] == '.')
return 1; /* full label match completes suffix match */
wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
return 0;
}
#endif /* CONFIG_NATIVE_WINDOWS */
struct tls_dn_field_order_cnt {
u8 cn;
u8 c;
u8 l;
u8 st;
u8 o;
u8 ou;
u8 email;
};
static int get_dn_field_index(const struct tls_dn_field_order_cnt *dn_cnt,
int nid)
{
switch (nid) {
case NID_commonName:
return dn_cnt->cn;
case NID_countryName:
return dn_cnt->c;
case NID_localityName:
return dn_cnt->l;
case NID_stateOrProvinceName:
return dn_cnt->st;
case NID_organizationName:
return dn_cnt->o;
case NID_organizationalUnitName:
return dn_cnt->ou;
case NID_pkcs9_emailAddress:
return dn_cnt->email;
default:
wpa_printf(MSG_ERROR,
"TLS: Unknown NID '%d' in check_cert_subject",
nid);
return -1;
}
}
/**
* match_dn_field - Match configuration DN field against Certificate DN field
* @cert: Certificate
* @nid: NID of DN field
* @field: Field name
* @value DN field value which is passed from configuration
* e.g., if configuration have C=US and this argument will point to US.
* @dn_cnt: DN matching context
* Returns: 1 on success and 0 on failure
*/
static int match_dn_field(const X509 *cert, int nid, const char *field,
const char *value,
const struct tls_dn_field_order_cnt *dn_cnt)
{
int i, ret = 0, len, config_dn_field_index, match_index = 0;
X509_NAME *name;
len = os_strlen(value);
name = X509_get_subject_name((X509 *) cert);
/* Assign incremented cnt for every field of DN to check DN field in
* right order */
config_dn_field_index = get_dn_field_index(dn_cnt, nid);
if (config_dn_field_index < 0)
return 0;
/* Fetch value based on NID */
for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) {
X509_NAME_ENTRY *e;
ASN1_STRING *cn;
e = X509_NAME_get_entry(name, i);
if (!e)
continue;
cn = X509_NAME_ENTRY_get_data(e);
if (!cn)
continue;
match_index++;
/* check for more than one DN field with same name */
if (match_index != config_dn_field_index)
continue;
/* Check wildcard at the right end side */
/* E.g., if OU=develop* mentioned in configuration, allow 'OU'
* of the subject in the client certificate to start with
* 'develop' */
if (len > 0 && value[len - 1] == '*') {
/* Compare actual certificate DN field value with
* configuration DN field value up to the specified
* length. */
ret = ASN1_STRING_length(cn) >= len - 1 &&
os_memcmp(ASN1_STRING_get0_data(cn), value,
len - 1) == 0;
} else {
/* Compare actual certificate DN field value with
* configuration DN field value */
ret = ASN1_STRING_length(cn) == len &&
os_memcmp(ASN1_STRING_get0_data(cn), value,
len) == 0;
}
if (!ret) {
wpa_printf(MSG_ERROR,
"OpenSSL: Failed to match %s '%s' with certificate DN field value '%s'",
field, value, ASN1_STRING_get0_data(cn));
}
break;
}
return ret;
}
/**
* get_value_from_field - Get value from DN field
* @cert: Certificate
* @field_str: DN field string which is passed from configuration file (e.g.,
* C=US)
* @dn_cnt: DN matching context
* Returns: 1 on success and 0 on failure
*/
static int get_value_from_field(const X509 *cert, char *field_str,
struct tls_dn_field_order_cnt *dn_cnt)
{
int nid;
char *context = NULL, *name, *value;
if (os_strcmp(field_str, "*") == 0)
return 1; /* wildcard matches everything */
name = str_token(field_str, "=", &context);
if (!name)
return 0;
/* Compare all configured DN fields and assign nid based on that to
* fetch correct value from certificate subject */
if (os_strcmp(name, "CN") == 0) {
nid = NID_commonName;
dn_cnt->cn++;
} else if(os_strcmp(name, "C") == 0) {
nid = NID_countryName;
dn_cnt->c++;
} else if (os_strcmp(name, "L") == 0) {
nid = NID_localityName;
dn_cnt->l++;
} else if (os_strcmp(name, "ST") == 0) {
nid = NID_stateOrProvinceName;
dn_cnt->st++;
} else if (os_strcmp(name, "O") == 0) {
nid = NID_organizationName;
dn_cnt->o++;
} else if (os_strcmp(name, "OU") == 0) {
nid = NID_organizationalUnitName;
dn_cnt->ou++;
} else if (os_strcmp(name, "emailAddress") == 0) {
nid = NID_pkcs9_emailAddress;
dn_cnt->email++;
} else {
wpa_printf(MSG_ERROR,
"TLS: Unknown field '%s' in check_cert_subject", name);
return 0;
}
value = str_token(field_str, "=", &context);
if (!value) {
wpa_printf(MSG_ERROR,
"TLS: Distinguished Name field '%s' value is not defined in check_cert_subject",
name);
return 0;
}
return match_dn_field(cert, nid, name, value, dn_cnt);
}
/**
* tls_match_dn_field - Match subject DN field with check_cert_subject
* @cert: Certificate
* @match: check_cert_subject string
* Returns: Return 1 on success and 0 on failure
*/
static int tls_match_dn_field(X509 *cert, const char *match)
{
const char *token, *last = NULL;
char field[256];
struct tls_dn_field_order_cnt dn_cnt;
os_memset(&dn_cnt, 0, sizeof(dn_cnt));
/* Maximum length of each DN field is 255 characters */
/* Process each '/' delimited field */
while ((token = cstr_token(match, "/", &last))) {
if (last - token >= (int) sizeof(field)) {
wpa_printf(MSG_ERROR,
"OpenSSL: Too long DN matching field value in '%s'",
match);
return 0;
}
os_memcpy(field, token, last - token);
field[last - token] = '\0';
if (!get_value_from_field(cert, field, &dn_cnt)) {
wpa_printf(MSG_DEBUG, "OpenSSL: No match for DN '%s'",
field);
return 0;
}
}
return 1;
}
#ifndef CONFIG_NATIVE_WINDOWS
static int tls_match_suffix_helper(X509 *cert, const char *match,
size_t match_len, int full)
{
GENERAL_NAME *gen;
void *ext;
int i;
stack_index_t j;
int dns_name = 0;
X509_NAME *name;
wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
full ? "": "suffix ", match);
ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
for (j = 0; ext && j < sk_GENERAL_NAME_num(ext); j++) {
gen = sk_GENERAL_NAME_value(ext, j);
if (gen->type != GEN_DNS)
continue;
dns_name++;
wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
gen->d.dNSName->data,
gen->d.dNSName->length);
if (domain_suffix_match(gen->d.dNSName->data,
gen->d.dNSName->length,
match, match_len, full) == 1) {
wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
full ? "Match" : "Suffix match");
sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
return 1;
}
}
sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
if (dns_name) {
wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
return 0;
}
name = X509_get_subject_name(cert);
i = -1;
for (;;) {
X509_NAME_ENTRY *e;
ASN1_STRING *cn;
i = X509_NAME_get_index_by_NID(name, NID_commonName, i);
if (i == -1)
break;
e = X509_NAME_get_entry(name, i);
if (e == NULL)
continue;
cn = X509_NAME_ENTRY_get_data(e);
if (cn == NULL)
continue;
wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
cn->data, cn->length);
if (domain_suffix_match(cn->data, cn->length,
match, match_len, full) == 1) {
wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
full ? "Match" : "Suffix match");
return 1;
}
}
wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
full ? "": "suffix ");
return 0;
}
#endif /* CONFIG_NATIVE_WINDOWS */
static int tls_match_suffix(X509 *cert, const char *match, int full)
{
#ifdef CONFIG_NATIVE_WINDOWS
/* wincrypt.h has conflicting X509_NAME definition */
return -1;
#else /* CONFIG_NATIVE_WINDOWS */
const char *token, *last = NULL;
/* Process each match alternative separately until a match is found */
while ((token = cstr_token(match, ";", &last))) {
if (tls_match_suffix_helper(cert, token, last - token, full))
return 1;
}
return 0;
#endif /* CONFIG_NATIVE_WINDOWS */
}
static enum tls_fail_reason openssl_tls_fail_reason(int err)
{
switch (err) {
case X509_V_ERR_CERT_REVOKED:
return TLS_FAIL_REVOKED;
case X509_V_ERR_CERT_NOT_YET_VALID:
case X509_V_ERR_CRL_NOT_YET_VALID:
return TLS_FAIL_NOT_YET_VALID;
case X509_V_ERR_CERT_HAS_EXPIRED:
case X509_V_ERR_CRL_HAS_EXPIRED:
return TLS_FAIL_EXPIRED;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
case X509_V_ERR_UNABLE_TO_GET_CRL:
case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
case X509_V_ERR_CERT_CHAIN_TOO_LONG:
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
case X509_V_ERR_INVALID_CA:
return TLS_FAIL_UNTRUSTED;
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
case X509_V_ERR_CERT_UNTRUSTED:
case X509_V_ERR_CERT_REJECTED:
return TLS_FAIL_BAD_CERTIFICATE;
default:
return TLS_FAIL_UNSPECIFIED;
}
}
static struct wpabuf * get_x509_cert(X509 *cert)
{
struct wpabuf *buf;
u8 *tmp;
int cert_len = i2d_X509(cert, NULL);
if (cert_len <= 0)
return NULL;
buf = wpabuf_alloc(cert_len);
if (buf == NULL)
return NULL;
tmp = wpabuf_put(buf, cert_len);
i2d_X509(cert, &tmp);
return buf;
}
static void openssl_tls_fail_event(struct tls_connection *conn,
X509 *err_cert, int err, int depth,
const char *subject, const char *err_str,
enum tls_fail_reason reason)
{
union tls_event_data ev;
struct wpabuf *cert = NULL;
struct tls_context *context = conn->context;
if (context->event_cb == NULL)
return;
cert = get_x509_cert(err_cert);
os_memset(&ev, 0, sizeof(ev));
ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
reason : openssl_tls_fail_reason(err);
ev.cert_fail.depth = depth;
ev.cert_fail.subject = subject;
ev.cert_fail.reason_txt = err_str;
ev.cert_fail.cert = cert;
context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
wpabuf_free(cert);
}
static int openssl_cert_tod(X509 *cert)
{
CERTIFICATEPOLICIES *ext;
stack_index_t i;
char buf[100];
int res;
int tod = 0;
ext = X509_get_ext_d2i(cert, NID_certificate_policies, NULL, NULL);
if (!ext)
return 0;
for (i = 0; i < sk_POLICYINFO_num(ext); i++) {
POLICYINFO *policy;
policy = sk_POLICYINFO_value(ext, i);
res = OBJ_obj2txt(buf, sizeof(buf), policy->policyid, 0);
if (res < 0 || (size_t) res >= sizeof(buf))
continue;
wpa_printf(MSG_DEBUG, "OpenSSL: Certificate Policy %s", buf);
if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.1") == 0)
tod = 1; /* TOD-STRICT */
else if (os_strcmp(buf, "1.3.6.1.4.1.40808.1.3.2") == 0 && !tod)
tod = 2; /* TOD-TOFU */
}
sk_POLICYINFO_pop_free(ext, POLICYINFO_free);
return tod;
}
static void openssl_tls_cert_event(struct tls_connection *conn,
X509 *err_cert, int depth,
const char *subject)
{
struct wpabuf *cert = NULL;
union tls_event_data ev;
struct tls_context *context = conn->context;
char *altsubject[TLS_MAX_ALT_SUBJECT];
int alt, num_altsubject = 0;
GENERAL_NAME *gen;
void *ext;
stack_index_t i;
ASN1_INTEGER *ser;
char serial_num[128];
#ifdef CONFIG_SHA256
u8 hash[32];
#endif /* CONFIG_SHA256 */
if (context->event_cb == NULL)
return;
os_memset(&ev, 0, sizeof(ev));
if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
context->cert_in_cb) {
cert = get_x509_cert(err_cert);
ev.peer_cert.cert = cert;
}
#ifdef CONFIG_SHA256
if (cert) {
const u8 *addr[1];
size_t len[1];
addr[0] = wpabuf_head(cert);
len[0] = wpabuf_len(cert);
if (sha256_vector(1, addr, len, hash) == 0) {
ev.peer_cert.hash = hash;
ev.peer_cert.hash_len = sizeof(hash);
}
}
#endif /* CONFIG_SHA256 */
ev.peer_cert.depth = depth;
ev.peer_cert.subject = subject;
ser = X509_get_serialNumber(err_cert);
if (ser) {
wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num),
ASN1_STRING_get0_data(ser),
ASN1_STRING_length(ser));
ev.peer_cert.serial_num = serial_num;
}
ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
char *pos;
if (num_altsubject == TLS_MAX_ALT_SUBJECT)
break;
gen = sk_GENERAL_NAME_value(ext, i);
if (gen->type != GEN_EMAIL &&
gen->type != GEN_DNS &&
gen->type != GEN_URI)
continue;
pos = os_malloc(10 + gen->d.ia5->length + 1);
if (pos == NULL)
break;
altsubject[num_altsubject++] = pos;
switch (gen->type) {
case GEN_EMAIL:
os_memcpy(pos, "EMAIL:", 6);
pos += 6;
break;
case GEN_DNS:
os_memcpy(pos, "DNS:", 4);
pos += 4;
break;
case GEN_URI:
os_memcpy(pos, "URI:", 4);
pos += 4;
break;
}
os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length);
pos += gen->d.ia5->length;
*pos = '\0';
}
sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
for (alt = 0; alt < num_altsubject; alt++)
ev.peer_cert.altsubject[alt] = altsubject[alt];
ev.peer_cert.num_altsubject = num_altsubject;
ev.peer_cert.tod = openssl_cert_tod(err_cert);
context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
wpabuf_free(cert);
for (alt = 0; alt < num_altsubject; alt++)
os_free(altsubject[alt]);
}
static void debug_print_cert(X509 *cert, const char *title)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
BIO *out;
size_t rlen;
char *txt;
int res;
if (wpa_debug_level > MSG_DEBUG)
return;
out = BIO_new(BIO_s_mem());
if (!out)
return;
X509_print(out, cert);
rlen = BIO_ctrl_pending(out);
txt = os_malloc(rlen + 1);
if (txt) {
res = BIO_read(out, txt, rlen);
if (res > 0) {
txt[res] = '\0';
wpa_printf(MSG_DEBUG, "OpenSSL: %s\n%s", title, txt);
}
os_free(txt);
}
BIO_free(out);
#endif /* CONFIG_NO_STDOUT_DEBUG */
}
static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
char buf[256];
X509 *err_cert;
int err, depth;
SSL *ssl;
struct tls_connection *conn;
struct tls_context *context;
char *match, *altmatch, *suffix_match, *domain_match;
const char *check_cert_subject;
const char *err_str;
err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
if (!err_cert)
return 0;
err = X509_STORE_CTX_get_error(x509_ctx);
depth = X509_STORE_CTX_get_error_depth(x509_ctx);
ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
SSL_get_ex_data_X509_STORE_CTX_idx());
os_snprintf(buf, sizeof(buf), "Peer certificate - depth %d", depth);
debug_print_cert(err_cert, buf);
X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
conn = SSL_get_app_data(ssl);
if (conn == NULL)
return 0;
if (depth == 0)
conn->peer_cert = err_cert;
else if (depth == 1)
conn->peer_issuer = err_cert;
else if (depth == 2)
conn->peer_issuer_issuer = err_cert;
context = conn->context;
match = conn->subject_match;
altmatch = conn->altsubject_match;
suffix_match = conn->suffix_match;
domain_match = conn->domain_match;
if (!preverify_ok && !conn->ca_cert_verify)
preverify_ok = 1;
if (!preverify_ok && depth > 0 && conn->server_cert_only)
preverify_ok = 1;
if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) &&
(err == X509_V_ERR_CERT_HAS_EXPIRED ||
err == X509_V_ERR_CERT_NOT_YET_VALID)) {
wpa_printf(MSG_DEBUG, "OpenSSL: Ignore certificate validity "
"time mismatch");
preverify_ok = 1;
}
if (!preverify_ok && !conn->data->check_crl_strict &&
(err == X509_V_ERR_CRL_HAS_EXPIRED ||
err == X509_V_ERR_CRL_NOT_YET_VALID)) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Ignore certificate validity CRL time mismatch");
preverify_ok = 1;
}
err_str = X509_verify_cert_error_string(err);
#ifdef CONFIG_SHA256
/*
* Do not require preverify_ok so we can explicity allow otherwise
* invalid pinned server certificates.
*/
if (depth == 0 && conn->server_cert_only) {
struct wpabuf *cert;
cert = get_x509_cert(err_cert);
if (!cert) {
wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
"server certificate data");
preverify_ok = 0;
} else {
u8 hash[32];
const u8 *addr[1];
size_t len[1];
addr[0] = wpabuf_head(cert);
len[0] = wpabuf_len(cert);
if (sha256_vector(1, addr, len, hash) < 0 ||
os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
err_str = "Server certificate mismatch";
err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
preverify_ok = 0;
} else if (!preverify_ok) {
/*
* Certificate matches pinned certificate, allow
* regardless of other problems.
*/
wpa_printf(MSG_DEBUG,
"OpenSSL: Ignore validation issues for a pinned server certificate");
preverify_ok = 1;
}
wpabuf_free(cert);
}
}
#endif /* CONFIG_SHA256 */
openssl_tls_cert_event(conn, err_cert, depth, buf);
if (!preverify_ok) {
if (depth > 0) {
/* Send cert event for the peer certificate so that
* the upper layers get information about it even if
* validation of a CA certificate fails. */
STACK_OF(X509) *chain;
chain = X509_STORE_CTX_get1_chain(x509_ctx);
if (chain && sk_X509_num(chain) > 0) {
char buf2[256];
X509 *cert;
cert = sk_X509_value(chain, 0);
X509_NAME_oneline(X509_get_subject_name(cert),
buf2, sizeof(buf2));
openssl_tls_cert_event(conn, cert, 0, buf2);
}
if (chain)
sk_X509_pop_free(chain, X509_free);
}
wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
" error %d (%s) depth %d for '%s'", err, err_str,
depth, buf);
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
err_str, TLS_FAIL_UNSPECIFIED);
return preverify_ok;
}
wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
"err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
preverify_ok, err, err_str,
conn->ca_cert_verify, depth, buf);
check_cert_subject = conn->check_cert_subject;
if (!check_cert_subject)
check_cert_subject = conn->data->check_cert_subject;
if (check_cert_subject) {
if (depth == 0 &&
!tls_match_dn_field(err_cert, check_cert_subject)) {
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"Distinguished Name",
TLS_FAIL_DN_MISMATCH);
}
}
if (depth == 0 && match && os_strstr(buf, match) == NULL) {
wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
"match with '%s'", buf, match);
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"Subject mismatch",
TLS_FAIL_SUBJECT_MISMATCH);
} else if (depth == 0 && altmatch &&
!tls_match_altsubject(err_cert, altmatch)) {
wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
"'%s' not found", altmatch);
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"AltSubject mismatch",
TLS_FAIL_ALTSUBJECT_MISMATCH);
} else if (depth == 0 && suffix_match &&
!tls_match_suffix(err_cert, suffix_match, 0)) {
wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
suffix_match);
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"Domain suffix mismatch",
TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
} else if (depth == 0 && domain_match &&
!tls_match_suffix(err_cert, domain_match, 1)) {
wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
domain_match);
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"Domain mismatch",
TLS_FAIL_DOMAIN_MISMATCH);
}
if (conn->cert_probe && preverify_ok && depth == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
"on probe-only run");
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"Server certificate chain probe",
TLS_FAIL_SERVER_CHAIN_PROBE);
}
#ifdef CONFIG_SUITEB
if (conn->flags & TLS_CONN_SUITEB) {
EVP_PKEY *pk;
RSA *rsa;
int len = -1;
pk = X509_get_pubkey(err_cert);
if (pk) {
rsa = EVP_PKEY_get1_RSA(pk);
if (rsa) {
len = RSA_bits(rsa);
RSA_free(rsa);
}
EVP_PKEY_free(pk);
}
if (len >= 0) {
wpa_printf(MSG_DEBUG,
"OpenSSL: RSA modulus size: %d bits", len);
if (len < 3072) {
preverify_ok = 0;
openssl_tls_fail_event(
conn, err_cert, err,
depth, buf,
"Insufficient RSA modulus size",
TLS_FAIL_INSUFFICIENT_KEY_LEN);
}
}
}
#endif /* CONFIG_SUITEB */
#ifdef OPENSSL_IS_BORINGSSL
if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
preverify_ok) {
enum ocsp_result res;
res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
conn->peer_issuer,
conn->peer_issuer_issuer);
if (res == OCSP_REVOKED) {
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"certificate revoked",
TLS_FAIL_REVOKED);
if (err == X509_V_OK)
X509_STORE_CTX_set_error(
x509_ctx, X509_V_ERR_CERT_REVOKED);
} else if (res != OCSP_GOOD &&
(conn->flags & TLS_CONN_REQUIRE_OCSP)) {
preverify_ok = 0;
openssl_tls_fail_event(conn, err_cert, err, depth, buf,
"bad certificate status response",
TLS_FAIL_UNSPECIFIED);
}
}
#endif /* OPENSSL_IS_BORINGSSL */
if (depth == 0 && preverify_ok && context->event_cb != NULL)
context->event_cb(context->cb_ctx,
TLS_CERT_CHAIN_SUCCESS, NULL);
if (depth == 0 && preverify_ok) {
os_free(conn->peer_subject);
conn->peer_subject = os_strdup(buf);
}
return preverify_ok;
}
#ifndef OPENSSL_NO_STDIO
static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
{
SSL_CTX *ssl_ctx = data->ssl;
X509_LOOKUP *lookup;
int ret = 0;
lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx),
X509_LOOKUP_file());
if (lookup == NULL) {
tls_show_errors(MSG_WARNING, __func__,
"Failed add lookup for X509 store");
return -1;
}
if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) {
unsigned long err = ERR_peek_error();
tls_show_errors(MSG_WARNING, __func__,
"Failed load CA in DER format");
if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
"cert already in hash table error",
__func__);
} else
ret = -1;
}
return ret;
}
#endif /* OPENSSL_NO_STDIO */
static int tls_connection_ca_cert(struct tls_data *data,
struct tls_connection *conn,
const char *ca_cert, const u8 *ca_cert_blob,
size_t ca_cert_blob_len, const char *ca_path)
{
SSL_CTX *ssl_ctx = data->ssl;
X509_STORE *store;
/*
* Remove previously configured trusted CA certificates before adding
* new ones.
*/
store = X509_STORE_new();
if (store == NULL) {
wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
"certificate store", __func__);
return -1;
}
SSL_CTX_set_cert_store(ssl_ctx, store);
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
conn->ca_cert_verify = 1;
if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate "
"chain");
conn->cert_probe = 1;
conn->ca_cert_verify = 0;
return 0;
}
if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
#ifdef CONFIG_SHA256
const char *pos = ca_cert + 7;
if (os_strncmp(pos, "server/sha256/", 14) != 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert "
"hash value '%s'", ca_cert);
return -1;
}
pos += 14;
if (os_strlen(pos) != 32 * 2) {
wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 "
"hash length in ca_cert '%s'", ca_cert);
return -1;
}
if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash "
"value in ca_cert '%s'", ca_cert);
return -1;
}
conn->server_cert_only = 1;
wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server "
"certificate match");
return 0;
#else /* CONFIG_SHA256 */
wpa_printf(MSG_INFO, "No SHA256 included in the build - "
"cannot validate server certificate hash");
return -1;
#endif /* CONFIG_SHA256 */
}
if (ca_cert_blob) {
X509 *cert = d2i_X509(NULL,
(const unsigned char **) &ca_cert_blob,
ca_cert_blob_len);
if (cert == NULL) {
BIO *bio = BIO_new_mem_buf(ca_cert_blob,
ca_cert_blob_len);
if (bio) {
cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO_free(bio);
}
if (!cert) {
tls_show_errors(MSG_WARNING, __func__,
"Failed to parse ca_cert_blob");
return -1;
}
while (ERR_get_error()) {
/* Ignore errors from DER conversion. */
}
}
if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
cert)) {
unsigned long err = ERR_peek_error();
tls_show_errors(MSG_WARNING, __func__,
"Failed to add ca_cert_blob to "
"certificate store");
if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
ERR_GET_REASON(err) ==
X509_R_CERT_ALREADY_IN_HASH_TABLE) {
wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
"cert already in hash table error",
__func__);
} else {
X509_free(cert);
return -1;
}
}
X509_free(cert);
wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob "
"to certificate store", __func__);
return 0;
}
#ifdef ANDROID
/* Single alias */
if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx),
&ca_cert[11]) < 0)
return -1;
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
return 0;
}
/* Multiple aliases separated by space */
if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) {
char *aliases = os_strdup(&ca_cert[12]);
const char *delim = " ";
int rc = 0;
char *savedptr;
char *alias;
if (!aliases)
return -1;
alias = strtok_r(aliases, delim, &savedptr);
for (; alias; alias = strtok_r(NULL, delim, &savedptr)) {
if (tls_add_ca_from_keystore_encoded(
SSL_CTX_get_cert_store(ssl_ctx), alias)) {
wpa_printf(MSG_WARNING,
"OpenSSL: %s - Failed to add ca_cert %s from keystore",
__func__, alias);
rc = -1;
break;
}
}
os_free(aliases);
if (rc)
return rc;
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
return 0;
}
#endif /* ANDROID */
#ifdef CONFIG_NATIVE_WINDOWS
if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) ==
0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from "
"system certificate store");
return 0;
}
#endif /* CONFIG_NATIVE_WINDOWS */
if (ca_cert || ca_path) {
#ifndef OPENSSL_NO_STDIO
if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) !=
1) {
tls_show_errors(MSG_WARNING, __func__,
"Failed to load root certificates");
if (ca_cert &&
tls_load_ca_der(data, ca_cert) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded "
"DER format CA certificate",
__func__);
} else
return -1;
} else {
wpa_printf(MSG_DEBUG, "TLS: Trusted root "
"certificate(s) loaded");
tls_get_errors(data);
}
#else /* OPENSSL_NO_STDIO */
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
__func__);
return -1;
#endif /* OPENSSL_NO_STDIO */
} else {
/* No ca_cert configured - do not try to verify server
* certificate */
conn->ca_cert_verify = 0;
}
return 0;
}
static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert)
{
SSL_CTX *ssl_ctx = data->ssl;
if (ca_cert) {
if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
{
tls_show_errors(MSG_WARNING, __func__,
"Failed to load root certificates");
return -1;
}
wpa_printf(MSG_DEBUG, "TLS: Trusted root "
"certificate(s) loaded");
#ifndef OPENSSL_NO_STDIO
/* Add the same CAs to the client certificate requests */
SSL_CTX_set_client_CA_list(ssl_ctx,
SSL_load_client_CA_file(ca_cert));
#endif /* OPENSSL_NO_STDIO */
os_free(data->ca_cert);
data->ca_cert = os_strdup(ca_cert);
}
return 0;
}
int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict)
{
int flags;
if (check_crl) {
struct tls_data *data = ssl_ctx;
X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl);
if (cs == NULL) {
tls_show_errors(MSG_INFO, __func__, "Failed to get "
"certificate store when enabling "
"check_crl");
return -1;
}
flags = X509_V_FLAG_CRL_CHECK;
if (check_crl == 2)
flags |= X509_V_FLAG_CRL_CHECK_ALL;
X509_STORE_set_flags(cs, flags);
data->check_crl = check_crl;
data->check_crl_strict = strict;
os_get_reltime(&data->crl_last_reload);
}
return 0;
}
static int tls_connection_set_subject_match(struct tls_connection *conn,
const char *subject_match,
const char *altsubject_match,
const char *suffix_match,
const char *domain_match,
const char *check_cert_subject)
{
os_free(conn->subject_match);
conn->subject_match = NULL;
if (subject_match) {
conn->subject_match = os_strdup(subject_match);
if (conn->subject_match == NULL)
return -1;
}
os_free(conn->altsubject_match);
conn->altsubject_match = NULL;
if (altsubject_match) {
conn->altsubject_match = os_strdup(altsubject_match);
if (conn->altsubject_match == NULL)
return -1;
}
os_free(conn->suffix_match);
conn->suffix_match = NULL;
if (suffix_match) {
conn->suffix_match = os_strdup(suffix_match);
if (conn->suffix_match == NULL)
return -1;
}
os_free(conn->domain_match);
conn->domain_match = NULL;
if (domain_match) {
conn->domain_match = os_strdup(domain_match);
if (conn->domain_match == NULL)
return -1;
}
os_free(conn->check_cert_subject);
conn->check_cert_subject = NULL;
if (check_cert_subject) {
conn->check_cert_subject = os_strdup(check_cert_subject);
if (!conn->check_cert_subject)
return -1;
}
return 0;
}
#ifdef CONFIG_SUITEB
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
static int suiteb_cert_cb(SSL *ssl, void *arg)
{
struct tls_connection *conn = arg;
/*
* This cert_cb() is not really the best location for doing a
* constraint check for the ServerKeyExchange message, but this seems to
* be the only place where the current OpenSSL sequence can be
* terminated cleanly with an TLS alert going out to the server.
*/
if (!(conn->flags & TLS_CONN_SUITEB))
return 1;
/* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */
if (conn->cipher_suite != 0x9f)
return 1;
if (conn->server_dh_prime_len >= 3072)
return 1;
wpa_printf(MSG_DEBUG,
"OpenSSL: Server DH prime length (%d bits) not sufficient for Suite B RSA - reject handshake",
conn->server_dh_prime_len);
return 0;
}
#endif /* OPENSSL_VERSION_NUMBER */
#endif /* CONFIG_SUITEB */
static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags,
const char *openssl_ciphers)
{
SSL *ssl = conn->ssl;
#ifdef SSL_OP_NO_TICKET
if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
SSL_set_options(ssl, SSL_OP_NO_TICKET);
else
SSL_clear_options(ssl, SSL_OP_NO_TICKET);
#endif /* SSL_OP_NO_TICKET */
#ifdef SSL_OP_NO_TLSv1
if (flags & TLS_CONN_DISABLE_TLSv1_0)
SSL_set_options(ssl, SSL_OP_NO_TLSv1);
else
SSL_clear_options(ssl, SSL_OP_NO_TLSv1);
#endif /* SSL_OP_NO_TLSv1 */
#ifdef SSL_OP_NO_TLSv1_1
if (flags & TLS_CONN_DISABLE_TLSv1_1)
SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
else
SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1);
#endif /* SSL_OP_NO_TLSv1_1 */
#ifdef SSL_OP_NO_TLSv1_2
if (flags & TLS_CONN_DISABLE_TLSv1_2)
SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
else
SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
#endif /* SSL_OP_NO_TLSv1_2 */
#ifdef SSL_OP_NO_TLSv1_3
if (flags & TLS_CONN_DISABLE_TLSv1_3)
SSL_set_options(ssl, SSL_OP_NO_TLSv1_3);
else
SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3);
#endif /* SSL_OP_NO_TLSv1_3 */
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (flags & (TLS_CONN_ENABLE_TLSv1_0 |
TLS_CONN_ENABLE_TLSv1_1 |
TLS_CONN_ENABLE_TLSv1_2)) {
int version = 0;
/* Explicit request to enable TLS versions even if needing to
* override systemwide policies. */
if (flags & TLS_CONN_ENABLE_TLSv1_0)
version = TLS1_VERSION;
else if (flags & TLS_CONN_ENABLE_TLSv1_1)
version = TLS1_1_VERSION;
else if (flags & TLS_CONN_ENABLE_TLSv1_2)
version = TLS1_2_VERSION;
if (!version) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Invalid TLS version configuration");
return -1;
}
if (SSL_set_min_proto_version(ssl, version) != 1) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Failed to set minimum TLS version");
return -1;
}
}
#endif /* >= 1.1.0 */
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
!defined(LIBRESSL_VERSION_NUMBER) && \
!defined(OPENSSL_IS_BORINGSSL)
if ((flags & (TLS_CONN_ENABLE_TLSv1_0 | TLS_CONN_ENABLE_TLSv1_1)) &&
SSL_get_security_level(ssl) >= 2) {
/*
* Need to drop to security level 1 to allow TLS versions older
* than 1.2 to be used when explicitly enabled in configuration.
*/
SSL_set_security_level(conn->ssl, 1);
}
#endif
#ifdef CONFIG_SUITEB
#ifdef OPENSSL_IS_BORINGSSL
/* Start with defaults from BoringSSL */
SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, NULL, 0);
#endif /* OPENSSL_IS_BORINGSSL */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
if (flags & TLS_CONN_SUITEB_NO_ECDH) {
const char *ciphers = "DHE-RSA-AES256-GCM-SHA384";
if (openssl_ciphers) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Override ciphers for Suite B (no ECDH): %s",
openssl_ciphers);
ciphers = openssl_ciphers;
}
if (SSL_set_cipher_list(ssl, ciphers) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B ciphers");
return -1;
}
} else if (flags & TLS_CONN_SUITEB) {
EC_KEY *ecdh;
const char *ciphers =
"ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384";
int nid[1] = { NID_secp384r1 };
if (openssl_ciphers) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Override ciphers for Suite B: %s",
openssl_ciphers);
ciphers = openssl_ciphers;
}
if (SSL_set_cipher_list(ssl, ciphers) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B ciphers");
return -1;
}
if (SSL_set1_curves(ssl, nid, 1) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B curves");
return -1;
}
ecdh = EC_KEY_new_by_curve_name(NID_secp384r1);
if (!ecdh || SSL_set_tmp_ecdh(ssl, ecdh) != 1) {
EC_KEY_free(ecdh);
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set ECDH parameter");
return -1;
}
EC_KEY_free(ecdh);
}
if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
#ifdef OPENSSL_IS_BORINGSSL
uint16_t sigalgs[1] = { SSL_SIGN_RSA_PKCS1_SHA384 };
if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
1) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B sigalgs");
return -1;
}
#else /* OPENSSL_IS_BORINGSSL */
/* ECDSA+SHA384 if need to add EC support here */
if (SSL_set1_sigalgs_list(ssl, "RSA+SHA384") != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B sigalgs");
return -1;
}
#endif /* OPENSSL_IS_BORINGSSL */
SSL_set_options(ssl, SSL_OP_NO_TLSv1);
SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
SSL_set_cert_cb(ssl, suiteb_cert_cb, conn);
}
#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */
if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) {
wpa_printf(MSG_ERROR,
"OpenSSL: Suite B RSA case not supported with this OpenSSL version");
return -1;
}
#endif /* OPENSSL_VERSION_NUMBER */
#ifdef OPENSSL_IS_BORINGSSL
if (openssl_ciphers && os_strcmp(openssl_ciphers, "SUITEB192") == 0) {
uint16_t sigalgs[1] = { SSL_SIGN_ECDSA_SECP384R1_SHA384 };
int nid[1] = { NID_secp384r1 };
if (SSL_set1_curves(ssl, nid, 1) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B curves");
return -1;
}
if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs,
1) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set Suite B sigalgs");
return -1;
}
}
#else /* OPENSSL_IS_BORINGSSL */
if (!(flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) &&
openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set openssl_ciphers '%s'",
openssl_ciphers);
return -1;
}
#endif /* OPENSSL_IS_BORINGSSL */
#else /* CONFIG_SUITEB */
if (openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set openssl_ciphers '%s'",
openssl_ciphers);
return -1;
}
#endif /* CONFIG_SUITEB */
if (flags & TLS_CONN_TEAP_ANON_DH) {
#ifndef TEAP_DH_ANON_CS
#define TEAP_DH_ANON_CS \
"ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:" \
"ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:" \
"ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:" \
"DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:" \
"DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:" \
"DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:" \
"ADH-AES256-GCM-SHA384:ADH-AES128-GCM-SHA256:" \
"ADH-AES256-SHA256:ADH-AES128-SHA256:ADH-AES256-SHA:ADH-AES128-SHA"
#endif
static const char *cs = TEAP_DH_ANON_CS;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
!defined(LIBRESSL_VERSION_NUMBER) && \
!defined(OPENSSL_IS_BORINGSSL)
/*
* Need to drop to security level 0 to allow anonymous
* cipher suites for EAP-TEAP.
*/
SSL_set_security_level(conn->ssl, 0);
#endif
wpa_printf(MSG_DEBUG,
"OpenSSL: Enable cipher suites for anonymous EAP-TEAP provisioning: %s",
cs);
if (SSL_set_cipher_list(conn->ssl, cs) != 1) {
tls_show_errors(MSG_INFO, __func__,
"Cipher suite configuration failed");
return -1;
}
}
return 0;
}
int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
int verify_peer, unsigned int flags,
const u8 *session_ctx, size_t session_ctx_len)
{
static int counter = 0;
struct tls_data *data = ssl_ctx;
if (conn == NULL)
return -1;
if (verify_peer == 2) {
conn->ca_cert_verify = 1;
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
} else if (verify_peer) {
conn->ca_cert_verify = 1;
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
} else {
conn->ca_cert_verify = 0;
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
}
if (tls_set_conn_flags(conn, flags, NULL) < 0)
return -1;
conn->flags = flags;
SSL_set_accept_state(conn->ssl);
if (data->tls_session_lifetime == 0) {
/*
* Set session id context to a unique value to make sure
* session resumption cannot be used either through session
* caching or TLS ticket extension.
*/
counter++;
SSL_set_session_id_context(conn->ssl,
(const unsigned char *) &counter,
sizeof(counter));
} else if (session_ctx) {
SSL_set_session_id_context(conn->ssl, session_ctx,
session_ctx_len);
}
return 0;
}
static int tls_connection_client_cert(struct tls_connection *conn,
const char *client_cert,
const u8 *client_cert_blob,
size_t client_cert_blob_len)
{
if (client_cert == NULL && client_cert_blob == NULL)
return 0;
#ifdef PKCS12_FUNCS
#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
/*
* Clear previously set extra chain certificates, if any, from PKCS#12
* processing in tls_parse_pkcs12() to allow OpenSSL to build a new
* chain properly.
*/
SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx);
#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */
#endif /* PKCS12_FUNCS */
if (client_cert_blob &&
SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob,
client_cert_blob_len) == 1) {
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> "
"OK");
return 0;
} else if (client_cert_blob) {
#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20901000L
tls_show_errors(MSG_DEBUG, __func__,
"SSL_use_certificate_ASN1 failed");
#else
BIO *bio;
X509 *x509;
tls_show_errors(MSG_DEBUG, __func__,
"SSL_use_certificate_ASN1 failed");
bio = BIO_new(BIO_s_mem());
if (!bio)
return -1;
BIO_write(bio, client_cert_blob, client_cert_blob_len);
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (!x509 || SSL_use_certificate(conn->ssl, x509) != 1) {
X509_free(x509);
BIO_free(bio);
return -1;
}
X509_free(x509);
wpa_printf(MSG_DEBUG,
"OpenSSL: Found PEM encoded certificate from blob");
while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL))) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Added an additional certificate into the chain");
SSL_add0_chain_cert(conn->ssl, x509);
}
BIO_free(bio);
return 0;
#endif
}
if (client_cert == NULL)
return -1;
#ifdef ANDROID
if (os_strncmp("keystore://", client_cert, 11) == 0) {
BIO *bio = BIO_from_keystore(&client_cert[11]);
X509 *x509 = NULL;
int ret = -1;
if (bio) {
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
}
if (x509) {
if (SSL_use_certificate(conn->ssl, x509) == 1)
ret = 0;
X509_free(x509);
}
/* Read additional certificates into the chain. */
while (bio) {
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (x509) {
/* Takes ownership of x509 */
SSL_add0_chain_cert(conn->ssl, x509);
} else {
BIO_free(bio);
bio = NULL;
}
}
return ret;
}
#endif /* ANDROID */
#ifndef OPENSSL_NO_STDIO
if (SSL_use_certificate_file(conn->ssl, client_cert,
SSL_FILETYPE_ASN1) == 1) {
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)"
" --> OK");
return 0;
}
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
!defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
if (SSL_use_certificate_chain_file(conn->ssl, client_cert) == 1) {
ERR_clear_error();
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_chain_file"
" --> OK");
return 0;
}
#else
if (SSL_use_certificate_file(conn->ssl, client_cert,
SSL_FILETYPE_PEM) == 1) {
ERR_clear_error();
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)"
" --> OK");
return 0;
}
#endif
tls_show_errors(MSG_DEBUG, __func__,
"SSL_use_certificate_file failed");
#else /* OPENSSL_NO_STDIO */
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
#endif /* OPENSSL_NO_STDIO */
return -1;
}
static int tls_global_client_cert(struct tls_data *data,
const char *client_cert)
{
#ifndef OPENSSL_NO_STDIO
SSL_CTX *ssl_ctx = data->ssl;
if (client_cert == NULL)
return 0;
if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
SSL_FILETYPE_ASN1) != 1 &&
SSL_CTX_use_certificate_chain_file(ssl_ctx, client_cert) != 1 &&
SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
SSL_FILETYPE_PEM) != 1) {
tls_show_errors(MSG_INFO, __func__,
"Failed to load client certificate");
return -1;
}
return 0;
#else /* OPENSSL_NO_STDIO */
if (client_cert == NULL)
return 0;
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
return -1;
#endif /* OPENSSL_NO_STDIO */
}
#ifdef PKCS12_FUNCS
static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
const char *passwd)
{
EVP_PKEY *pkey;
X509 *cert;
STACK_OF(X509) *certs;
int res = 0;
char buf[256];
pkey = NULL;
cert = NULL;
certs = NULL;
if (!passwd)
passwd = "";
if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
tls_show_errors(MSG_DEBUG, __func__,
"Failed to parse PKCS12 file");
PKCS12_free(p12);
return -1;
}
wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data");
if (cert) {
X509_NAME_oneline(X509_get_subject_name(cert), buf,
sizeof(buf));
wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: "
"subject='%s'", buf);
if (ssl) {
if (SSL_use_certificate(ssl, cert) != 1)
res = -1;
} else {
if (SSL_CTX_use_certificate(data->ssl, cert) != 1)
res = -1;
}
X509_free(cert);
}
if (pkey) {
wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12");
if (ssl) {
if (SSL_use_PrivateKey(ssl, pkey) != 1)
res = -1;
} else {
if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1)
res = -1;
}
EVP_PKEY_free(pkey);
}
if (certs) {
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
if (ssl)
SSL_clear_chain_certs(ssl);
else
SSL_CTX_clear_chain_certs(data->ssl);
while ((cert = sk_X509_pop(certs)) != NULL) {
X509_NAME_oneline(X509_get_subject_name(cert), buf,
sizeof(buf));
wpa_printf(MSG_DEBUG, "TLS: additional certificate"
" from PKCS12: subject='%s'", buf);
if ((ssl && SSL_add1_chain_cert(ssl, cert) != 1) ||
(!ssl && SSL_CTX_add1_chain_cert(data->ssl,
cert) != 1)) {
tls_show_errors(MSG_DEBUG, __func__,
"Failed to add additional certificate");
res = -1;
X509_free(cert);
break;
}
X509_free(cert);
}
if (!res) {
/* Try to continue anyway */
}
sk_X509_pop_free(certs, X509_free);
#ifndef OPENSSL_IS_BORINGSSL
if (ssl)
res = SSL_build_cert_chain(
ssl,
SSL_BUILD_CHAIN_FLAG_CHECK |
SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
else
res = SSL_CTX_build_cert_chain(
data->ssl,
SSL_BUILD_CHAIN_FLAG_CHECK |
SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
if (!res) {
tls_show_errors(MSG_DEBUG, __func__,
"Failed to build certificate chain");
} else if (res == 2) {
wpa_printf(MSG_DEBUG,
"TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates");
}
#endif /* OPENSSL_IS_BORINGSSL */
/*
* Try to continue regardless of result since it is possible for
* the extra certificates not to be required.
*/
res = 0;
#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
SSL_CTX_clear_extra_chain_certs(data->ssl);
while ((cert = sk_X509_pop(certs)) != NULL) {
X509_NAME_oneline(X509_get_subject_name(cert), buf,
sizeof(buf));
wpa_printf(MSG_DEBUG, "TLS: additional certificate"
" from PKCS12: subject='%s'", buf);
/*
* There is no SSL equivalent for the chain cert - so
* always add it to the context...
*/
if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
{
X509_free(cert);
res = -1;
break;
}
}
sk_X509_pop_free(certs, X509_free);
#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
}
PKCS12_free(p12);
if (res < 0)
tls_get_errors(data);
return res;
}
#endif /* PKCS12_FUNCS */
static int tls_read_pkcs12(struct tls_data *data, SSL *ssl,
const char *private_key, const char *passwd)
{
#ifdef PKCS12_FUNCS
FILE *f;
PKCS12 *p12;
f = fopen(private_key, "rb");
if (f == NULL)
return -1;
p12 = d2i_PKCS12_fp(f, NULL);
fclose(f);
if (p12 == NULL) {
tls_show_errors(MSG_INFO, __func__,
"Failed to use PKCS#12 file");
return -1;
}
return tls_parse_pkcs12(data, ssl, p12, passwd);
#else /* PKCS12_FUNCS */
wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
"p12/pfx files");
return -1;
#endif /* PKCS12_FUNCS */
}
static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl,
const u8 *blob, size_t len, const char *passwd)
{
#ifdef PKCS12_FUNCS
PKCS12 *p12;
p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len);
if (p12 == NULL) {
tls_show_errors(MSG_INFO, __func__,
"Failed to use PKCS#12 blob");
return -1;
}
return tls_parse_pkcs12(data, ssl, p12, passwd);
#else /* PKCS12_FUNCS */
wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
"p12/pfx blobs");
return -1;
#endif /* PKCS12_FUNCS */
}
#ifndef OPENSSL_NO_ENGINE
static int tls_engine_get_cert(struct tls_connection *conn,
const char *cert_id,
X509 **cert)
{
/* this runs after the private key is loaded so no PIN is required */
struct {
const char *cert_id;
X509 *cert;
} params;
params.cert_id = cert_id;
params.cert = NULL;
if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL",
0, &params, NULL, 1)) {
unsigned long err = ERR_get_error();
wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id"
" '%s' [%s]", cert_id,
ERR_error_string(err, NULL));
if (tls_is_pin_error(err))
return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
}
if (!params.cert) {
wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id"
" '%s'", cert_id);
return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
}
*cert = params.cert;
return 0;
}
#endif /* OPENSSL_NO_ENGINE */
static int tls_connection_engine_client_cert(struct tls_connection *conn,
const char *cert_id)
{
#ifndef OPENSSL_NO_ENGINE
X509 *cert;
if (tls_engine_get_cert(conn, cert_id, &cert))
return -1;
if (!SSL_use_certificate(conn->ssl, cert)) {
tls_show_errors(MSG_ERROR, __func__,
"SSL_use_certificate failed");
X509_free(cert);
return -1;
}
X509_free(cert);
wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> "
"OK");
return 0;
#else /* OPENSSL_NO_ENGINE */
return -1;
#endif /* OPENSSL_NO_ENGINE */
}
static int tls_connection_engine_ca_cert(struct tls_data *data,
struct tls_connection *conn,
const char *ca_cert_id)
{
#ifndef OPENSSL_NO_ENGINE
X509 *cert;
SSL_CTX *ssl_ctx = data->ssl;
X509_STORE *store;
if (tls_engine_get_cert(conn, ca_cert_id, &cert))
return -1;
/* start off the same as tls_connection_ca_cert */
store = X509_STORE_new();
if (store == NULL) {
wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
"certificate store", __func__);
X509_free(cert);
return -1;
}
SSL_CTX_set_cert_store(ssl_ctx, store);
if (!X509_STORE_add_cert(store, cert)) {
unsigned long err = ERR_peek_error();
tls_show_errors(MSG_WARNING, __func__,
"Failed to add CA certificate from engine "
"to certificate store");
if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert"
" already in hash table error",
__func__);
} else {
X509_free(cert);
return -1;
}
}
X509_free(cert);
wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
"to certificate store", __func__);
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
conn->ca_cert_verify = 1;
return 0;
#else /* OPENSSL_NO_ENGINE */
return -1;
#endif /* OPENSSL_NO_ENGINE */
}
static int tls_connection_engine_private_key(struct tls_connection *conn)
{
#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
tls_show_errors(MSG_ERROR, __func__,
"ENGINE: cannot use private key for TLS");
return -1;
}
if (!SSL_check_private_key(conn->ssl)) {
tls_show_errors(MSG_INFO, __func__,
"Private key failed verification");
return -1;
}
return 0;
#else /* OPENSSL_NO_ENGINE */
wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but "
"engine support was not compiled in");
return -1;
#endif /* OPENSSL_NO_ENGINE */
}
#ifndef OPENSSL_NO_STDIO
static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
{
if (!password)
return 0;
os_strlcpy(buf, (const char *) password, size);
return os_strlen(buf);
}
#endif /* OPENSSL_NO_STDIO */
static int tls_use_private_key_file(struct tls_data *data, SSL *ssl,
const char *private_key,
const char *private_key_passwd)
{
#ifndef OPENSSL_NO_STDIO
BIO *bio;
EVP_PKEY *pkey;
int ret;
/* First try ASN.1 (DER). */
bio = BIO_new_file(private_key, "r");
if (!bio)
return -1;
pkey = d2i_PrivateKey_bio(bio, NULL);
BIO_free(bio);
if (pkey) {
wpa_printf(MSG_DEBUG, "OpenSSL: %s (DER) --> loaded", __func__);
} else {
/* Try PEM with the provided password. */
bio = BIO_new_file(private_key, "r");
if (!bio)
return -1;
pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_passwd_cb,
(void *) private_key_passwd);
BIO_free(bio);
if (!pkey)
return -1;
wpa_printf(MSG_DEBUG, "OpenSSL: %s (PEM) --> loaded", __func__);
/* Clear errors from the previous failed load. */
ERR_clear_error();
}
if (ssl)
ret = SSL_use_PrivateKey(ssl, pkey);
else
ret = SSL_CTX_use_PrivateKey(data->ssl, pkey);
EVP_PKEY_free(pkey);
return ret == 1 ? 0 : -1;
#else /* OPENSSL_NO_STDIO */
wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
return -1;
#endif /* OPENSSL_NO_STDIO */
}
static int tls_connection_private_key(struct tls_data *data,
struct tls_connection *conn,
const char *private_key,
const char *private_key_passwd,
const u8 *private_key_blob,
size_t private_key_blob_len)
{
+ BIO *bio;
int ok;
if (private_key == NULL && private_key_blob == NULL)
return 0;
ok = 0;
while (private_key_blob) {
if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
(u8 *) private_key_blob,
private_key_blob_len) == 1) {
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
"ASN1(EVP_PKEY_RSA) --> OK");
ok = 1;
break;
}
if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl,
(u8 *) private_key_blob,
private_key_blob_len) == 1) {
wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
"ASN1(EVP_PKEY_DSA) --> OK");
ok = 1;
break;
}
#ifndef OPENSSL_NO_EC
if (SSL_use_PrivateKey_ASN1(EVP_PKEY_EC, conn->ssl,
(u8 *) private_key_blob,
private_key_blob_len) == 1) {
wpa_printf(MSG_DEBUG,
"OpenSSL: SSL_use_PrivateKey_ASN1(EVP_PKEY_EC) --> OK");
ok = 1;
break;
}
#endif /* OPENSSL_NO_EC */
if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
(u8 *) private_key_blob,
private_key_blob_len) == 1) {
wpa_printf(MSG_DEBUG, "OpenSSL: "
"SSL_use_RSAPrivateKey_ASN1 --> OK");
ok = 1;
break;
}
+ bio = BIO_new_mem_buf((u8 *) private_key_blob,
+ private_key_blob_len);
+ if (bio) {
+ EVP_PKEY *pkey;
+
+ pkey = PEM_read_bio_PrivateKey(
+ bio, NULL, tls_passwd_cb,
+ (void *) private_key_passwd);
+ if (pkey) {
+ if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: SSL_use_PrivateKey --> OK");
+ ok = 1;
+ EVP_PKEY_free(pkey);
+ BIO_free(bio);
+ break;
+ }
+ EVP_PKEY_free(pkey);
+ }
+ BIO_free(bio);
+ }
+
if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
private_key_blob_len,
private_key_passwd) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
"OK");
ok = 1;
break;
}
break;
}
while (!ok && private_key) {
if (tls_use_private_key_file(data, conn->ssl, private_key,
private_key_passwd) == 0) {
ok = 1;
break;
}
if (tls_read_pkcs12(data, conn->ssl, private_key,
private_key_passwd) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
"--> OK");
ok = 1;
break;
}
if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) {
wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to "
"access certificate store --> OK");
ok = 1;
break;
}
break;
}
if (!ok) {
tls_show_errors(MSG_INFO, __func__,
"Failed to load private key");
return -1;
}
ERR_clear_error();
if (!SSL_check_private_key(conn->ssl)) {
tls_show_errors(MSG_INFO, __func__, "Private key failed "
"verification");
return -1;
}
wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully");
return 0;
}
static int tls_global_private_key(struct tls_data *data,
const char *private_key,
const char *private_key_passwd)
{
SSL_CTX *ssl_ctx = data->ssl;
if (private_key == NULL)
return 0;
if (tls_use_private_key_file(data, NULL, private_key,
private_key_passwd) &&
tls_read_pkcs12(data, NULL, private_key, private_key_passwd)) {
tls_show_errors(MSG_INFO, __func__,
"Failed to load private key");
ERR_clear_error();
return -1;
}
ERR_clear_error();
if (!SSL_CTX_check_private_key(ssl_ctx)) {
tls_show_errors(MSG_INFO, __func__,
"Private key failed verification");
return -1;
}
return 0;
}
static int tls_connection_dh(struct tls_connection *conn, const char *dh_file)
{
#ifdef OPENSSL_NO_DH
if (dh_file == NULL)
return 0;
wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
"dh_file specified");
return -1;
#else /* OPENSSL_NO_DH */
DH *dh;
BIO *bio;
/* TODO: add support for dh_blob */
if (dh_file == NULL)
return 0;
if (conn == NULL)
return -1;
bio = BIO_new_file(dh_file, "r");
if (bio == NULL) {
wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
dh_file, ERR_error_string(ERR_get_error(), NULL));
return -1;
}
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
#ifndef OPENSSL_NO_DSA
while (dh == NULL) {
DSA *dsa;
wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
" trying to parse as DSA params", dh_file,
ERR_error_string(ERR_get_error(), NULL));
bio = BIO_new_file(dh_file, "r");
if (bio == NULL)
break;
dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
BIO_free(bio);
if (!dsa) {
wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
"'%s': %s", dh_file,
ERR_error_string(ERR_get_error(), NULL));
break;
}
wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
dh = DSA_dup_DH(dsa);
DSA_free(dsa);
if (dh == NULL) {
wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
"params into DH params");
break;
}
break;
}
#endif /* !OPENSSL_NO_DSA */
if (dh == NULL) {
wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
"'%s'", dh_file);
return -1;
}
if (SSL_set_tmp_dh(conn->ssl, dh) != 1) {
wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
"%s", dh_file,
ERR_error_string(ERR_get_error(), NULL));
DH_free(dh);
return -1;
}
DH_free(dh);
return 0;
#endif /* OPENSSL_NO_DH */
}
static int tls_global_dh(struct tls_data *data, const char *dh_file)
{
#ifdef OPENSSL_NO_DH
if (dh_file == NULL)
return 0;
wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
"dh_file specified");
return -1;
#else /* OPENSSL_NO_DH */
SSL_CTX *ssl_ctx = data->ssl;
DH *dh;
BIO *bio;
/* TODO: add support for dh_blob */
if (dh_file == NULL)
return 0;
if (ssl_ctx == NULL)
return -1;
bio = BIO_new_file(dh_file, "r");
if (bio == NULL) {
wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
dh_file, ERR_error_string(ERR_get_error(), NULL));
return -1;
}
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
BIO_free(bio);
#ifndef OPENSSL_NO_DSA
while (dh == NULL) {
DSA *dsa;
wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
" trying to parse as DSA params", dh_file,
ERR_error_string(ERR_get_error(), NULL));
bio = BIO_new_file(dh_file, "r");
if (bio == NULL)
break;
dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
BIO_free(bio);
if (!dsa) {
wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
"'%s': %s", dh_file,
ERR_error_string(ERR_get_error(), NULL));
break;
}
wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
dh = DSA_dup_DH(dsa);
DSA_free(dsa);
if (dh == NULL) {
wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
"params into DH params");
break;
}
break;
}
#endif /* !OPENSSL_NO_DSA */
if (dh == NULL) {
wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
"'%s'", dh_file);
return -1;
}
if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) != 1) {
wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
"%s", dh_file,
ERR_error_string(ERR_get_error(), NULL));
DH_free(dh);
return -1;
}
DH_free(dh);
return 0;
#endif /* OPENSSL_NO_DH */
}
int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
struct tls_random *keys)
{
SSL *ssl;
if (conn == NULL || keys == NULL)
return -1;
ssl = conn->ssl;
if (ssl == NULL)
return -1;
os_memset(keys, 0, sizeof(*keys));
keys->client_random = conn->client_random;
keys->client_random_len = SSL_get_client_random(
ssl, conn->client_random, sizeof(conn->client_random));
keys->server_random = conn->server_random;
keys->server_random_len = SSL_get_server_random(
ssl, conn->server_random, sizeof(conn->server_random));
return 0;
}
#ifdef OPENSSL_NEED_EAP_FAST_PRF
static int openssl_get_keyblock_size(SSL *ssl)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)
const EVP_CIPHER *c;
const EVP_MD *h;
int md_size;
if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL ||
ssl->read_hash == NULL)
return -1;
c = ssl->enc_read_ctx->cipher;
h = EVP_MD_CTX_md(ssl->read_hash);
if (h)
md_size = EVP_MD_size(h);
else if (ssl->s3)
md_size = ssl->s3->tmp.new_mac_secret_size;
else
return -1;
wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d "
"IV_len=%d", EVP_CIPHER_key_length(c), md_size,
EVP_CIPHER_iv_length(c));
return 2 * (EVP_CIPHER_key_length(c) +
md_size +
EVP_CIPHER_iv_length(c));
#else
const SSL_CIPHER *ssl_cipher;
int cipher, digest;
const EVP_CIPHER *c;
const EVP_MD *h;
int mac_key_len, enc_key_len, fixed_iv_len;
ssl_cipher = SSL_get_current_cipher(ssl);
if (!ssl_cipher)
return -1;
cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher);
digest = SSL_CIPHER_get_digest_nid(ssl_cipher);
wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d",
cipher, digest);
if (cipher < 0 || digest < 0)
return -1;
if (cipher == NID_undef) {
wpa_printf(MSG_DEBUG, "OpenSSL: no cipher in use?!");
return -1;
}
c = EVP_get_cipherbynid(cipher);
if (!c)
return -1;
enc_key_len = EVP_CIPHER_key_length(c);
if (EVP_CIPHER_mode(c) == EVP_CIPH_GCM_MODE ||
EVP_CIPHER_mode(c) == EVP_CIPH_CCM_MODE)
fixed_iv_len = 4; /* only part of IV from PRF */
else
fixed_iv_len = EVP_CIPHER_iv_length(c);
if (digest == NID_undef) {
wpa_printf(MSG_DEBUG, "OpenSSL: no digest in use (e.g., AEAD)");
mac_key_len = 0;
} else {
h = EVP_get_digestbynid(digest);
if (!h)
return -1;
mac_key_len = EVP_MD_size(h);
}
wpa_printf(MSG_DEBUG,
"OpenSSL: keyblock size: mac_key_len=%d enc_key_len=%d fixed_iv_len=%d",
mac_key_len, enc_key_len, fixed_iv_len);
return 2 * (mac_key_len + enc_key_len + fixed_iv_len);
#endif
}
#endif /* OPENSSL_NEED_EAP_FAST_PRF */
int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
const char *label, const u8 *context,
size_t context_len, u8 *out, size_t out_len)
{
if (!conn ||
SSL_export_keying_material(conn->ssl, out, out_len, label,
os_strlen(label), context, context_len,
context != NULL) != 1)
return -1;
return 0;
}
int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
u8 *out, size_t out_len)
{
#ifdef OPENSSL_NEED_EAP_FAST_PRF
SSL *ssl;
SSL_SESSION *sess;
u8 *rnd;
int ret = -1;
int skip = 0;
u8 *tmp_out = NULL;
u8 *_out = out;
unsigned char client_random[SSL3_RANDOM_SIZE];
unsigned char server_random[SSL3_RANDOM_SIZE];
unsigned char master_key[64];
size_t master_key_len;
const char *ver;
/*
* TLS library did not support EAP-FAST key generation, so get the
* needed TLS session parameters and use an internal implementation of
* TLS PRF to derive the key.
*/
if (conn == NULL)
return -1;
ssl = conn->ssl;
if (ssl == NULL)
return -1;
ver = SSL_get_version(ssl);
sess = SSL_get_session(ssl);
if (!ver || !sess)
return -1;
skip = openssl_get_keyblock_size(ssl);
if (skip < 0)
return -1;
tmp_out = os_malloc(skip + out_len);
if (!tmp_out)
return -1;
_out = tmp_out;
rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
if (!rnd) {
os_free(tmp_out);
return -1;
}
SSL_get_client_random(ssl, client_random, sizeof(client_random));
SSL_get_server_random(ssl, server_random, sizeof(server_random));
master_key_len = SSL_SESSION_get_master_key(sess, master_key,
sizeof(master_key));
os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, SSL3_RANDOM_SIZE);
if (os_strcmp(ver, "TLSv1.2") == 0) {
tls_prf_sha256(master_key, master_key_len,
"key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
_out, skip + out_len);
ret = 0;
} else if (tls_prf_sha1_md5(master_key, master_key_len,
"key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
_out, skip + out_len) == 0) {
ret = 0;
}
forced_memzero(master_key, sizeof(master_key));
os_free(rnd);
if (ret == 0)
os_memcpy(out, _out + skip, out_len);
bin_clear_free(tmp_out, skip);
return ret;
#else /* OPENSSL_NEED_EAP_FAST_PRF */
wpa_printf(MSG_ERROR,
"OpenSSL: EAP-FAST keys cannot be exported in FIPS mode");
return -1;
#endif /* OPENSSL_NEED_EAP_FAST_PRF */
}
static struct wpabuf *
openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data)
{
int res;
struct wpabuf *out_data;
/*
* Give TLS handshake data from the server (if available) to OpenSSL
* for processing.
*/
if (in_data && wpabuf_len(in_data) > 0 &&
BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
< 0) {
tls_show_errors(MSG_INFO, __func__,
"Handshake failed - BIO_write");
return NULL;
}
/* Initiate TLS handshake or continue the existing handshake */
if (conn->server)
res = SSL_accept(conn->ssl);
else
res = SSL_connect(conn->ssl);
if (res != 1) {
int err = SSL_get_error(conn->ssl, res);
if (err == SSL_ERROR_WANT_READ)
wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want "
"more data");
else if (err == SSL_ERROR_WANT_WRITE)
wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to "
"write");
else {
tls_show_errors(MSG_INFO, __func__, "SSL_connect");
conn->failed++;
if (!conn->server && !conn->client_hello_generated) {
/* The server would not understand TLS Alert
* before ClientHello, so simply terminate
* handshake on this type of error case caused
* by a likely internal error like no ciphers
* available. */
wpa_printf(MSG_DEBUG,
"OpenSSL: Could not generate ClientHello");
conn->write_alerts++;
return NULL;
}
}
}
if (!conn->server && !conn->failed)
conn->client_hello_generated = 1;
#ifdef CONFIG_SUITEB
if ((conn->flags & TLS_CONN_SUITEB) && !conn->server &&
os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 &&
conn->server_dh_prime_len < 3072) {
struct tls_context *context = conn->context;
/*
* This should not be reached since earlier cert_cb should have
* terminated the handshake. Keep this check here for extra
* protection if anything goes wrong with the more low-level
* checks based on having to parse the TLS handshake messages.
*/
wpa_printf(MSG_DEBUG,
"OpenSSL: Server DH prime length: %d bits",
conn->server_dh_prime_len);
if (context->event_cb) {
union tls_event_data ev;
os_memset(&ev, 0, sizeof(ev));
ev.alert.is_local = 1;
ev.alert.type = "fatal";
ev.alert.description = "insufficient security";
context->event_cb(context->cb_ctx, TLS_ALERT, &ev);
}
/*
* Could send a TLS Alert to the server, but for now, simply
* terminate handshake.
*/
conn->failed++;
conn->write_alerts++;
return NULL;
}
#endif /* CONFIG_SUITEB */
/* Get the TLS handshake data to be sent to the server */
res = BIO_ctrl_pending(conn->ssl_out);
wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
out_data = wpabuf_alloc(res);
if (out_data == NULL) {
wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
"handshake output (%d bytes)", res);
if (BIO_reset(conn->ssl_out) < 0) {
tls_show_errors(MSG_INFO, __func__,
"BIO_reset failed");
}
return NULL;
}
res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data),
res);
if (res < 0) {
tls_show_errors(MSG_INFO, __func__,
"Handshake failed - BIO_read");
if (BIO_reset(conn->ssl_out) < 0) {
tls_show_errors(MSG_INFO, __func__,
"BIO_reset failed");
}
wpabuf_free(out_data);
return NULL;
}
wpabuf_put(out_data, res);
return out_data;
}
static struct wpabuf *
openssl_get_appl_data(struct tls_connection *conn, size_t max_len)
{
struct wpabuf *appl_data;
int res;
appl_data = wpabuf_alloc(max_len + 100);
if (appl_data == NULL)
return NULL;
res = SSL_read(conn->ssl, wpabuf_mhead(appl_data),
wpabuf_size(appl_data));
if (res < 0) {
int err = SSL_get_error(conn->ssl, res);
if (err == SSL_ERROR_WANT_READ ||
err == SSL_ERROR_WANT_WRITE) {
wpa_printf(MSG_DEBUG, "SSL: No Application Data "
"included");
} else {
tls_show_errors(MSG_INFO, __func__,
"Failed to read possible "
"Application Data");
}
wpabuf_free(appl_data);
return NULL;
}
wpabuf_put(appl_data, res);
wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished "
"message", appl_data);
return appl_data;
}
static struct wpabuf *
openssl_connection_handshake(struct tls_connection *conn,
const struct wpabuf *in_data,
struct wpabuf **appl_data)
{
struct wpabuf *out_data;
if (appl_data)
*appl_data = NULL;
out_data = openssl_handshake(conn, in_data);
if (out_data == NULL)
return NULL;
if (conn->invalid_hb_used) {
wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
wpabuf_free(out_data);
return NULL;
}
if (SSL_is_init_finished(conn->ssl)) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Handshake finished - resumed=%d",
tls_connection_resumed(conn->ssl_ctx, conn));
if (conn->server) {
char *buf;
size_t buflen = 2000;
buf = os_malloc(buflen);
if (buf) {
if (SSL_get_shared_ciphers(conn->ssl, buf,
buflen)) {
buf[buflen - 1] = '\0';
wpa_printf(MSG_DEBUG,
"OpenSSL: Shared ciphers: %s",
buf);
}
os_free(buf);
}
}
if (appl_data && in_data)
*appl_data = openssl_get_appl_data(conn,
wpabuf_len(in_data));
}
if (conn->invalid_hb_used) {
wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
if (appl_data) {
wpabuf_free(*appl_data);
*appl_data = NULL;
}
wpabuf_free(out_data);
return NULL;
}
return out_data;
}
struct wpabuf *
tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
const struct wpabuf *in_data,
struct wpabuf **appl_data)
{
return openssl_connection_handshake(conn, in_data, appl_data);
}
struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data,
struct wpabuf **appl_data)
{
conn->server = 1;
return openssl_connection_handshake(conn, in_data, appl_data);
}
struct wpabuf * tls_connection_encrypt(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data)
{
int res;
struct wpabuf *buf;
if (conn == NULL)
return NULL;
/* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */
if ((res = BIO_reset(conn->ssl_in)) < 0 ||
(res = BIO_reset(conn->ssl_out)) < 0) {
tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
return NULL;
}
res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data));
if (res < 0) {
tls_show_errors(MSG_INFO, __func__,
"Encryption failed - SSL_write");
return NULL;
}
/* Read encrypted data to be sent to the server */
buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
if (buf == NULL)
return NULL;
res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf));
if (res < 0) {
tls_show_errors(MSG_INFO, __func__,
"Encryption failed - BIO_read");
wpabuf_free(buf);
return NULL;
}
wpabuf_put(buf, res);
return buf;
}
struct wpabuf * tls_connection_decrypt(void *tls_ctx,
struct tls_connection *conn,
const struct wpabuf *in_data)
{
int res;
struct wpabuf *buf;
/* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */
res = BIO_write(conn->ssl_in, wpabuf_head(in_data),
wpabuf_len(in_data));
if (res < 0) {
tls_show_errors(MSG_INFO, __func__,
"Decryption failed - BIO_write");
return NULL;
}
if (BIO_reset(conn->ssl_out) < 0) {
tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
return NULL;
}
/* Read decrypted data for further processing */
/*
* Even though we try to disable TLS compression, it is possible that
* this cannot be done with all TLS libraries. Add extra buffer space
* to handle the possibility of the decrypted data being longer than
* input data.
*/
buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
if (buf == NULL)
return NULL;
res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
if (res < 0) {
int err = SSL_get_error(conn->ssl, res);
if (err == SSL_ERROR_WANT_READ) {
wpa_printf(MSG_DEBUG,
"SSL: SSL_connect - want more data");
res = 0;
} else {
tls_show_errors(MSG_INFO, __func__,
"Decryption failed - SSL_read");
wpabuf_free(buf);
return NULL;
}
}
wpabuf_put(buf, res);
if (conn->invalid_hb_used) {
wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
wpabuf_free(buf);
return NULL;
}
return buf;
}
int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
{
return conn ? SSL_session_reused(conn->ssl) : 0;
}
int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
u8 *ciphers)
{
char buf[500], *pos, *end;
u8 *c;
int ret;
if (conn == NULL || conn->ssl == NULL || ciphers == NULL)
return -1;
buf[0] = '\0';
pos = buf;
end = pos + sizeof(buf);
c = ciphers;
while (*c != TLS_CIPHER_NONE) {
const char *suite;
switch (*c) {
case TLS_CIPHER_RC4_SHA:
suite = "RC4-SHA";
break;
case TLS_CIPHER_AES128_SHA:
suite = "AES128-SHA";
break;
case TLS_CIPHER_RSA_DHE_AES128_SHA:
suite = "DHE-RSA-AES128-SHA";
break;
case TLS_CIPHER_ANON_DH_AES128_SHA:
suite = "ADH-AES128-SHA";
break;
case TLS_CIPHER_RSA_DHE_AES256_SHA:
suite = "DHE-RSA-AES256-SHA";
break;
case TLS_CIPHER_AES256_SHA:
suite = "AES256-SHA";
break;
default:
wpa_printf(MSG_DEBUG, "TLS: Unsupported "
"cipher selection: %d", *c);
return -1;
}
ret = os_snprintf(pos, end - pos, ":%s", suite);
if (os_snprintf_error(end - pos, ret))
break;
pos += ret;
c++;
}
if (!buf[0]) {
wpa_printf(MSG_DEBUG, "OpenSSL: No ciphers listed");
return -1;
}
wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
#ifdef EAP_FAST_OR_TEAP
if (os_strstr(buf, ":ADH-")) {
/*
* Need to drop to security level 0 to allow anonymous
* cipher suites for EAP-FAST.
*/
SSL_set_security_level(conn->ssl, 0);
} else if (SSL_get_security_level(conn->ssl) == 0) {
/* Force at least security level 1 */
SSL_set_security_level(conn->ssl, 1);
}
#endif /* EAP_FAST_OR_TEAP */
#endif
if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
tls_show_errors(MSG_INFO, __func__,
"Cipher suite configuration failed");
return -1;
}
return 0;
}
int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
const char *name;
if (conn == NULL || conn->ssl == NULL)
return -1;
name = SSL_get_version(conn->ssl);
if (name == NULL)
return -1;
os_strlcpy(buf, name, buflen);
return 0;
}
int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
char *buf, size_t buflen)
{
const char *name;
if (conn == NULL || conn->ssl == NULL)
return -1;
name = SSL_get_cipher(conn->ssl);
if (name == NULL)
return -1;
os_strlcpy(buf, name, buflen);
return 0;
}
int tls_connection_enable_workaround(void *ssl_ctx,
struct tls_connection *conn)
{
SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
return 0;
}
#ifdef EAP_FAST_OR_TEAP
/* ClientHello TLS extensions require a patch to openssl, so this function is
* commented out unless explicitly needed for EAP-FAST in order to be able to
* build this file with unmodified openssl. */
int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
int ext_type, const u8 *data,
size_t data_len)
{
if (conn == NULL || conn->ssl == NULL || ext_type != 35)
return -1;
if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
data_len) != 1)
return -1;
return 0;
}
#endif /* EAP_FAST_OR_TEAP */
int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
{
if (conn == NULL)
return -1;
return conn->failed;
}
int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
{
if (conn == NULL)
return -1;
return conn->read_alerts;
}
int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
{
if (conn == NULL)
return -1;
return conn->write_alerts;
}
#ifdef HAVE_OCSP
static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
BIO *out;
size_t rlen;
char *txt;
int res;
if (wpa_debug_level > MSG_DEBUG)
return;
out = BIO_new(BIO_s_mem());
if (!out)
return;
OCSP_RESPONSE_print(out, rsp, 0);
rlen = BIO_ctrl_pending(out);
txt = os_malloc(rlen + 1);
if (!txt) {
BIO_free(out);
return;
}
res = BIO_read(out, txt, rlen);
if (res > 0) {
txt[res] = '\0';
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP Response\n%s", txt);
}
os_free(txt);
BIO_free(out);
#endif /* CONFIG_NO_STDOUT_DEBUG */
}
static int ocsp_resp_cb(SSL *s, void *arg)
{
struct tls_connection *conn = arg;
const unsigned char *p;
int len, status, reason, res;
OCSP_RESPONSE *rsp;
OCSP_BASICRESP *basic;
OCSP_CERTID *id;
ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update;
X509_STORE *store;
STACK_OF(X509) *certs = NULL;
len = SSL_get_tlsext_status_ocsp_resp(s, &p);
if (!p) {
wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
}
wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len);
rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
if (!rsp) {
wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response");
return 0;
}
ocsp_debug_print_resp(rsp);
status = OCSP_response_status(rsp);
if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)",
status, OCSP_response_status_str(status));
return 0;
}
basic = OCSP_response_get1_basic(rsp);
if (!basic) {
wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
return 0;
}
store = SSL_CTX_get_cert_store(conn->ssl_ctx);
if (conn->peer_issuer) {
debug_print_cert(conn->peer_issuer, "Add OCSP issuer");
if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) {
tls_show_errors(MSG_INFO, __func__,
"OpenSSL: Could not add issuer to certificate store");
}
certs = sk_X509_new_null();
if (certs) {
X509 *cert;
cert = X509_dup(conn->peer_issuer);
if (cert && !sk_X509_push(certs, cert)) {
tls_show_errors(
MSG_INFO, __func__,
"OpenSSL: Could not add issuer to OCSP responder trust store");
X509_free(cert);
sk_X509_free(certs);
certs = NULL;
}
if (certs && conn->peer_issuer_issuer) {
cert = X509_dup(conn->peer_issuer_issuer);
if (cert && !sk_X509_push(certs, cert)) {
tls_show_errors(
MSG_INFO, __func__,
"OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
X509_free(cert);
}
}
}
}
status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER);
sk_X509_pop_free(certs, X509_free);
if (status <= 0) {
tls_show_errors(MSG_INFO, __func__,
"OpenSSL: OCSP response failed verification");
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
return 0;
}
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
if (!conn->peer_cert) {
wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check");
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
return 0;
}
if (!conn->peer_issuer) {
wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check");
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
return 0;
}
id = OCSP_cert_to_id(EVP_sha256(), conn->peer_cert, conn->peer_issuer);
if (!id) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Could not create OCSP certificate identifier (SHA256)");
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
return 0;
}
res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
&this_update, &next_update);
if (!res) {
OCSP_CERTID_free(id);
id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer);
if (!id) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Could not create OCSP certificate identifier (SHA1)");
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
return 0;
}
res = OCSP_resp_find_status(basic, id, &status, &reason,
&produced_at, &this_update,
&next_update);
}
if (!res) {
wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
(conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" :
" (OCSP not required)");
OCSP_CERTID_free(id);
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
}
OCSP_CERTID_free(id);
if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
tls_show_errors(MSG_INFO, __func__,
"OpenSSL: OCSP status times invalid");
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
return 0;
}
OCSP_BASICRESP_free(basic);
OCSP_RESPONSE_free(rsp);
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s",
OCSP_cert_status_str(status));
if (status == V_OCSP_CERTSTATUS_GOOD)
return 1;
if (status == V_OCSP_CERTSTATUS_REVOKED)
return 0;
if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
return 0;
}
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
return 1;
}
static int ocsp_status_cb(SSL *s, void *arg)
{
char *tmp;
char *resp;
size_t len;
if (tls_global->ocsp_stapling_response == NULL) {
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - no response configured");
return SSL_TLSEXT_ERR_OK;
}
resp = os_readfile(tls_global->ocsp_stapling_response, &len);
if (resp == NULL) {
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - could not read response file");
/* TODO: Build OCSPResponse with responseStatus = internalError
*/
return SSL_TLSEXT_ERR_OK;
}
wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status callback - send cached response");
tmp = OPENSSL_malloc(len);
if (tmp == NULL) {
os_free(resp);
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
os_memcpy(tmp, resp, len);
os_free(resp);
SSL_set_tlsext_status_ocsp_resp(s, tmp, len);
return SSL_TLSEXT_ERR_OK;
}
#endif /* HAVE_OCSP */
static size_t max_str_len(const char **lines)
{
const char **p;
size_t max_len = 0;
for (p = lines; *p; p++) {
size_t len = os_strlen(*p);
if (len > max_len)
max_len = len;
}
return max_len;
}
static int match_lines_in_file(const char *path, const char **lines)
{
FILE *f;
char *buf;
size_t bufsize;
int found = 0, is_linestart = 1;
bufsize = max_str_len(lines) + sizeof("\r\n");
buf = os_malloc(bufsize);
if (!buf)
return 0;
f = fopen(path, "r");
if (!f) {
os_free(buf);
return 0;
}
while (!found && fgets(buf, bufsize, f)) {
int is_lineend;
size_t len;
const char **p;
len = strcspn(buf, "\r\n");
is_lineend = buf[len] != '\0';
buf[len] = '\0';
if (is_linestart && is_lineend) {
for (p = lines; !found && *p; p++)
found = os_strcmp(buf, *p) == 0;
}
is_linestart = is_lineend;
}
fclose(f);
bin_clear_free(buf, bufsize);
return found;
}
static int is_tpm2_key(const char *path)
{
/* Check both new and old format of TPM2 PEM guard tag */
static const char *tpm2_tags[] = {
"-----BEGIN TSS2 PRIVATE KEY-----",
"-----BEGIN TSS2 KEY BLOB-----",
NULL
};
return match_lines_in_file(path, tpm2_tags);
}
int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
const struct tls_connection_params *params)
{
struct tls_data *data = tls_ctx;
int ret;
unsigned long err;
int can_pkcs11 = 0;
const char *key_id = params->key_id;
const char *cert_id = params->cert_id;
const char *ca_cert_id = params->ca_cert_id;
const char *engine_id = params->engine ? params->engine_id : NULL;
const char *ciphers;
if (conn == NULL)
return -1;
if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
wpa_printf(MSG_INFO,
"OpenSSL: ocsp=3 not supported");
return -1;
}
/*
* If the engine isn't explicitly configured, and any of the
* cert/key fields are actually PKCS#11 URIs, then automatically
* use the PKCS#11 ENGINE.
*/
if (!engine_id || os_strcmp(engine_id, "pkcs11") == 0)
can_pkcs11 = 1;
if (!key_id && params->private_key && can_pkcs11 &&
os_strncmp(params->private_key, "pkcs11:", 7) == 0) {
can_pkcs11 = 2;
key_id = params->private_key;
}
if (!cert_id && params->client_cert && can_pkcs11 &&
os_strncmp(params->client_cert, "pkcs11:", 7) == 0) {
can_pkcs11 = 2;
cert_id = params->client_cert;
}
if (!ca_cert_id && params->ca_cert && can_pkcs11 &&
os_strncmp(params->ca_cert, "pkcs11:", 7) == 0) {
can_pkcs11 = 2;
ca_cert_id = params->ca_cert;
}
/* If we need to automatically enable the PKCS#11 ENGINE, do so. */
if (can_pkcs11 == 2 && !engine_id)
engine_id = "pkcs11";
/* If private_key points to a TPM2-wrapped key, automatically enable
* tpm2 engine and use it to unwrap the key. */
if (params->private_key &&
(!engine_id || os_strcmp(engine_id, "tpm2") == 0) &&
is_tpm2_key(params->private_key)) {
wpa_printf(MSG_DEBUG, "OpenSSL: Found TPM2 wrapped key %s",
params->private_key);
key_id = key_id ? key_id : params->private_key;
engine_id = engine_id ? engine_id : "tpm2";
}
#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
if (params->flags & TLS_CONN_EAP_FAST) {
wpa_printf(MSG_DEBUG,
"OpenSSL: Use TLSv1_method() for EAP-FAST");
if (SSL_set_ssl_method(conn->ssl, TLSv1_method()) != 1) {
tls_show_errors(MSG_INFO, __func__,
"Failed to set TLSv1_method() for EAP-FAST");
return -1;
}
}
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
#ifdef SSL_OP_NO_TLSv1_3
if (params->flags & TLS_CONN_EAP_FAST) {
/* Need to disable TLS v1.3 at least for now since OpenSSL 1.1.1
* refuses to start the handshake with the modified ciphersuite
* list (no TLS v1.3 ciphersuites included) for EAP-FAST. */
wpa_printf(MSG_DEBUG, "OpenSSL: Disable TLSv1.3 for EAP-FAST");
SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_3);
}
#endif /* SSL_OP_NO_TLSv1_3 */
#endif
#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
while ((err = ERR_get_error())) {
wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
__func__, ERR_error_string(err, NULL));
}
if (engine_id) {
wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine %s",
engine_id);
ret = tls_engine_init(conn, engine_id, params->pin,
key_id, cert_id, ca_cert_id);
if (ret)
return ret;
}
if (tls_connection_set_subject_match(conn,
params->subject_match,
params->altsubject_match,
params->suffix_match,
params->domain_match,
params->check_cert_subject))
return -1;
if (engine_id && ca_cert_id) {
if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
} else if (tls_connection_ca_cert(data, conn, params->ca_cert,
params->ca_cert_blob,
params->ca_cert_blob_len,
params->ca_path))
return -1;
if (engine_id && cert_id) {
if (tls_connection_engine_client_cert(conn, cert_id))
return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
} else if (tls_connection_client_cert(conn, params->client_cert,
params->client_cert_blob,
params->client_cert_blob_len))
return -1;
if (engine_id && key_id) {
wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
if (tls_connection_engine_private_key(conn))
return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
} else if (tls_connection_private_key(data, conn,
params->private_key,
params->private_key_passwd,
params->private_key_blob,
params->private_key_blob_len)) {
wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'",
params->private_key);
return -1;
}
if (tls_connection_dh(conn, params->dh_file)) {
wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
params->dh_file);
return -1;
}
ciphers = params->openssl_ciphers;
#ifdef CONFIG_SUITEB
#ifdef OPENSSL_IS_BORINGSSL
if (ciphers && os_strcmp(ciphers, "SUITEB192") == 0) {
/* BoringSSL removed support for SUITEB192, so need to handle
* this with hardcoded ciphersuite and additional checks for
* other parameters. */
ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384";
}
#endif /* OPENSSL_IS_BORINGSSL */
#endif /* CONFIG_SUITEB */
if (ciphers && SSL_set_cipher_list(conn->ssl, ciphers) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set cipher string '%s'",
ciphers);
return -1;
}
if (!params->openssl_ecdh_curves) {
#ifndef OPENSSL_IS_BORINGSSL
#ifndef OPENSSL_NO_EC
#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \
(OPENSSL_VERSION_NUMBER < 0x10100000L)
if (SSL_set_ecdh_auto(conn->ssl, 1) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set ECDH curves to auto");
return -1;
}
#endif /* >= 1.0.2 && < 1.1.0 */
#endif /* OPENSSL_NO_EC */
#endif /* OPENSSL_IS_BORINGSSL */
} else if (params->openssl_ecdh_curves[0]) {
#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L)
wpa_printf(MSG_INFO,
"OpenSSL: ECDH configuration nnot supported");
return -1;
#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */
#ifndef OPENSSL_NO_EC
if (SSL_set1_curves_list(conn->ssl,
params->openssl_ecdh_curves) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set ECDH curves '%s'",
params->openssl_ecdh_curves);
return -1;
}
#else /* OPENSSL_NO_EC */
wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported");
return -1;
#endif /* OPENSSL_NO_EC */
#endif /* OPENSSL_IS_BORINGSSL */
}
if (tls_set_conn_flags(conn, params->flags,
params->openssl_ciphers) < 0)
return -1;
#ifdef OPENSSL_IS_BORINGSSL
if (params->flags & TLS_CONN_REQUEST_OCSP) {
SSL_enable_ocsp_stapling(conn->ssl);
}
#else /* OPENSSL_IS_BORINGSSL */
#ifdef HAVE_OCSP
if (params->flags & TLS_CONN_REQUEST_OCSP) {
SSL_CTX *ssl_ctx = data->ssl;
SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp);
SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb);
SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn);
}
#else /* HAVE_OCSP */
if (params->flags & TLS_CONN_REQUIRE_OCSP) {
wpa_printf(MSG_INFO,
"OpenSSL: No OCSP support included - reject configuration");
return -1;
}
if (params->flags & TLS_CONN_REQUEST_OCSP) {
wpa_printf(MSG_DEBUG,
"OpenSSL: No OCSP support included - allow optional OCSP case to continue");
}
#endif /* HAVE_OCSP */
#endif /* OPENSSL_IS_BORINGSSL */
conn->flags = params->flags;
tls_get_errors(data);
return 0;
}
static void openssl_debug_dump_cipher_list(SSL_CTX *ssl_ctx)
{
SSL *ssl;
int i;
ssl = SSL_new(ssl_ctx);
if (!ssl)
return;
wpa_printf(MSG_DEBUG,
"OpenSSL: Enabled cipher suites in priority order");
for (i = 0; ; i++) {
const char *cipher;
cipher = SSL_get_cipher_list(ssl, i);
if (!cipher)
break;
wpa_printf(MSG_DEBUG, "Cipher %d: %s", i, cipher);
}
SSL_free(ssl);
}
#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(BORINGSSL_API_VERSION)
static const char * openssl_pkey_type_str(const EVP_PKEY *pkey)
{
if (!pkey)
return "NULL";
switch (EVP_PKEY_type(EVP_PKEY_id(pkey))) {
case EVP_PKEY_RSA:
return "RSA";
case EVP_PKEY_DSA:
return "DSA";
case EVP_PKEY_DH:
return "DH";
case EVP_PKEY_EC:
return "EC";
}
return "?";
}
static void openssl_debug_dump_certificate(int i, X509 *cert)
{
char buf[256];
EVP_PKEY *pkey;
ASN1_INTEGER *ser;
char serial_num[128];
if (!cert)
return;
X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
ser = X509_get_serialNumber(cert);
if (ser)
wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num),
ASN1_STRING_get0_data(ser),
ASN1_STRING_length(ser));
else
serial_num[0] = '\0';
pkey = X509_get_pubkey(cert);
wpa_printf(MSG_DEBUG, "%d: %s (%s) %s", i, buf,
openssl_pkey_type_str(pkey), serial_num);
EVP_PKEY_free(pkey);
}
static void openssl_debug_dump_certificates(SSL_CTX *ssl_ctx)
{
STACK_OF(X509) *certs;
wpa_printf(MSG_DEBUG, "OpenSSL: Configured certificate chain");
if (SSL_CTX_get0_chain_certs(ssl_ctx, &certs) == 1) {
int i;
for (i = sk_X509_num(certs); i > 0; i--)
openssl_debug_dump_certificate(i, sk_X509_value(certs,
i - 1));
}
openssl_debug_dump_certificate(0, SSL_CTX_get0_certificate(ssl_ctx));
}
#endif
static void openssl_debug_dump_certificate_chains(SSL_CTX *ssl_ctx)
{
#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(BORINGSSL_API_VERSION)
int res;
for (res = SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_FIRST);
res == 1;
res = SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_NEXT))
openssl_debug_dump_certificates(ssl_ctx);
SSL_CTX_set_current_cert(ssl_ctx, SSL_CERT_SET_FIRST);
#endif
}
static void openssl_debug_dump_ctx(SSL_CTX *ssl_ctx)
{
openssl_debug_dump_cipher_list(ssl_ctx);
openssl_debug_dump_certificate_chains(ssl_ctx);
}
int tls_global_set_params(void *tls_ctx,
const struct tls_connection_params *params)
{
struct tls_data *data = tls_ctx;
SSL_CTX *ssl_ctx = data->ssl;
unsigned long err;
while ((err = ERR_get_error())) {
wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
__func__, ERR_error_string(err, NULL));
}
os_free(data->check_cert_subject);
data->check_cert_subject = NULL;
if (params->check_cert_subject) {
data->check_cert_subject =
os_strdup(params->check_cert_subject);
if (!data->check_cert_subject)
return -1;
}
if (tls_global_ca_cert(data, params->ca_cert) ||
tls_global_client_cert(data, params->client_cert) ||
tls_global_private_key(data, params->private_key,
params->private_key_passwd) ||
tls_global_client_cert(data, params->client_cert2) ||
tls_global_private_key(data, params->private_key2,
params->private_key_passwd2) ||
tls_global_dh(data, params->dh_file)) {
wpa_printf(MSG_INFO, "TLS: Failed to set global parameters");
return -1;
}
if (params->openssl_ciphers &&
SSL_CTX_set_cipher_list(ssl_ctx, params->openssl_ciphers) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set cipher string '%s'",
params->openssl_ciphers);
return -1;
}
if (!params->openssl_ecdh_curves) {
#ifndef OPENSSL_IS_BORINGSSL
#ifndef OPENSSL_NO_EC
#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \
(OPENSSL_VERSION_NUMBER < 0x10100000L)
if (SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set ECDH curves to auto");
return -1;
}
#endif /* >= 1.0.2 && < 1.1.0 */
#endif /* OPENSSL_NO_EC */
#endif /* OPENSSL_IS_BORINGSSL */
} else if (params->openssl_ecdh_curves[0]) {
#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L)
wpa_printf(MSG_INFO,
"OpenSSL: ECDH configuration nnot supported");
return -1;
#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */
#ifndef OPENSSL_NO_EC
#if OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
#endif
if (SSL_CTX_set1_curves_list(ssl_ctx,
params->openssl_ecdh_curves) !=
1) {
wpa_printf(MSG_INFO,
"OpenSSL: Failed to set ECDH curves '%s'",
params->openssl_ecdh_curves);
return -1;
}
#else /* OPENSSL_NO_EC */
wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported");
return -1;
#endif /* OPENSSL_NO_EC */
#endif /* OPENSSL_IS_BORINGSSL */
}
#ifdef SSL_OP_NO_TICKET
if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
else
SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
#endif /* SSL_OP_NO_TICKET */
#ifdef HAVE_OCSP
SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_status_cb);
SSL_CTX_set_tlsext_status_arg(ssl_ctx, ssl_ctx);
os_free(tls_global->ocsp_stapling_response);
if (params->ocsp_stapling_response)
tls_global->ocsp_stapling_response =
os_strdup(params->ocsp_stapling_response);
else
tls_global->ocsp_stapling_response = NULL;
#endif /* HAVE_OCSP */
openssl_debug_dump_ctx(ssl_ctx);
return 0;
}
#ifdef EAP_FAST_OR_TEAP
/* Pre-shared secred requires a patch to openssl, so this function is
* commented out unless explicitly needed for EAP-FAST in order to be able to
* build this file with unmodified openssl. */
#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
STACK_OF(SSL_CIPHER) *peer_ciphers,
const SSL_CIPHER **cipher, void *arg)
#else /* OPENSSL_IS_BORINGSSL */
static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
STACK_OF(SSL_CIPHER) *peer_ciphers,
SSL_CIPHER **cipher, void *arg)
#endif /* OPENSSL_IS_BORINGSSL */
{
struct tls_connection *conn = arg;
int ret;
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER < 0x20700000L)
if (conn == NULL || conn->session_ticket_cb == NULL)
return 0;
ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
conn->session_ticket,
conn->session_ticket_len,
s->s3->client_random,
s->s3->server_random, secret);
#else
unsigned char client_random[SSL3_RANDOM_SIZE];
unsigned char server_random[SSL3_RANDOM_SIZE];
if (conn == NULL || conn->session_ticket_cb == NULL)
return 0;
SSL_get_client_random(s, client_random, sizeof(client_random));
SSL_get_server_random(s, server_random, sizeof(server_random));
ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
conn->session_ticket,
conn->session_ticket_len,
client_random,
server_random, secret);
#endif
os_free(conn->session_ticket);
conn->session_ticket = NULL;
if (ret <= 0)
return 0;
*secret_len = SSL_MAX_MASTER_KEY_LENGTH;
return 1;
}
static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
int len, void *arg)
{
struct tls_connection *conn = arg;
if (conn == NULL || conn->session_ticket_cb == NULL)
return 0;
wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len);
os_free(conn->session_ticket);
conn->session_ticket = NULL;
wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
"extension", data, len);
conn->session_ticket = os_memdup(data, len);
if (conn->session_ticket == NULL)
return 0;
conn->session_ticket_len = len;
return 1;
}
#endif /* EAP_FAST_OR_TEAP */
int tls_connection_set_session_ticket_cb(void *tls_ctx,
struct tls_connection *conn,
tls_session_ticket_cb cb,
void *ctx)
{
#ifdef EAP_FAST_OR_TEAP
conn->session_ticket_cb = cb;
conn->session_ticket_cb_ctx = ctx;
if (cb) {
if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
conn) != 1)
return -1;
SSL_set_session_ticket_ext_cb(conn->ssl,
tls_session_ticket_ext_cb, conn);
} else {
if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
return -1;
SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL);
}
return 0;
#else /* EAP_FAST_OR_TEAP */
return -1;
#endif /* EAP_FAST_OR_TEAP */
}
int tls_get_library_version(char *buf, size_t buf_len)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
OPENSSL_VERSION_TEXT,
OpenSSL_version(OPENSSL_VERSION));
#else
return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
OPENSSL_VERSION_TEXT,
SSLeay_version(SSLEAY_VERSION));
#endif
}
void tls_connection_set_success_data(struct tls_connection *conn,
struct wpabuf *data)
{
SSL_SESSION *sess;
struct wpabuf *old;
if (tls_ex_idx_session < 0)
goto fail;
sess = SSL_get_session(conn->ssl);
if (!sess)
goto fail;
old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
if (old) {
wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p",
old);
wpabuf_free(old);
}
if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
goto fail;
wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data);
conn->success_data = 1;
return;
fail:
wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data");
wpabuf_free(data);
}
void tls_connection_set_success_data_resumed(struct tls_connection *conn)
{
wpa_printf(MSG_DEBUG,
"OpenSSL: Success data accepted for resumed session");
conn->success_data = 1;
}
const struct wpabuf *
tls_connection_get_success_data(struct tls_connection *conn)
{
SSL_SESSION *sess;
if (tls_ex_idx_session < 0 ||
!(sess = SSL_get_session(conn->ssl)))
return NULL;
return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
}
void tls_connection_remove_session(struct tls_connection *conn)
{
SSL_SESSION *sess;
sess = SSL_get_session(conn->ssl);
if (!sess)
return;
if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1)
wpa_printf(MSG_DEBUG,
"OpenSSL: Session was not cached");
else
wpa_printf(MSG_DEBUG,
"OpenSSL: Removed cached session to disable session resumption");
}
int tls_get_tls_unique(struct tls_connection *conn, u8 *buf, size_t max_len)
{
size_t len;
int reused;
reused = SSL_session_reused(conn->ssl);
if ((conn->server && !reused) || (!conn->server && reused))
len = SSL_get_peer_finished(conn->ssl, buf, max_len);
else
len = SSL_get_finished(conn->ssl, buf, max_len);
if (len == 0 || len > max_len)
return -1;
return len;
}
u16 tls_connection_get_cipher_suite(struct tls_connection *conn)
{
const SSL_CIPHER *cipher;
cipher = SSL_get_current_cipher(conn->ssl);
if (!cipher)
return 0;
#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
return SSL_CIPHER_get_protocol_id(cipher);
#else
return SSL_CIPHER_get_id(cipher) & 0xFFFF;
#endif
}
const char * tls_connection_get_peer_subject(struct tls_connection *conn)
{
if (conn)
return conn->peer_subject;
return NULL;
}
bool tls_connection_get_own_cert_used(struct tls_connection *conn)
{
if (conn)
return SSL_get_certificate(conn->ssl) != NULL;
return false;
}
diff --git a/contrib/wpa/src/drivers/driver.h b/contrib/wpa/src/drivers/driver.h
index 804ac6806f61..2020184c5f94 100644
--- a/contrib/wpa/src/drivers/driver.h
+++ b/contrib/wpa/src/drivers/driver.h
@@ -1,6154 +1,6156 @@
/*
* Driver interface definition
* Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This file defines a driver interface used by both %wpa_supplicant and
* hostapd. The first part of the file defines data structures used in various
* driver operations. This is followed by the struct wpa_driver_ops that each
* driver wrapper will beed to define with callback functions for requesting
* driver operations. After this, there are definitions for driver event
* reporting with wpa_supplicant_event() and some convenience helper functions
* that can be used to report events.
*/
#ifndef DRIVER_H
#define DRIVER_H
#define WPA_SUPPLICANT_DRIVER_VERSION 4
#include "common/defs.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_common.h"
#ifdef CONFIG_MACSEC
#include "pae/ieee802_1x_kay.h"
#endif /* CONFIG_MACSEC */
#include "utils/list.h"
#define HOSTAPD_CHAN_DISABLED 0x00000001
#define HOSTAPD_CHAN_NO_IR 0x00000002
#define HOSTAPD_CHAN_RADAR 0x00000008
#define HOSTAPD_CHAN_HT40PLUS 0x00000010
#define HOSTAPD_CHAN_HT40MINUS 0x00000020
#define HOSTAPD_CHAN_HT40 0x00000040
#define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080
#define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000
#define HOSTAPD_CHAN_DFS_USABLE 0x00000100
#define HOSTAPD_CHAN_DFS_UNAVAILABLE 0x00000200
#define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300
#define HOSTAPD_CHAN_DFS_MASK 0x00000300
#define HOSTAPD_CHAN_VHT_10_70 0x00000800
#define HOSTAPD_CHAN_VHT_30_50 0x00001000
#define HOSTAPD_CHAN_VHT_50_30 0x00002000
#define HOSTAPD_CHAN_VHT_70_10 0x00004000
#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
#define HOSTAPD_CHAN_VHT_10_150 0x00100000
#define HOSTAPD_CHAN_VHT_30_130 0x00200000
#define HOSTAPD_CHAN_VHT_50_110 0x00400000
#define HOSTAPD_CHAN_VHT_70_90 0x00800000
#define HOSTAPD_CHAN_VHT_90_70 0x01000000
#define HOSTAPD_CHAN_VHT_110_50 0x02000000
#define HOSTAPD_CHAN_VHT_130_30 0x04000000
#define HOSTAPD_CHAN_VHT_150_10 0x08000000
/* Allowed bandwidth mask */
enum hostapd_chan_width_attr {
HOSTAPD_CHAN_WIDTH_10 = BIT(0),
HOSTAPD_CHAN_WIDTH_20 = BIT(1),
HOSTAPD_CHAN_WIDTH_40P = BIT(2),
HOSTAPD_CHAN_WIDTH_40M = BIT(3),
HOSTAPD_CHAN_WIDTH_80 = BIT(4),
HOSTAPD_CHAN_WIDTH_160 = BIT(5),
};
/* Filter gratuitous ARP */
#define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0)
/* Filter unsolicited Neighbor Advertisement */
#define WPA_DATA_FRAME_FILTER_FLAG_NA BIT(1)
/* Filter unicast IP packets encrypted using the GTK */
#define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2)
#define HOSTAPD_DFS_REGION_FCC 1
#define HOSTAPD_DFS_REGION_ETSI 2
#define HOSTAPD_DFS_REGION_JP 3
/**
* enum reg_change_initiator - Regulatory change initiator
*/
enum reg_change_initiator {
REGDOM_SET_BY_CORE,
REGDOM_SET_BY_USER,
REGDOM_SET_BY_DRIVER,
REGDOM_SET_BY_COUNTRY_IE,
REGDOM_BEACON_HINT,
};
/**
* enum reg_type - Regulatory change types
*/
enum reg_type {
REGDOM_TYPE_UNKNOWN,
REGDOM_TYPE_COUNTRY,
REGDOM_TYPE_WORLD,
REGDOM_TYPE_CUSTOM_WORLD,
REGDOM_TYPE_INTERSECTION,
};
/**
* struct hostapd_wmm_rule - WMM regulatory rule
* @min_cwmin: Lower bound of CW_min value
* @min_cwmax: Lower bound of CW_max value
* @min_aifs: Lower bound of AIFS value
* @max_txop: Upper bound of TXOP, value in units of 32 usec
*/
struct hostapd_wmm_rule {
int min_cwmin;
int min_cwmax;
int min_aifs;
int max_txop;
};
/**
* struct hostapd_channel_data - Channel information
*/
struct hostapd_channel_data {
/**
* chan - Channel number (IEEE 802.11)
*/
short chan;
/**
* freq - Frequency in MHz
*/
int freq;
/**
* flag - Channel flags (HOSTAPD_CHAN_*)
*/
int flag;
/**
* allowed_bw - Allowed channel width bitmask
*
* See enum hostapd_chan_width_attr.
*/
u32 allowed_bw;
/**
* max_tx_power - Regulatory transmit power limit in dBm
*/
u8 max_tx_power;
/**
* survey_list - Linked list of surveys (struct freq_survey)
*/
struct dl_list survey_list;
/**
* min_nf - Minimum observed noise floor, in dBm, based on all
* surveyed channel data
*/
s8 min_nf;
#ifdef CONFIG_ACS
/**
* interference_factor - Computed interference factor on this
* channel (used internally in src/ap/acs.c; driver wrappers do not
* need to set this)
*/
long double interference_factor;
#endif /* CONFIG_ACS */
/**
* dfs_cac_ms - DFS CAC time in milliseconds
*/
unsigned int dfs_cac_ms;
/**
* wmm_rules_valid - Indicates wmm_rules state
*/
int wmm_rules_valid;
/**
* wmm_rules - WMM regulatory rules
*/
struct hostapd_wmm_rule wmm_rules[WMM_AC_NUM];
};
#define HE_MAC_CAPAB_0 0
#define HE_MAX_MAC_CAPAB_SIZE 6
#define HE_MAX_PHY_CAPAB_SIZE 11
#define HE_MAX_MCS_CAPAB_SIZE 12
#define HE_MAX_PPET_CAPAB_SIZE 25
/**
* struct he_capabilities - IEEE 802.11ax HE capabilities
*/
struct he_capabilities {
u8 he_supported;
u8 phy_cap[HE_MAX_PHY_CAPAB_SIZE];
u8 mac_cap[HE_MAX_MAC_CAPAB_SIZE];
u8 mcs[HE_MAX_MCS_CAPAB_SIZE];
u8 ppet[HE_MAX_PPET_CAPAB_SIZE];
u16 he_6ghz_capa;
};
#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
enum ieee80211_op_mode {
IEEE80211_MODE_INFRA = 0,
IEEE80211_MODE_IBSS = 1,
IEEE80211_MODE_AP = 2,
IEEE80211_MODE_MESH = 5,
/* only add new entries before IEEE80211_MODE_NUM */
IEEE80211_MODE_NUM
};
/**
* struct ieee80211_edmg_config - EDMG configuration
*
* This structure describes most essential parameters needed
* for IEEE 802.11ay EDMG configuration
*
* @channels: Bitmap that indicates the 2.16 GHz channel(s)
* that are allowed to be used for transmissions.
* Bit 0 indicates channel 1, bit 1 indicates channel 2, etc.
* Set to 0 to indicate EDMG not supported.
* @bw_config: Channel BW Configuration subfield encodes
* the allowed channel bandwidth configurations
*/
struct ieee80211_edmg_config {
u8 channels;
enum edmg_bw_config bw_config;
};
/**
* struct hostapd_hw_modes - Supported hardware mode information
*/
struct hostapd_hw_modes {
/**
* mode - Hardware mode
*/
enum hostapd_hw_mode mode;
/**
* num_channels - Number of entries in the channels array
*/
int num_channels;
/**
* channels - Array of supported channels
*/
struct hostapd_channel_data *channels;
/**
* num_rates - Number of entries in the rates array
*/
int num_rates;
/**
* rates - Array of supported rates in 100 kbps units
*/
int *rates;
/**
* ht_capab - HT (IEEE 802.11n) capabilities
*/
u16 ht_capab;
/**
* mcs_set - MCS (IEEE 802.11n) rate parameters
*/
u8 mcs_set[16];
/**
* a_mpdu_params - A-MPDU (IEEE 802.11n) parameters
*/
u8 a_mpdu_params;
/**
* vht_capab - VHT (IEEE 802.11ac) capabilities
*/
u32 vht_capab;
/**
* vht_mcs_set - VHT MCS (IEEE 802.11ac) rate parameters
*/
u8 vht_mcs_set[8];
unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
/**
* he_capab - HE (IEEE 802.11ax) capabilities
*/
struct he_capabilities he_capab[IEEE80211_MODE_NUM];
/**
* This structure describes the most essential parameters needed
* for IEEE 802.11ay EDMG configuration.
*/
struct ieee80211_edmg_config edmg;
};
#define IEEE80211_CAP_ESS 0x0001
#define IEEE80211_CAP_IBSS 0x0002
#define IEEE80211_CAP_PRIVACY 0x0010
#define IEEE80211_CAP_RRM 0x1000
/* DMG (60 GHz) IEEE 802.11ad */
/* type - bits 0..1 */
#define IEEE80211_CAP_DMG_MASK 0x0003
#define IEEE80211_CAP_DMG_IBSS 0x0001 /* Tx by: STA */
#define IEEE80211_CAP_DMG_PBSS 0x0002 /* Tx by: PCP */
#define IEEE80211_CAP_DMG_AP 0x0003 /* Tx by: AP */
#define WPA_SCAN_QUAL_INVALID BIT(0)
#define WPA_SCAN_NOISE_INVALID BIT(1)
#define WPA_SCAN_LEVEL_INVALID BIT(2)
#define WPA_SCAN_LEVEL_DBM BIT(3)
#define WPA_SCAN_ASSOCIATED BIT(5)
/**
* struct wpa_scan_res - Scan result for an BSS/IBSS
* @flags: information flags about the BSS/IBSS (WPA_SCAN_*)
* @bssid: BSSID
* @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
* @beacon_int: beacon interval in TUs (host byte order)
* @caps: capability information field in host byte order
* @qual: signal quality
* @noise: noise level
* @level: signal level
* @tsf: Timestamp
* @age: Age of the information in milliseconds (i.e., how many milliseconds
* ago the last Beacon or Probe Response frame was received)
* @est_throughput: Estimated throughput in kbps (this is calculated during
* scan result processing if left zero by the driver wrapper)
* @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
* @parent_tsf: Time when the Beacon/Probe Response frame was received in terms
* of TSF of the BSS specified by %tsf_bssid.
* @tsf_bssid: The BSS that %parent_tsf TSF time refers to.
* @ie_len: length of the following IE field in octets
* @beacon_ie_len: length of the following Beacon IE field in octets
*
* This structure is used as a generic format for scan results from the
* driver. Each driver interface implementation is responsible for converting
* the driver or OS specific scan results into this format.
*
* If the driver does not support reporting all IEs, the IE data structure is
* constructed of the IEs that are available. This field will also need to
* include SSID in IE format. All drivers are encouraged to be extended to
* report all IEs to make it easier to support future additions.
*
* This structure data is followed by ie_len octets of IEs from Probe Response
* frame (or if the driver does not indicate source of IEs, these may also be
* from Beacon frame). After the first set of IEs, another set of IEs may follow
* (with beacon_ie_len octets of data) if the driver provides both IE sets.
*/
struct wpa_scan_res {
unsigned int flags;
u8 bssid[ETH_ALEN];
int freq;
u16 beacon_int;
u16 caps;
int qual;
int noise;
int level;
u64 tsf;
unsigned int age;
unsigned int est_throughput;
int snr;
u64 parent_tsf;
u8 tsf_bssid[ETH_ALEN];
size_t ie_len;
size_t beacon_ie_len;
/* Followed by ie_len + beacon_ie_len octets of IE data */
};
/**
* struct wpa_scan_results - Scan results
* @res: Array of pointers to allocated variable length scan result entries
* @num: Number of entries in the scan result array
* @fetch_time: Time when the results were fetched from the driver
*/
struct wpa_scan_results {
struct wpa_scan_res **res;
size_t num;
struct os_reltime fetch_time;
};
/**
* struct wpa_interface_info - Network interface information
* @next: Pointer to the next interface or NULL if this is the last one
* @ifname: Interface name that can be used with init() or init2()
* @desc: Human readable adapter description (e.g., vendor/model) or NULL if
* not available
* @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one
* is not an allocated copy, i.e., get_interfaces() caller will not free
* this)
*/
struct wpa_interface_info {
struct wpa_interface_info *next;
char *ifname;
char *desc;
const char *drv_name;
};
#define WPAS_MAX_SCAN_SSIDS 16
/**
* struct wpa_driver_scan_ssid - SSIDs to scan for
* @ssid - specific SSID to scan for (ProbeReq)
* %NULL or zero-length SSID is used to indicate active scan
* with wildcard SSID.
* @ssid_len - Length of the SSID in octets
*/
struct wpa_driver_scan_ssid {
const u8 *ssid;
size_t ssid_len;
};
/**
* struct wpa_driver_scan_params - Scan parameters
* Data for struct wpa_driver_ops::scan2().
*/
struct wpa_driver_scan_params {
/**
* ssids - SSIDs to scan for
*/
struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
/**
* num_ssids - Number of entries in ssids array
* Zero indicates a request for a passive scan.
*/
size_t num_ssids;
/**
* extra_ies - Extra IE(s) to add into Probe Request or %NULL
*/
const u8 *extra_ies;
/**
* extra_ies_len - Length of extra_ies in octets
*/
size_t extra_ies_len;
/**
* freqs - Array of frequencies to scan or %NULL for all frequencies
*
* The frequency is set in MHz. The array is zero-terminated.
*/
int *freqs;
/**
* filter_ssids - Filter for reporting SSIDs
*
* This optional parameter can be used to request the driver wrapper to
* filter scan results to include only the specified SSIDs. %NULL
* indicates that no filtering is to be done. This can be used to
* reduce memory needs for scan results in environments that have large
* number of APs with different SSIDs.
*
* The driver wrapper is allowed to take this allocated buffer into its
* own use by setting the pointer to %NULL. In that case, the driver
* wrapper is responsible for freeing the buffer with os_free() once it
* is not needed anymore.
*/
struct wpa_driver_scan_filter {
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
} *filter_ssids;
/**
* num_filter_ssids - Number of entries in filter_ssids array
*/
size_t num_filter_ssids;
/**
* filter_rssi - Filter by RSSI
*
* The driver may filter scan results in firmware to reduce host
* wakeups and thereby save power. Specify the RSSI threshold in s32
* dBm.
*/
s32 filter_rssi;
/**
* p2p_probe - Used to disable CCK (802.11b) rates for P2P probes
*
* When set, the driver is expected to remove rates 1, 2, 5.5, and 11
* Mbps from the support rates element(s) in the Probe Request frames
* and not to transmit the frames at any of those rates.
*/
unsigned int p2p_probe:1;
/**
* only_new_results - Request driver to report only new results
*
* This is used to request the driver to report only BSSes that have
* been detected after this scan request has been started, i.e., to
* flush old cached BSS entries.
*/
unsigned int only_new_results:1;
/**
* low_priority - Requests driver to use a lower scan priority
*
* This is used to request the driver to use a lower scan priority
* if it supports such a thing.
*/
unsigned int low_priority:1;
/**
* mac_addr_rand - Requests driver to randomize MAC address
*/
unsigned int mac_addr_rand:1;
/**
* mac_addr - MAC address used with randomization. The address cannot be
* a multicast one, i.e., bit 0 of byte 0 should not be set.
*/
u8 *mac_addr;
/**
* mac_addr_mask - MAC address mask used with randomization.
*
* Bits that are 0 in the mask should be randomized. Bits that are 1 in
* the mask should be taken as is from mac_addr. The mask should not
* allow the generation of a multicast address, i.e., bit 0 of byte 0
* must be set.
*/
const u8 *mac_addr_mask;
/**
* sched_scan_plans - Scan plans for scheduled scan
*
* Each scan plan consists of the number of iterations to scan and the
* interval between scans. When a scan plan finishes (i.e., it was run
* for the specified number of iterations), the next scan plan is
* executed. The scan plans are executed in the order they appear in
* the array (lower index first). The last scan plan will run infinitely
* (until requested to stop), thus must not specify the number of
* iterations. All other scan plans must specify the number of
* iterations.
*/
struct sched_scan_plan {
u32 interval; /* In seconds */
u32 iterations; /* Zero to run infinitely */
} *sched_scan_plans;
/**
* sched_scan_plans_num - Number of scan plans in sched_scan_plans array
*/
unsigned int sched_scan_plans_num;
/**
* sched_scan_start_delay - Delay to use before starting the first scan
*
* Delay (in seconds) before scheduling first scan plan cycle. The
* driver may ignore this parameter and start immediately (or at any
* other time), if this feature is not supported.
*/
u32 sched_scan_start_delay;
/**
* bssid - Specific BSSID to scan for
*
* This optional parameter can be used to replace the default wildcard
* BSSID with a specific BSSID to scan for if results are needed from
* only a single BSS.
*/
const u8 *bssid;
/**
* scan_cookie - Unique identification representing the scan request
*
* This scan_cookie carries a unique identification representing the
* scan request if the host driver/kernel supports concurrent scan
* requests. This cookie is returned from the corresponding driver
* interface.
*
* Note: Unlike other parameters in this structure, scan_cookie is used
* only to return information instead of setting parameters for the
* scan.
*/
u64 scan_cookie;
/**
* duration - Dwell time on each channel
*
* This optional parameter can be used to set the dwell time on each
* channel. In TUs.
*/
u16 duration;
/**
* duration_mandatory - Whether the specified duration is mandatory
*
* If this is set, the duration specified by the %duration field is
* mandatory (and the driver should reject the scan request if it is
* unable to comply with the specified duration), otherwise it is the
* maximum duration and the actual duration may be shorter.
*/
unsigned int duration_mandatory:1;
/**
* relative_rssi_set - Whether relative RSSI parameters are set
*/
unsigned int relative_rssi_set:1;
/**
* relative_rssi - Relative RSSI for reporting better BSSs
*
* Amount of RSSI by which a BSS should be better than the current
* connected BSS to report the new BSS to user space.
*/
s8 relative_rssi;
/**
* relative_adjust_band - Band to which RSSI should be adjusted
*
* The relative_adjust_rssi should be added to the band specified
* by relative_adjust_band.
*/
enum set_band relative_adjust_band;
/**
* relative_adjust_rssi - RSSI to be added to relative_adjust_band
*
* An amount of relative_band_rssi should be added to the BSSs that
* belong to the band specified by relative_adjust_band while comparing
* with other bands for BSS reporting.
*/
s8 relative_adjust_rssi;
/**
* oce_scan
*
* Enable the following OCE scan features: (WFA OCE TechSpec v1.0)
* - Accept broadcast Probe Response frame.
* - Probe Request frame deferral and suppression.
* - Max Channel Time - driver fills FILS request params IE with
* Maximum Channel Time.
* - Send 1st Probe Request frame in rate of minimum 5.5 Mbps.
*/
unsigned int oce_scan:1;
/**
* p2p_include_6ghz - Include 6 GHz channels for P2P full scan
*
*/
unsigned int p2p_include_6ghz:1;
/*
* NOTE: Whenever adding new parameters here, please make sure
* wpa_scan_clone_params() and wpa_scan_free_params() get updated with
* matching changes.
*/
};
/**
* struct wpa_driver_auth_params - Authentication parameters
* Data for struct wpa_driver_ops::authenticate().
*/
struct wpa_driver_auth_params {
int freq;
const u8 *bssid;
const u8 *ssid;
size_t ssid_len;
int auth_alg;
const u8 *ie;
size_t ie_len;
const u8 *wep_key[4];
size_t wep_key_len[4];
int wep_tx_keyidx;
int local_state_change;
/**
* p2p - Whether this connection is a P2P group
*/
int p2p;
/**
* auth_data - Additional elements for Authentication frame
*
* This buffer starts with the Authentication transaction sequence
* number field. If no special handling of such elements is needed, this
* pointer is %NULL. This is used with SAE and FILS.
*/
const u8 *auth_data;
/**
* auth_data_len - Length of auth_data buffer in octets
*/
size_t auth_data_len;
};
/**
* enum wps_mode - WPS mode
*/
enum wps_mode {
/**
* WPS_MODE_NONE - No WPS provisioning being used
*/
WPS_MODE_NONE,
/**
* WPS_MODE_OPEN - WPS provisioning with AP that is in open mode
*/
WPS_MODE_OPEN,
/**
* WPS_MODE_PRIVACY - WPS provisioning with AP that is using protection
*/
WPS_MODE_PRIVACY
};
/**
* struct hostapd_freq_params - Channel parameters
*/
struct hostapd_freq_params {
/**
* mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..)
*/
enum hostapd_hw_mode mode;
/**
* freq - Primary channel center frequency in MHz
*/
int freq;
/**
* channel - Channel number
*/
int channel;
/**
* ht_enabled - Whether HT is enabled
*/
int ht_enabled;
/**
* sec_channel_offset - Secondary channel offset for HT40
*
* 0 = HT40 disabled,
* -1 = HT40 enabled, secondary channel below primary,
* 1 = HT40 enabled, secondary channel above primary
*/
int sec_channel_offset;
/**
* vht_enabled - Whether VHT is enabled
*/
int vht_enabled;
/**
* he_enabled - Whether HE is enabled
*/
int he_enabled;
/**
* center_freq1 - Segment 0 center frequency in MHz
*
* Valid for both HT and VHT.
*/
int center_freq1;
/**
* center_freq2 - Segment 1 center frequency in MHz
*
* Non-zero only for bandwidth 80 and an 80+80 channel
*/
int center_freq2;
/**
* bandwidth - Channel bandwidth in MHz (20, 40, 80, 160)
*/
int bandwidth;
/**
* This structure describes the most essential parameters needed
* for IEEE 802.11ay EDMG configuration.
*/
struct ieee80211_edmg_config edmg;
};
/**
* struct wpa_driver_sta_auth_params - Authentication parameters
* Data for struct wpa_driver_ops::sta_auth().
*/
struct wpa_driver_sta_auth_params {
/**
* own_addr - Source address and BSSID for authentication frame
*/
const u8 *own_addr;
/**
* addr - MAC address of the station to associate
*/
const u8 *addr;
/**
* seq - authentication sequence number
*/
u16 seq;
/**
* status - authentication response status code
*/
u16 status;
/**
* ie - authentication frame ie buffer
*/
const u8 *ie;
/**
* len - ie buffer length
*/
size_t len;
/**
* fils_auth - Indicates whether FILS authentication is being performed
*/
int fils_auth;
/**
* fils_anonce - ANonce (required for FILS)
*/
u8 fils_anonce[WPA_NONCE_LEN];
/**
* fils_snonce - SNonce (required for FILS)
*/
u8 fils_snonce[WPA_NONCE_LEN];
/**
* fils_kek - key for encryption (required for FILS)
*/
u8 fils_kek[WPA_KEK_MAX_LEN];
/**
* fils_kek_len - Length of the fils_kek in octets (required for FILS)
*/
size_t fils_kek_len;
};
/**
* struct wpa_driver_associate_params - Association parameters
* Data for struct wpa_driver_ops::associate().
*/
struct wpa_driver_associate_params {
/**
* bssid - BSSID of the selected AP
* This can be %NULL, if ap_scan=2 mode is used and the driver is
* responsible for selecting with which BSS to associate. */
const u8 *bssid;
/**
* bssid_hint - BSSID of a proposed AP
*
* This indicates which BSS has been found a suitable candidate for
* initial association for drivers that use driver/firmwate-based BSS
* selection. Unlike the @bssid parameter, @bssid_hint does not limit
* the driver from selecting other BSSes in the ESS.
*/
const u8 *bssid_hint;
/**
* ssid - The selected SSID
*/
const u8 *ssid;
/**
* ssid_len - Length of the SSID (1..32)
*/
size_t ssid_len;
/**
* freq - channel parameters
*/
struct hostapd_freq_params freq;
/**
* freq_hint - Frequency of the channel the proposed AP is using
*
* This provides a channel on which a suitable BSS has been found as a
* hint for the driver. Unlike the @freq parameter, @freq_hint does not
* limit the driver from selecting other channels for
* driver/firmware-based BSS selection.
*/
int freq_hint;
/**
* bg_scan_period - Background scan period in seconds, 0 to disable
* background scan, or -1 to indicate no change to default driver
* configuration
*/
int bg_scan_period;
/**
* beacon_int - Beacon interval for IBSS or 0 to use driver default
*/
int beacon_int;
/**
* wpa_ie - WPA information element for (Re)Association Request
* WPA information element to be included in (Re)Association
* Request (including information element id and length). Use
* of this WPA IE is optional. If the driver generates the WPA
* IE, it can use pairwise_suite, group_suite, group_mgmt_suite, and
* key_mgmt_suite to select proper algorithms. In this case,
* the driver has to notify wpa_supplicant about the used WPA
* IE by generating an event that the interface code will
* convert into EVENT_ASSOCINFO data (see below).
*
* When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE
* instead. The driver can determine which version is used by
* looking at the first byte of the IE (0xdd for WPA, 0x30 for
* WPA2/RSN).
*
* When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE.
*/
const u8 *wpa_ie;
/**
* wpa_ie_len - length of the wpa_ie
*/
size_t wpa_ie_len;
/**
* wpa_proto - Bitfield of WPA_PROTO_* values to indicate WPA/WPA2
*/
unsigned int wpa_proto;
/**
* pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*)
*
* This is usually ignored if @wpa_ie is used.
*/
unsigned int pairwise_suite;
/**
* group_suite - Selected group cipher suite (WPA_CIPHER_*)
*
* This is usually ignored if @wpa_ie is used.
*/
unsigned int group_suite;
/**
* mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*)
*
* This is usually ignored if @wpa_ie is used.
*/
unsigned int mgmt_group_suite;
/**
* key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
*
* This is usually ignored if @wpa_ie is used.
*/
unsigned int key_mgmt_suite;
/**
* auth_alg - Allowed authentication algorithms
* Bit field of WPA_AUTH_ALG_*
*/
int auth_alg;
/**
* mode - Operation mode (infra/ibss) IEEE80211_MODE_*
*/
int mode;
/**
* wep_key - WEP keys for static WEP configuration
*/
const u8 *wep_key[4];
/**
* wep_key_len - WEP key length for static WEP configuration
*/
size_t wep_key_len[4];
/**
* wep_tx_keyidx - WEP TX key index for static WEP configuration
*/
int wep_tx_keyidx;
/**
* mgmt_frame_protection - IEEE 802.11w management frame protection
*/
enum mfp_options mgmt_frame_protection;
/**
* passphrase - RSN passphrase for PSK
*
* This value is made available only for WPA/WPA2-Personal (PSK) and
* only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This
* is the 8..63 character ASCII passphrase, if available. Please note
* that this can be %NULL if passphrase was not used to generate the
* PSK. In that case, the psk field must be used to fetch the PSK.
*/
const char *passphrase;
/**
* psk - RSN PSK (alternative for passphrase for PSK)
*
* This value is made available only for WPA/WPA2-Personal (PSK) and
* only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This
* is the 32-octet (256-bit) PSK, if available. The driver wrapper
* should be prepared to handle %NULL value as an error.
*/
const u8 *psk;
/**
* drop_unencrypted - Enable/disable unencrypted frame filtering
*
* Configure the driver to drop all non-EAPOL frames (both receive and
* transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must
* still be allowed for key negotiation.
*/
int drop_unencrypted;
/**
* prev_bssid - Previously used BSSID in this ESS
*
* When not %NULL, this is a request to use reassociation instead of
* association.
*/
const u8 *prev_bssid;
/**
* wps - WPS mode
*
* If the driver needs to do special configuration for WPS association,
* this variable provides more information on what type of association
* is being requested. Most drivers should not need ot use this.
*/
enum wps_mode wps;
/**
* p2p - Whether this connection is a P2P group
*/
int p2p;
/**
* uapsd - UAPSD parameters for the network
* -1 = do not change defaults
* AP mode: 1 = enabled, 0 = disabled
* STA mode: bits 0..3 UAPSD enabled for VO,VI,BK,BE
*/
int uapsd;
/**
* fixed_bssid - Whether to force this BSSID in IBSS mode
* 1 = Fix this BSSID and prevent merges.
* 0 = Do not fix BSSID.
*/
int fixed_bssid;
/**
* fixed_freq - Fix control channel in IBSS mode
* 0 = don't fix control channel (default)
* 1 = fix control channel; this prevents IBSS merging with another
* channel
*/
int fixed_freq;
/**
* disable_ht - Disable HT (IEEE 802.11n) for this connection
*/
int disable_ht;
/**
* htcaps - HT Capabilities over-rides
*
* Only bits set in the mask will be used, and not all values are used
* by the kernel anyway. Currently, MCS, MPDU and MSDU fields are used.
*
* Pointer to struct ieee80211_ht_capabilities.
*/
const u8 *htcaps;
/**
* htcaps_mask - HT Capabilities over-rides mask
*
* Pointer to struct ieee80211_ht_capabilities.
*/
const u8 *htcaps_mask;
#ifdef CONFIG_VHT_OVERRIDES
/**
* disable_vht - Disable VHT for this connection
*/
int disable_vht;
/**
* VHT capability overrides.
*/
const struct ieee80211_vht_capabilities *vhtcaps;
const struct ieee80211_vht_capabilities *vhtcaps_mask;
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_HE_OVERRIDES
/**
* disable_he - Disable HE for this connection
*/
int disable_he;
#endif /* CONFIG_HE_OVERRIDES */
/**
* req_key_mgmt_offload - Request key management offload for connection
*
* Request key management offload for this connection if the device
* supports it.
*/
int req_key_mgmt_offload;
/**
* req_handshake_offload - Request EAPOL handshake offload
*
* Request EAPOL handshake offload for this connection if the device
* supports it.
*/
int req_handshake_offload;
/**
* Flag for indicating whether this association includes support for
* RRM (Radio Resource Measurements)
*/
int rrm_used;
/**
* pbss - If set, connect to a PCP in a PBSS. Otherwise, connect to an
* AP as usual. Valid for DMG network only.
*/
int pbss;
/**
* fils_kek - KEK for FILS association frame protection (AES-SIV)
*/
const u8 *fils_kek;
/**
* fils_kek_len: Length of fils_kek in bytes
*/
size_t fils_kek_len;
/**
* fils_nonces - Nonces for FILS association frame protection
* (AES-SIV AAD)
*/
const u8 *fils_nonces;
/**
* fils_nonces_len: Length of fils_nonce in bytes
*/
size_t fils_nonces_len;
/**
* fils_erp_username - Username part of keyName-NAI
*/
const u8 *fils_erp_username;
/**
* fils_erp_username_len - Length of fils_erp_username in bytes
*/
size_t fils_erp_username_len;
/**
* fils_erp_realm - Realm/domain name to use in FILS ERP
*/
const u8 *fils_erp_realm;
/**
* fils_erp_realm_len - Length of fils_erp_realm in bytes
*/
size_t fils_erp_realm_len;
/**
* fils_erp_next_seq_num - The next sequence number to use in FILS ERP
* messages
*/
u16 fils_erp_next_seq_num;
/**
* fils_erp_rrk - Re-authentication root key (rRK) for the keyName-NAI
* specified by fils_erp_username@fils_erp_realm.
*/
const u8 *fils_erp_rrk;
/**
* fils_erp_rrk_len - Length of fils_erp_rrk in bytes
*/
size_t fils_erp_rrk_len;
/**
* sae_pwe - SAE mechanism for PWE derivation
* 0 = hunting-and-pecking loop only
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
int sae_pwe;
};
enum hide_ssid {
NO_SSID_HIDING,
HIDDEN_SSID_ZERO_LEN,
HIDDEN_SSID_ZERO_CONTENTS
};
enum ch_switch_state {
CH_SW_STARTED,
CH_SW_FINISHED
};
struct wowlan_triggers {
u8 any;
u8 disconnect;
u8 magic_pkt;
u8 gtk_rekey_failure;
u8 eap_identity_req;
u8 four_way_handshake;
u8 rfkill_release;
};
struct wpa_driver_ap_params {
/**
* head - Beacon head from IEEE 802.11 header to IEs before TIM IE
*/
u8 *head;
/**
* head_len - Length of the head buffer in octets
*/
size_t head_len;
/**
* tail - Beacon tail following TIM IE
*/
u8 *tail;
/**
* tail_len - Length of the tail buffer in octets
*/
size_t tail_len;
/**
* dtim_period - DTIM period
*/
int dtim_period;
/**
* beacon_int - Beacon interval
*/
int beacon_int;
/**
* basic_rates: -1 terminated array of basic rates in 100 kbps
*
* This parameter can be used to set a specific basic rate set for the
* BSS. If %NULL, default basic rate set is used.
*/
int *basic_rates;
/**
* beacon_rate: Beacon frame data rate
*
* This parameter can be used to set a specific Beacon frame data rate
* for the BSS. The interpretation of this value depends on the
* rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS,
* HE: HE-MCS). If beacon_rate == 0 and rate_type == 0
* (BEACON_RATE_LEGACY), the default Beacon frame data rate is used.
*/
unsigned int beacon_rate;
/**
* beacon_rate_type: Beacon data rate type (legacy/HT/VHT/HE)
*/
enum beacon_rate_type rate_type;
/**
* proberesp - Probe Response template
*
* This is used by drivers that reply to Probe Requests internally in
* AP mode and require the full Probe Response template.
*/
u8 *proberesp;
/**
* proberesp_len - Length of the proberesp buffer in octets
*/
size_t proberesp_len;
/**
* ssid - The SSID to use in Beacon/Probe Response frames
*/
const u8 *ssid;
/**
* ssid_len - Length of the SSID (1..32)
*/
size_t ssid_len;
/**
* hide_ssid - Whether to hide the SSID
*/
enum hide_ssid hide_ssid;
/**
* pairwise_ciphers - WPA_CIPHER_* bitfield
*/
unsigned int pairwise_ciphers;
/**
* group_cipher - WPA_CIPHER_*
*/
unsigned int group_cipher;
/**
* key_mgmt_suites - WPA_KEY_MGMT_* bitfield
*/
unsigned int key_mgmt_suites;
/**
* auth_algs - WPA_AUTH_ALG_* bitfield
*/
unsigned int auth_algs;
/**
* wpa_version - WPA_PROTO_* bitfield
*/
unsigned int wpa_version;
/**
* privacy - Whether privacy is used in the BSS
*/
int privacy;
/**
* beacon_ies - WPS/P2P IE(s) for Beacon frames
*
* This is used to add IEs like WPS IE and P2P IE by drivers that do
* not use the full Beacon template.
*/
const struct wpabuf *beacon_ies;
/**
* proberesp_ies - P2P/WPS IE(s) for Probe Response frames
*
* This is used to add IEs like WPS IE and P2P IE by drivers that
* reply to Probe Request frames internally.
*/
const struct wpabuf *proberesp_ies;
/**
* assocresp_ies - WPS IE(s) for (Re)Association Response frames
*
* This is used to add IEs like WPS IE by drivers that reply to
* (Re)Association Request frames internally.
*/
const struct wpabuf *assocresp_ies;
/**
* isolate - Whether to isolate frames between associated stations
*
* If this is non-zero, the AP is requested to disable forwarding of
* frames between associated stations.
*/
int isolate;
/**
* cts_protect - Whether CTS protection is enabled
*/
int cts_protect;
/**
* preamble - Whether short preamble is enabled
*/
int preamble;
/**
* short_slot_time - Whether short slot time is enabled
*
* 0 = short slot time disable, 1 = short slot time enabled, -1 = do
* not set (e.g., when 802.11g mode is not in use)
*/
int short_slot_time;
/**
* ht_opmode - HT operation mode or -1 if HT not in use
*/
int ht_opmode;
/**
* interworking - Whether Interworking is enabled
*/
int interworking;
/**
* hessid - Homogeneous ESS identifier or %NULL if not set
*/
const u8 *hessid;
/**
* access_network_type - Access Network Type (0..15)
*
* This is used for filtering Probe Request frames when Interworking is
* enabled.
*/
u8 access_network_type;
/**
* ap_max_inactivity - Timeout in seconds to detect STA's inactivity
*
* This is used by driver which advertises this capability.
*/
int ap_max_inactivity;
/**
* ctwindow - Client Traffic Window (in TUs)
*/
u8 p2p_go_ctwindow;
/**
* disable_dgaf - Whether group-addressed frames are disabled
*/
int disable_dgaf;
/**
* osen - Whether OSEN security is enabled
*/
int osen;
/**
* freq - Channel parameters for dynamic bandwidth changes
*/
struct hostapd_freq_params *freq;
/**
* reenable - Whether this is to re-enable beaconing
*/
int reenable;
/**
* pbss - Whether to start a PCP (in PBSS) instead of an AP in
* infrastructure BSS. Valid only for DMG network.
*/
int pbss;
/**
* multicast_to_unicast - Whether to use multicast_to_unicast
*
* If this is non-zero, the AP is requested to perform multicast to
* unicast conversion for ARP, IPv4, and IPv6 frames (possibly within
* 802.1Q). If enabled, such frames are to be sent to each station
* separately, with the DA replaced by their own MAC address rather
* than the group address.
*
* Note that this may break certain expectations of the receiver, such
* as the ability to drop unicast IP packets received within multicast
* L2 frames, or the ability to not send ICMP destination unreachable
* messages for packets received in L2 multicast (which is required,
* but the receiver can't tell the difference if this new option is
* enabled.)
*
* This also doesn't implement the 802.11 DMS (directed multicast
* service).
*/
int multicast_to_unicast;
/**
* ftm_responder - Whether FTM responder is enabled
*/
int ftm_responder;
/**
* lci - Binary data, the content of an LCI report IE with type 8 as
* defined in IEEE Std 802.11-2016, 9.4.2.22.10
*/
const struct wpabuf *lci;
/**
* civic - Binary data, the content of a measurement report IE with
* type 11 as defined in IEEE Std 802.11-2016, 9.4.2.22.13
*/
const struct wpabuf *civic;
/**
* he_spr_ctrl - Spatial Reuse control field of SPR element
*/
u8 he_spr_ctrl;
/**
* he_spr_non_srg_obss_pd_max_offset - Non-SRG Maximum TX power offset
*/
u8 he_spr_non_srg_obss_pd_max_offset;
/**
* he_spr_srg_obss_pd_min_offset - Minimum TX power offset
*/
u8 he_spr_srg_obss_pd_min_offset;
/**
* he_spr_srg_obss_pd_max_offset - Maximum TX power offset
*/
u8 he_spr_srg_obss_pd_max_offset;
/**
* he_spr_bss_color_bitmap - BSS color values used by members of the
* SRG.
*/
u8 he_spr_bss_color_bitmap[8];
/**
* he_spr_partial_bssid_bitmap - Partial BSSID values used by members
* of the SRG.
*/
u8 he_spr_partial_bssid_bitmap[8];
/**
* he_bss_color - Whether the BSS Color is disabled
*/
int he_bss_color_disabled;
/**
* he_bss_color_partial - The BSS Color AID equation
*/
int he_bss_color_partial;
/**
* he_bss_color - The BSS Color of the AP
*/
int he_bss_color;
/**
* twt_responder - Whether Target Wait Time responder is enabled
*/
int twt_responder;
/**
* sae_pwe - SAE mechanism for PWE derivation
* 0 = hunting-and-pecking loop only
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
int sae_pwe;
/**
* FILS Discovery frame minimum interval in TUs
*/
u32 fd_min_int;
/**
* FILS Discovery frame maximum interval in TUs (0 = FD frame disabled)
*/
u32 fd_max_int;
/**
* FILS Discovery frame template data
*/
u8 *fd_frame_tmpl;
/**
* FILS Discovery frame template length
*/
size_t fd_frame_tmpl_len;
/**
* Unsolicited broadcast Probe Response interval in TUs
*/
unsigned int unsol_bcast_probe_resp_interval;
/**
* Unsolicited broadcast Probe Response template data
*/
u8 *unsol_bcast_probe_resp_tmpl;
/**
* Unsolicited broadcast Probe Response template length
*/
size_t unsol_bcast_probe_resp_tmpl_len;
};
struct wpa_driver_mesh_bss_params {
#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS 0x00000001
#define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT 0x00000002
#define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS 0x00000004
#define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE 0x00000008
#define WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD 0x00000010
/*
* TODO: Other mesh configuration parameters would go here.
* See NL80211_MESHCONF_* for all the mesh config parameters.
*/
unsigned int flags;
int auto_plinks;
int peer_link_timeout;
int max_peer_links;
int rssi_threshold;
u16 ht_opmode;
};
struct wpa_driver_mesh_join_params {
const u8 *meshid;
int meshid_len;
const int *basic_rates;
const u8 *ies;
int ie_len;
struct hostapd_freq_params freq;
int beacon_int;
int dtim_period;
struct wpa_driver_mesh_bss_params conf;
#define WPA_DRIVER_MESH_FLAG_USER_MPM 0x00000001
#define WPA_DRIVER_MESH_FLAG_DRIVER_MPM 0x00000002
#define WPA_DRIVER_MESH_FLAG_SAE_AUTH 0x00000004
#define WPA_DRIVER_MESH_FLAG_AMPE 0x00000008
unsigned int flags;
bool handle_dfs;
};
struct wpa_driver_set_key_params {
/**
* ifname - Interface name (for multi-SSID/VLAN support) */
const char *ifname;
/**
* alg - Encryption algorithm
*
* (%WPA_ALG_NONE, %WPA_ALG_WEP, %WPA_ALG_TKIP, %WPA_ALG_CCMP,
* %WPA_ALG_BIP_AES_CMAC_128, %WPA_ALG_GCMP, %WPA_ALG_GCMP_256,
* %WPA_ALG_CCMP_256, %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256,
* %WPA_ALG_BIP_CMAC_256);
* %WPA_ALG_NONE clears the key. */
enum wpa_alg alg;
/**
* addr - Address of the peer STA
*
* (BSSID of the current AP when setting pairwise key in station mode),
* ff:ff:ff:ff:ff:ff for broadcast keys, %NULL for default keys that
* are used both for broadcast and unicast; when clearing keys, %NULL
* is used to indicate that both the broadcast-only and default key of
* the specified key index is to be cleared */
const u8 *addr;
/**
* key_idx - Key index
*
* (0..3), usually 0 for unicast keys; 4..5 for IGTK; 6..7 for BIGTK */
int key_idx;
/**
* set_tx - Configure this key as the default Tx key
*
* Only used when driver does not support separate unicast/individual
* key */
int set_tx;
/**
* seq - Sequence number/packet number
*
* seq_len octets, the next packet number to be used for in replay
* protection; configured for Rx keys (in most cases, this is only used
* with broadcast keys and set to zero for unicast keys); %NULL if not
* set */
const u8 *seq;
/**
* seq_len - Length of the seq, depends on the algorithm
*
* TKIP: 6 octets, CCMP/GCMP: 6 octets, IGTK: 6 octets */
size_t seq_len;
/**
* key - Key buffer
*
* TKIP: 16-byte temporal key, 8-byte Tx Mic key, 8-byte Rx Mic Key */
const u8 *key;
/**
* key_len - Length of the key buffer in octets
*
* WEP: 5 or 13, TKIP: 32, CCMP/GCMP: 16, IGTK: 16 */
size_t key_len;
/**
* vlan_id - VLAN index (0..4095) for VLAN offload cases */
int vlan_id;
/**
* key_flag - Additional key flags
*
* %KEY_FLAG_MODIFY
* Set when an already installed key must be updated.
* So far the only use-case is changing RX/TX status for
* pairwise 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.
* %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).
*
* Valid and pre-defined combinations are:
* %KEY_FLAG_GROUP_RX_TX
* WEP key not to be installed as default key.
* %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 when Extended
* Key ID is supported by the driver.)
* %KEY_FLAG_PAIRWISE_RX_TX_MODIFY
* Enable TX for a pairwise key installed with
* KEY_FLAG_PAIRWISE_RX.
*
* Not a valid standalone key type but pre-defined to be combined
* with other key_flags:
* %KEY_FLAG_RX_TX
* RX/TX key. */
enum key_flag key_flag;
};
enum wpa_driver_if_type {
/**
* WPA_IF_STATION - Station mode interface
*/
WPA_IF_STATION,
/**
* WPA_IF_AP_VLAN - AP mode VLAN interface
*
* This interface shares its address and Beacon frame with the main
* BSS.
*/
WPA_IF_AP_VLAN,
/**
* WPA_IF_AP_BSS - AP mode BSS interface
*
* This interface has its own address and Beacon frame.
*/
WPA_IF_AP_BSS,
/**
* WPA_IF_P2P_GO - P2P Group Owner
*/
WPA_IF_P2P_GO,
/**
* WPA_IF_P2P_CLIENT - P2P Client
*/
WPA_IF_P2P_CLIENT,
/**
* WPA_IF_P2P_GROUP - P2P Group interface (will become either
* WPA_IF_P2P_GO or WPA_IF_P2P_CLIENT, but the role is not yet known)
*/
WPA_IF_P2P_GROUP,
/**
* WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
* abstracted P2P Device function in the driver
*/
WPA_IF_P2P_DEVICE,
/*
* WPA_IF_MESH - Mesh interface
*/
WPA_IF_MESH,
/*
* WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
*/
WPA_IF_TDLS,
/*
* WPA_IF_IBSS - IBSS interface (used for pref freq only)
*/
WPA_IF_IBSS,
/*
* WPA_IF_NAN - NAN Device
*/
WPA_IF_NAN,
/* keep last */
WPA_IF_MAX
};
/**
* struct wpa_driver_capa - Driver capability information
*/
struct wpa_driver_capa {
#define WPA_DRIVER_CAPA_KEY_MGMT_WPA 0x00000001
#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2 0x00000002
#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK 0x00000004
#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK 0x00000008
#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE 0x00000010
#define WPA_DRIVER_CAPA_KEY_MGMT_FT 0x00000020
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK 0x00000040
#define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080
#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B 0x00000100
#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 0x00000200
#define WPA_DRIVER_CAPA_KEY_MGMT_OWE 0x00000400
#define WPA_DRIVER_CAPA_KEY_MGMT_DPP 0x00000800
#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 0x00001000
#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 0x00002000
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000
#define WPA_DRIVER_CAPA_KEY_MGMT_SAE 0x00010000
#define WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256 0x00020000
#define WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256 0x00040000
#define WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE 0x00080000
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE 0x00100000
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384 0x00200000
#define WPA_DRIVER_CAPA_KEY_MGMT_CCKM 0x00400000
#define WPA_DRIVER_CAPA_KEY_MGMT_OSEN 0x00800000
/** Bitfield of supported key management suites */
unsigned int key_mgmt;
unsigned int key_mgmt_iftype[WPA_IF_MAX];
#define WPA_DRIVER_CAPA_ENC_WEP40 0x00000001
#define WPA_DRIVER_CAPA_ENC_WEP104 0x00000002
#define WPA_DRIVER_CAPA_ENC_TKIP 0x00000004
#define WPA_DRIVER_CAPA_ENC_CCMP 0x00000008
#define WPA_DRIVER_CAPA_ENC_WEP128 0x00000010
#define WPA_DRIVER_CAPA_ENC_GCMP 0x00000020
#define WPA_DRIVER_CAPA_ENC_GCMP_256 0x00000040
#define WPA_DRIVER_CAPA_ENC_CCMP_256 0x00000080
#define WPA_DRIVER_CAPA_ENC_BIP 0x00000100
#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128 0x00000200
#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256 0x00000400
#define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256 0x00000800
#define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED 0x00001000
/** Bitfield of supported cipher suites */
unsigned int enc;
#define WPA_DRIVER_AUTH_OPEN 0x00000001
#define WPA_DRIVER_AUTH_SHARED 0x00000002
#define WPA_DRIVER_AUTH_LEAP 0x00000004
/** Bitfield of supported IEEE 802.11 authentication algorithms */
unsigned int auth;
/** Driver generated WPA/RSN IE */
#define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001
/** Driver needs static WEP key setup after association command */
#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
/** Driver takes care of all DFS operations */
#define WPA_DRIVER_FLAGS_DFS_OFFLOAD 0x00000004
/** Driver takes care of RSN 4-way handshake internally; PMK is configured with
* struct wpa_driver_ops::set_key using key_flag = KEY_FLAG_PMK */
#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X 0x00000008
/** Driver is for a wired Ethernet interface */
#define WPA_DRIVER_FLAGS_WIRED 0x00000010
/** Driver provides separate commands for authentication and association (SME in
* wpa_supplicant). */
#define WPA_DRIVER_FLAGS_SME 0x00000020
/** Driver supports AP mode */
#define WPA_DRIVER_FLAGS_AP 0x00000040
/** Driver needs static WEP key setup after association has been completed */
#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080
/** Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */
#define WPA_DRIVER_FLAGS_HT_2040_COEX 0x00000100
/** Driver supports concurrent P2P operations */
#define WPA_DRIVER_FLAGS_P2P_CONCURRENT 0x00000200
/**
* Driver uses the initial interface as a dedicated management interface, i.e.,
* it cannot be used for P2P group operations or non-P2P purposes.
*/
#define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE 0x00000400
/** This interface is P2P capable (P2P GO or P2P Client) */
#define WPA_DRIVER_FLAGS_P2P_CAPABLE 0x00000800
/** Driver supports station and key removal when stopping an AP */
#define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT 0x00001000
/**
* Driver uses the initial interface for P2P management interface and non-P2P
* purposes (e.g., connect to infra AP), but this interface cannot be used for
* P2P group operations.
*/
#define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P 0x00002000
/**
- * Driver is known to use sane error codes, i.e., when it indicates that
+ * Driver is known to use valid error codes, i.e., when it indicates that
* something (e.g., association) fails, there was indeed a failure and the
* operation does not end up getting completed successfully later.
*/
-#define WPA_DRIVER_FLAGS_SANE_ERROR_CODES 0x00004000
+#define WPA_DRIVER_FLAGS_VALID_ERROR_CODES 0x00004000
/** Driver supports off-channel TX */
#define WPA_DRIVER_FLAGS_OFFCHANNEL_TX 0x00008000
/** Driver indicates TX status events for EAPOL Data frames */
#define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS 0x00010000
/** Driver indicates TX status events for Deauth/Disassoc frames */
#define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS 0x00020000
/** Driver supports roaming (BSS selection) in firmware */
#define WPA_DRIVER_FLAGS_BSS_SELECTION 0x00040000
/** Driver supports operating as a TDLS peer */
#define WPA_DRIVER_FLAGS_TDLS_SUPPORT 0x00080000
/** Driver requires external TDLS setup/teardown/discovery */
#define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP 0x00100000
/** Driver indicates support for Probe Response offloading in AP mode */
#define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD 0x00200000
/** Driver supports U-APSD in AP mode */
#define WPA_DRIVER_FLAGS_AP_UAPSD 0x00400000
/** Driver supports inactivity timer in AP mode */
#define WPA_DRIVER_FLAGS_INACTIVITY_TIMER 0x00800000
/** Driver expects user space implementation of MLME in AP mode */
#define WPA_DRIVER_FLAGS_AP_MLME 0x01000000
/** Driver supports SAE with user space SME */
#define WPA_DRIVER_FLAGS_SAE 0x02000000
/** Driver makes use of OBSS scan mechanism in wpa_supplicant */
#define WPA_DRIVER_FLAGS_OBSS_SCAN 0x04000000
/** Driver supports IBSS (Ad-hoc) mode */
#define WPA_DRIVER_FLAGS_IBSS 0x08000000
/** Driver supports radar detection */
#define WPA_DRIVER_FLAGS_RADAR 0x10000000
/** Driver supports a dedicated interface for P2P Device */
#define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE 0x20000000
/** Driver supports QoS Mapping */
#define WPA_DRIVER_FLAGS_QOS_MAPPING 0x40000000
/** Driver supports CSA in AP mode */
#define WPA_DRIVER_FLAGS_AP_CSA 0x80000000
/** Driver supports mesh */
#define WPA_DRIVER_FLAGS_MESH 0x0000000100000000ULL
/** Driver support ACS offload */
#define WPA_DRIVER_FLAGS_ACS_OFFLOAD 0x0000000200000000ULL
/** Driver supports key management offload */
#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD 0x0000000400000000ULL
/** Driver supports TDLS channel switching */
#define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH 0x0000000800000000ULL
/** Driver supports IBSS with HT datarates */
#define WPA_DRIVER_FLAGS_HT_IBSS 0x0000001000000000ULL
/** Driver supports IBSS with VHT datarates */
#define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL
/** Driver supports automatic band selection */
#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL
/** Driver supports simultaneous off-channel operations */
#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL
/** Driver supports full AP client state */
#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL
/** Driver supports P2P Listen offload */
#define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL
/** Driver supports FILS */
#define WPA_DRIVER_FLAGS_SUPPORT_FILS 0x0000040000000000ULL
/** Driver supports Beacon frame TX rate configuration (legacy rates) */
#define WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY 0x0000080000000000ULL
/** Driver supports Beacon frame TX rate configuration (HT rates) */
#define WPA_DRIVER_FLAGS_BEACON_RATE_HT 0x0000100000000000ULL
/** Driver supports Beacon frame TX rate configuration (VHT rates) */
#define WPA_DRIVER_FLAGS_BEACON_RATE_VHT 0x0000200000000000ULL
/** Driver supports mgmt_tx with random TX address in non-connected state */
#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA 0x0000400000000000ULL
/** Driver supports mgmt_tx with random TX addr in connected state */
#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED 0x0000800000000000ULL
/** Driver supports better BSS reporting with sched_scan in connected mode */
#define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI 0x0001000000000000ULL
/** Driver supports HE capabilities */
#define WPA_DRIVER_FLAGS_HE_CAPABILITIES 0x0002000000000000ULL
/** Driver supports FILS shared key offload */
#define WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD 0x0004000000000000ULL
/** Driver supports all OCE STA specific mandatory features */
#define WPA_DRIVER_FLAGS_OCE_STA 0x0008000000000000ULL
/** Driver supports all OCE AP specific mandatory features */
#define WPA_DRIVER_FLAGS_OCE_AP 0x0010000000000000ULL
/**
* Driver supports all OCE STA-CFON specific mandatory features only.
* If a driver sets this bit but not the %WPA_DRIVER_FLAGS_OCE_AP, the
* userspace shall assume that this driver may not support all OCE AP
* functionality but can support only OCE STA-CFON functionality.
*/
#define WPA_DRIVER_FLAGS_OCE_STA_CFON 0x0020000000000000ULL
/** Driver supports MFP-optional in the connect command */
#define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL
/** Driver is a self-managed regulatory device */
#define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL
/** Driver supports FTM responder functionality */
#define WPA_DRIVER_FLAGS_FTM_RESPONDER 0x0100000000000000ULL
/** Driver support 4-way handshake offload for WPA-Personal */
#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK 0x0200000000000000ULL
/** Driver supports a separate control port TX for EAPOL frames */
#define WPA_DRIVER_FLAGS_CONTROL_PORT 0x0400000000000000ULL
/** Driver supports VLAN offload */
#define WPA_DRIVER_FLAGS_VLAN_OFFLOAD 0x0800000000000000ULL
/** Driver supports UPDATE_FT_IES command */
#define WPA_DRIVER_FLAGS_UPDATE_FT_IES 0x1000000000000000ULL
/** Driver can correctly rekey PTKs without Extended Key ID */
#define WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS 0x2000000000000000ULL
/** Driver supports Beacon protection */
#define WPA_DRIVER_FLAGS_BEACON_PROTECTION 0x4000000000000000ULL
/** Driver supports Extended Key ID */
#define WPA_DRIVER_FLAGS_EXTENDED_KEY_ID 0x8000000000000000ULL
u64 flags;
/** Driver supports a separate control port RX for EAPOL frames */
#define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX 0x0000000000000001ULL
/** Driver supports TX status reports for EAPOL frames through control port */
#define WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS 0x0000000000000002ULL
/** Driver supports secure LTF */
#define WPA_DRIVER_FLAGS2_SEC_LTF 0x0000000000000004ULL
/** Driver supports secure RTT measurement exchange */
#define WPA_DRIVER_FLAGS2_SEC_RTT 0x0000000000000008ULL
/**
* Driver supports protection of range negotiation and measurement management
* frames
*/
#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG 0x0000000000000010ULL
/** Driver supports Beacon frame TX rate configuration (HE rates) */
#define WPA_DRIVER_FLAGS2_BEACON_RATE_HE 0x0000000000000020ULL
/** Driver supports Beacon protection only in client mode */
#define WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT 0x0000000000000040ULL
/** Driver supports Operating Channel Validation */
#define WPA_DRIVER_FLAGS2_OCV 0x0000000000000080ULL
/** Driver expects user space implementation of SME in AP mode */
#define WPA_DRIVER_FLAGS2_AP_SME 0x0000000000000100ULL
u64 flags2;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
(drv_flags & WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE)
unsigned int wmm_ac_supported:1;
unsigned int mac_addr_rand_scan_supported:1;
unsigned int mac_addr_rand_sched_scan_supported:1;
/** Maximum number of supported active probe SSIDs */
int max_scan_ssids;
/** Maximum number of supported active probe SSIDs for sched_scan */
int max_sched_scan_ssids;
/** Maximum number of supported scan plans for scheduled scan */
unsigned int max_sched_scan_plans;
/** Maximum interval in a scan plan. In seconds */
u32 max_sched_scan_plan_interval;
/** Maximum number of iterations in a single scan plan */
u32 max_sched_scan_plan_iterations;
/** Whether sched_scan (offloaded scanning) is supported */
int sched_scan_supported;
/** Maximum number of supported match sets for sched_scan */
int max_match_sets;
/**
* max_remain_on_chan - Maximum remain-on-channel duration in msec
*/
unsigned int max_remain_on_chan;
/**
* max_stations - Maximum number of associated stations the driver
* supports in AP mode
*/
unsigned int max_stations;
/**
* probe_resp_offloads - Bitmap of supported protocols by the driver
* for Probe Response offloading.
*/
/** Driver Probe Response offloading support for WPS ver. 1 */
#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS 0x00000001
/** Driver Probe Response offloading support for WPS ver. 2 */
#define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2 0x00000002
/** Driver Probe Response offloading support for P2P */
#define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P 0x00000004
/** Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
#define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING 0x00000008
unsigned int probe_resp_offloads;
unsigned int max_acl_mac_addrs;
/**
* Number of supported concurrent channels
*/
unsigned int num_multichan_concurrent;
/**
* extended_capa - extended capabilities in driver/device
*
* Must be allocated and freed by driver and the pointers must be
* valid for the lifetime of the driver, i.e., freed in deinit()
*/
const u8 *extended_capa, *extended_capa_mask;
unsigned int extended_capa_len;
struct wowlan_triggers wowlan_triggers;
/** Driver adds the DS Params Set IE in Probe Request frames */
#define WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES 0x00000001
/** Driver adds the WFA TPC IE in Probe Request frames */
#define WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES 0x00000002
/** Driver handles quiet period requests */
#define WPA_DRIVER_FLAGS_QUIET 0x00000004
/**
* Driver is capable of inserting the current TX power value into the body of
* transmitted frames.
* Background: Some Action frames include a TPC Report IE. This IE contains a
* TX power field, which has to be updated by lower layers. One such Action
* frame is Link Measurement Report (part of RRM). Another is TPC Report (part
* of spectrum management). Note that this insertion takes place at a fixed
* offset, namely the 6th byte in the Action frame body.
*/
#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008
/**
* Driver supports RRM. With this support, the driver will accept to use RRM in
* (Re)Association Request frames, without supporting quiet period.
*/
#define WPA_DRIVER_FLAGS_SUPPORT_RRM 0x00000010
/** Driver supports setting the scan dwell time */
#define WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL 0x00000020
/** Driver supports Beacon Report Measurement */
#define WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT 0x00000040
u32 rrm_flags;
/* Driver concurrency capabilities */
unsigned int conc_capab;
/* Maximum number of concurrent channels on 2.4 GHz */
unsigned int max_conc_chan_2_4;
/* Maximum number of concurrent channels on 5 GHz */
unsigned int max_conc_chan_5_0;
/* Maximum number of supported CSA counters */
u16 max_csa_counters;
};
struct hostapd_data;
#define STA_DRV_DATA_TX_MCS BIT(0)
#define STA_DRV_DATA_RX_MCS BIT(1)
#define STA_DRV_DATA_TX_VHT_MCS BIT(2)
#define STA_DRV_DATA_RX_VHT_MCS BIT(3)
#define STA_DRV_DATA_TX_VHT_NSS BIT(4)
#define STA_DRV_DATA_RX_VHT_NSS BIT(5)
#define STA_DRV_DATA_TX_SHORT_GI BIT(6)
#define STA_DRV_DATA_RX_SHORT_GI BIT(7)
#define STA_DRV_DATA_LAST_ACK_RSSI BIT(8)
+#define STA_DRV_DATA_CONN_TIME BIT(9)
struct hostap_sta_driver_data {
unsigned long rx_packets, tx_packets;
unsigned long long rx_bytes, tx_bytes;
unsigned long long rx_airtime, tx_airtime;
int bytes_64bit; /* whether 64-bit byte counters are supported */
unsigned long current_tx_rate;
unsigned long current_rx_rate;
unsigned long inactive_msec;
+ unsigned long connected_sec;
unsigned long flags; /* bitfield of STA_DRV_DATA_* */
unsigned long num_ps_buf_frames;
unsigned long tx_retry_failed;
unsigned long tx_retry_count;
s8 last_ack_rssi;
unsigned long backlog_packets;
unsigned long backlog_bytes;
s8 signal;
u8 rx_vhtmcs;
u8 tx_vhtmcs;
u8 rx_mcs;
u8 tx_mcs;
u8 rx_vht_nss;
u8 tx_vht_nss;
};
struct hostapd_sta_add_params {
const u8 *addr;
u16 aid;
u16 capability;
const u8 *supp_rates;
size_t supp_rates_len;
u16 listen_interval;
const struct ieee80211_ht_capabilities *ht_capabilities;
const struct ieee80211_vht_capabilities *vht_capabilities;
int vht_opmode_enabled;
u8 vht_opmode;
const struct ieee80211_he_capabilities *he_capab;
size_t he_capab_len;
const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab;
u32 flags; /* bitmask of WPA_STA_* flags */
u32 flags_mask; /* unset bits in flags */
#ifdef CONFIG_MESH
enum mesh_plink_state plink_state;
u16 peer_aid;
#endif /* CONFIG_MESH */
int set; /* Set STA parameters instead of add */
u8 qosinfo;
const u8 *ext_capab;
size_t ext_capab_len;
const u8 *supp_channels;
size_t supp_channels_len;
const u8 *supp_oper_classes;
size_t supp_oper_classes_len;
int support_p2p_ps;
};
struct mac_address {
u8 addr[ETH_ALEN];
};
struct hostapd_acl_params {
u8 acl_policy;
unsigned int num_mac_acl;
struct mac_address mac_acl[0];
};
struct wpa_init_params {
void *global_priv;
const u8 *bssid;
const char *ifname;
const char *driver_params;
int use_pae_group_addr;
char **bridge;
size_t num_bridge;
u8 *own_addr; /* buffer for writing own MAC address */
};
struct wpa_bss_params {
/** Interface name (for multi-SSID/VLAN support) */
const char *ifname;
/** Whether IEEE 802.1X or WPA/WPA2 is enabled */
int enabled;
int wpa;
int ieee802_1x;
int wpa_group;
int wpa_pairwise;
int wpa_key_mgmt;
int rsn_preauth;
enum mfp_options ieee80211w;
};
#define WPA_STA_AUTHORIZED BIT(0)
#define WPA_STA_WMM BIT(1)
#define WPA_STA_SHORT_PREAMBLE BIT(2)
#define WPA_STA_MFP BIT(3)
#define WPA_STA_TDLS_PEER BIT(4)
#define WPA_STA_AUTHENTICATED BIT(5)
#define WPA_STA_ASSOCIATED BIT(6)
enum tdls_oper {
TDLS_DISCOVERY_REQ,
TDLS_SETUP,
TDLS_TEARDOWN,
TDLS_ENABLE_LINK,
TDLS_DISABLE_LINK,
TDLS_ENABLE,
TDLS_DISABLE
};
enum wnm_oper {
WNM_SLEEP_ENTER_CONFIRM,
WNM_SLEEP_ENTER_FAIL,
WNM_SLEEP_EXIT_CONFIRM,
WNM_SLEEP_EXIT_FAIL,
WNM_SLEEP_TFS_REQ_IE_ADD, /* STA requests driver to add TFS req IE */
WNM_SLEEP_TFS_REQ_IE_NONE, /* STA requests empty TFS req IE */
WNM_SLEEP_TFS_REQ_IE_SET, /* AP requests driver to set TFS req IE for
* a STA */
WNM_SLEEP_TFS_RESP_IE_ADD, /* AP requests driver to add TFS resp IE
* for a STA */
WNM_SLEEP_TFS_RESP_IE_NONE, /* AP requests empty TFS resp IE */
WNM_SLEEP_TFS_RESP_IE_SET, /* AP requests driver to set TFS resp IE
* for a STA */
WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */
};
/* enum smps_mode - SMPS mode definitions */
enum smps_mode {
SMPS_AUTOMATIC,
SMPS_OFF,
SMPS_DYNAMIC,
SMPS_STATIC,
/* Keep last */
SMPS_INVALID,
};
#define WPA_INVALID_NOISE 9999
/**
* struct wpa_signal_info - Information about channel signal quality
* @frequency: control frequency
* @above_threshold: true if the above threshold was crossed
* (relevant for a CQM event)
* @current_signal: in dBm
* @avg_signal: in dBm
* @avg_beacon_signal: in dBm
* @current_noise: %WPA_INVALID_NOISE if not supported
* @current_txrate: current TX rate
* @chanwidth: channel width
* @center_frq1: center frequency for the first segment
* @center_frq2: center frequency for the second segment (if relevant)
*/
struct wpa_signal_info {
u32 frequency;
int above_threshold;
int current_signal;
int avg_signal;
int avg_beacon_signal;
int current_noise;
int current_txrate;
enum chan_width chanwidth;
int center_frq1;
int center_frq2;
};
/**
* struct wpa_channel_info - Information about the current channel
* @frequency: Center frequency of the primary 20 MHz channel
* @chanwidth: Width of the current operating channel
* @sec_channel: Location of the secondary 20 MHz channel (either +1 or -1).
* This field is only filled in when using a 40 MHz channel.
* @center_frq1: Center frequency of frequency segment 0
* @center_frq2: Center frequency of frequency segment 1 (for 80+80 channels)
* @seg1_idx: Frequency segment 1 index when using a 80+80 channel. This is
* derived from center_frq2 for convenience.
*/
struct wpa_channel_info {
u32 frequency;
enum chan_width chanwidth;
int sec_channel;
int center_frq1;
int center_frq2;
u8 seg1_idx;
};
/**
* struct beacon_data - Beacon data
* @head: Head portion of Beacon frame (before TIM IE)
* @tail: Tail portion of Beacon frame (after TIM IE)
* @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL
* @proberesp_ies: Extra information element(s) to add into Probe Response
* frames or %NULL
* @assocresp_ies: Extra information element(s) to add into (Re)Association
* Response frames or %NULL
* @probe_resp: Probe Response frame template
* @head_len: Length of @head
* @tail_len: Length of @tail
* @beacon_ies_len: Length of beacon_ies in octets
* @proberesp_ies_len: Length of proberesp_ies in octets
* @proberesp_ies_len: Length of proberesp_ies in octets
* @probe_resp_len: Length of probe response template (@probe_resp)
*/
struct beacon_data {
u8 *head, *tail;
u8 *beacon_ies;
u8 *proberesp_ies;
u8 *assocresp_ies;
u8 *probe_resp;
size_t head_len, tail_len;
size_t beacon_ies_len;
size_t proberesp_ies_len;
size_t assocresp_ies_len;
size_t probe_resp_len;
};
/**
* struct csa_settings - Settings for channel switch command
* @cs_count: Count in Beacon frames (TBTT) to perform the switch
* @block_tx: 1 - block transmission for CSA period
* @freq_params: Next channel frequency parameter
* @beacon_csa: Beacon/probe resp/asooc resp info for CSA period
* @beacon_after: Next beacon/probe resp/asooc resp info
* @counter_offset_beacon: Offset to the count field in beacon's tail
* @counter_offset_presp: Offset to the count field in probe resp.
*/
struct csa_settings {
u8 cs_count;
u8 block_tx;
struct hostapd_freq_params freq_params;
struct beacon_data beacon_csa;
struct beacon_data beacon_after;
u16 counter_offset_beacon[2];
u16 counter_offset_presp[2];
};
/* TDLS peer capabilities for send_tdls_mgmt() */
enum tdls_peer_capability {
TDLS_PEER_HT = BIT(0),
TDLS_PEER_VHT = BIT(1),
TDLS_PEER_WMM = BIT(2),
TDLS_PEER_HE = BIT(3),
};
/* valid info in the wmm_params struct */
enum wmm_params_valid_info {
WMM_PARAMS_UAPSD_QUEUES_INFO = BIT(0),
};
/**
* struct wmm_params - WMM parameterss configured for this association
* @info_bitmap: Bitmap of valid wmm_params info; indicates what fields
* of the struct contain valid information.
* @uapsd_queues: Bitmap of ACs configured for uapsd (valid only if
* %WMM_PARAMS_UAPSD_QUEUES_INFO is set)
*/
struct wmm_params {
u8 info_bitmap;
u8 uapsd_queues;
};
#ifdef CONFIG_MACSEC
struct macsec_init_params {
bool always_include_sci;
bool use_es;
bool use_scb;
};
#endif /* CONFIG_MACSEC */
enum drv_br_port_attr {
DRV_BR_PORT_ATTR_PROXYARP,
DRV_BR_PORT_ATTR_HAIRPIN_MODE,
};
enum drv_br_net_param {
DRV_BR_NET_PARAM_GARP_ACCEPT,
DRV_BR_MULTICAST_SNOOPING,
};
struct drv_acs_params {
/* Selected mode (HOSTAPD_MODE_*) */
enum hostapd_hw_mode hw_mode;
/* Indicates whether HT is enabled */
int ht_enabled;
/* Indicates whether HT40 is enabled */
int ht40_enabled;
/* Indicates whether VHT is enabled */
int vht_enabled;
/* Configured ACS channel width */
u16 ch_width;
/* ACS frequency list info */
const int *freq_list;
/* Indicates whether EDMG is enabled */
int edmg_enabled;
};
struct wpa_bss_trans_info {
u8 mbo_transition_reason;
u8 n_candidates;
u8 *bssid;
};
struct wpa_bss_candidate_info {
u8 num;
struct candidate_list {
u8 bssid[ETH_ALEN];
u8 is_accept;
u32 reject_reason;
} *candidates;
};
struct wpa_pmkid_params {
const u8 *bssid;
const u8 *ssid;
size_t ssid_len;
const u8 *fils_cache_id;
const u8 *pmkid;
const u8 *pmk;
size_t pmk_len;
u32 pmk_lifetime;
u8 pmk_reauth_threshold;
};
/* Mask used to specify which connection parameters have to be updated */
enum wpa_drv_update_connect_params_mask {
WPA_DRV_UPDATE_ASSOC_IES = BIT(0),
WPA_DRV_UPDATE_FILS_ERP_INFO = BIT(1),
WPA_DRV_UPDATE_AUTH_TYPE = BIT(2),
};
/**
* struct external_auth - External authentication trigger parameters
*
* These are used across the external authentication request and event
* interfaces.
* @action: Action type / trigger for external authentication. Only significant
* for the event interface.
* @bssid: BSSID of the peer with which the authentication has to happen. Used
* by both the request and event interface.
* @ssid: SSID of the AP. Used by both the request and event interface.
* @ssid_len: SSID length in octets.
* @key_mgmt_suite: AKM suite of the respective authentication. Optional for
* the request interface.
* @status: Status code, %WLAN_STATUS_SUCCESS for successful authentication,
* use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give
* the real status code for failures. Used only for the request interface
* from user space to the driver.
* @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE).
*/
struct external_auth {
enum {
EXT_AUTH_START,
EXT_AUTH_ABORT,
} action;
const u8 *bssid;
const u8 *ssid;
size_t ssid_len;
unsigned int key_mgmt_suite;
u16 status;
const u8 *pmkid;
};
/* enum nested_attr - Used to specify if subcommand uses nested attributes */
enum nested_attr {
NESTED_ATTR_NOT_USED = 0,
NESTED_ATTR_USED = 1,
NESTED_ATTR_UNSPECIFIED = 2,
};
/**
* struct wpa_driver_ops - Driver interface API definition
*
* This structure defines the API that each driver interface needs to implement
* for core wpa_supplicant code. All driver specific functionality is captured
* in this wrapper.
*/
struct wpa_driver_ops {
/** Name of the driver interface */
const char *name;
/** One line description of the driver interface */
const char *desc;
/**
* get_bssid - Get the current BSSID
* @priv: private driver interface data
* @bssid: buffer for BSSID (ETH_ALEN = 6 bytes)
*
* Returns: 0 on success, -1 on failure
*
* Query kernel driver for the current BSSID and copy it to bssid.
* Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not
* associated.
*/
int (*get_bssid)(void *priv, u8 *bssid);
/**
* get_ssid - Get the current SSID
* @priv: private driver interface data
* @ssid: buffer for SSID (at least 32 bytes)
*
* Returns: Length of the SSID on success, -1 on failure
*
* Query kernel driver for the current SSID and copy it to ssid.
* Returning zero is recommended if the STA is not associated.
*
* Note: SSID is an array of octets, i.e., it is not nul terminated and
* can, at least in theory, contain control characters (including nul)
* and as such, should be processed as binary data, not a printable
* string.
*/
int (*get_ssid)(void *priv, u8 *ssid);
/**
* set_key - Configure encryption key
* @priv: private driver interface data
* @params: Key parameters
* Returns: 0 on success, -1 on failure
*
* Configure the given key for the kernel driver. If the driver
* supports separate individual keys (4 default keys + 1 individual),
* addr can be used to determine whether the key is default or
* individual. If only 4 keys are supported, the default key with key
* index 0 is used as the individual key. STA must be configured to use
* it as the default Tx key (set_tx is set) and accept Rx for all the
* key indexes. In most cases, WPA uses only key indexes 1 and 2 for
* broadcast keys, so key index 0 is available for this kind of
* configuration.
*
* Please note that TKIP keys include separate TX and RX MIC keys and
* some drivers may expect them in different order than wpa_supplicant
* is using. If the TX/RX keys are swapped, all TKIP encrypted packets
* will trigger Michael MIC errors. This can be fixed by changing the
* order of MIC keys by swapping te bytes 16..23 and 24..31 of the key
* in driver_*.c set_key() implementation, see driver_ndis.c for an
* example on how this can be done.
*/
int (*set_key)(void *priv, struct wpa_driver_set_key_params *params);
/**
* init - Initialize driver interface
* @ctx: context to be used when calling wpa_supplicant functions,
* e.g., wpa_supplicant_event()
* @ifname: interface name, e.g., wlan0
*
* Returns: Pointer to private data, %NULL on failure
*
* Initialize driver interface, including event processing for kernel
* driver events (e.g., associated, scan results, Michael MIC failure).
* This function can allocate a private configuration data area for
* @ctx, file descriptor, interface name, etc. information that may be
* needed in future driver operations. If this is not used, non-NULL
* value will need to be returned because %NULL is used to indicate
* failure. The returned value will be used as 'void *priv' data for
* all other driver_ops functions.
*
* The main event loop (eloop.c) of wpa_supplicant can be used to
* register callback for read sockets (eloop_register_read_sock()).
*
* See below for more information about events and
* wpa_supplicant_event() function.
*/
void * (*init)(void *ctx, const char *ifname);
/**
* deinit - Deinitialize driver interface
* @priv: private driver interface data from init()
*
* Shut down driver interface and processing of driver events. Free
* private data buffer if one was allocated in init() handler.
*/
void (*deinit)(void *priv);
/**
* set_param - Set driver configuration parameters
* @priv: private driver interface data from init()
* @param: driver specific configuration parameters
*
* Returns: 0 on success, -1 on failure
*
* Optional handler for notifying driver interface about configuration
* parameters (driver_param).
*/
int (*set_param)(void *priv, const char *param);
/**
* set_countermeasures - Enable/disable TKIP countermeasures
* @priv: private driver interface data
* @enabled: 1 = countermeasures enabled, 0 = disabled
*
* Returns: 0 on success, -1 on failure
*
* Configure TKIP countermeasures. When these are enabled, the driver
* should drop all received and queued frames that are using TKIP.
*/
int (*set_countermeasures)(void *priv, int enabled);
/**
* deauthenticate - Request driver to deauthenticate
* @priv: private driver interface data
* @addr: peer address (BSSID of the AP)
* @reason_code: 16-bit reason code to be sent in the deauthentication
* frame
*
* Returns: 0 on success, -1 on failure
*/
int (*deauthenticate)(void *priv, const u8 *addr, u16 reason_code);
/**
* associate - Request driver to associate
* @priv: private driver interface data
* @params: association parameters
*
* Returns: 0 on success, -1 on failure
*/
int (*associate)(void *priv,
struct wpa_driver_associate_params *params);
/**
* add_pmkid - Add PMKSA cache entry to the driver
* @priv: private driver interface data
* @params: PMKSA parameters
*
* Returns: 0 on success, -1 on failure
*
* This function is called when a new PMK is received, as a result of
* either normal authentication or RSN pre-authentication. The PMKSA
* parameters are either a set of bssid, pmkid, and pmk; or a set of
* ssid, fils_cache_id, pmkid, and pmk.
*
* If the driver generates RSN IE, i.e., it does not use wpa_ie in
* associate(), add_pmkid() can be used to add new PMKSA cache entries
* in the driver. If the driver uses wpa_ie from wpa_supplicant, this
* driver_ops function does not need to be implemented. Likewise, if
* the driver does not support WPA, this function is not needed.
*/
int (*add_pmkid)(void *priv, struct wpa_pmkid_params *params);
/**
* remove_pmkid - Remove PMKSA cache entry to the driver
* @priv: private driver interface data
* @params: PMKSA parameters
*
* Returns: 0 on success, -1 on failure
*
* This function is called when the supplicant drops a PMKSA cache
* entry for any reason. The PMKSA parameters are either a set of
* bssid and pmkid; or a set of ssid, fils_cache_id, and pmkid.
*
* If the driver generates RSN IE, i.e., it does not use wpa_ie in
* associate(), remove_pmkid() can be used to synchronize PMKSA caches
* between the driver and wpa_supplicant. If the driver uses wpa_ie
* from wpa_supplicant, this driver_ops function does not need to be
* implemented. Likewise, if the driver does not support WPA, this
* function is not needed.
*/
int (*remove_pmkid)(void *priv, struct wpa_pmkid_params *params);
/**
* flush_pmkid - Flush PMKSA cache
* @priv: private driver interface data
*
* Returns: 0 on success, -1 on failure
*
* This function is called when the supplicant drops all PMKSA cache
* entries for any reason.
*
* If the driver generates RSN IE, i.e., it does not use wpa_ie in
* associate(), remove_pmkid() can be used to synchronize PMKSA caches
* between the driver and wpa_supplicant. If the driver uses wpa_ie
* from wpa_supplicant, this driver_ops function does not need to be
* implemented. Likewise, if the driver does not support WPA, this
* function is not needed.
*/
int (*flush_pmkid)(void *priv);
/**
* get_capa - Get driver capabilities
* @priv: private driver interface data
*
* Returns: 0 on success, -1 on failure
*
* Get driver/firmware/hardware capabilities.
*/
int (*get_capa)(void *priv, struct wpa_driver_capa *capa);
/**
* poll - Poll driver for association information
* @priv: private driver interface data
*
* This is an option callback that can be used when the driver does not
* provide event mechanism for association events. This is called when
* receiving WPA EAPOL-Key messages that require association
* information. The driver interface is supposed to generate associnfo
* event before returning from this callback function. In addition, the
* driver interface should generate an association event after having
* sent out associnfo.
*/
void (*poll)(void *priv);
/**
* get_ifindex - Get interface index
* @priv: private driver interface data
*
* Returns: Interface index
*/
unsigned int (*get_ifindex)(void *priv);
/**
* get_ifname - Get interface name
* @priv: private driver interface data
*
* Returns: Pointer to the interface name. This can differ from the
* interface name used in init() call. Init() is called first.
*
* This optional function can be used to allow the driver interface to
* replace the interface name with something else, e.g., based on an
* interface mapping from a more descriptive name.
*/
const char * (*get_ifname)(void *priv);
/**
* get_mac_addr - Get own MAC address
* @priv: private driver interface data
*
* Returns: Pointer to own MAC address or %NULL on failure
*
* This optional function can be used to get the own MAC address of the
* device from the driver interface code. This is only needed if the
* l2_packet implementation for the OS does not provide easy access to
* a MAC address. */
const u8 * (*get_mac_addr)(void *priv);
/**
* set_operstate - Sets device operating state to DORMANT or UP
* @priv: private driver interface data
* @state: 0 = dormant, 1 = up
* Returns: 0 on success, -1 on failure
*
* This is an optional function that can be used on operating systems
* that support a concept of controlling network device state from user
* space applications. This function, if set, gets called with
* state = 1 when authentication has been completed and with state = 0
* when connection is lost.
*/
int (*set_operstate)(void *priv, int state);
/**
* mlme_setprotection - MLME-SETPROTECTION.request primitive
* @priv: Private driver interface data
* @addr: Address of the station for which to set protection (may be
* %NULL for group keys)
* @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_*
* @key_type: MLME_SETPROTECTION_KEY_TYPE_*
* Returns: 0 on success, -1 on failure
*
* This is an optional function that can be used to set the driver to
* require protection for Tx and/or Rx frames. This uses the layer
* interface defined in IEEE 802.11i-2004 clause 10.3.22.1
* (MLME-SETPROTECTION.request). Many drivers do not use explicit
* set protection operation; instead, they set protection implicitly
* based on configured keys.
*/
int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type,
int key_type);
/**
* get_hw_feature_data - Get hardware support data (channels and rates)
* @priv: Private driver interface data
* @num_modes: Variable for returning the number of returned modes
* flags: Variable for returning hardware feature flags
* @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*)
* Returns: Pointer to allocated hardware data on success or %NULL on
* failure. Caller is responsible for freeing this.
*/
struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
u16 *num_modes,
u16 *flags, u8 *dfs);
/**
* send_mlme - Send management frame from MLME
* @priv: Private driver interface data
* @data: IEEE 802.11 management frame with IEEE 802.11 header
* @data_len: Size of the management frame
* @noack: Do not wait for this frame to be acked (disable retries)
* @freq: Frequency (in MHz) to send the frame on, or 0 to let the
* driver decide
* @csa_offs: Array of CSA offsets or %NULL
* @csa_offs_len: Number of elements in csa_offs
* @no_encrypt: Do not encrypt frame even if appropriate key exists
* (used only for testing purposes)
* @wait: Time to wait off-channel for a response (in ms), or zero
* Returns: 0 on success, -1 on failure
*/
int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
int noack, unsigned int freq, const u16 *csa_offs,
size_t csa_offs_len, int no_encrypt,
unsigned int wait);
/**
* update_ft_ies - Update FT (IEEE 802.11r) IEs
* @priv: Private driver interface data
* @md: Mobility domain (2 octets) (also included inside ies)
* @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs
* @ies_len: Length of FT IEs in bytes
* Returns: 0 on success, -1 on failure
*
* The supplicant uses this callback to let the driver know that keying
* material for FT is available and that the driver can use the
* provided IEs in the next message in FT authentication sequence.
*
* This function is only needed for driver that support IEEE 802.11r
* (Fast BSS Transition).
*/
int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies,
size_t ies_len);
/**
* get_scan_results2 - Fetch the latest scan results
* @priv: private driver interface data
*
* Returns: Allocated buffer of scan results (caller is responsible for
* freeing the data structure) on success, NULL on failure
*/
struct wpa_scan_results * (*get_scan_results2)(void *priv);
/**
* set_country - Set country
* @priv: Private driver interface data
* @alpha2: country to which to switch to
* Returns: 0 on success, -1 on failure
*
* This function is for drivers which support some form
* of setting a regulatory domain.
*/
int (*set_country)(void *priv, const char *alpha2);
/**
* get_country - Get country
* @priv: Private driver interface data
* @alpha2: Buffer for returning country code (at least 3 octets)
* Returns: 0 on success, -1 on failure
*/
int (*get_country)(void *priv, char *alpha2);
/**
* global_init - Global driver initialization
* @ctx: wpa_global pointer
* Returns: Pointer to private data (global), %NULL on failure
*
* This optional function is called to initialize the driver wrapper
* for global data, i.e., data that applies to all interfaces. If this
* function is implemented, global_deinit() will also need to be
* implemented to free the private data. The driver will also likely
* use init2() function instead of init() to get the pointer to global
* data available to per-interface initializer.
*/
void * (*global_init)(void *ctx);
/**
* global_deinit - Global driver deinitialization
* @priv: private driver global data from global_init()
*
* Terminate any global driver related functionality and free the
* global data structure.
*/
void (*global_deinit)(void *priv);
/**
* init2 - Initialize driver interface (with global data)
* @ctx: context to be used when calling wpa_supplicant functions,
* e.g., wpa_supplicant_event()
* @ifname: interface name, e.g., wlan0
* @global_priv: private driver global data from global_init()
* Returns: Pointer to private data, %NULL on failure
*
* This function can be used instead of init() if the driver wrapper
* uses global data.
*/
void * (*init2)(void *ctx, const char *ifname, void *global_priv);
/**
* get_interfaces - Get information about available interfaces
* @global_priv: private driver global data from global_init()
* Returns: Allocated buffer of interface information (caller is
* responsible for freeing the data structure) on success, NULL on
* failure
*/
struct wpa_interface_info * (*get_interfaces)(void *global_priv);
/**
* scan2 - Request the driver to initiate scan
* @priv: private driver interface data
* @params: Scan parameters
*
* Returns: 0 on success, -1 on failure
*
* Once the scan results are ready, the driver should report scan
* results event for wpa_supplicant which will eventually request the
* results with wpa_driver_get_scan_results2().
*/
int (*scan2)(void *priv, struct wpa_driver_scan_params *params);
/**
* authenticate - Request driver to authenticate
* @priv: private driver interface data
* @params: authentication parameters
* Returns: 0 on success, -1 on failure
*
* This is an optional function that can be used with drivers that
* support separate authentication and association steps, i.e., when
* wpa_supplicant can act as the SME. If not implemented, associate()
* function is expected to take care of IEEE 802.11 authentication,
* too.
*/
int (*authenticate)(void *priv,
struct wpa_driver_auth_params *params);
/**
* set_ap - Set Beacon and Probe Response information for AP mode
* @priv: Private driver interface data
* @params: Parameters to use in AP mode
*
* This function is used to configure Beacon template and/or extra IEs
* to add for Beacon and Probe Response frames for the driver in
* AP mode. The driver is responsible for building the full Beacon
* frame by concatenating the head part with TIM IE generated by the
* driver/firmware and finishing with the tail part. Depending on the
* driver architectue, this can be done either by using the full
* template or the set of additional IEs (e.g., WPS and P2P IE).
* Similarly, Probe Response processing depends on the driver design.
* If the driver (or firmware) takes care of replying to Probe Request
* frames, the extra IEs provided here needs to be added to the Probe
* Response frames.
*
* Returns: 0 on success, -1 on failure
*/
int (*set_ap)(void *priv, struct wpa_driver_ap_params *params);
/**
* set_acl - Set ACL in AP mode
* @priv: Private driver interface data
* @params: Parameters to configure ACL
* Returns: 0 on success, -1 on failure
*
* This is used only for the drivers which support MAC address ACL.
*/
int (*set_acl)(void *priv, struct hostapd_acl_params *params);
/**
* hapd_init - Initialize driver interface (hostapd only)
* @hapd: Pointer to hostapd context
* @params: Configuration for the driver wrapper
* Returns: Pointer to private data, %NULL on failure
*
* This function is used instead of init() or init2() when the driver
* wrapper is used with hostapd.
*/
void * (*hapd_init)(struct hostapd_data *hapd,
struct wpa_init_params *params);
/**
* hapd_deinit - Deinitialize driver interface (hostapd only)
* @priv: Private driver interface data from hapd_init()
*/
void (*hapd_deinit)(void *priv);
/**
* set_ieee8021x - Enable/disable IEEE 802.1X support (AP only)
* @priv: Private driver interface data
* @params: BSS parameters
* Returns: 0 on success, -1 on failure
*
* This is an optional function to configure the kernel driver to
* enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This
* can be left undefined (set to %NULL) if IEEE 802.1X support is
* always enabled and the driver uses set_ap() to set WPA/RSN IE
* for Beacon frames.
*
* DEPRECATED - use set_ap() instead
*/
int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params);
/**
* set_privacy - Enable/disable privacy (AP only)
* @priv: Private driver interface data
* @enabled: 1 = privacy enabled, 0 = disabled
* Returns: 0 on success, -1 on failure
*
* This is an optional function to configure privacy field in the
* kernel driver for Beacon frames. This can be left undefined (set to
* %NULL) if the driver uses the Beacon template from set_ap().
*
* DEPRECATED - use set_ap() instead
*/
int (*set_privacy)(void *priv, int enabled);
/**
* get_seqnum - Fetch the current TSC/packet number (AP only)
* @ifname: The interface name (main or virtual)
* @priv: Private driver interface data
* @addr: MAC address of the station or %NULL for group keys
* @idx: Key index
* @seq: Buffer for returning the latest used TSC/packet number
* Returns: 0 on success, -1 on failure
*
* This function is used to fetch the last used TSC/packet number for
* a TKIP, CCMP, GCMP, or BIP/IGTK key. It is mainly used with group
* keys, so there is no strict requirement on implementing support for
* unicast keys (i.e., addr != %NULL).
*/
int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr,
int idx, u8 *seq);
/**
* flush - Flush all association stations (AP only)
* @priv: Private driver interface data
* Returns: 0 on success, -1 on failure
*
* This function requests the driver to disassociate all associated
* stations. This function does not need to be implemented if the
* driver does not process association frames internally.
*/
int (*flush)(void *priv);
/**
* set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
* @priv: Private driver interface data
* @elem: Information elements
* @elem_len: Length of the elem buffer in octets
* Returns: 0 on success, -1 on failure
*
* This is an optional function to add information elements in the
* kernel driver for Beacon and Probe Response frames. This can be left
* undefined (set to %NULL) if the driver uses the Beacon template from
* set_ap().
*
* DEPRECATED - use set_ap() instead
*/
int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
/**
* read_sta_data - Fetch station data
* @priv: Private driver interface data
* @data: Buffer for returning station information
* @addr: MAC address of the station
* Returns: 0 on success, -1 on failure
*/
int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data,
const u8 *addr);
/**
* tx_control_port - Send a frame over the 802.1X controlled port
* @priv: Private driver interface data
* @dest: Destination MAC address
* @proto: Ethertype in host byte order
* @buf: Frame payload starting from IEEE 802.1X header
* @len: Frame payload length
* @no_encrypt: Do not encrypt frame
*
* Returns 0 on success, else an error
*
* This is like a normal Ethernet send except that the driver is aware
* (by other means than the Ethertype) that this frame is special,
* and more importantly it gains an ordering between the transmission of
* the frame and other driver management operations such as key
* installations. This can be used to work around known limitations in
* IEEE 802.11 protocols such as race conditions between rekeying 4-way
* handshake message 4/4 and a PTK being overwritten.
*
* This function is only used for a given interface if the driver
* instance reports WPA_DRIVER_FLAGS_CONTROL_PORT capability. Otherwise,
* API users will fall back to sending the frame via a normal socket.
*/
int (*tx_control_port)(void *priv, const u8 *dest,
u16 proto, const u8 *buf, size_t len,
int no_encrypt);
/**
* hapd_send_eapol - Send an EAPOL packet (AP only)
* @priv: private driver interface data
* @addr: Destination MAC address
* @data: EAPOL packet starting with IEEE 802.1X header
* @data_len: Length of the EAPOL packet in octets
* @encrypt: Whether the frame should be encrypted
* @own_addr: Source MAC address
* @flags: WPA_STA_* flags for the destination station
*
* Returns: 0 on success, -1 on failure
*/
int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data,
size_t data_len, int encrypt,
const u8 *own_addr, u32 flags);
/**
* sta_deauth - Deauthenticate a station (AP only)
* @priv: Private driver interface data
* @own_addr: Source address and BSSID for the Deauthentication frame
* @addr: MAC address of the station to deauthenticate
* @reason: Reason code for the Deauthentiation frame
* Returns: 0 on success, -1 on failure
*
* This function requests a specific station to be deauthenticated and
* a Deauthentication frame to be sent to it.
*/
int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr,
u16 reason);
/**
* sta_disassoc - Disassociate a station (AP only)
* @priv: Private driver interface data
* @own_addr: Source address and BSSID for the Disassociation frame
* @addr: MAC address of the station to disassociate
* @reason: Reason code for the Disassociation frame
* Returns: 0 on success, -1 on failure
*
* This function requests a specific station to be disassociated and
* a Disassociation frame to be sent to it.
*/
int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr,
u16 reason);
/**
* sta_remove - Remove a station entry (AP only)
* @priv: Private driver interface data
* @addr: MAC address of the station to be removed
* Returns: 0 on success, -1 on failure
*/
int (*sta_remove)(void *priv, const u8 *addr);
/**
* hapd_get_ssid - Get the current SSID (AP only)
* @priv: Private driver interface data
* @buf: Buffer for returning the SSID
* @len: Maximum length of the buffer
* Returns: Length of the SSID on success, -1 on failure
*
* This function need not be implemented if the driver uses Beacon
* template from set_ap() and does not reply to Probe Request frames.
*/
int (*hapd_get_ssid)(void *priv, u8 *buf, int len);
/**
* hapd_set_ssid - Set SSID (AP only)
* @priv: Private driver interface data
* @buf: SSID
* @len: Length of the SSID in octets
* Returns: 0 on success, -1 on failure
*
* DEPRECATED - use set_ap() instead
*/
int (*hapd_set_ssid)(void *priv, const u8 *buf, int len);
/**
* hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP)
* @priv: Private driver interface data
* @enabled: 1 = countermeasures enabled, 0 = disabled
* Returns: 0 on success, -1 on failure
*
* This need not be implemented if the driver does not take care of
* association processing.
*/
int (*hapd_set_countermeasures)(void *priv, int enabled);
/**
* sta_add - Add a station entry
* @priv: Private driver interface data
* @params: Station parameters
* Returns: 0 on success, -1 on failure
*
* This function is used to add or set (params->set 1) a station
* entry in the driver. Adding STA entries is used only if the driver
* does not take care of association processing.
*
* With drivers that don't support full AP client state, this function
* is used to add a station entry to the driver once the station has
* completed association.
*
* With TDLS, this function is used to add or set (params->set 1)
* TDLS peer entries (even with drivers that do not support full AP
* client state).
*/
int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
/**
* get_inact_sec - Get station inactivity duration (AP only)
* @priv: Private driver interface data
* @addr: Station address
* Returns: Number of seconds station has been inactive, -1 on failure
*/
int (*get_inact_sec)(void *priv, const u8 *addr);
/**
* sta_clear_stats - Clear station statistics (AP only)
* @priv: Private driver interface data
* @addr: Station address
* Returns: 0 on success, -1 on failure
*/
int (*sta_clear_stats)(void *priv, const u8 *addr);
/**
* set_freq - Set channel/frequency (AP only)
* @priv: Private driver interface data
* @freq: Channel parameters
* Returns: 0 on success, -1 on failure
*/
int (*set_freq)(void *priv, struct hostapd_freq_params *freq);
/**
* set_rts - Set RTS threshold
* @priv: Private driver interface data
* @rts: RTS threshold in octets
* Returns: 0 on success, -1 on failure
*/
int (*set_rts)(void *priv, int rts);
/**
* set_frag - Set fragmentation threshold
* @priv: Private driver interface data
* @frag: Fragmentation threshold in octets
* Returns: 0 on success, -1 on failure
*/
int (*set_frag)(void *priv, int frag);
/**
* sta_set_flags - Set station flags (AP only)
* @priv: Private driver interface data
* @addr: Station address
* @total_flags: Bitmap of all WPA_STA_* flags currently set
* @flags_or: Bitmap of WPA_STA_* flags to add
* @flags_and: Bitmap of WPA_STA_* flags to us as a mask
* Returns: 0 on success, -1 on failure
*/
int (*sta_set_flags)(void *priv, const u8 *addr,
unsigned int total_flags, unsigned int flags_or,
unsigned int flags_and);
/**
* sta_set_airtime_weight - Set station airtime weight (AP only)
* @priv: Private driver interface data
* @addr: Station address
* @weight: New weight for station airtime assignment
* Returns: 0 on success, -1 on failure
*/
int (*sta_set_airtime_weight)(void *priv, const u8 *addr,
unsigned int weight);
/**
* set_tx_queue_params - Set TX queue parameters
* @priv: Private driver interface data
* @queue: Queue number (0 = VO, 1 = VI, 2 = BE, 3 = BK)
* @aifs: AIFS
* @cw_min: cwMin
* @cw_max: cwMax
* @burst_time: Maximum length for bursting in 0.1 msec units
*/
int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min,
int cw_max, int burst_time);
/**
* if_add - Add a virtual interface
* @priv: Private driver interface data
* @type: Interface type
* @ifname: Interface name for the new virtual interface
* @addr: Local address to use for the interface or %NULL to use the
* parent interface address
* @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
* @drv_priv: Pointer for overwriting the driver context or %NULL if
* not allowed (applies only to %WPA_IF_AP_BSS type)
* @force_ifname: Buffer for returning an interface name that the
* driver ended up using if it differs from the requested ifname
* @if_addr: Buffer for returning the allocated interface address
* (this may differ from the requested addr if the driver cannot
* change interface address)
* @bridge: Bridge interface to use or %NULL if no bridge configured
* @use_existing: Whether to allow existing interface to be used
* @setup_ap: Whether to setup AP for %WPA_IF_AP_BSS interfaces
* Returns: 0 on success, -1 on failure
*/
int (*if_add)(void *priv, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr, void *bss_ctx,
void **drv_priv, char *force_ifname, u8 *if_addr,
const char *bridge, int use_existing, int setup_ap);
/**
* if_remove - Remove a virtual interface
* @priv: Private driver interface data
* @type: Interface type
* @ifname: Interface name of the virtual interface to be removed
* Returns: 0 on success, -1 on failure
*/
int (*if_remove)(void *priv, enum wpa_driver_if_type type,
const char *ifname);
/**
* set_sta_vlan - Bind a station into a specific interface (AP only)
* @priv: Private driver interface data
* @ifname: Interface (main or virtual BSS or VLAN)
* @addr: MAC address of the associated station
* @vlan_id: VLAN ID
* Returns: 0 on success, -1 on failure
*
* This function is used to bind a station to a specific virtual
* interface. It is only used if when virtual interfaces are supported,
* e.g., to assign stations to different VLAN interfaces based on
* information from a RADIUS server. This allows separate broadcast
* domains to be used with a single BSS.
*/
int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname,
int vlan_id);
/**
* commit - Optional commit changes handler (AP only)
* @priv: driver private data
* Returns: 0 on success, -1 on failure
*
* This optional handler function can be registered if the driver
* interface implementation needs to commit changes (e.g., by setting
* network interface up) at the end of initial configuration. If set,
* this handler will be called after initial setup has been completed.
*/
int (*commit)(void *priv);
/**
* set_radius_acl_auth - Notification of RADIUS ACL change
* @priv: Private driver interface data
* @mac: MAC address of the station
* @accepted: Whether the station was accepted
* @session_timeout: Session timeout for the station
* Returns: 0 on success, -1 on failure
*/
int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted,
u32 session_timeout);
/**
* set_radius_acl_expire - Notification of RADIUS ACL expiration
* @priv: Private driver interface data
* @mac: MAC address of the station
* Returns: 0 on success, -1 on failure
*/
int (*set_radius_acl_expire)(void *priv, const u8 *mac);
/**
* set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP)
* @priv: Private driver interface data
* @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s)
* @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove
* extra IE(s)
* @assocresp: WPS IE(s) for (Re)Association Response frames or %NULL
* to remove extra IE(s)
* Returns: 0 on success, -1 on failure
*
* This is an optional function to add WPS IE in the kernel driver for
* Beacon and Probe Response frames. This can be left undefined (set
* to %NULL) if the driver uses the Beacon template from set_ap()
* and does not process Probe Request frames. If the driver takes care
* of (Re)Association frame processing, the assocresp buffer includes
* WPS IE(s) that need to be added to (Re)Association Response frames
* whenever a (Re)Association Request frame indicated use of WPS.
*
* This will also be used to add P2P IE(s) into Beacon/Probe Response
* frames when operating as a GO. The driver is responsible for adding
* timing related attributes (e.g., NoA) in addition to the IEs
* included here by appending them after these buffers. This call is
* also used to provide Probe Response IEs for P2P Listen state
* operations for drivers that generate the Probe Response frames
* internally.
*
* DEPRECATED - use set_ap() instead
*/
int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon,
const struct wpabuf *proberesp,
const struct wpabuf *assocresp);
/**
* set_supp_port - Set IEEE 802.1X Supplicant Port status
* @priv: Private driver interface data
* @authorized: Whether the port is authorized
* Returns: 0 on success, -1 on failure
*/
int (*set_supp_port)(void *priv, int authorized);
/**
* set_wds_sta - Bind a station into a 4-address WDS (AP only)
* @priv: Private driver interface data
* @addr: MAC address of the associated station
* @aid: Association ID
* @val: 1 = bind to 4-address WDS; 0 = unbind
* @bridge_ifname: Bridge interface to use for the WDS station or %NULL
* to indicate that bridge is not to be used
* @ifname_wds: Buffer to return the interface name for the new WDS
* station or %NULL to indicate name is not returned.
* Returns: 0 on success, -1 on failure
*/
int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
const char *bridge_ifname, char *ifname_wds);
/**
* send_action - Transmit an Action frame
* @priv: Private driver interface data
* @freq: Frequency (in MHz) of the channel
* @wait: Time to wait off-channel for a response (in ms), or zero
* @dst: Destination MAC address (Address 1)
* @src: Source MAC address (Address 2)
* @bssid: BSSID (Address 3)
* @data: Frame body
* @data_len: data length in octets
@ @no_cck: Whether CCK rates must not be used to transmit this frame
* Returns: 0 on success, -1 on failure
*
* This command can be used to request the driver to transmit an action
* frame to the specified destination.
*
* If the %WPA_DRIVER_FLAGS_OFFCHANNEL_TX flag is set, the frame will
* be transmitted on the given channel and the device will wait for a
* response on that channel for the given wait time.
*
* If the flag is not set, the wait time will be ignored. In this case,
* if a remain-on-channel duration is in progress, the frame must be
* transmitted on that channel; alternatively the frame may be sent on
* the current operational channel (if in associated state in station
* mode or while operating as an AP.)
*
* If @src differs from the device MAC address, use of a random
* transmitter address is requested for this message exchange.
*/
int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
const u8 *dst, const u8 *src, const u8 *bssid,
const u8 *data, size_t data_len, int no_cck);
/**
* send_action_cancel_wait - Cancel action frame TX wait
* @priv: Private driver interface data
*
* This command cancels the wait time associated with sending an action
* frame. It is only available when %WPA_DRIVER_FLAGS_OFFCHANNEL_TX is
* set in the driver flags.
*/
void (*send_action_cancel_wait)(void *priv);
/**
* remain_on_channel - Remain awake on a channel
* @priv: Private driver interface data
* @freq: Frequency (in MHz) of the channel
* @duration: Duration in milliseconds
* Returns: 0 on success, -1 on failure
*
* This command is used to request the driver to remain awake on the
* specified channel for the specified duration and report received
* Action frames with EVENT_RX_MGMT events. Optionally, received
* Probe Request frames may also be requested to be reported by calling
* probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ.
*
* The driver may not be at the requested channel when this function
* returns, i.e., the return code is only indicating whether the
* request was accepted. The caller will need to wait until the
* EVENT_REMAIN_ON_CHANNEL event indicates that the driver has
* completed the channel change. This may take some time due to other
* need for the radio and the caller should be prepared to timing out
* its wait since there are no guarantees on when this request can be
* executed.
*/
int (*remain_on_channel)(void *priv, unsigned int freq,
unsigned int duration);
/**
* cancel_remain_on_channel - Cancel remain-on-channel operation
* @priv: Private driver interface data
*
* This command can be used to cancel a remain-on-channel operation
* before its originally requested duration has passed. This could be
* used, e.g., when remain_on_channel() is used to request extra time
* to receive a response to an Action frame and the response is
* received when there is still unneeded time remaining on the
* remain-on-channel operation.
*/
int (*cancel_remain_on_channel)(void *priv);
/**
* probe_req_report - Request Probe Request frames to be indicated
* @priv: Private driver interface data
* @report: Whether to report received Probe Request frames
* Returns: 0 on success, -1 on failure (or if not supported)
*
* This command can be used to request the driver to indicate when
* Probe Request frames are received with EVENT_RX_PROBE_REQ events.
* Since this operation may require extra resources, e.g., due to less
* optimal hardware/firmware RX filtering, many drivers may disable
* Probe Request reporting at least in station mode. This command is
* used to notify the driver when the Probe Request frames need to be
* reported, e.g., during remain-on-channel operations.
*/
int (*probe_req_report)(void *priv, int report);
/**
* deinit_ap - Deinitialize AP mode
* @priv: Private driver interface data
* Returns: 0 on success, -1 on failure (or if not supported)
*
* This optional function can be used to disable AP mode related
* configuration. If the interface was not dynamically added,
* change the driver mode to station mode to allow normal station
* operations like scanning to be completed.
*/
int (*deinit_ap)(void *priv);
/**
* deinit_p2p_cli - Deinitialize P2P client mode
* @priv: Private driver interface data
* Returns: 0 on success, -1 on failure (or if not supported)
*
* This optional function can be used to disable P2P client mode. If the
* interface was not dynamically added, change the interface type back
* to station mode.
*/
int (*deinit_p2p_cli)(void *priv);
/**
* suspend - Notification on system suspend/hibernate event
* @priv: Private driver interface data
*/
void (*suspend)(void *priv);
/**
* resume - Notification on system resume/thaw event
* @priv: Private driver interface data
*/
void (*resume)(void *priv);
/**
* signal_monitor - Set signal monitoring parameters
* @priv: Private driver interface data
* @threshold: Threshold value for signal change events; 0 = disabled
* @hysteresis: Minimum change in signal strength before indicating a
* new event
* Returns: 0 on success, -1 on failure (or if not supported)
*
* This function can be used to configure monitoring of signal strength
* with the current AP. Whenever signal strength drops below the
* %threshold value or increases above it, EVENT_SIGNAL_CHANGE event
* should be generated assuming the signal strength has changed at
* least %hysteresis from the previously indicated signal change event.
*/
int (*signal_monitor)(void *priv, int threshold, int hysteresis);
/**
* get_noa - Get current Notice of Absence attribute payload
* @priv: Private driver interface data
* @buf: Buffer for returning NoA
* @buf_len: Buffer length in octets
* Returns: Number of octets used in buf, 0 to indicate no NoA is being
* advertized, or -1 on failure
*
* This function is used to fetch the current Notice of Absence
* attribute value from GO.
*/
int (*get_noa)(void *priv, u8 *buf, size_t buf_len);
/**
* set_noa - Set Notice of Absence parameters for GO (testing)
* @priv: Private driver interface data
* @count: Count
* @start: Start time in ms from next TBTT
* @duration: Duration in ms
* Returns: 0 on success or -1 on failure
*
* This function is used to set Notice of Absence parameters for GO. It
* is used only for testing. To disable NoA, all parameters are set to
* 0.
*/
int (*set_noa)(void *priv, u8 count, int start, int duration);
/**
* set_p2p_powersave - Set P2P power save options
* @priv: Private driver interface data
* @legacy_ps: 0 = disable, 1 = enable, 2 = maximum PS, -1 = no change
* @opp_ps: 0 = disable, 1 = enable, -1 = no change
* @ctwindow: 0.. = change (msec), -1 = no change
* Returns: 0 on success or -1 on failure
*/
int (*set_p2p_powersave)(void *priv, int legacy_ps, int opp_ps,
int ctwindow);
/**
* ampdu - Enable/disable aggregation
* @priv: Private driver interface data
* @ampdu: 1/0 = enable/disable A-MPDU aggregation
* Returns: 0 on success or -1 on failure
*/
int (*ampdu)(void *priv, int ampdu);
/**
* get_radio_name - Get physical radio name for the device
* @priv: Private driver interface data
* Returns: Radio name or %NULL if not known
*
* The returned data must not be modified by the caller. It is assumed
* that any interface that has the same radio name as another is
* sharing the same physical radio. This information can be used to
* share scan results etc. information between the virtual interfaces
* to speed up various operations.
*/
const char * (*get_radio_name)(void *priv);
/**
* send_tdls_mgmt - for sending TDLS management packets
* @priv: private driver interface data
* @dst: Destination (peer) MAC address
* @action_code: TDLS action code for the mssage
* @dialog_token: Dialog Token to use in the message (if needed)
* @status_code: Status Code or Reason Code to use (if needed)
* @peer_capab: TDLS peer capability (TDLS_PEER_* bitfield)
* @initiator: Is the current end the TDLS link initiator
* @buf: TDLS IEs to add to the message
* @len: Length of buf in octets
* Returns: 0 on success, negative (<0) on failure
*
* This optional function can be used to send packet to driver which is
* responsible for receiving and sending all TDLS packets.
*/
int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code,
u8 dialog_token, u16 status_code, u32 peer_capab,
int initiator, const u8 *buf, size_t len);
/**
* tdls_oper - Ask the driver to perform high-level TDLS operations
* @priv: Private driver interface data
* @oper: TDLS high-level operation. See %enum tdls_oper
* @peer: Destination (peer) MAC address
* Returns: 0 on success, negative (<0) on failure
*
* This optional function can be used to send high-level TDLS commands
* to the driver.
*/
int (*tdls_oper)(void *priv, enum tdls_oper oper, const u8 *peer);
/**
* wnm_oper - Notify driver of the WNM frame reception
* @priv: Private driver interface data
* @oper: WNM operation. See %enum wnm_oper
* @peer: Destination (peer) MAC address
* @buf: Buffer for the driver to fill in (for getting IE)
* @buf_len: Return the len of buf
* Returns: 0 on success, negative (<0) on failure
*/
int (*wnm_oper)(void *priv, enum wnm_oper oper, const u8 *peer,
u8 *buf, u16 *buf_len);
/**
* set_qos_map - Set QoS Map
* @priv: Private driver interface data
* @qos_map_set: QoS Map
* @qos_map_set_len: Length of QoS Map
*/
int (*set_qos_map)(void *priv, const u8 *qos_map_set,
u8 qos_map_set_len);
/**
* br_add_ip_neigh - Add a neigh to the bridge ip neigh table
* @priv: Private driver interface data
* @version: IP version of the IP address, 4 or 6
* @ipaddr: IP address for the neigh entry
* @prefixlen: IP address prefix length
* @addr: Corresponding MAC address
* Returns: 0 on success, negative (<0) on failure
*/
int (*br_add_ip_neigh)(void *priv, u8 version, const u8 *ipaddr,
int prefixlen, const u8 *addr);
/**
* br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table
* @priv: Private driver interface data
* @version: IP version of the IP address, 4 or 6
* @ipaddr: IP address for the neigh entry
* Returns: 0 on success, negative (<0) on failure
*/
int (*br_delete_ip_neigh)(void *priv, u8 version, const u8 *ipaddr);
/**
* br_port_set_attr - Set a bridge port attribute
* @attr: Bridge port attribute to set
* @val: Value to be set
* Returns: 0 on success, negative (<0) on failure
*/
int (*br_port_set_attr)(void *priv, enum drv_br_port_attr attr,
unsigned int val);
/**
* br_port_set_attr - Set a bridge network parameter
* @param: Bridge parameter to set
* @val: Value to be set
* Returns: 0 on success, negative (<0) on failure
*/
int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
unsigned int val);
/**
* get_wowlan - Get wake-on-wireless status
* @priv: Private driver interface data
*/
int (*get_wowlan)(void *priv);
/**
* set_wowlan - Set wake-on-wireless triggers
* @priv: Private driver interface data
* @triggers: wowlan triggers
*/
int (*set_wowlan)(void *priv, const struct wowlan_triggers *triggers);
/**
* signal_poll - Get current connection information
* @priv: Private driver interface data
* @signal_info: Connection info structure
*/
int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
/**
* channel_info - Get parameters of the current operating channel
* @priv: Private driver interface data
* @channel_info: Channel info structure
* Returns: 0 on success, negative (<0) on failure
*/
int (*channel_info)(void *priv, struct wpa_channel_info *channel_info);
/**
* set_authmode - Set authentication algorithm(s) for static WEP
* @priv: Private driver interface data
* @authmode: 1=Open System, 2=Shared Key, 3=both
* Returns: 0 on success, -1 on failure
*
* This function can be used to set authentication algorithms for AP
* mode when static WEP is used. If the driver uses user space MLME/SME
* implementation, there is no need to implement this function.
*
* DEPRECATED - use set_ap() instead
*/
int (*set_authmode)(void *priv, int authmode);
#ifdef ANDROID
/**
* driver_cmd - Execute driver-specific command
* @priv: Private driver interface data
* @cmd: Command to execute
* @buf: Return buffer
* @buf_len: Buffer length
* Returns: 0 on success, -1 on failure
*/
int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len);
#endif /* ANDROID */
/**
* vendor_cmd - Execute vendor specific command
* @priv: Private driver interface data
* @vendor_id: Vendor id
* @subcmd: Vendor command id
* @nested_attr_flag: Specifies if vendor subcommand uses nested
* attributes or not
* @data: Vendor command parameters (%NULL if no parameters)
* @data_len: Data length
* @buf: Return buffer (%NULL to ignore reply)
* Returns: 0 on success, negative (<0) on failure
*
* This function handles vendor specific commands that are passed to
* the driver/device. The command is identified by vendor id and
* command id. The nested_attr_flag specifies whether the subcommand
* uses nested attributes or not. Parameters can be passed
* as argument to the command in the data buffer. Reply (if any) will be
* filled in the supplied return buffer.
*
* The exact driver behavior is driver interface and vendor specific. As
* an example, this will be converted to a vendor specific cfg80211
* command in case of the nl80211 driver interface.
*/
int (*vendor_cmd)(void *priv, unsigned int vendor_id,
unsigned int subcmd, const u8 *data, size_t data_len,
enum nested_attr nested_attr_flag,
struct wpabuf *buf);
/**
* set_rekey_info - Set rekey information
* @priv: Private driver interface data
* @kek: Current KEK
* @kek_len: KEK length in octets
* @kck: Current KCK
* @kck_len: KCK length in octets
* @replay_ctr: Current EAPOL-Key Replay Counter
*
* This optional function can be used to provide information for the
* driver/firmware to process EAPOL-Key frames in Group Key Handshake
* while the host (including wpa_supplicant) is sleeping.
*/
void (*set_rekey_info)(void *priv, const u8 *kek, size_t kek_len,
const u8 *kck, size_t kck_len,
const u8 *replay_ctr);
/**
* sta_assoc - Station association indication
* @priv: Private driver interface data
* @own_addr: Source address and BSSID for association frame
* @addr: MAC address of the station to associate
* @reassoc: flag to indicate re-association
* @status: association response status code
* @ie: assoc response ie buffer
* @len: ie buffer length
* Returns: 0 on success, -1 on failure
*
* This function indicates the driver to send (Re)Association
* Response frame to the station.
*/
int (*sta_assoc)(void *priv, const u8 *own_addr, const u8 *addr,
int reassoc, u16 status, const u8 *ie, size_t len);
/**
* sta_auth - Station authentication indication
* @priv: private driver interface data
* @params: Station authentication parameters
*
* Returns: 0 on success, -1 on failure
*/
int (*sta_auth)(void *priv,
struct wpa_driver_sta_auth_params *params);
/**
* add_tspec - Add traffic stream
* @priv: Private driver interface data
* @addr: MAC address of the station to associate
* @tspec_ie: tspec ie buffer
* @tspec_ielen: tspec ie length
* Returns: 0 on success, -1 on failure
*
* This function adds the traffic steam for the station
* and fills the medium_time in tspec_ie.
*/
int (*add_tspec)(void *priv, const u8 *addr, u8 *tspec_ie,
size_t tspec_ielen);
/**
* add_sta_node - Add a station node in the driver
* @priv: Private driver interface data
* @addr: MAC address of the station to add
* @auth_alg: authentication algorithm used by the station
* Returns: 0 on success, -1 on failure
*
* This function adds the station node in the driver, when
* the station gets added by FT-over-DS.
*/
int (*add_sta_node)(void *priv, const u8 *addr, u16 auth_alg);
/**
* sched_scan - Request the driver to initiate scheduled scan
* @priv: Private driver interface data
* @params: Scan parameters
* Returns: 0 on success, -1 on failure
*
* This operation should be used for scheduled scan offload to
* the hardware. Every time scan results are available, the
* driver should report scan results event for wpa_supplicant
* which will eventually request the results with
* wpa_driver_get_scan_results2(). This operation is optional
* and if not provided or if it returns -1, we fall back to
* normal host-scheduled scans.
*/
int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params);
/**
* stop_sched_scan - Request the driver to stop a scheduled scan
* @priv: Private driver interface data
* Returns: 0 on success, -1 on failure
*
* This should cause the scheduled scan to be stopped and
* results should stop being sent. Must be supported if
* sched_scan is supported.
*/
int (*stop_sched_scan)(void *priv);
/**
* poll_client - Probe (null data or such) the given station
* @priv: Private driver interface data
* @own_addr: MAC address of sending interface
* @addr: MAC address of the station to probe
* @qos: Indicates whether station is QoS station
*
* This function is used to verify whether an associated station is
* still present. This function does not need to be implemented if the
* driver provides such inactivity polling mechanism.
*/
void (*poll_client)(void *priv, const u8 *own_addr,
const u8 *addr, int qos);
/**
* radio_disable - Disable/enable radio
* @priv: Private driver interface data
* @disabled: 1=disable 0=enable radio
* Returns: 0 on success, -1 on failure
*
* This optional command is for testing purposes. It can be used to
* disable the radio on a testbed device to simulate out-of-radio-range
* conditions.
*/
int (*radio_disable)(void *priv, int disabled);
/**
* switch_channel - Announce channel switch and migrate the GO to the
* given frequency
* @priv: Private driver interface data
* @settings: Settings for CSA period and new channel
* Returns: 0 on success, -1 on failure
*
* This function is used to move the GO to the legacy STA channel to
* avoid frequency conflict in single channel concurrency.
*/
int (*switch_channel)(void *priv, struct csa_settings *settings);
/**
* add_tx_ts - Add traffic stream
* @priv: Private driver interface data
* @tsid: Traffic stream ID
* @addr: Receiver address
* @user_prio: User priority of the traffic stream
* @admitted_time: Admitted time for this TS in units of
* 32 microsecond periods (per second).
* Returns: 0 on success, -1 on failure
*/
int (*add_tx_ts)(void *priv, u8 tsid, const u8 *addr, u8 user_prio,
u16 admitted_time);
/**
* del_tx_ts - Delete traffic stream
* @priv: Private driver interface data
* @tsid: Traffic stream ID
* @addr: Receiver address
* Returns: 0 on success, -1 on failure
*/
int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr);
/**
* Enable channel-switching with TDLS peer
* @priv: Private driver interface data
* @addr: MAC address of the TDLS peer
* @oper_class: Operating class of the switch channel
* @params: Channel specification
* Returns: 0 on success, -1 on failure
*
* The function indicates to driver that it can start switching to a
* different channel with a specified TDLS peer. The switching is
* assumed on until canceled with tdls_disable_channel_switch().
*/
int (*tdls_enable_channel_switch)(
void *priv, const u8 *addr, u8 oper_class,
const struct hostapd_freq_params *params);
/**
* Disable channel switching with TDLS peer
* @priv: Private driver interface data
* @addr: MAC address of the TDLS peer
* Returns: 0 on success, -1 on failure
*
* This function indicates to the driver that it should stop switching
* with a given TDLS peer.
*/
int (*tdls_disable_channel_switch)(void *priv, const u8 *addr);
/**
* start_dfs_cac - Listen for radar interference on the channel
* @priv: Private driver interface data
* @freq: Channel parameters
* Returns: 0 on success, -1 on failure
*/
int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq);
/**
* stop_ap - Removes beacon from AP
* @priv: Private driver interface data
* Returns: 0 on success, -1 on failure (or if not supported)
*
* This optional function can be used to disable AP mode related
* configuration. Unlike deinit_ap, it does not change to station
* mode.
*/
int (*stop_ap)(void *priv);
/**
* get_survey - Retrieve survey data
* @priv: Private driver interface data
* @freq: If set, survey data for the specified frequency is only
* being requested. If not set, all survey data is requested.
* Returns: 0 on success, -1 on failure
*
* Use this to retrieve:
*
* - the observed channel noise floor
* - the amount of time we have spent on the channel
* - the amount of time during which we have spent on the channel that
* the radio has determined the medium is busy and we cannot
* transmit
* - the amount of time we have spent receiving data
* - the amount of time we have spent transmitting data
*
* This data can be used for spectrum heuristics. One example is
* Automatic Channel Selection (ACS). The channel survey data is
* kept on a linked list on the channel data, one entry is added
* for each survey. The min_nf of the channel is updated for each
* survey.
*/
int (*get_survey)(void *priv, unsigned int freq);
/**
* status - Get driver interface status information
* @priv: Private driver interface data
* @buf: Buffer for printing the status information
* @buflen: Maximum length of the buffer
* Returns: Length of written status information or -1 on failure
*/
int (*status)(void *priv, char *buf, size_t buflen);
/**
* roaming - Set roaming policy for driver-based BSS selection
* @priv: Private driver interface data
* @allowed: Whether roaming within ESS is allowed
* @bssid: Forced BSSID if roaming is disabled or %NULL if not set
* Returns: Length of written status information or -1 on failure
*
* This optional callback can be used to update roaming policy from the
* associate() command (bssid being set there indicates that the driver
* should not roam before getting this roaming() call to allow roaming.
* If the driver does not indicate WPA_DRIVER_FLAGS_BSS_SELECTION
* capability, roaming policy is handled within wpa_supplicant and there
* is no need to implement or react to this callback.
*/
int (*roaming)(void *priv, int allowed, const u8 *bssid);
/**
* disable_fils - Enable/disable FILS feature
* @priv: Private driver interface data
* @disable: 0-enable and 1-disable FILS feature
* Returns: 0 on success, -1 on failure
*
* This callback can be used to configure driver and below layers to
* enable/disable all FILS features.
*/
int (*disable_fils)(void *priv, int disable);
/**
* set_mac_addr - Set MAC address
* @priv: Private driver interface data
* @addr: MAC address to use or %NULL for setting back to permanent
* Returns: 0 on success, -1 on failure
*/
int (*set_mac_addr)(void *priv, const u8 *addr);
#ifdef CONFIG_MACSEC
int (*macsec_init)(void *priv, struct macsec_init_params *params);
int (*macsec_deinit)(void *priv);
/**
* macsec_get_capability - Inform MKA of this driver's capability
* @priv: Private driver interface data
* @cap: Driver's capability
* Returns: 0 on success, -1 on failure
*/
int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
/**
* enable_protect_frames - Set protect frames status
* @priv: Private driver interface data
* @enabled: true = protect frames enabled
* false = protect frames disabled
* Returns: 0 on success, -1 on failure (or if not supported)
*/
int (*enable_protect_frames)(void *priv, bool enabled);
/**
* enable_encrypt - Set encryption status
* @priv: Private driver interface data
* @enabled: true = encrypt outgoing traffic
* false = integrity-only protection on outgoing traffic
* Returns: 0 on success, -1 on failure (or if not supported)
*/
int (*enable_encrypt)(void *priv, bool enabled);
/**
* set_replay_protect - Set replay protect status and window size
* @priv: Private driver interface data
* @enabled: true = replay protect enabled
* false = replay protect disabled
* @window: replay window size, valid only when replay protect enabled
* Returns: 0 on success, -1 on failure (or if not supported)
*/
int (*set_replay_protect)(void *priv, bool enabled, u32 window);
/**
* set_current_cipher_suite - Set current cipher suite
* @priv: Private driver interface data
* @cs: EUI64 identifier
* Returns: 0 on success, -1 on failure (or if not supported)
*/
int (*set_current_cipher_suite)(void *priv, u64 cs);
/**
* enable_controlled_port - Set controlled port status
* @priv: Private driver interface data
* @enabled: true = controlled port enabled
* false = controlled port disabled
* Returns: 0 on success, -1 on failure (or if not supported)
*/
int (*enable_controlled_port)(void *priv, bool enabled);
/**
* get_receive_lowest_pn - Get receive lowest pn
* @priv: Private driver interface data
* @sa: secure association
* Returns: 0 on success, -1 on failure (or if not supported)
*/
int (*get_receive_lowest_pn)(void *priv, struct receive_sa *sa);
/**
* get_transmit_next_pn - Get transmit next pn
* @priv: Private driver interface data
* @sa: secure association
* Returns: 0 on success, -1 on failure (or if not supported)
*/
int (*get_transmit_next_pn)(void *priv, struct transmit_sa *sa);
/**
* set_transmit_next_pn - Set transmit next pn
* @priv: Private driver interface data
* @sa: secure association
* Returns: 0 on success, -1 on failure (or if not supported)
*/
int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa);
/**
* set_receive_lowest_pn - Set receive lowest PN
* @priv: Private driver interface data
* @sa: secure association
* Returns: 0 on success, -1 on failure (or if not supported)
*/
int (*set_receive_lowest_pn)(void *priv, struct receive_sa *sa);
/**
* create_receive_sc - create secure channel for receiving
* @priv: Private driver interface data
* @sc: secure channel
* @conf_offset: confidentiality offset (0, 30, or 50)
* @validation: frame validation policy (0 = Disabled, 1 = Checked,
* 2 = Strict)
* Returns: 0 on success, -1 on failure (or if not supported)
*/
int (*create_receive_sc)(void *priv, struct receive_sc *sc,
unsigned int conf_offset,
int validation);
/**
* delete_receive_sc - delete secure connection for receiving
* @priv: private driver interface data from init()
* @sc: secure channel
* Returns: 0 on success, -1 on failure
*/
int (*delete_receive_sc)(void *priv, struct receive_sc *sc);
/**
* create_receive_sa - create secure association for receive
* @priv: private driver interface data from init()
* @sa: secure association
* Returns: 0 on success, -1 on failure
*/
int (*create_receive_sa)(void *priv, struct receive_sa *sa);
/**
* delete_receive_sa - Delete secure association for receive
* @priv: Private driver interface data from init()
* @sa: Secure association
* Returns: 0 on success, -1 on failure
*/
int (*delete_receive_sa)(void *priv, struct receive_sa *sa);
/**
* enable_receive_sa - enable the SA for receive
* @priv: private driver interface data from init()
* @sa: secure association
* Returns: 0 on success, -1 on failure
*/
int (*enable_receive_sa)(void *priv, struct receive_sa *sa);
/**
* disable_receive_sa - disable SA for receive
* @priv: private driver interface data from init()
* @sa: secure association
* Returns: 0 on success, -1 on failure
*/
int (*disable_receive_sa)(void *priv, struct receive_sa *sa);
/**
* create_transmit_sc - create secure connection for transmit
* @priv: private driver interface data from init()
* @sc: secure channel
* @conf_offset: confidentiality offset (0, 30, or 50)
* Returns: 0 on success, -1 on failure
*/
int (*create_transmit_sc)(void *priv, struct transmit_sc *sc,
unsigned int conf_offset);
/**
* delete_transmit_sc - delete secure connection for transmit
* @priv: private driver interface data from init()
* @sc: secure channel
* Returns: 0 on success, -1 on failure
*/
int (*delete_transmit_sc)(void *priv, struct transmit_sc *sc);
/**
* create_transmit_sa - create secure association for transmit
* @priv: private driver interface data from init()
* @sa: secure association
* Returns: 0 on success, -1 on failure
*/
int (*create_transmit_sa)(void *priv, struct transmit_sa *sa);
/**
* delete_transmit_sa - Delete secure association for transmit
* @priv: Private driver interface data from init()
* @sa: Secure association
* Returns: 0 on success, -1 on failure
*/
int (*delete_transmit_sa)(void *priv, struct transmit_sa *sa);
/**
* enable_transmit_sa - enable SA for transmit
* @priv: private driver interface data from init()
* @sa: secure association
* Returns: 0 on success, -1 on failure
*/
int (*enable_transmit_sa)(void *priv, struct transmit_sa *sa);
/**
* disable_transmit_sa - disable SA for transmit
* @priv: private driver interface data from init()
* @sa: secure association
* Returns: 0 on success, -1 on failure
*/
int (*disable_transmit_sa)(void *priv, struct transmit_sa *sa);
#endif /* CONFIG_MACSEC */
/**
* init_mesh - Driver specific initialization for mesh
* @priv: Private driver interface data
* Returns: 0 on success, -1 on failure
*/
int (*init_mesh)(void *priv);
/**
* join_mesh - Join a mesh network
* @priv: Private driver interface data
* @params: Mesh configuration parameters
* Returns: 0 on success, -1 on failure
*/
int (*join_mesh)(void *priv,
struct wpa_driver_mesh_join_params *params);
/**
* leave_mesh - Leave a mesh network
* @priv: Private driver interface data
* Returns 0 on success, -1 on failure
*/
int (*leave_mesh)(void *priv);
/**
* probe_mesh_link - Inject a frame over direct mesh link to a given
* peer skipping the next_hop lookup from mpath table.
* @priv: Private driver interface data
* @addr: Peer MAC address
* @eth: Ethernet frame to be sent
* @len: Ethernet frame lengtn in bytes
* Returns 0 on success, -1 on failure
*/
int (*probe_mesh_link)(void *priv, const u8 *addr, const u8 *eth,
size_t len);
/**
* do_acs - Automatically select channel
* @priv: Private driver interface data
* @params: Parameters for ACS
* Returns 0 on success, -1 on failure
*
* This command can be used to offload ACS to the driver if the driver
* indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD).
*/
int (*do_acs)(void *priv, struct drv_acs_params *params);
/**
* set_band - Notify driver of band(s) selection
* @priv: Private driver interface data
* @band_mask: The selected band(s) bit mask (from enum set_band)
* Returns 0 on success, -1 on failure
*/
int (*set_band)(void *priv, u32 band_mask);
/**
* get_pref_freq_list - Get preferred frequency list for an interface
* @priv: Private driver interface data
* @if_type: Interface type
* @num: Number of channels
* @freq_list: Preferred channel frequency list encoded in MHz values
* Returns 0 on success, -1 on failure
*
* This command can be used to query the preferred frequency list from
* the driver specific to a particular interface type.
*/
int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
unsigned int *num, unsigned int *freq_list);
/**
* set_prob_oper_freq - Indicate probable P2P operating channel
* @priv: Private driver interface data
* @freq: Channel frequency in MHz
* Returns 0 on success, -1 on failure
*
* This command can be used to inform the driver of the operating
* frequency that an ongoing P2P group formation is likely to come up
* on. Local device is assuming P2P Client role.
*/
int (*set_prob_oper_freq)(void *priv, unsigned int freq);
/**
* abort_scan - Request the driver to abort an ongoing scan
* @priv: Private driver interface data
* @scan_cookie: Cookie identifying the scan request. This is used only
* when the vendor interface QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN
* was used to trigger scan. Otherwise, 0 is used.
* Returns 0 on success, -1 on failure
*/
int (*abort_scan)(void *priv, u64 scan_cookie);
/**
* configure_data_frame_filters - Request to configure frame filters
* @priv: Private driver interface data
* @filter_flags: The type of frames to filter (bitfield of
* WPA_DATA_FRAME_FILTER_FLAG_*)
* Returns: 0 on success or -1 on failure
*/
int (*configure_data_frame_filters)(void *priv, u32 filter_flags);
/**
* get_ext_capab - Get extended capabilities for the specified interface
* @priv: Private driver interface data
* @type: Interface type for which to get extended capabilities
* @ext_capab: Extended capabilities fetched
* @ext_capab_mask: Extended capabilities mask
* @ext_capab_len: Length of the extended capabilities
* Returns: 0 on success or -1 on failure
*/
int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type,
const u8 **ext_capab, const u8 **ext_capab_mask,
unsigned int *ext_capab_len);
/**
* p2p_lo_start - Start offloading P2P listen to device
* @priv: Private driver interface data
* @freq: Listening frequency (MHz) for P2P listen
* @period: Length of the listen operation in milliseconds
* @interval: Interval for running the listen operation in milliseconds
* @count: Number of times to run the listen operation
* @device_types: Device primary and secondary types
* @dev_types_len: Number of bytes for device_types
* @ies: P2P IE and WSC IE for Probe Response frames
* @ies_len: Length of ies in bytes
* Returns: 0 on success or -1 on failure
*/
int (*p2p_lo_start)(void *priv, unsigned int freq,
unsigned int period, unsigned int interval,
unsigned int count,
const u8 *device_types, size_t dev_types_len,
const u8 *ies, size_t ies_len);
/**
* p2p_lo_stop - Stop P2P listen offload
* @priv: Private driver interface data
* Returns: 0 on success or -1 on failure
*/
int (*p2p_lo_stop)(void *priv);
/**
* set_default_scan_ies - Set default scan IEs
* @priv: Private driver interface data
* @ies: Scan default IEs buffer
* @ies_len: Length of IEs in bytes
* Returns: 0 on success or -1 on failure
*
* The driver can use these by default when there are no scan IEs coming
* in the subsequent scan requests. Also in case of one or more of IEs
* given in set_default_scan_ies() are missing in the subsequent scan
* request, the driver should merge the missing scan IEs in the scan
* request from the IEs set by set_default_scan_ies() in the Probe
* Request frames sent.
*/
int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len);
/**
* set_tdls_mode - Set TDLS trigger mode to the host driver
* @priv: Private driver interface data
* @tdls_external_control: Represents if TDLS external trigger control
* mode is enabled/disabled.
*
* This optional callback can be used to configure the TDLS external
* trigger control mode to the host driver.
*/
int (*set_tdls_mode)(void *priv, int tdls_external_control);
/**
* get_bss_transition_status - Get candidate BSS's transition status
* @priv: Private driver interface data
* @params: Candidate BSS list
*
* Get the accept or reject reason code for a list of BSS transition
* candidates.
*/
struct wpa_bss_candidate_info *
(*get_bss_transition_status)(void *priv,
struct wpa_bss_trans_info *params);
/**
* ignore_assoc_disallow - Configure driver to ignore assoc_disallow
* @priv: Private driver interface data
* @ignore_disallow: 0 to not ignore, 1 to ignore
* Returns: 0 on success, -1 on failure
*/
int (*ignore_assoc_disallow)(void *priv, int ignore_disallow);
/**
* set_bssid_tmp_disallow - Set disallowed BSSIDs to the driver
* @priv: Private driver interface data
* @num_bssid: Number of temporarily disallowed BSSIDs
* @bssids: List of temporarily disallowed BSSIDs
*/
int (*set_bssid_tmp_disallow)(void *priv, unsigned int num_bssid,
const u8 *bssid);
/**
* update_connect_params - Update the connection parameters
* @priv: Private driver interface data
* @params: Association parameters
* @mask: Bit mask indicating which parameters in @params have to be
* updated
* Returns: 0 on success, -1 on failure
*
* Update the connection parameters when in connected state so that the
* driver uses the updated parameters for subsequent roaming. This is
* used only with drivers that implement internal BSS selection and
* roaming.
*/
int (*update_connect_params)(
void *priv, struct wpa_driver_associate_params *params,
enum wpa_drv_update_connect_params_mask mask);
/**
* send_external_auth_status - Indicate the status of external
* authentication processing to the host driver.
* @priv: Private driver interface data
* @params: Status of authentication processing.
* Returns: 0 on success, -1 on failure
*/
int (*send_external_auth_status)(void *priv,
struct external_auth *params);
/**
* set_4addr_mode - Set 4-address mode
* @priv: Private driver interface data
* @bridge_ifname: Bridge interface name
* @val: 0 - disable 4addr mode, 1 - enable 4addr mode
* Returns: 0 on success, < 0 on failure
*/
int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val);
/**
* update_dh_ie - Update DH IE
* @priv: Private driver interface data
* @peer_mac: Peer MAC address
* @reason_code: Reacon code
* @ie: DH IE
* @ie_len: DH IE length in bytes
* Returns: 0 on success, -1 on failure
*
* This callback is used to let the driver know the DH processing result
* and DH IE for a pending association.
*/
int (*update_dh_ie)(void *priv, const u8 *peer_mac, u16 reason_code,
const u8 *ie, size_t ie_len);
/**
* dpp_listen - Notify driver about start/stop of DPP listen
* @priv: Private driver interface data
* @enable: Whether listen state is enabled (or disabled)
* Returns: 0 on success, -1 on failure
*
* This optional callback can be used to update RX frame filtering to
* explicitly allow reception of broadcast Public Action frames.
*/
int (*dpp_listen)(void *priv, bool enable);
#ifdef CONFIG_TESTING_OPTIONS
int (*register_frame)(void *priv, u16 type,
const u8 *match, size_t match_len,
bool multicast);
#endif /* CONFIG_TESTING_OPTIONS */
};
/**
* enum wpa_event_type - Event type for wpa_supplicant_event() calls
*/
enum wpa_event_type {
/**
* EVENT_ASSOC - Association completed
*
* This event needs to be delivered when the driver completes IEEE
* 802.11 association or reassociation successfully.
* wpa_driver_ops::get_bssid() is expected to provide the current BSSID
* after this event has been generated. In addition, optional
* EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide
* more information about the association. If the driver interface gets
* both of these events at the same time, it can also include the
* assoc_info data in EVENT_ASSOC call.
*/
EVENT_ASSOC,
/**
* EVENT_DISASSOC - Association lost
*
* This event should be called when association is lost either due to
* receiving deauthenticate or disassociate frame from the AP or when
* sending either of these frames to the current AP. If the driver
* supports separate deauthentication event, EVENT_DISASSOC should only
* be used for disassociation and EVENT_DEAUTH for deauthentication.
* In AP mode, union wpa_event_data::disassoc_info is required.
*/
EVENT_DISASSOC,
/**
* EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected
*
* This event must be delivered when a Michael MIC error is detected by
* the local driver. Additional data for event processing is
* provided with union wpa_event_data::michael_mic_failure. This
* information is used to request new encyption key and to initiate
* TKIP countermeasures if needed.
*/
EVENT_MICHAEL_MIC_FAILURE,
/**
* EVENT_SCAN_RESULTS - Scan results available
*
* This event must be called whenever scan results are available to be
* fetched with struct wpa_driver_ops::get_scan_results(). This event
* is expected to be used some time after struct wpa_driver_ops::scan()
* is called. If the driver provides an unsolicited event when the scan
* has been completed, this event can be used to trigger
* EVENT_SCAN_RESULTS call. If such event is not available from the
* driver, the driver wrapper code is expected to use a registered
* timeout to generate EVENT_SCAN_RESULTS call after the time that the
* scan is expected to be completed. Optional information about
* completed scan can be provided with union wpa_event_data::scan_info.
*/
EVENT_SCAN_RESULTS,
/**
* EVENT_ASSOCINFO - Report optional extra information for association
*
* This event can be used to report extra association information for
* EVENT_ASSOC processing. This extra information includes IEs from
* association frames and Beacon/Probe Response frames in union
* wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before
* EVENT_ASSOC. Alternatively, the driver interface can include
* assoc_info data in the EVENT_ASSOC call if it has all the
* information available at the same point.
*/
EVENT_ASSOCINFO,
/**
* EVENT_INTERFACE_STATUS - Report interface status changes
*
* This optional event can be used to report changes in interface
* status (interface added/removed) using union
* wpa_event_data::interface_status. This can be used to trigger
* wpa_supplicant to stop and re-start processing for the interface,
* e.g., when a cardbus card is ejected/inserted.
*/
EVENT_INTERFACE_STATUS,
/**
* EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication
*
* This event can be used to inform wpa_supplicant about candidates for
* RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible
* for scan request (ap_scan=2 mode), this event is required for
* pre-authentication. If wpa_supplicant is performing scan request
* (ap_scan=1), this event is optional since scan results can be used
* to add pre-authentication candidates. union
* wpa_event_data::pmkid_candidate is used to report the BSSID of the
* candidate and priority of the candidate, e.g., based on the signal
* strength, in order to try to pre-authenticate first with candidates
* that are most likely targets for re-association.
*
* EVENT_PMKID_CANDIDATE can be called whenever the driver has updates
* on the candidate list. In addition, it can be called for the current
* AP and APs that have existing PMKSA cache entries. wpa_supplicant
* will automatically skip pre-authentication in cases where a valid
* PMKSA exists. When more than one candidate exists, this event should
* be generated once for each candidate.
*
* Driver will be notified about successful pre-authentication with
* struct wpa_driver_ops::add_pmkid() calls.
*/
EVENT_PMKID_CANDIDATE,
/**
* EVENT_TDLS - Request TDLS operation
*
* This event can be used to request a TDLS operation to be performed.
*/
EVENT_TDLS,
/**
* EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs
*
* The driver is expected to report the received FT IEs from
* FT authentication sequence from the AP. The FT IEs are included in
* the extra information in union wpa_event_data::ft_ies.
*/
EVENT_FT_RESPONSE,
/**
* EVENT_IBSS_RSN_START - Request RSN authentication in IBSS
*
* The driver can use this event to inform wpa_supplicant about a STA
* in an IBSS with which protected frames could be exchanged. This
* event starts RSN authentication with the other STA to authenticate
* the STA and set up encryption keys with it.
*/
EVENT_IBSS_RSN_START,
/**
* EVENT_AUTH - Authentication result
*
* This event should be called when authentication attempt has been
* completed. This is only used if the driver supports separate
* authentication step (struct wpa_driver_ops::authenticate).
* Information about authentication result is included in
* union wpa_event_data::auth.
*/
EVENT_AUTH,
/**
* EVENT_DEAUTH - Authentication lost
*
* This event should be called when authentication is lost either due
* to receiving deauthenticate frame from the AP or when sending that
* frame to the current AP.
* In AP mode, union wpa_event_data::deauth_info is required.
*/
EVENT_DEAUTH,
/**
* EVENT_ASSOC_REJECT - Association rejected
*
* This event should be called when (re)association attempt has been
* rejected by the AP. Information about the association response is
* included in union wpa_event_data::assoc_reject.
*/
EVENT_ASSOC_REJECT,
/**
* EVENT_AUTH_TIMED_OUT - Authentication timed out
*/
EVENT_AUTH_TIMED_OUT,
/**
* EVENT_ASSOC_TIMED_OUT - Association timed out
*/
EVENT_ASSOC_TIMED_OUT,
/**
* EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS
*/
EVENT_WPS_BUTTON_PUSHED,
/**
* EVENT_TX_STATUS - Report TX status
*/
EVENT_TX_STATUS,
/**
* EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA
*/
EVENT_RX_FROM_UNKNOWN,
/**
* EVENT_RX_MGMT - Report RX of a management frame
*/
EVENT_RX_MGMT,
/**
* EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started
*
* This event is used to indicate when the driver has started the
* requested remain-on-channel duration. Information about the
* operation is included in union wpa_event_data::remain_on_channel.
*/
EVENT_REMAIN_ON_CHANNEL,
/**
* EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out
*
* This event is used to indicate when the driver has completed
* remain-on-channel duration, i.e., may noot be available on the
* requested channel anymore. Information about the
* operation is included in union wpa_event_data::remain_on_channel.
*/
EVENT_CANCEL_REMAIN_ON_CHANNEL,
/**
* EVENT_RX_PROBE_REQ - Indicate received Probe Request frame
*
* This event is used to indicate when a Probe Request frame has been
* received. Information about the received frame is included in
* union wpa_event_data::rx_probe_req. The driver is required to report
* these events only after successfully completed probe_req_report()
* commands to request the events (i.e., report parameter is non-zero)
* in station mode. In AP mode, Probe Request frames should always be
* reported.
*/
EVENT_RX_PROBE_REQ,
/**
* EVENT_NEW_STA - New wired device noticed
*
* This event is used to indicate that a new device has been detected
* in a network that does not use association-like functionality (i.e.,
* mainly wired Ethernet). This can be used to start EAPOL
* authenticator when receiving a frame from a device. The address of
* the device is included in union wpa_event_data::new_sta.
*/
EVENT_NEW_STA,
/**
* EVENT_EAPOL_RX - Report received EAPOL frame
*
* When in AP mode with hostapd, this event is required to be used to
* deliver the receive EAPOL frames from the driver.
*/
EVENT_EAPOL_RX,
/**
* EVENT_SIGNAL_CHANGE - Indicate change in signal strength
*
* This event is used to indicate changes in the signal strength
* observed in frames received from the current AP if signal strength
* monitoring has been enabled with signal_monitor().
*/
EVENT_SIGNAL_CHANGE,
/**
* EVENT_INTERFACE_ENABLED - Notify that interface was enabled
*
* This event is used to indicate that the interface was enabled after
* having been previously disabled, e.g., due to rfkill.
*/
EVENT_INTERFACE_ENABLED,
/**
* EVENT_INTERFACE_DISABLED - Notify that interface was disabled
*
* This event is used to indicate that the interface was disabled,
* e.g., due to rfkill.
*/
EVENT_INTERFACE_DISABLED,
/**
* EVENT_CHANNEL_LIST_CHANGED - Channel list changed
*
* This event is used to indicate that the channel list has changed,
* e.g., because of a regulatory domain change triggered by scan
* results including an AP advertising a country code.
*/
EVENT_CHANNEL_LIST_CHANGED,
/**
* EVENT_INTERFACE_UNAVAILABLE - Notify that interface is unavailable
*
* This event is used to indicate that the driver cannot maintain this
* interface in its operation mode anymore. The most likely use for
* this is to indicate that AP mode operation is not available due to
* operating channel would need to be changed to a DFS channel when
* the driver does not support radar detection and another virtual
* interfaces caused the operating channel to change. Other similar
* resource conflicts could also trigger this for station mode
* interfaces. This event can be propagated when channel switching
* fails.
*/
EVENT_INTERFACE_UNAVAILABLE,
/**
* EVENT_BEST_CHANNEL
*
* Driver generates this event whenever it detects a better channel
* (e.g., based on RSSI or channel use). This information can be used
* to improve channel selection for a new AP/P2P group.
*/
EVENT_BEST_CHANNEL,
/**
* EVENT_UNPROT_DEAUTH - Unprotected Deauthentication frame received
*
* This event should be called when a Deauthentication frame is dropped
* due to it not being protected (MFP/IEEE 802.11w).
* union wpa_event_data::unprot_deauth is required to provide more
* details of the frame.
*/
EVENT_UNPROT_DEAUTH,
/**
* EVENT_UNPROT_DISASSOC - Unprotected Disassociation frame received
*
* This event should be called when a Disassociation frame is dropped
* due to it not being protected (MFP/IEEE 802.11w).
* union wpa_event_data::unprot_disassoc is required to provide more
* details of the frame.
*/
EVENT_UNPROT_DISASSOC,
/**
* EVENT_STATION_LOW_ACK
*
* Driver generates this event whenever it detected that a particular
* station was lost. Detection can be through massive transmission
* failures for example.
*/
EVENT_STATION_LOW_ACK,
/**
* EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
*/
EVENT_IBSS_PEER_LOST,
/**
* EVENT_DRIVER_GTK_REKEY - Device/driver did GTK rekey
*
* This event carries the new replay counter to notify wpa_supplicant
* of the current EAPOL-Key Replay Counter in case the driver/firmware
* completed Group Key Handshake while the host (including
* wpa_supplicant was sleeping).
*/
EVENT_DRIVER_GTK_REKEY,
/**
* EVENT_SCHED_SCAN_STOPPED - Scheduled scan was stopped
*/
EVENT_SCHED_SCAN_STOPPED,
/**
* EVENT_DRIVER_CLIENT_POLL_OK - Station responded to poll
*
* This event indicates that the station responded to the poll
* initiated with @poll_client.
*/
EVENT_DRIVER_CLIENT_POLL_OK,
/**
* EVENT_EAPOL_TX_STATUS - notify of EAPOL TX status
*/
EVENT_EAPOL_TX_STATUS,
/**
* EVENT_CH_SWITCH - AP or GO decided to switch channels
*
* Described in wpa_event_data.ch_switch
* */
EVENT_CH_SWITCH,
/**
* EVENT_CH_SWITCH_STARTED - AP or GO started to switch channels
*
* This is a pre-switch event indicating the shortly following switch
* of operating channels.
*
* Described in wpa_event_data.ch_switch
*/
EVENT_CH_SWITCH_STARTED,
/**
* EVENT_WNM - Request WNM operation
*
* This event can be used to request a WNM operation to be performed.
*/
EVENT_WNM,
/**
* EVENT_CONNECT_FAILED_REASON - Connection failure reason in AP mode
*
* This event indicates that the driver reported a connection failure
* with the specified client (for example, max client reached, etc.) in
* AP mode.
*/
EVENT_CONNECT_FAILED_REASON,
/**
* EVENT_DFS_RADAR_DETECTED - Notify of radar detection
*
* A radar has been detected on the supplied frequency, hostapd should
* react accordingly (e.g., change channel).
*/
EVENT_DFS_RADAR_DETECTED,
/**
* EVENT_DFS_CAC_FINISHED - Notify that channel availability check has been completed
*
* After a successful CAC, the channel can be marked clear and used.
*/
EVENT_DFS_CAC_FINISHED,
/**
* EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
*
* The CAC was not successful, and the channel remains in the previous
* state. This may happen due to a radar being detected or other
* external influences.
*/
EVENT_DFS_CAC_ABORTED,
/**
* EVENT_DFS_NOP_FINISHED - Notify that non-occupancy period is over
*
* The channel which was previously unavailable is now available again.
*/
EVENT_DFS_NOP_FINISHED,
/**
* EVENT_SURVEY - Received survey data
*
* This event gets triggered when a driver query is issued for survey
* data and the requested data becomes available. The returned data is
* stored in struct survey_results. The results provide at most one
* survey entry for each frequency and at minimum will provide one
* survey entry for one frequency. The survey data can be os_malloc()'d
* and then os_free()'d, so the event callback must only copy data.
*/
EVENT_SURVEY,
/**
* EVENT_SCAN_STARTED - Scan started
*
* This indicates that driver has started a scan operation either based
* on a request from wpa_supplicant/hostapd or from another application.
* EVENT_SCAN_RESULTS is used to indicate when the scan has been
* completed (either successfully or by getting cancelled).
*/
EVENT_SCAN_STARTED,
/**
* EVENT_AVOID_FREQUENCIES - Received avoid frequency range
*
* This event indicates a set of frequency ranges that should be avoided
* to reduce issues due to interference or internal co-existence
* information in the driver.
*/
EVENT_AVOID_FREQUENCIES,
/**
* EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification
*/
EVENT_NEW_PEER_CANDIDATE,
/**
* EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS
*
* Indicates a pair of primary and secondary channels chosen by ACS
* in device.
*/
EVENT_ACS_CHANNEL_SELECTED,
/**
* EVENT_DFS_CAC_STARTED - Notify that channel availability check has
* been started.
*
* This event indicates that channel availability check has been started
* on a DFS frequency by a driver that supports DFS Offload.
*/
EVENT_DFS_CAC_STARTED,
/**
* EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped
*/
EVENT_P2P_LO_STOP,
/**
* EVENT_BEACON_LOSS - Beacon loss detected
*
* This event indicates that no Beacon frames has been received from
* the current AP. This may indicate that the AP is not anymore in
* range.
*/
EVENT_BEACON_LOSS,
/**
* EVENT_DFS_PRE_CAC_EXPIRED - Notify that channel availability check
* done previously (Pre-CAC) on the channel has expired. This would
* normally be on a non-ETSI DFS regulatory domain. DFS state of the
* channel will be moved from available to usable. A new CAC has to be
* performed before start operating on this channel.
*/
EVENT_DFS_PRE_CAC_EXPIRED,
/**
* EVENT_EXTERNAL_AUTH - This event interface is used by host drivers
* that do not define separate commands for authentication and
* association (~WPA_DRIVER_FLAGS_SME) but offload the 802.11
* authentication to wpa_supplicant. This event carries all the
* necessary information from the host driver for the authentication to
* happen.
*/
EVENT_EXTERNAL_AUTH,
/**
* EVENT_PORT_AUTHORIZED - Notification that a connection is authorized
*
* This event should be indicated when the driver completes the 4-way
* handshake. This event should be preceded by an EVENT_ASSOC that
* indicates the completion of IEEE 802.11 association.
*/
EVENT_PORT_AUTHORIZED,
/**
* EVENT_STATION_OPMODE_CHANGED - Notify STA's HT/VHT operation mode
* change event.
*/
EVENT_STATION_OPMODE_CHANGED,
/**
* EVENT_INTERFACE_MAC_CHANGED - Notify that interface MAC changed
*
* This event is emitted when the MAC changes while the interface is
* enabled. When an interface was disabled and becomes enabled, it
* must be always assumed that the MAC possibly changed.
*/
EVENT_INTERFACE_MAC_CHANGED,
/**
* EVENT_WDS_STA_INTERFACE_STATUS - Notify WDS STA interface status
*
* This event is emitted when an interface is added/removed for WDS STA.
*/
EVENT_WDS_STA_INTERFACE_STATUS,
/**
* EVENT_UPDATE_DH - Notification of updated DH information
*/
EVENT_UPDATE_DH,
/**
* EVENT_UNPROT_BEACON - Unprotected Beacon frame received
*
* This event should be called when a Beacon frame is dropped due to it
* not being protected correctly. union wpa_event_data::unprot_beacon
* is required to provide more details of the frame.
*/
EVENT_UNPROT_BEACON,
};
/**
* struct freq_survey - Channel survey info
*
* @ifidx: Interface index in which this survey was observed
* @freq: Center of frequency of the surveyed channel
* @nf: Channel noise floor in dBm
* @channel_time: Amount of time in ms the radio spent on the channel
* @channel_time_busy: Amount of time in ms the radio detected some signal
* that indicated to the radio the channel was not clear
* @channel_time_rx: Amount of time the radio spent receiving data
* @channel_time_tx: Amount of time the radio spent transmitting data
* @filled: bitmask indicating which fields have been reported, see
* SURVEY_HAS_* defines.
* @list: Internal list pointers
*/
struct freq_survey {
u32 ifidx;
unsigned int freq;
s8 nf;
u64 channel_time;
u64 channel_time_busy;
u64 channel_time_rx;
u64 channel_time_tx;
unsigned int filled;
struct dl_list list;
};
#define SURVEY_HAS_NF BIT(0)
#define SURVEY_HAS_CHAN_TIME BIT(1)
#define SURVEY_HAS_CHAN_TIME_BUSY BIT(2)
#define SURVEY_HAS_CHAN_TIME_RX BIT(3)
#define SURVEY_HAS_CHAN_TIME_TX BIT(4)
/**
* enum sta_connect_fail_reason_codes - STA connect failure reason code values
* @STA_CONNECT_FAIL_REASON_UNSPECIFIED: No reason code specified for
* connection failure.
* @STA_CONNECT_FAIL_REASON_NO_BSS_FOUND: No Probe Response frame received
* for unicast Probe Request frame.
* @STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL: STA failed to send auth request.
* @STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED: AP didn't send ACK for
* auth request.
* @STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED: Auth response is not
* received from AP.
* @STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL: STA failed to send
* Association Request frame.
* @STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED: AP didn't send ACK for
* Association Request frame.
* @STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED: Association Response
* frame is not received from AP.
*/
enum sta_connect_fail_reason_codes {
STA_CONNECT_FAIL_REASON_UNSPECIFIED = 0,
STA_CONNECT_FAIL_REASON_NO_BSS_FOUND = 1,
STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL = 2,
STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED = 3,
STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED = 4,
STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL = 5,
STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED = 6,
STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED = 7,
};
/**
* union wpa_event_data - Additional data for wpa_supplicant_event() calls
*/
union wpa_event_data {
/**
* struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events
*
* This structure is optional for EVENT_ASSOC calls and required for
* EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the
* driver interface does not need to generate separate EVENT_ASSOCINFO
* calls.
*/
struct assoc_info {
/**
* reassoc - Flag to indicate association or reassociation
*/
int reassoc;
/**
* req_ies - (Re)Association Request IEs
*
* If the driver generates WPA/RSN IE, this event data must be
* returned for WPA handshake to have needed information. If
* wpa_supplicant-generated WPA/RSN IE is used, this
* information event is optional.
*
* This should start with the first IE (fixed fields before IEs
* are not included).
*/
const u8 *req_ies;
/**
* req_ies_len - Length of req_ies in bytes
*/
size_t req_ies_len;
/**
* resp_ies - (Re)Association Response IEs
*
* Optional association data from the driver. This data is not
* required WPA, but may be useful for some protocols and as
* such, should be reported if this is available to the driver
* interface.
*
* This should start with the first IE (fixed fields before IEs
* are not included).
*/
const u8 *resp_ies;
/**
* resp_ies_len - Length of resp_ies in bytes
*/
size_t resp_ies_len;
/**
* resp_frame - (Re)Association Response frame
*/
const u8 *resp_frame;
/**
* resp_frame_len - (Re)Association Response frame length
*/
size_t resp_frame_len;
/**
* beacon_ies - Beacon or Probe Response IEs
*
* Optional Beacon/ProbeResp data: IEs included in Beacon or
* Probe Response frames from the current AP (i.e., the one
* that the client just associated with). This information is
* used to update WPA/RSN IE for the AP. If this field is not
* set, the results from previous scan will be used. If no
* data for the new AP is found, scan results will be requested
* again (without scan request). At this point, the driver is
* expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is
* used).
*
* This should start with the first IE (fixed fields before IEs
* are not included).
*/
const u8 *beacon_ies;
/**
* beacon_ies_len - Length of beacon_ies */
size_t beacon_ies_len;
/**
* freq - Frequency of the operational channel in MHz
*/
unsigned int freq;
/**
* wmm_params - WMM parameters used in this association.
*/
struct wmm_params wmm_params;
/**
* addr - Station address (for AP mode)
*/
const u8 *addr;
/**
* The following is the key management offload information
* @authorized
* @key_replay_ctr
* @key_replay_ctr_len
* @ptk_kck
* @ptk_kek_len
* @ptk_kek
* @ptk_kek_len
*/
/**
* authorized - Status of key management offload,
* 1 = successful
*/
int authorized;
/**
* key_replay_ctr - Key replay counter value last used
* in a valid EAPOL-Key frame
*/
const u8 *key_replay_ctr;
/**
* key_replay_ctr_len - The length of key_replay_ctr
*/
size_t key_replay_ctr_len;
/**
* ptk_kck - The derived PTK KCK
*/
const u8 *ptk_kck;
/**
* ptk_kek_len - The length of ptk_kck
*/
size_t ptk_kck_len;
/**
* ptk_kek - The derived PTK KEK
* This is used in key management offload and also in FILS SK
* offload.
*/
const u8 *ptk_kek;
/**
* ptk_kek_len - The length of ptk_kek
*/
size_t ptk_kek_len;
/**
* subnet_status - The subnet status:
* 0 = unknown, 1 = unchanged, 2 = changed
*/
u8 subnet_status;
/**
* The following information is used in FILS SK offload
* @fils_erp_next_seq_num
* @fils_pmk
* @fils_pmk_len
* @fils_pmkid
*/
/**
* fils_erp_next_seq_num - The next sequence number to use in
* FILS ERP messages
*/
u16 fils_erp_next_seq_num;
/**
* fils_pmk - A new PMK if generated in case of FILS
* authentication
*/
const u8 *fils_pmk;
/**
* fils_pmk_len - Length of fils_pmk
*/
size_t fils_pmk_len;
/**
* fils_pmkid - PMKID used or generated in FILS authentication
*/
const u8 *fils_pmkid;
} assoc_info;
/**
* struct disassoc_info - Data for EVENT_DISASSOC events
*/
struct disassoc_info {
/**
* addr - Station address (for AP mode)
*/
const u8 *addr;
/**
* reason_code - Reason Code (host byte order) used in
* Deauthentication frame
*/
u16 reason_code;
/**
* ie - Optional IE(s) in Disassociation frame
*/
const u8 *ie;
/**
* ie_len - Length of ie buffer in octets
*/
size_t ie_len;
/**
* locally_generated - Whether the frame was locally generated
*/
int locally_generated;
} disassoc_info;
/**
* struct deauth_info - Data for EVENT_DEAUTH events
*/
struct deauth_info {
/**
* addr - Station address (for AP mode)
*/
const u8 *addr;
/**
* reason_code - Reason Code (host byte order) used in
* Deauthentication frame
*/
u16 reason_code;
/**
* ie - Optional IE(s) in Deauthentication frame
*/
const u8 *ie;
/**
* ie_len - Length of ie buffer in octets
*/
size_t ie_len;
/**
* locally_generated - Whether the frame was locally generated
*/
int locally_generated;
} deauth_info;
/**
* struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE
*/
struct michael_mic_failure {
int unicast;
const u8 *src;
} michael_mic_failure;
/**
* struct interface_status - Data for EVENT_INTERFACE_STATUS
*/
struct interface_status {
unsigned int ifindex;
char ifname[100];
enum {
EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
} ievent;
} interface_status;
/**
* struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE
*/
struct pmkid_candidate {
/** BSSID of the PMKID candidate */
u8 bssid[ETH_ALEN];
/** Smaller the index, higher the priority */
int index;
/** Whether RSN IE includes pre-authenticate flag */
int preauth;
} pmkid_candidate;
/**
* struct tdls - Data for EVENT_TDLS
*/
struct tdls {
u8 peer[ETH_ALEN];
enum {
TDLS_REQUEST_SETUP,
TDLS_REQUEST_TEARDOWN,
TDLS_REQUEST_DISCOVER,
} oper;
u16 reason_code; /* for teardown */
} tdls;
/**
* struct wnm - Data for EVENT_WNM
*/
struct wnm {
u8 addr[ETH_ALEN];
enum {
WNM_OPER_SLEEP,
} oper;
enum {
WNM_SLEEP_ENTER,
WNM_SLEEP_EXIT
} sleep_action;
int sleep_intval;
u16 reason_code;
u8 *buf;
u16 buf_len;
} wnm;
/**
* struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
*
* During FT (IEEE 802.11r) authentication sequence, the driver is
* expected to use this event to report received FT IEs (MDIE, FTIE,
* RSN IE, TIE, possible resource request) to the supplicant. The FT
* IEs for the next message will be delivered through the
* struct wpa_driver_ops::update_ft_ies() callback.
*/
struct ft_ies {
const u8 *ies;
size_t ies_len;
int ft_action;
u8 target_ap[ETH_ALEN];
/** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */
const u8 *ric_ies;
/** Length of ric_ies buffer in octets */
size_t ric_ies_len;
} ft_ies;
/**
* struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START
*/
struct ibss_rsn_start {
u8 peer[ETH_ALEN];
} ibss_rsn_start;
/**
* struct auth_info - Data for EVENT_AUTH events
*/
struct auth_info {
u8 peer[ETH_ALEN];
u8 bssid[ETH_ALEN];
u16 auth_type;
u16 auth_transaction;
u16 status_code;
const u8 *ies;
size_t ies_len;
} auth;
/**
* struct assoc_reject - Data for EVENT_ASSOC_REJECT events
*/
struct assoc_reject {
/**
* bssid - BSSID of the AP that rejected association
*/
const u8 *bssid;
/**
* resp_ies - (Re)Association Response IEs
*
* Optional association data from the driver. This data is not
* required WPA, but may be useful for some protocols and as
* such, should be reported if this is available to the driver
* interface.
*
* This should start with the first IE (fixed fields before IEs
* are not included).
*/
const u8 *resp_ies;
/**
* resp_ies_len - Length of resp_ies in bytes
*/
size_t resp_ies_len;
/**
* status_code - Status Code from (Re)association Response
*/
u16 status_code;
/**
* timed_out - Whether failure is due to timeout (etc.) rather
* than explicit rejection response from the AP.
*/
int timed_out;
/**
* timeout_reason - Reason for the timeout
*/
const char *timeout_reason;
/**
* fils_erp_next_seq_num - The next sequence number to use in
* FILS ERP messages
*/
u16 fils_erp_next_seq_num;
/**
* reason_code - Connection failure reason code from the driver
*/
enum sta_connect_fail_reason_codes reason_code;
} assoc_reject;
struct timeout_event {
u8 addr[ETH_ALEN];
} timeout_event;
/**
* struct tx_status - Data for EVENT_TX_STATUS events
*/
struct tx_status {
u16 type;
u16 stype;
const u8 *dst;
const u8 *data;
size_t data_len;
int ack;
} tx_status;
/**
* struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events
*/
struct rx_from_unknown {
const u8 *bssid;
const u8 *addr;
int wds;
} rx_from_unknown;
/**
* struct rx_mgmt - Data for EVENT_RX_MGMT events
*/
struct rx_mgmt {
const u8 *frame;
size_t frame_len;
u32 datarate;
/**
* drv_priv - Pointer to store driver private BSS information
*
* If not set to NULL, this is used for comparison with
* hostapd_data->drv_priv to determine which BSS should process
* the frame.
*/
void *drv_priv;
/**
* freq - Frequency (in MHz) on which the frame was received
*/
int freq;
/**
* ssi_signal - Signal strength in dBm (or 0 if not available)
*/
int ssi_signal;
} rx_mgmt;
/**
* struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events
*
* This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events.
*/
struct remain_on_channel {
/**
* freq - Channel frequency in MHz
*/
unsigned int freq;
/**
* duration - Duration to remain on the channel in milliseconds
*/
unsigned int duration;
} remain_on_channel;
/**
* struct scan_info - Optional data for EVENT_SCAN_RESULTS events
* @aborted: Whether the scan was aborted
* @freqs: Scanned frequencies in MHz (%NULL = all channels scanned)
* @num_freqs: Number of entries in freqs array
* @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
* SSID)
* @num_ssids: Number of entries in ssids array
* @external_scan: Whether the scan info is for an external scan
* @nl_scan_event: 1 if the source of this scan event is a normal scan,
* 0 if the source of the scan event is a vendor scan
* @scan_start_tsf: Time when the scan started in terms of TSF of the
* BSS that the interface that requested the scan is connected to
* (if available).
* @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf
* is set.
*/
struct scan_info {
int aborted;
const int *freqs;
size_t num_freqs;
struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
size_t num_ssids;
int external_scan;
int nl_scan_event;
u64 scan_start_tsf;
u8 scan_start_tsf_bssid[ETH_ALEN];
} scan_info;
/**
* struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events
*/
struct rx_probe_req {
/**
* sa - Source address of the received Probe Request frame
*/
const u8 *sa;
/**
* da - Destination address of the received Probe Request frame
* or %NULL if not available
*/
const u8 *da;
/**
* bssid - BSSID of the received Probe Request frame or %NULL
* if not available
*/
const u8 *bssid;
/**
* ie - IEs from the Probe Request body
*/
const u8 *ie;
/**
* ie_len - Length of ie buffer in octets
*/
size_t ie_len;
/**
* signal - signal strength in dBm (or 0 if not available)
*/
int ssi_signal;
} rx_probe_req;
/**
* struct new_sta - Data for EVENT_NEW_STA events
*/
struct new_sta {
const u8 *addr;
} new_sta;
/**
* struct eapol_rx - Data for EVENT_EAPOL_RX events
*/
struct eapol_rx {
const u8 *src;
const u8 *data;
size_t data_len;
} eapol_rx;
/**
* signal_change - Data for EVENT_SIGNAL_CHANGE events
*/
struct wpa_signal_info signal_change;
/**
* struct best_channel - Data for EVENT_BEST_CHANNEL events
* @freq_24: Best 2.4 GHz band channel frequency in MHz
* @freq_5: Best 5 GHz band channel frequency in MHz
* @freq_overall: Best channel frequency in MHz
*
* 0 can be used to indicate no preference in either band.
*/
struct best_channel {
int freq_24;
int freq_5;
int freq_overall;
} best_chan;
struct unprot_deauth {
const u8 *sa;
const u8 *da;
u16 reason_code;
} unprot_deauth;
struct unprot_disassoc {
const u8 *sa;
const u8 *da;
u16 reason_code;
} unprot_disassoc;
/**
* struct low_ack - Data for EVENT_STATION_LOW_ACK events
* @addr: station address
* @num_packets: Number of packets lost (consecutive packets not
* acknowledged)
*/
struct low_ack {
u8 addr[ETH_ALEN];
u32 num_packets;
} low_ack;
/**
* struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
*/
struct ibss_peer_lost {
u8 peer[ETH_ALEN];
} ibss_peer_lost;
/**
* struct driver_gtk_rekey - Data for EVENT_DRIVER_GTK_REKEY
*/
struct driver_gtk_rekey {
const u8 *bssid;
const u8 *replay_ctr;
} driver_gtk_rekey;
/**
* struct client_poll - Data for EVENT_DRIVER_CLIENT_POLL_OK events
* @addr: station address
*/
struct client_poll {
u8 addr[ETH_ALEN];
} client_poll;
/**
* struct eapol_tx_status
* @dst: Original destination
* @data: Data starting with IEEE 802.1X header (!)
* @data_len: Length of data
* @ack: Indicates ack or lost frame
*
* This corresponds to hapd_send_eapol if the frame sent
* there isn't just reported as EVENT_TX_STATUS.
*/
struct eapol_tx_status {
const u8 *dst;
const u8 *data;
int data_len;
int ack;
} eapol_tx_status;
/**
* struct ch_switch
* @freq: Frequency of new channel in MHz
* @ht_enabled: Whether this is an HT channel
* @ch_offset: Secondary channel offset
* @ch_width: Channel width
* @cf1: Center frequency 1
* @cf2: Center frequency 2
*/
struct ch_switch {
int freq;
int ht_enabled;
int ch_offset;
enum chan_width ch_width;
int cf1;
int cf2;
} ch_switch;
/**
* struct connect_failed - Data for EVENT_CONNECT_FAILED_REASON
* @addr: Remote client address
* @code: Reason code for connection failure
*/
struct connect_failed_reason {
u8 addr[ETH_ALEN];
enum {
MAX_CLIENT_REACHED,
BLOCKED_CLIENT
} code;
} connect_failed_reason;
/**
* struct dfs_event - Data for radar detected events
* @freq: Frequency of the channel in MHz
*/
struct dfs_event {
int freq;
int ht_enabled;
int chan_offset;
enum chan_width chan_width;
int cf1;
int cf2;
} dfs_event;
/**
* survey_results - Survey result data for EVENT_SURVEY
* @freq_filter: Requested frequency survey filter, 0 if request
* was for all survey data
* @survey_list: Linked list of survey data (struct freq_survey)
*/
struct survey_results {
unsigned int freq_filter;
struct dl_list survey_list; /* struct freq_survey */
} survey_results;
/**
* channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED
* @initiator: Initiator of the regulatory change
* @type: Regulatory change type
* @alpha2: Country code (or "" if not available)
*/
struct channel_list_changed {
enum reg_change_initiator initiator;
enum reg_type type;
char alpha2[3];
} channel_list_changed;
/**
* freq_range - List of frequency ranges
*
* This is used as the data with EVENT_AVOID_FREQUENCIES.
*/
struct wpa_freq_range_list freq_range;
/**
* struct mesh_peer
*
* @peer: Peer address
* @ies: Beacon IEs
* @ie_len: Length of @ies
*
* Notification of new candidate mesh peer.
*/
struct mesh_peer {
const u8 *peer;
const u8 *ies;
size_t ie_len;
} mesh_peer;
/**
* struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
* @pri_freq: Selected primary frequency
* @sec_freq: Selected secondary frequency
* @edmg_channel: Selected EDMG channel
* @vht_seg0_center_ch: VHT mode Segment0 center channel
* The value is the index of the channel center frequency for
* 20 MHz, 40 MHz, and 80 MHz channels. The value is the center
* frequency index of the primary 80 MHz segment for 160 MHz and
* 80+80 MHz channels.
* @vht_seg1_center_ch: VHT mode Segment1 center channel
* The value is zero for 20 MHz, 40 MHz, and 80 MHz channels. The
* value is the index of the channel center frequency for 160 MHz
* channels and the center frequency index of the secondary 80 MHz
* segment for 80+80 MHz channels.
* @ch_width: Selected Channel width by driver. Driver may choose to
* change hostapd configured ACS channel width due driver internal
* channel restrictions.
* hw_mode: Selected band (used with hw_mode=any)
*/
struct acs_selected_channels {
unsigned int pri_freq;
unsigned int sec_freq;
u8 edmg_channel;
u8 vht_seg0_center_ch;
u8 vht_seg1_center_ch;
u16 ch_width;
enum hostapd_hw_mode hw_mode;
} acs_selected_channels;
/**
* struct p2p_lo_stop - Reason code for P2P Listen offload stop event
* @reason_code: Reason for stopping offload
* P2P_LO_STOPPED_REASON_COMPLETE: Listen offload finished as
* scheduled.
* P2P_LO_STOPPED_REASON_RECV_STOP_CMD: Host requested offload to
* be stopped.
* P2P_LO_STOPPED_REASON_INVALID_PARAM: Invalid listen offload
* parameters.
* P2P_LO_STOPPED_REASON_NOT_SUPPORTED: Listen offload not
* supported by device.
*/
struct p2p_lo_stop {
enum {
P2P_LO_STOPPED_REASON_COMPLETE = 0,
P2P_LO_STOPPED_REASON_RECV_STOP_CMD,
P2P_LO_STOPPED_REASON_INVALID_PARAM,
P2P_LO_STOPPED_REASON_NOT_SUPPORTED,
} reason_code;
} p2p_lo_stop;
/* For EVENT_EXTERNAL_AUTH */
struct external_auth external_auth;
/**
* struct sta_opmode - Station's operation mode change event
* @addr: The station MAC address
* @smps_mode: SMPS mode of the station
* @chan_width: Channel width of the station
* @rx_nss: RX_NSS of the station
*
* This is used as data with EVENT_STATION_OPMODE_CHANGED.
*/
struct sta_opmode {
const u8 *addr;
enum smps_mode smps_mode;
enum chan_width chan_width;
u8 rx_nss;
} sta_opmode;
/**
* struct wds_sta_interface - Data for EVENT_WDS_STA_INTERFACE_STATUS.
*/
struct wds_sta_interface {
const u8 *sta_addr;
const char *ifname;
enum {
INTERFACE_ADDED,
INTERFACE_REMOVED
} istatus;
} wds_sta_interface;
/**
* struct update_dh - Data for EVENT_UPDATE_DH
*/
struct update_dh {
const u8 *peer;
const u8 *ie;
size_t ie_len;
} update_dh;
/**
* struct unprot_beacon - Data for EVENT_UNPROT_BEACON
*/
struct unprot_beacon {
const u8 *sa;
} unprot_beacon;
};
/**
* wpa_supplicant_event - Report a driver event for wpa_supplicant
* @ctx: Context pointer (wpa_s); this is the ctx variable registered
* with struct wpa_driver_ops::init()
* @event: event type (defined above)
* @data: possible extra data for the event
*
* Driver wrapper code should call this function whenever an event is received
* from the driver.
*/
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data);
/**
* wpa_supplicant_event_global - Report a driver event for wpa_supplicant
* @ctx: Context pointer (wpa_s); this is the ctx variable registered
* with struct wpa_driver_ops::init()
* @event: event type (defined above)
* @data: possible extra data for the event
*
* Same as wpa_supplicant_event(), but we search for the interface in
* wpa_global.
*/
void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
union wpa_event_data *data);
/*
* The following inline functions are provided for convenience to simplify
* event indication for some of the common events.
*/
static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
size_t ielen, int reassoc)
{
union wpa_event_data event;
os_memset(&event, 0, sizeof(event));
event.assoc_info.reassoc = reassoc;
event.assoc_info.req_ies = ie;
event.assoc_info.req_ies_len = ielen;
event.assoc_info.addr = addr;
wpa_supplicant_event(ctx, EVENT_ASSOC, &event);
}
static inline void drv_event_disassoc(void *ctx, const u8 *addr)
{
union wpa_event_data event;
os_memset(&event, 0, sizeof(event));
event.disassoc_info.addr = addr;
wpa_supplicant_event(ctx, EVENT_DISASSOC, &event);
}
static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data,
size_t data_len)
{
union wpa_event_data event;
os_memset(&event, 0, sizeof(event));
event.eapol_rx.src = src;
event.eapol_rx.data = data;
event.eapol_rx.data_len = data_len;
wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
}
/* driver_common.c */
void wpa_scan_results_free(struct wpa_scan_results *res);
/* Convert wpa_event_type to a string for logging */
const char * event_to_string(enum wpa_event_type event);
/* Convert chan_width to a string for logging and control interfaces */
const char * channel_width_to_string(enum chan_width width);
int channel_width_to_int(enum chan_width width);
int ht_supported(const struct hostapd_hw_modes *mode);
int vht_supported(const struct hostapd_hw_modes *mode);
struct wowlan_triggers *
wpa_get_wowlan_triggers(const char *wowlan_triggers,
const struct wpa_driver_capa *capa);
/* Convert driver flag to string */
const char * driver_flag_to_string(u64 flag);
const char * driver_flag2_to_string(u64 flag2);
/* NULL terminated array of linked in driver wrappers */
extern const struct wpa_driver_ops *const wpa_drivers[];
/* Available drivers */
#ifdef CONFIG_DRIVER_WEXT
extern const struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
#endif /* CONFIG_DRIVER_WEXT */
#ifdef CONFIG_DRIVER_NL80211
/* driver_nl80211.c */
extern const struct wpa_driver_ops wpa_driver_nl80211_ops;
#endif /* CONFIG_DRIVER_NL80211 */
#ifdef CONFIG_DRIVER_HOSTAP
extern const struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
#endif /* CONFIG_DRIVER_HOSTAP */
#ifdef CONFIG_DRIVER_BSD
extern const struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
#endif /* CONFIG_DRIVER_BSD */
#ifdef CONFIG_DRIVER_OPENBSD
/* driver_openbsd.c */
extern const struct wpa_driver_ops wpa_driver_openbsd_ops;
#endif /* CONFIG_DRIVER_OPENBSD */
#ifdef CONFIG_DRIVER_NDIS
extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
#endif /* CONFIG_DRIVER_NDIS */
#ifdef CONFIG_DRIVER_WIRED
extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
#endif /* CONFIG_DRIVER_WIRED */
#ifdef CONFIG_DRIVER_MACSEC_QCA
/* driver_macsec_qca.c */
extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops;
#endif /* CONFIG_DRIVER_MACSEC_QCA */
#ifdef CONFIG_DRIVER_MACSEC_LINUX
/* driver_macsec_linux.c */
extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops;
#endif /* CONFIG_DRIVER_MACSEC_LINUX */
#ifdef CONFIG_DRIVER_ROBOSWITCH
/* driver_roboswitch.c */
extern const struct wpa_driver_ops wpa_driver_roboswitch_ops;
#endif /* CONFIG_DRIVER_ROBOSWITCH */
#ifdef CONFIG_DRIVER_ATHEROS
/* driver_atheros.c */
extern const struct wpa_driver_ops wpa_driver_atheros_ops;
#endif /* CONFIG_DRIVER_ATHEROS */
#ifdef CONFIG_DRIVER_NONE
extern const struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
#endif /* CONFIG_DRIVER_NONE */
#endif /* DRIVER_H */
diff --git a/contrib/wpa/src/drivers/driver_common.c b/contrib/wpa/src/drivers/driver_common.c
index a7ebe956680b..1cb976011d2b 100644
--- a/contrib/wpa/src/drivers/driver_common.c
+++ b/contrib/wpa/src/drivers/driver_common.c
@@ -1,335 +1,335 @@
/*
* Common driver-related functions
* Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "utils/common.h"
#include "driver.h"
void wpa_scan_results_free(struct wpa_scan_results *res)
{
size_t i;
if (res == NULL)
return;
for (i = 0; i < res->num; i++)
os_free(res->res[i]);
os_free(res->res);
os_free(res);
}
const char * event_to_string(enum wpa_event_type event)
{
#define E2S(n) case EVENT_ ## n: return #n
switch (event) {
E2S(ASSOC);
E2S(DISASSOC);
E2S(MICHAEL_MIC_FAILURE);
E2S(SCAN_RESULTS);
E2S(ASSOCINFO);
E2S(INTERFACE_STATUS);
E2S(PMKID_CANDIDATE);
E2S(TDLS);
E2S(FT_RESPONSE);
E2S(IBSS_RSN_START);
E2S(AUTH);
E2S(DEAUTH);
E2S(ASSOC_REJECT);
E2S(AUTH_TIMED_OUT);
E2S(ASSOC_TIMED_OUT);
E2S(WPS_BUTTON_PUSHED);
E2S(TX_STATUS);
E2S(RX_FROM_UNKNOWN);
E2S(RX_MGMT);
E2S(REMAIN_ON_CHANNEL);
E2S(CANCEL_REMAIN_ON_CHANNEL);
E2S(RX_PROBE_REQ);
E2S(NEW_STA);
E2S(EAPOL_RX);
E2S(SIGNAL_CHANGE);
E2S(INTERFACE_ENABLED);
E2S(INTERFACE_DISABLED);
E2S(CHANNEL_LIST_CHANGED);
E2S(INTERFACE_UNAVAILABLE);
E2S(BEST_CHANNEL);
E2S(UNPROT_DEAUTH);
E2S(UNPROT_DISASSOC);
E2S(STATION_LOW_ACK);
E2S(IBSS_PEER_LOST);
E2S(DRIVER_GTK_REKEY);
E2S(SCHED_SCAN_STOPPED);
E2S(DRIVER_CLIENT_POLL_OK);
E2S(EAPOL_TX_STATUS);
E2S(CH_SWITCH);
E2S(CH_SWITCH_STARTED);
E2S(WNM);
E2S(CONNECT_FAILED_REASON);
E2S(DFS_RADAR_DETECTED);
E2S(DFS_CAC_FINISHED);
E2S(DFS_CAC_ABORTED);
E2S(DFS_NOP_FINISHED);
E2S(SURVEY);
E2S(SCAN_STARTED);
E2S(AVOID_FREQUENCIES);
E2S(NEW_PEER_CANDIDATE);
E2S(ACS_CHANNEL_SELECTED);
E2S(DFS_CAC_STARTED);
E2S(P2P_LO_STOP);
E2S(BEACON_LOSS);
E2S(DFS_PRE_CAC_EXPIRED);
E2S(EXTERNAL_AUTH);
E2S(PORT_AUTHORIZED);
E2S(STATION_OPMODE_CHANGED);
E2S(INTERFACE_MAC_CHANGED);
E2S(WDS_STA_INTERFACE_STATUS);
E2S(UPDATE_DH);
E2S(UNPROT_BEACON);
}
return "UNKNOWN";
#undef E2S
}
const char * channel_width_to_string(enum chan_width width)
{
switch (width) {
case CHAN_WIDTH_20_NOHT:
return "20 MHz (no HT)";
case CHAN_WIDTH_20:
return "20 MHz";
case CHAN_WIDTH_40:
return "40 MHz";
case CHAN_WIDTH_80:
return "80 MHz";
case CHAN_WIDTH_80P80:
return "80+80 MHz";
case CHAN_WIDTH_160:
return "160 MHz";
default:
return "unknown";
}
}
int channel_width_to_int(enum chan_width width)
{
switch (width) {
case CHAN_WIDTH_20_NOHT:
case CHAN_WIDTH_20:
return 20;
case CHAN_WIDTH_40:
return 40;
case CHAN_WIDTH_80:
return 80;
case CHAN_WIDTH_80P80:
case CHAN_WIDTH_160:
return 160;
default:
return 0;
}
}
int ht_supported(const struct hostapd_hw_modes *mode)
{
if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
/*
* The driver did not indicate whether it supports HT. Assume
* it does to avoid connection issues.
*/
return 1;
}
/*
* IEEE Std 802.11n-2009 20.1.1:
* An HT non-AP STA shall support all EQM rates for one spatial stream.
*/
return mode->mcs_set[0] == 0xff;
}
int vht_supported(const struct hostapd_hw_modes *mode)
{
if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) {
/*
* The driver did not indicate whether it supports VHT. Assume
* it does to avoid connection issues.
*/
return 1;
}
/*
* A VHT non-AP STA shall support MCS 0-7 for one spatial stream.
* TODO: Verify if this complies with the standard
*/
return (mode->vht_mcs_set[0] & 0x3) != 3;
}
static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
int capa_trigger, u8 *param_trigger)
{
if (os_strcmp(start, trigger) != 0)
return 0;
if (!capa_trigger)
return 0;
*param_trigger = 1;
return 1;
}
struct wowlan_triggers *
wpa_get_wowlan_triggers(const char *wowlan_triggers,
const struct wpa_driver_capa *capa)
{
struct wowlan_triggers *triggers;
char *start, *end, *buf;
int last;
if (!wowlan_triggers)
return NULL;
buf = os_strdup(wowlan_triggers);
if (buf == NULL)
return NULL;
triggers = os_zalloc(sizeof(*triggers));
if (triggers == NULL)
goto out;
#define CHECK_TRIGGER(trigger) \
wpa_check_wowlan_trigger(start, #trigger, \
capa->wowlan_triggers.trigger, \
&triggers->trigger)
start = buf;
while (*start != '\0') {
while (isblank((unsigned char) *start))
start++;
if (*start == '\0')
break;
end = start;
while (!isblank((unsigned char) *end) && *end != '\0')
end++;
last = *end == '\0';
*end = '\0';
if (!CHECK_TRIGGER(any) &&
!CHECK_TRIGGER(disconnect) &&
!CHECK_TRIGGER(magic_pkt) &&
!CHECK_TRIGGER(gtk_rekey_failure) &&
!CHECK_TRIGGER(eap_identity_req) &&
!CHECK_TRIGGER(four_way_handshake) &&
!CHECK_TRIGGER(rfkill_release)) {
wpa_printf(MSG_DEBUG,
"Unknown/unsupported wowlan trigger '%s'",
start);
os_free(triggers);
triggers = NULL;
goto out;
}
if (last)
break;
start = end + 1;
}
#undef CHECK_TRIGGER
out:
os_free(buf);
return triggers;
}
const char * driver_flag_to_string(u64 flag)
{
#define DF2S(x) case WPA_DRIVER_FLAGS_ ## x: return #x
switch (flag) {
DF2S(DRIVER_IE);
DF2S(SET_KEYS_AFTER_ASSOC);
DF2S(DFS_OFFLOAD);
DF2S(4WAY_HANDSHAKE_PSK);
DF2S(4WAY_HANDSHAKE_8021X);
DF2S(WIRED);
DF2S(SME);
DF2S(AP);
DF2S(SET_KEYS_AFTER_ASSOC_DONE);
DF2S(HT_2040_COEX);
DF2S(P2P_CONCURRENT);
DF2S(P2P_DEDICATED_INTERFACE);
DF2S(P2P_CAPABLE);
DF2S(AP_TEARDOWN_SUPPORT);
DF2S(P2P_MGMT_AND_NON_P2P);
- DF2S(SANE_ERROR_CODES);
+ DF2S(VALID_ERROR_CODES);
DF2S(OFFCHANNEL_TX);
DF2S(EAPOL_TX_STATUS);
DF2S(DEAUTH_TX_STATUS);
DF2S(BSS_SELECTION);
DF2S(TDLS_SUPPORT);
DF2S(TDLS_EXTERNAL_SETUP);
DF2S(PROBE_RESP_OFFLOAD);
DF2S(AP_UAPSD);
DF2S(INACTIVITY_TIMER);
DF2S(AP_MLME);
DF2S(SAE);
DF2S(OBSS_SCAN);
DF2S(IBSS);
DF2S(RADAR);
DF2S(DEDICATED_P2P_DEVICE);
DF2S(QOS_MAPPING);
DF2S(AP_CSA);
DF2S(MESH);
DF2S(ACS_OFFLOAD);
DF2S(KEY_MGMT_OFFLOAD);
DF2S(TDLS_CHANNEL_SWITCH);
DF2S(HT_IBSS);
DF2S(VHT_IBSS);
DF2S(SUPPORT_HW_MODE_ANY);
DF2S(OFFCHANNEL_SIMULTANEOUS);
DF2S(FULL_AP_CLIENT_STATE);
DF2S(P2P_LISTEN_OFFLOAD);
DF2S(SUPPORT_FILS);
DF2S(BEACON_RATE_LEGACY);
DF2S(BEACON_RATE_HT);
DF2S(BEACON_RATE_VHT);
DF2S(MGMT_TX_RANDOM_TA);
DF2S(MGMT_TX_RANDOM_TA_CONNECTED);
DF2S(SCHED_SCAN_RELATIVE_RSSI);
DF2S(HE_CAPABILITIES);
DF2S(FILS_SK_OFFLOAD);
DF2S(OCE_STA);
DF2S(OCE_AP);
DF2S(OCE_STA_CFON);
DF2S(MFP_OPTIONAL);
DF2S(SELF_MANAGED_REGULATORY);
DF2S(FTM_RESPONDER);
DF2S(CONTROL_PORT);
DF2S(VLAN_OFFLOAD);
DF2S(UPDATE_FT_IES);
DF2S(SAFE_PTK0_REKEYS);
DF2S(BEACON_PROTECTION);
DF2S(EXTENDED_KEY_ID);
}
return "UNKNOWN";
#undef DF2S
}
const char * driver_flag2_to_string(u64 flag2)
{
#define DF2S(x) case WPA_DRIVER_FLAGS2_ ## x: return #x
switch (flag2) {
DF2S(CONTROL_PORT_RX);
DF2S(CONTROL_PORT_TX_STATUS);
}
return "UNKNOWN";
#undef DF2S
}
diff --git a/contrib/wpa/src/drivers/driver_hostap.h b/contrib/wpa/src/drivers/driver_hostap.h
index 4c1e6d69f0a7..ac0b83ad24d1 100644
--- a/contrib/wpa/src/drivers/driver_hostap.h
+++ b/contrib/wpa/src/drivers/driver_hostap.h
@@ -1,210 +1,208 @@
/*
* Driver interaction with Linux Host AP driver
* Copyright (c) 2002-2006, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef HOSTAP_DRIVER_H
#define HOSTAP_DRIVER_H
/* netdevice private ioctls (used, e.g., with iwpriv from user space) */
/* New wireless extensions API - SET/GET convention (even ioctl numbers are
* root only)
*/
#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0)
#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1)
#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2)
#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3)
#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4)
#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6)
#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8)
#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10)
#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12)
#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14)
#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16)
#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18)
#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20)
#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22)
/* following are not in SIOCGIWPRIV list; check permission in the driver code
*/
#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13)
#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14)
/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */
enum {
/* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */
PRISM2_PARAM_TXRATECTRL = 2,
PRISM2_PARAM_BEACON_INT = 3,
PRISM2_PARAM_PSEUDO_IBSS = 4,
PRISM2_PARAM_ALC = 5,
/* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */
PRISM2_PARAM_DUMP = 7,
PRISM2_PARAM_OTHER_AP_POLICY = 8,
PRISM2_PARAM_AP_MAX_INACTIVITY = 9,
PRISM2_PARAM_AP_BRIDGE_PACKETS = 10,
PRISM2_PARAM_DTIM_PERIOD = 11,
PRISM2_PARAM_AP_NULLFUNC_ACK = 12,
PRISM2_PARAM_MAX_WDS = 13,
PRISM2_PARAM_AP_AUTOM_AP_WDS = 14,
PRISM2_PARAM_AP_AUTH_ALGS = 15,
PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16,
PRISM2_PARAM_HOST_ENCRYPT = 17,
PRISM2_PARAM_HOST_DECRYPT = 18,
- PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19,
- PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20,
PRISM2_PARAM_HOST_ROAMING = 21,
PRISM2_PARAM_BCRX_STA_KEY = 22,
PRISM2_PARAM_IEEE_802_1X = 23,
PRISM2_PARAM_ANTSEL_TX = 24,
PRISM2_PARAM_ANTSEL_RX = 25,
PRISM2_PARAM_MONITOR_TYPE = 26,
PRISM2_PARAM_WDS_TYPE = 27,
PRISM2_PARAM_HOSTSCAN = 28,
PRISM2_PARAM_AP_SCAN = 29,
PRISM2_PARAM_ENH_SEC = 30,
PRISM2_PARAM_IO_DEBUG = 31,
PRISM2_PARAM_BASIC_RATES = 32,
PRISM2_PARAM_OPER_RATES = 33,
PRISM2_PARAM_HOSTAPD = 34,
PRISM2_PARAM_HOSTAPD_STA = 35,
PRISM2_PARAM_WPA = 36,
PRISM2_PARAM_PRIVACY_INVOKED = 37,
PRISM2_PARAM_TKIP_COUNTERMEASURES = 38,
PRISM2_PARAM_DROP_UNENCRYPTED = 39,
PRISM2_PARAM_SCAN_CHANNEL_MASK = 40,
};
enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1,
HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 };
/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */
enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1,
AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3,
AP_MAC_CMD_KICKALL = 4 };
/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */
enum {
PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */,
/* Note! Old versions of prism2_srec have a fatal error in CRC-16
* calculation, which will corrupt all non-volatile downloads.
* PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to
* prevent use of old versions of prism2_srec for non-volatile
* download. */
PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */,
PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */,
/* Persistent versions of volatile download commands (keep firmware
* data in memory and automatically re-download after hw_reset */
PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5,
PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6,
};
struct prism2_download_param {
u32 dl_cmd;
u32 start_addr;
u32 num_areas;
struct prism2_download_area {
u32 addr; /* wlan card address */
u32 len;
caddr_t ptr; /* pointer to data in user space */
} data[0];
};
#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072
#define PRISM2_MAX_DOWNLOAD_LEN 262144
/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */
enum {
PRISM2_HOSTAPD_FLUSH = 1,
PRISM2_HOSTAPD_ADD_STA = 2,
PRISM2_HOSTAPD_REMOVE_STA = 3,
PRISM2_HOSTAPD_GET_INFO_STA = 4,
/* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */
PRISM2_SET_ENCRYPTION = 6,
PRISM2_GET_ENCRYPTION = 7,
PRISM2_HOSTAPD_SET_FLAGS_STA = 8,
PRISM2_HOSTAPD_GET_RID = 9,
PRISM2_HOSTAPD_SET_RID = 10,
PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11,
PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12,
PRISM2_HOSTAPD_MLME = 13,
PRISM2_HOSTAPD_SCAN_REQ = 14,
PRISM2_HOSTAPD_STA_CLEAR_STATS = 15,
};
#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024
#define PRISM2_HOSTAPD_RID_HDR_LEN \
((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data))
#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \
((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data))
/* Maximum length for algorithm names (-1 for nul termination) used in ioctl()
*/
#define HOSTAP_CRYPT_ALG_NAME_LEN 16
struct prism2_hostapd_param {
u32 cmd;
u8 sta_addr[ETH_ALEN];
union {
struct {
u16 aid;
u16 capability;
u8 tx_supp_rates;
} add_sta;
struct {
u32 inactive_sec;
} get_info_sta;
struct {
u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN];
u32 flags;
u32 err;
u8 idx;
u8 seq[8]; /* sequence counter (set: RX, get: TX) */
u16 key_len;
u8 key[0];
} crypt;
struct {
u32 flags_and;
u32 flags_or;
} set_flags_sta;
struct {
u16 rid;
u16 len;
u8 data[0];
} rid;
struct {
u8 len;
u8 data[0];
} generic_elem;
struct {
#define MLME_STA_DEAUTH 0
#define MLME_STA_DISASSOC 1
u16 cmd;
u16 reason_code;
} mlme;
struct {
u8 ssid_len;
u8 ssid[SSID_MAX_LEN];
} scan_req;
} u;
};
#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0)
#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1)
#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2
#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3
#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4
#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5
#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6
#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7
#endif /* HOSTAP_DRIVER_H */
diff --git a/contrib/wpa/src/drivers/driver_macsec_qca.c b/contrib/wpa/src/drivers/driver_macsec_qca.c
index 928f02499510..54964f37e3ed 100644
--- a/contrib/wpa/src/drivers/driver_macsec_qca.c
+++ b/contrib/wpa/src/drivers/driver_macsec_qca.c
@@ -1,1049 +1,1071 @@
/*
* Wired Ethernet driver interface for QCA MACsec driver
* Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
* Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
* Copyright (c) 2019, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <sys/ioctl.h>
#include <net/if.h>
#include <inttypes.h>
#ifdef __linux__
#include <netpacket/packet.h>
#include <net/if_arp.h>
#include <net/if.h>
#endif /* __linux__ */
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
#include <net/if_dl.h>
#include <net/if_media.h>
#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
#ifdef __sun__
#include <sys/sockio.h>
#endif /* __sun__ */
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/defs.h"
#include "common/ieee802_1x_defs.h"
#include "common/eapol_common.h"
#include "pae/ieee802_1x_kay.h"
#include "driver.h"
#include "driver_wired_common.h"
#include "nss_macsec_secy.h"
#include "nss_macsec_secy_rx.h"
#include "nss_macsec_secy_tx.h"
#define MAXSC 16
#define SAK_128_LEN 16
#define SAK_256_LEN 32
/* TCI field definition */
#define TCI_ES 0x40
#define TCI_SC 0x20
#define TCI_SCB 0x10
#define TCI_E 0x08
#define TCI_C 0x04
#ifdef _MSC_VER
#pragma pack(push, 1)
#endif /* _MSC_VER */
#ifdef _MSC_VER
#pragma pack(pop)
#endif /* _MSC_VER */
struct channel_map {
struct ieee802_1x_mka_sci sci;
};
struct macsec_qca_data {
struct driver_wired_common_data common;
int use_pae_group_addr;
u32 secy_id;
/* shadow */
bool always_include_sci;
bool use_es;
bool use_scb;
bool protect_frames;
bool replay_protect;
u32 replay_window;
struct channel_map receive_channel_map[MAXSC];
struct channel_map transmit_channel_map[MAXSC];
};
static void __macsec_drv_init(struct macsec_qca_data *drv)
{
int ret = 0;
fal_rx_ctl_filt_t rx_ctl_filt;
fal_tx_ctl_filt_t tx_ctl_filt;
wpa_printf(MSG_INFO, "%s: secy_id=%d", __func__, drv->secy_id);
/* Enable Secy and Let EAPoL bypass */
ret = nss_macsec_secy_en_set(drv->secy_id, true);
if (ret)
wpa_printf(MSG_ERROR, "nss_macsec_secy_en_set: FAIL");
ret = nss_macsec_secy_sc_sa_mapping_mode_set(drv->secy_id,
FAL_SC_SA_MAP_1_4);
if (ret)
wpa_printf(MSG_ERROR,
"nss_macsec_secy_sc_sa_mapping_mode_set: FAIL");
os_memset(&rx_ctl_filt, 0, sizeof(rx_ctl_filt));
rx_ctl_filt.bypass = 1;
rx_ctl_filt.match_type = IG_CTL_COMPARE_ETHER_TYPE;
rx_ctl_filt.match_mask = 0xffff;
rx_ctl_filt.ether_type_da_range = 0x888e;
ret = nss_macsec_secy_rx_ctl_filt_set(drv->secy_id, 0, &rx_ctl_filt);
if (ret)
wpa_printf(MSG_ERROR, "nss_macsec_secy_rx_ctl_filt_set: FAIL");
os_memset(&tx_ctl_filt, 0, sizeof(tx_ctl_filt));
tx_ctl_filt.bypass = 1;
tx_ctl_filt.match_type = EG_CTL_COMPARE_ETHER_TYPE;
tx_ctl_filt.match_mask = 0xffff;
tx_ctl_filt.ether_type_da_range = 0x888e;
ret = nss_macsec_secy_tx_ctl_filt_set(drv->secy_id, 0, &tx_ctl_filt);
if (ret)
wpa_printf(MSG_ERROR, "nss_macsec_secy_tx_ctl_filt_set: FAIL");
}
static void __macsec_drv_deinit(struct macsec_qca_data *drv)
{
nss_macsec_secy_en_set(drv->secy_id, false);
nss_macsec_secy_rx_sc_del_all(drv->secy_id);
nss_macsec_secy_tx_sc_del_all(drv->secy_id);
}
#ifdef __linux__
static void macsec_qca_handle_data(void *ctx, unsigned char *buf, size_t len)
{
#ifdef HOSTAPD
struct ieee8023_hdr *hdr;
u8 *pos, *sa;
size_t left;
union wpa_event_data event;
/* at least 6 bytes src macaddress, 6 bytes dst macaddress
* and 2 bytes ethertype
*/
if (len < 14) {
wpa_printf(MSG_MSGDUMP,
"macsec_qca_handle_data: too short (%lu)",
(unsigned long) len);
return;
}
hdr = (struct ieee8023_hdr *) buf;
switch (ntohs(hdr->ethertype)) {
case ETH_P_PAE:
wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
sa = hdr->src;
os_memset(&event, 0, sizeof(event));
event.new_sta.addr = sa;
wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
pos = (u8 *) (hdr + 1);
left = len - sizeof(*hdr);
drv_event_eapol_rx(ctx, sa, pos, left);
break;
default:
wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
ntohs(hdr->ethertype));
break;
}
#endif /* HOSTAPD */
}
static void macsec_qca_handle_read(int sock, void *eloop_ctx, void *sock_ctx)
{
int len;
unsigned char buf[3000];
len = recv(sock, buf, sizeof(buf), 0);
if (len < 0) {
wpa_printf(MSG_ERROR, "macsec_qca: recv: %s", strerror(errno));
return;
}
macsec_qca_handle_data(eloop_ctx, buf, len);
}
#endif /* __linux__ */
static int macsec_qca_init_sockets(struct macsec_qca_data *drv, u8 *own_addr)
{
#ifdef __linux__
struct ifreq ifr;
struct sockaddr_ll addr;
drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
if (drv->common.sock < 0) {
wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
strerror(errno));
return -1;
}
if (eloop_register_read_sock(drv->common.sock, macsec_qca_handle_read,
drv->common.ctx, NULL)) {
wpa_printf(MSG_INFO, "Could not register read socket");
return -1;
}
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
strerror(errno));
return -1;
}
os_memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_ifindex = ifr.ifr_ifindex;
wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
addr.sll_ifindex);
if (bind(drv->common.sock, (struct sockaddr *) &addr,
sizeof(addr)) < 0) {
wpa_printf(MSG_ERROR, "macsec_qca: bind: %s", strerror(errno));
return -1;
}
/* filter multicast address */
if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
pae_group_addr, 1) < 0) {
wpa_printf(MSG_ERROR,
"macsec_qca_init_sockets: Failed to add multicast group membership");
return -1;
}
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
strerror(errno));
return -1;
}
if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
ifr.ifr_hwaddr.sa_family);
return -1;
}
os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
return 0;
#else /* __linux__ */
return -1;
#endif /* __linux__ */
}
+static int macsec_qca_secy_id_get(const char *ifname, u32 *secy_id)
+{
+#ifdef NSS_MACSEC_SECY_ID_GET_FUNC
+ /* Get secy id from nss macsec driver */
+ return nss_macsec_secy_id_get((u8 *) ifname, secy_id);
+#else /* NSS_MACSEC_SECY_ID_GET_FUNC */
+ /* Board specific settings */
+ if (os_strcmp(ifname, "eth2") == 0) {
+ *secy_id = 1;
+ } else if (os_strcmp(ifname, "eth3") == 0) {
+ *secy_id = 2;
+ } else if (os_strcmp(ifname, "eth4") == 0 ||
+ os_strcmp(ifname, "eth0") == 0) {
+ *secy_id = 0;
+ } else if (os_strcmp(ifname, "eth5") == 0 ||
+ os_strcmp(ifname, "eth1") == 0) {
+ *secy_id = 1;
+ } else {
+ *secy_id = -1;
+ return -1;
+ }
+
+ return 0;
+#endif /* NSS_MACSEC_SECY_ID_GET_FUNC */
+}
+
+
static void * macsec_qca_init(void *ctx, const char *ifname)
{
struct macsec_qca_data *drv;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
return NULL;
- /* Board specific settings */
- if (os_memcmp("eth2", ifname, 4) == 0)
- drv->secy_id = 1;
- else if (os_memcmp("eth3", ifname, 4) == 0)
- drv->secy_id = 2;
- else
- drv->secy_id = -1;
+ if (macsec_qca_secy_id_get(ifname, &drv->secy_id)) {
+ wpa_printf(MSG_ERROR,
+ "macsec_qca: Failed to get secy_id for %s", ifname);
+ os_free(drv);
+ return NULL;
+ }
if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
os_free(drv);
return NULL;
}
return drv;
}
static void macsec_qca_deinit(void *priv)
{
struct macsec_qca_data *drv = priv;
driver_wired_deinit_common(&drv->common);
os_free(drv);
}
static void * macsec_qca_hapd_init(struct hostapd_data *hapd,
struct wpa_init_params *params)
{
struct macsec_qca_data *drv;
drv = os_zalloc(sizeof(struct macsec_qca_data));
if (!drv) {
wpa_printf(MSG_INFO,
"Could not allocate memory for macsec_qca driver data");
return NULL;
}
- /* Board specific settings */
- if (os_memcmp("eth2", params->ifname, 4) == 0)
- drv->secy_id = 1;
- else if (os_memcmp("eth3", params->ifname, 4) == 0)
- drv->secy_id = 2;
- else if (os_memcmp("eth4", params->ifname, 4) == 0)
- drv->secy_id = 0;
- else if (os_memcmp("eth5", params->ifname, 4) == 0)
- drv->secy_id = 1;
- else
- drv->secy_id = -1;
+ if (macsec_qca_secy_id_get(params->ifname, &drv->secy_id)) {
+ wpa_printf(MSG_ERROR,
+ "macsec_qca: Failed to get secy_id for %s",
+ params->ifname);
+ os_free(drv);
+ return NULL;
+ }
drv->common.ctx = hapd;
os_strlcpy(drv->common.ifname, params->ifname,
sizeof(drv->common.ifname));
drv->use_pae_group_addr = params->use_pae_group_addr;
if (macsec_qca_init_sockets(drv, params->own_addr)) {
os_free(drv);
return NULL;
}
return drv;
}
static void macsec_qca_hapd_deinit(void *priv)
{
struct macsec_qca_data *drv = priv;
if (drv->common.sock >= 0) {
eloop_unregister_read_sock(drv->common.sock);
close(drv->common.sock);
}
os_free(drv);
}
static int macsec_qca_send_eapol(void *priv, const u8 *addr,
const u8 *data, size_t data_len, int encrypt,
const u8 *own_addr, u32 flags)
{
struct macsec_qca_data *drv = priv;
struct ieee8023_hdr *hdr;
size_t len;
u8 *pos;
int res;
len = sizeof(*hdr) + data_len;
hdr = os_zalloc(len);
if (!hdr) {
wpa_printf(MSG_INFO,
"malloc() failed for macsec_qca_send_eapol(len=%lu)",
(unsigned long) len);
return -1;
}
os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
ETH_ALEN);
os_memcpy(hdr->src, own_addr, ETH_ALEN);
hdr->ethertype = htons(ETH_P_PAE);
pos = (u8 *) (hdr + 1);
os_memcpy(pos, data, data_len);
res = send(drv->common.sock, (u8 *) hdr, len, 0);
os_free(hdr);
if (res < 0) {
wpa_printf(MSG_ERROR,
"macsec_qca_send_eapol - packet len: %lu - failed: send: %s",
(unsigned long) len, strerror(errno));
}
return res;
}
static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params)
{
struct macsec_qca_data *drv = priv;
drv->always_include_sci = params->always_include_sci;
drv->use_es = params->use_es;
drv->use_scb = params->use_scb;
wpa_printf(MSG_DEBUG, "%s: es=%d, scb=%d, sci=%d",
__func__, drv->use_es, drv->use_scb,
drv->always_include_sci);
__macsec_drv_init(drv);
return 0;
}
static int macsec_qca_macsec_deinit(void *priv)
{
struct macsec_qca_data *drv = priv;
wpa_printf(MSG_DEBUG, "%s", __func__);
__macsec_drv_deinit(drv);
return 0;
}
static int macsec_qca_get_capability(void *priv, enum macsec_cap *cap)
{
wpa_printf(MSG_DEBUG, "%s", __func__);
*cap = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
return 0;
}
static int macsec_qca_enable_protect_frames(void *priv, bool enabled)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
drv->protect_frames = enabled;
return ret;
}
static int macsec_qca_set_replay_protect(void *priv, bool enabled,
unsigned int window)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
wpa_printf(MSG_DEBUG, "%s: enabled=%d, win=%u",
__func__, enabled, window);
drv->replay_protect = enabled;
drv->replay_window = window;
return ret;
}
static fal_cipher_suite_e macsec_qca_cs_type_get(u64 cs)
{
if (cs == CS_ID_GCM_AES_128)
return FAL_CIPHER_SUITE_AES_GCM_128;
if (cs == CS_ID_GCM_AES_256)
return FAL_CIPHER_SUITE_AES_GCM_256;
return FAL_CIPHER_SUITE_MAX;
}
static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs)
{
struct macsec_qca_data *drv = priv;
fal_cipher_suite_e cs_type;
if (cs != CS_ID_GCM_AES_128 && cs != CS_ID_GCM_AES_256) {
wpa_printf(MSG_ERROR,
"%s: NOT supported CipherSuite: %016" PRIx64,
__func__, cs);
return -1;
}
wpa_printf(MSG_DEBUG, "%s: CipherSuite: %016" PRIx64, __func__, cs);
cs_type = macsec_qca_cs_type_get(cs);
return nss_macsec_secy_cipher_suite_set(drv->secy_id, cs_type);
}
static int macsec_qca_enable_controlled_port(void *priv, bool enabled)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
wpa_printf(MSG_DEBUG, "%s: enable=%d", __func__, enabled);
ret += nss_macsec_secy_controlled_port_en_set(drv->secy_id, enabled);
return ret;
}
static int macsec_qca_lookup_channel(struct channel_map *map,
struct ieee802_1x_mka_sci *sci,
u32 *channel)
{
u32 i;
for (i = 0; i < MAXSC; i++) {
if (os_memcmp(&map[i].sci, sci,
sizeof(struct ieee802_1x_mka_sci)) == 0) {
*channel = i;
return 0;
}
}
return -1;
}
static void macsec_qca_register_channel(struct channel_map *map,
struct ieee802_1x_mka_sci *sci,
u32 channel)
{
os_memcpy(&map[channel].sci, sci, sizeof(struct ieee802_1x_mka_sci));
}
static int macsec_qca_lookup_receive_channel(struct macsec_qca_data *drv,
struct receive_sc *sc,
u32 *channel)
{
return macsec_qca_lookup_channel(drv->receive_channel_map, &sc->sci,
channel);
}
static void macsec_qca_register_receive_channel(struct macsec_qca_data *drv,
struct receive_sc *sc,
u32 channel)
{
macsec_qca_register_channel(drv->receive_channel_map, &sc->sci,
channel);
}
static int macsec_qca_lookup_transmit_channel(struct macsec_qca_data *drv,
struct transmit_sc *sc,
u32 *channel)
{
return macsec_qca_lookup_channel(drv->transmit_channel_map, &sc->sci,
channel);
}
static void macsec_qca_register_transmit_channel(struct macsec_qca_data *drv,
struct transmit_sc *sc,
u32 channel)
{
macsec_qca_register_channel(drv->transmit_channel_map, &sc->sci,
channel);
}
static int macsec_qca_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
u32 next_pn = 0;
bool enabled = false;
u32 win;
u32 channel;
ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
if (ret != 0)
return ret;
ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, sa->an,
&next_pn);
ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel,
&enabled);
ret += nss_macsec_secy_rx_sc_anti_replay_window_get(drv->secy_id,
channel, &win);
if (enabled)
sa->lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
else
sa->lowest_pn = next_pn;
wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, sa->lowest_pn);
return ret;
}
static int macsec_qca_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
u32 channel;
ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
if (ret != 0)
return ret;
ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, sa->an,
&sa->next_pn);
wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, sa->next_pn);
return ret;
}
static int macsec_qca_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
u32 channel;
ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
if (ret != 0)
return ret;
ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
sa->next_pn);
wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, sa->next_pn);
return ret;
}
static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
u32 sc_ch = 0;
bool in_use = false;
for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
ret = nss_macsec_secy_rx_sc_in_used_get(drv->secy_id, sc_ch,
&in_use);
if (ret)
continue;
if (!in_use) {
*channel = sc_ch;
wpa_printf(MSG_DEBUG, "%s: channel=%d",
__func__, *channel);
return 0;
}
}
wpa_printf(MSG_DEBUG, "%s: no available channel", __func__);
return -1;
}
static int macsec_qca_create_receive_sc(void *priv, struct receive_sc *sc,
unsigned int conf_offset,
int validation)
{
struct macsec_qca_data *drv = priv;
int ret = 0;
fal_rx_prc_lut_t entry;
fal_rx_sc_validate_frame_e vf;
enum validate_frames validate_frames = validation;
u32 channel;
const u8 *sci_addr = sc->sci.addr;
u16 sci_port = be_to_host16(sc->sci.port);
ret = macsec_qca_get_available_receive_sc(priv, &channel);
if (ret != 0)
return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
/* rx prc lut */
os_memset(&entry, 0, sizeof(entry));
os_memcpy(entry.sci, sci_addr, ETH_ALEN);
entry.sci[6] = (sci_port >> 8) & 0xff;
entry.sci[7] = sci_port & 0xff;
entry.sci_mask = 0xf;
entry.valid = 1;
entry.channel = channel;
entry.action = FAL_RX_PRC_ACTION_PROCESS;
entry.offset = conf_offset;
/* rx validate frame */
if (validate_frames == Strict)
vf = FAL_RX_SC_VALIDATE_FRAME_STRICT;
else if (validate_frames == Checked)
vf = FAL_RX_SC_VALIDATE_FRAME_CHECK;
else
vf = FAL_RX_SC_VALIDATE_FRAME_DISABLED;
ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
ret += nss_macsec_secy_rx_sc_create(drv->secy_id, channel);
ret += nss_macsec_secy_rx_sc_validate_frame_set(drv->secy_id, channel,
vf);
ret += nss_macsec_secy_rx_sc_replay_protect_set(drv->secy_id, channel,
drv->replay_protect);
ret += nss_macsec_secy_rx_sc_anti_replay_window_set(drv->secy_id,
channel,
drv->replay_window);
macsec_qca_register_receive_channel(drv, sc, channel);
return ret;
}
static int macsec_qca_delete_receive_sc(void *priv, struct receive_sc *sc)
{
struct macsec_qca_data *drv = priv;
int ret;
fal_rx_prc_lut_t entry;
u32 channel;
ret = macsec_qca_lookup_receive_channel(priv, sc, &channel);
if (ret != 0)
return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
/* rx prc lut */
os_memset(&entry, 0, sizeof(entry));
ret += nss_macsec_secy_rx_sc_del(drv->secy_id, channel);
ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
return ret;
}
static int macsec_qca_create_receive_sa(void *priv, struct receive_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret;
fal_rx_sak_t rx_sak;
int i = 0;
u32 channel;
fal_rx_prc_lut_t entry;
u32 offset;
ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
if (ret != 0)
return ret;
wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x",
__func__, channel, sa->an, sa->lowest_pn);
os_memset(&rx_sak, 0, sizeof(rx_sak));
rx_sak.sak_len = sa->pkey->key_len;
if (sa->pkey->key_len == SAK_128_LEN) {
for (i = 0; i < 16; i++)
rx_sak.sak[i] = sa->pkey->key[15 - i];
} else if (sa->pkey->key_len == SAK_256_LEN) {
for (i = 0; i < 16; i++) {
rx_sak.sak1[i] = sa->pkey->key[15 - i];
rx_sak.sak[i] = sa->pkey->key[31 - i];
}
} else {
return -1;
}
if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0)
offset = 0;
else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30)
offset = 30;
else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50)
offset = 50;
else
return -1;
ret += nss_macsec_secy_rx_prc_lut_get(drv->secy_id, channel, &entry);
entry.offset = offset;
ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, sa->an);
ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, sa->an,
&rx_sak);
return ret;
}
static int macsec_qca_enable_receive_sa(void *priv, struct receive_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret;
u32 channel;
ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
if (ret != 0)
return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
sa->an);
ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
true);
return ret;
}
static int macsec_qca_disable_receive_sa(void *priv, struct receive_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret;
u32 channel;
ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
if (ret != 0)
return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
sa->an);
ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
false);
return ret;
}
static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
{
struct macsec_qca_data *drv = priv;
u32 sc_ch = 0;
bool in_use = false;
for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
if (nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
&in_use))
continue;
if (!in_use) {
*channel = sc_ch;
wpa_printf(MSG_DEBUG, "%s: channel=%d",
__func__, *channel);
return 0;
}
}
wpa_printf(MSG_DEBUG, "%s: no avaiable channel", __func__);
return -1;
}
static int macsec_qca_create_transmit_sc(void *priv, struct transmit_sc *sc,
unsigned int conf_offset)
{
struct macsec_qca_data *drv = priv;
int ret;
fal_tx_class_lut_t entry;
u8 psci[ETH_ALEN + 2];
u32 channel;
u16 sci_port = be_to_host16(sc->sci.port);
ret = macsec_qca_get_available_transmit_sc(priv, &channel);
if (ret != 0)
return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
/* class lut */
os_memset(&entry, 0, sizeof(entry));
entry.valid = 1;
entry.action = FAL_TX_CLASS_ACTION_FORWARD;
entry.channel = channel;
os_memcpy(psci, sc->sci.addr, ETH_ALEN);
psci[6] = (sci_port >> 8) & 0xff;
psci[7] = sci_port & 0xff;
ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
ret += nss_macsec_secy_tx_sc_protect_set(drv->secy_id, channel,
drv->protect_frames);
ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
channel,
conf_offset);
macsec_qca_register_transmit_channel(drv, sc, channel);
return ret;
}
static int macsec_qca_delete_transmit_sc(void *priv, struct transmit_sc *sc)
{
struct macsec_qca_data *drv = priv;
int ret;
fal_tx_class_lut_t entry;
u32 channel;
ret = macsec_qca_lookup_transmit_channel(priv, sc, &channel);
if (ret != 0)
return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
/* class lut */
os_memset(&entry, 0, sizeof(entry));
ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
ret += nss_macsec_secy_tx_sc_del(drv->secy_id, channel);
return ret;
}
static int macsec_qca_create_transmit_sa(void *priv, struct transmit_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret;
u8 tci = 0;
fal_tx_sak_t tx_sak;
int i;
u32 channel;
u32 offset;
ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
if (ret != 0)
return ret;
wpa_printf(MSG_DEBUG,
"%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d",
__func__, channel, sa->an, sa->next_pn, sa->confidentiality);
if (drv->always_include_sci)
tci |= TCI_SC;
else if (drv->use_es)
tci |= TCI_ES;
else if (drv->use_scb)
tci |= TCI_SCB;
if (sa->confidentiality)
tci |= TCI_E | TCI_C;
os_memset(&tx_sak, 0, sizeof(tx_sak));
tx_sak.sak_len = sa->pkey->key_len;
if (sa->pkey->key_len == SAK_128_LEN) {
for (i = 0; i < 16; i++)
tx_sak.sak[i] = sa->pkey->key[15 - i];
} else if (sa->pkey->key_len == SAK_256_LEN) {
for (i = 0; i < 16; i++) {
tx_sak.sak1[i] = sa->pkey->key[15 - i];
tx_sak.sak[i] = sa->pkey->key[31 - i];
}
} else {
return -1;
}
if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0)
offset = 0;
else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30)
offset = 30;
else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50)
offset = 50;
else
return -1;
ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
channel,
offset);
ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
sa->next_pn);
ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, sa->an,
&tx_sak);
ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel,
(tci >> 2));
ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, sa->an);
return ret;
}
static int macsec_qca_enable_transmit_sa(void *priv, struct transmit_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret;
u32 channel;
ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
if (ret != 0)
return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
sa->an);
ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
true);
return ret;
}
static int macsec_qca_disable_transmit_sa(void *priv, struct transmit_sa *sa)
{
struct macsec_qca_data *drv = priv;
int ret;
u32 channel;
ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
if (ret != 0)
return ret;
wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
sa->an);
ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
false);
return ret;
}
const struct wpa_driver_ops wpa_driver_macsec_qca_ops = {
.name = "macsec_qca",
.desc = "QCA MACsec Ethernet driver",
.get_ssid = driver_wired_get_ssid,
.get_bssid = driver_wired_get_bssid,
.get_capa = driver_wired_get_capa,
.init = macsec_qca_init,
.deinit = macsec_qca_deinit,
.hapd_init = macsec_qca_hapd_init,
.hapd_deinit = macsec_qca_hapd_deinit,
.hapd_send_eapol = macsec_qca_send_eapol,
.macsec_init = macsec_qca_macsec_init,
.macsec_deinit = macsec_qca_macsec_deinit,
.macsec_get_capability = macsec_qca_get_capability,
.enable_protect_frames = macsec_qca_enable_protect_frames,
.set_replay_protect = macsec_qca_set_replay_protect,
.set_current_cipher_suite = macsec_qca_set_current_cipher_suite,
.enable_controlled_port = macsec_qca_enable_controlled_port,
.get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn,
.get_transmit_next_pn = macsec_qca_get_transmit_next_pn,
.set_transmit_next_pn = macsec_qca_set_transmit_next_pn,
.create_receive_sc = macsec_qca_create_receive_sc,
.delete_receive_sc = macsec_qca_delete_receive_sc,
.create_receive_sa = macsec_qca_create_receive_sa,
.enable_receive_sa = macsec_qca_enable_receive_sa,
.disable_receive_sa = macsec_qca_disable_receive_sa,
.create_transmit_sc = macsec_qca_create_transmit_sc,
.delete_transmit_sc = macsec_qca_delete_transmit_sc,
.create_transmit_sa = macsec_qca_create_transmit_sa,
.enable_transmit_sa = macsec_qca_enable_transmit_sa,
.disable_transmit_sa = macsec_qca_disable_transmit_sa,
};
diff --git a/contrib/wpa/src/drivers/driver_ndis.c b/contrib/wpa/src/drivers/driver_ndis.c
index 8555f96d3283..698553ec7dea 100644
--- a/contrib/wpa/src/drivers/driver_ndis.c
+++ b/contrib/wpa/src/drivers/driver_ndis.c
@@ -1,3240 +1,3240 @@
/*
* WPA Supplicant - Windows/NDIS driver interface
* Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifdef __CYGWIN__
/* Avoid some header file conflicts by not including standard headers for
* cygwin builds when Packet32.h is included. */
#include "build_config.h"
int close(int fd);
#else /* __CYGWIN__ */
#include "includes.h"
#endif /* __CYGWIN__ */
#ifdef CONFIG_USE_NDISUIO
#include <winsock2.h>
#else /* CONFIG_USE_NDISUIO */
#include <Packet32.h>
#endif /* CONFIG_USE_NDISUIO */
#ifdef __MINGW32_VERSION
#include <ddk/ntddndis.h>
#else /* __MINGW32_VERSION */
#include <ntddndis.h>
#endif /* __MINGW32_VERSION */
#ifdef _WIN32_WCE
#include <winioctl.h>
#include <nuiouser.h>
#include <devload.h>
#endif /* _WIN32_WCE */
#include "common.h"
#include "driver.h"
#include "eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "driver_ndis.h"
int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv);
#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data);
#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
static void wpa_driver_ndis_deinit(void *priv);
static void wpa_driver_ndis_poll(void *drv);
static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx);
static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv);
static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv);
static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv);
static const u8 pae_group_addr[ETH_ALEN] =
{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
/* FIX: to be removed once this can be compiled with the complete NDIS
* header files */
#ifndef OID_802_11_BSSID
#define OID_802_11_BSSID 0x0d010101
#define OID_802_11_SSID 0x0d010102
#define OID_802_11_INFRASTRUCTURE_MODE 0x0d010108
#define OID_802_11_ADD_WEP 0x0D010113
#define OID_802_11_REMOVE_WEP 0x0D010114
#define OID_802_11_DISASSOCIATE 0x0D010115
#define OID_802_11_BSSID_LIST 0x0d010217
#define OID_802_11_AUTHENTICATION_MODE 0x0d010118
#define OID_802_11_PRIVACY_FILTER 0x0d010119
#define OID_802_11_BSSID_LIST_SCAN 0x0d01011A
#define OID_802_11_WEP_STATUS 0x0d01011B
#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS
#define OID_802_11_ADD_KEY 0x0d01011D
#define OID_802_11_REMOVE_KEY 0x0d01011E
#define OID_802_11_ASSOCIATION_INFORMATION 0x0d01011F
#define OID_802_11_TEST 0x0d010120
#define OID_802_11_CAPABILITY 0x0d010122
#define OID_802_11_PMKID 0x0d010123
#define NDIS_802_11_LENGTH_SSID 32
#define NDIS_802_11_LENGTH_RATES 8
#define NDIS_802_11_LENGTH_RATES_EX 16
typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
typedef struct NDIS_802_11_SSID {
ULONG SsidLength;
UCHAR Ssid[NDIS_802_11_LENGTH_SSID];
} NDIS_802_11_SSID;
typedef LONG NDIS_802_11_RSSI;
typedef enum NDIS_802_11_NETWORK_TYPE {
Ndis802_11FH,
Ndis802_11DS,
Ndis802_11OFDM5,
Ndis802_11OFDM24,
Ndis802_11NetworkTypeMax
} NDIS_802_11_NETWORK_TYPE;
typedef struct NDIS_802_11_CONFIGURATION_FH {
ULONG Length;
ULONG HopPattern;
ULONG HopSet;
ULONG DwellTime;
} NDIS_802_11_CONFIGURATION_FH;
typedef struct NDIS_802_11_CONFIGURATION {
ULONG Length;
ULONG BeaconPeriod;
ULONG ATIMWindow;
ULONG DSConfig;
NDIS_802_11_CONFIGURATION_FH FHConfig;
} NDIS_802_11_CONFIGURATION;
typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
Ndis802_11IBSS,
Ndis802_11Infrastructure,
Ndis802_11AutoUnknown,
Ndis802_11InfrastructureMax
} NDIS_802_11_NETWORK_INFRASTRUCTURE;
typedef enum NDIS_802_11_AUTHENTICATION_MODE {
Ndis802_11AuthModeOpen,
Ndis802_11AuthModeShared,
Ndis802_11AuthModeAutoSwitch,
Ndis802_11AuthModeWPA,
Ndis802_11AuthModeWPAPSK,
Ndis802_11AuthModeWPANone,
Ndis802_11AuthModeWPA2,
Ndis802_11AuthModeWPA2PSK,
Ndis802_11AuthModeMax
} NDIS_802_11_AUTHENTICATION_MODE;
typedef enum NDIS_802_11_WEP_STATUS {
Ndis802_11WEPEnabled,
Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
Ndis802_11WEPDisabled,
Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
Ndis802_11WEPKeyAbsent,
Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
Ndis802_11WEPNotSupported,
Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
Ndis802_11Encryption2Enabled,
Ndis802_11Encryption2KeyAbsent,
Ndis802_11Encryption3Enabled,
Ndis802_11Encryption3KeyAbsent
} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS;
typedef enum NDIS_802_11_PRIVACY_FILTER {
Ndis802_11PrivFilterAcceptAll,
Ndis802_11PrivFilter8021xWEP
} NDIS_802_11_PRIVACY_FILTER;
typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES];
typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];
typedef struct NDIS_WLAN_BSSID_EX {
ULONG Length;
NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */
UCHAR Reserved[2];
NDIS_802_11_SSID Ssid;
ULONG Privacy;
NDIS_802_11_RSSI Rssi;
NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
NDIS_802_11_CONFIGURATION Configuration;
NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
NDIS_802_11_RATES_EX SupportedRates;
ULONG IELength;
UCHAR IEs[1];
} NDIS_WLAN_BSSID_EX;
typedef struct NDIS_802_11_BSSID_LIST_EX {
ULONG NumberOfItems;
NDIS_WLAN_BSSID_EX Bssid[1];
} NDIS_802_11_BSSID_LIST_EX;
typedef struct NDIS_802_11_FIXED_IEs {
UCHAR Timestamp[8];
USHORT BeaconInterval;
USHORT Capabilities;
} NDIS_802_11_FIXED_IEs;
typedef struct NDIS_802_11_WEP {
ULONG Length;
ULONG KeyIndex;
ULONG KeyLength;
UCHAR KeyMaterial[1];
} NDIS_802_11_WEP;
typedef ULONG NDIS_802_11_KEY_INDEX;
typedef ULONGLONG NDIS_802_11_KEY_RSC;
typedef struct NDIS_802_11_KEY {
ULONG Length;
ULONG KeyIndex;
ULONG KeyLength;
NDIS_802_11_MAC_ADDRESS BSSID;
NDIS_802_11_KEY_RSC KeyRSC;
UCHAR KeyMaterial[1];
} NDIS_802_11_KEY;
typedef struct NDIS_802_11_REMOVE_KEY {
ULONG Length;
ULONG KeyIndex;
NDIS_802_11_MAC_ADDRESS BSSID;
} NDIS_802_11_REMOVE_KEY;
typedef struct NDIS_802_11_AI_REQFI {
USHORT Capabilities;
USHORT ListenInterval;
NDIS_802_11_MAC_ADDRESS CurrentAPAddress;
} NDIS_802_11_AI_REQFI;
typedef struct NDIS_802_11_AI_RESFI {
USHORT Capabilities;
USHORT StatusCode;
USHORT AssociationId;
} NDIS_802_11_AI_RESFI;
typedef struct NDIS_802_11_ASSOCIATION_INFORMATION {
ULONG Length;
USHORT AvailableRequestFixedIEs;
NDIS_802_11_AI_REQFI RequestFixedIEs;
ULONG RequestIELength;
ULONG OffsetRequestIEs;
USHORT AvailableResponseFixedIEs;
NDIS_802_11_AI_RESFI ResponseFixedIEs;
ULONG ResponseIELength;
ULONG OffsetResponseIEs;
} NDIS_802_11_ASSOCIATION_INFORMATION;
typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
typedef struct NDIS_802_11_CAPABILITY {
ULONG Length;
ULONG Version;
ULONG NoOfPMKIDs;
ULONG NoOfAuthEncryptPairsSupported;
NDIS_802_11_AUTHENTICATION_ENCRYPTION
AuthenticationEncryptionSupported[1];
} NDIS_802_11_CAPABILITY;
typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
typedef struct BSSID_INFO {
NDIS_802_11_MAC_ADDRESS BSSID;
NDIS_802_11_PMKID_VALUE PMKID;
} BSSID_INFO;
typedef struct NDIS_802_11_PMKID {
ULONG Length;
ULONG BSSIDInfoCount;
BSSID_INFO BSSIDInfo[1];
} NDIS_802_11_PMKID;
typedef enum NDIS_802_11_STATUS_TYPE {
Ndis802_11StatusType_Authentication,
Ndis802_11StatusType_PMKID_CandidateList = 2,
Ndis802_11StatusTypeMax
} NDIS_802_11_STATUS_TYPE;
typedef struct NDIS_802_11_STATUS_INDICATION {
NDIS_802_11_STATUS_TYPE StatusType;
} NDIS_802_11_STATUS_INDICATION;
typedef struct PMKID_CANDIDATE {
NDIS_802_11_MAC_ADDRESS BSSID;
ULONG Flags;
} PMKID_CANDIDATE;
#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
ULONG Version;
ULONG NumCandidates;
PMKID_CANDIDATE CandidateList[1];
} NDIS_802_11_PMKID_CANDIDATE_LIST;
typedef struct NDIS_802_11_AUTHENTICATION_REQUEST {
ULONG Length;
NDIS_802_11_MAC_ADDRESS Bssid;
ULONG Flags;
} NDIS_802_11_AUTHENTICATION_REQUEST;
#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01
#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02
#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06
#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E
#endif /* OID_802_11_BSSID */
#ifndef OID_802_11_PMKID
/* Platform SDK for XP did not include WPA2, so add needed definitions */
#define OID_802_11_CAPABILITY 0x0d010122
#define OID_802_11_PMKID 0x0d010123
#define Ndis802_11AuthModeWPA2 6
#define Ndis802_11AuthModeWPA2PSK 7
#define Ndis802_11StatusType_PMKID_CandidateList 2
typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
typedef struct NDIS_802_11_CAPABILITY {
ULONG Length;
ULONG Version;
ULONG NoOfPMKIDs;
ULONG NoOfAuthEncryptPairsSupported;
NDIS_802_11_AUTHENTICATION_ENCRYPTION
AuthenticationEncryptionSupported[1];
} NDIS_802_11_CAPABILITY;
typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
typedef struct BSSID_INFO {
NDIS_802_11_MAC_ADDRESS BSSID;
NDIS_802_11_PMKID_VALUE PMKID;
} BSSID_INFO;
typedef struct NDIS_802_11_PMKID {
ULONG Length;
ULONG BSSIDInfoCount;
BSSID_INFO BSSIDInfo[1];
} NDIS_802_11_PMKID;
typedef struct PMKID_CANDIDATE {
NDIS_802_11_MAC_ADDRESS BSSID;
ULONG Flags;
} PMKID_CANDIDATE;
#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
ULONG Version;
ULONG NumCandidates;
PMKID_CANDIDATE CandidateList[1];
} NDIS_802_11_PMKID_CANDIDATE_LIST;
#endif /* OID_802_11_CAPABILITY */
#ifndef OID_DOT11_CURRENT_OPERATION_MODE
/* Native 802.11 OIDs */
#define OID_DOT11_NDIS_START 0x0D010300
#define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8)
#define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11)
typedef enum _DOT11_BSS_TYPE {
dot11_BSS_type_infrastructure = 1,
dot11_BSS_type_independent = 2,
dot11_BSS_type_any = 3
} DOT11_BSS_TYPE, * PDOT11_BSS_TYPE;
typedef UCHAR DOT11_MAC_ADDRESS[6];
typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS;
typedef enum _DOT11_SCAN_TYPE {
dot11_scan_type_active = 1,
dot11_scan_type_passive = 2,
dot11_scan_type_auto = 3,
dot11_scan_type_forced = 0x80000000
} DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE;
typedef struct _DOT11_SCAN_REQUEST_V2 {
DOT11_BSS_TYPE dot11BSSType;
DOT11_MAC_ADDRESS dot11BSSID;
DOT11_SCAN_TYPE dot11ScanType;
BOOLEAN bRestrictedScan;
ULONG udot11SSIDsOffset;
ULONG uNumOfdot11SSIDs;
BOOLEAN bUseRequestIE;
ULONG uRequestIDsOffset;
ULONG uNumOfRequestIDs;
ULONG uPhyTypeInfosOffset;
ULONG uNumOfPhyTypeInfos;
ULONG uIEsOffset;
ULONG uIEsLength;
UCHAR ucBuffer[1];
} DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2;
#endif /* OID_DOT11_CURRENT_OPERATION_MODE */
#ifdef CONFIG_USE_NDISUIO
#ifndef _WIN32_WCE
#ifdef __MINGW32_VERSION
typedef ULONG NDIS_OID;
#endif /* __MINGW32_VERSION */
/* from nuiouser.h */
#define FSCTL_NDISUIO_BASE FILE_DEVICE_NETWORK
#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
#define IOCTL_NDISUIO_OPEN_DEVICE \
_NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NDISUIO_QUERY_OID_VALUE \
_NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NDISUIO_SET_OID_VALUE \
_NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NDISUIO_SET_ETHER_TYPE \
_NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NDISUIO_QUERY_BINDING \
_NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NDISUIO_BIND_WAIT \
_NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
FILE_READ_ACCESS | FILE_WRITE_ACCESS)
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
typedef struct _NDISUIO_SET_OID
{
NDIS_OID Oid;
UCHAR Data[sizeof(ULONG)];
} NDISUIO_SET_OID, *PNDISUIO_SET_OID;
typedef struct _NDISUIO_QUERY_BINDING
{
ULONG BindingIndex;
ULONG DeviceNameOffset;
ULONG DeviceNameLength;
ULONG DeviceDescrOffset;
ULONG DeviceDescrLength;
} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;
#endif /* _WIN32_WCE */
#endif /* CONFIG_USE_NDISUIO */
static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
char *data, size_t len)
{
#ifdef CONFIG_USE_NDISUIO
NDISUIO_QUERY_OID *o;
size_t buflen = sizeof(*o) + len;
DWORD written;
int ret;
size_t hdrlen;
o = os_zalloc(buflen);
if (o == NULL)
return -1;
o->Oid = oid;
#ifdef _WIN32_WCE
o->ptcDeviceName = drv->adapter_name;
#endif /* _WIN32_WCE */
if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE,
o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written,
NULL)) {
wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE "
"failed (oid=%08x): %d", oid, (int) GetLastError());
os_free(o);
return -1;
}
hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data);
if (written < hdrlen) {
wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); "
"too short", oid, (unsigned int) written);
os_free(o);
return -1;
}
written -= hdrlen;
if (written > len) {
wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > "
"len (%d)",oid, (unsigned int) written, len);
os_free(o);
return -1;
}
os_memcpy(data, o->Data, written);
ret = written;
os_free(o);
return ret;
#else /* CONFIG_USE_NDISUIO */
char *buf;
PACKET_OID_DATA *o;
int ret;
buf = os_zalloc(sizeof(*o) + len);
if (buf == NULL)
return -1;
o = (PACKET_OID_DATA *) buf;
o->Oid = oid;
o->Length = len;
if (!PacketRequest(drv->adapter, FALSE, o)) {
wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%lu) failed",
__func__, oid, len);
os_free(buf);
return -1;
}
if (o->Length > len) {
wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%lu)",
__func__, oid, (unsigned int) o->Length, len);
os_free(buf);
return -1;
}
os_memcpy(data, o->Data, o->Length);
ret = o->Length;
os_free(buf);
return ret;
#endif /* CONFIG_USE_NDISUIO */
}
static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
const char *data, size_t len)
{
#ifdef CONFIG_USE_NDISUIO
NDISUIO_SET_OID *o;
size_t buflen, reallen;
DWORD written;
char txt[50];
os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
buflen = sizeof(*o) + len;
reallen = buflen - sizeof(o->Data);
o = os_zalloc(buflen);
if (o == NULL)
return -1;
o->Oid = oid;
#ifdef _WIN32_WCE
o->ptcDeviceName = drv->adapter_name;
#endif /* _WIN32_WCE */
if (data)
os_memcpy(o->Data, data, len);
if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE,
o, reallen, NULL, 0, &written, NULL)) {
wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE "
"(oid=%08x) failed: %d", oid, (int) GetLastError());
os_free(o);
return -1;
}
os_free(o);
return 0;
#else /* CONFIG_USE_NDISUIO */
char *buf;
PACKET_OID_DATA *o;
char txt[50];
os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
buf = os_zalloc(sizeof(*o) + len);
if (buf == NULL)
return -1;
o = (PACKET_OID_DATA *) buf;
o->Oid = oid;
o->Length = len;
if (data)
os_memcpy(o->Data, data, len);
if (!PacketRequest(drv->adapter, TRUE, o)) {
wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%lu) failed",
__func__, oid, len);
os_free(buf);
return -1;
}
os_free(buf);
return 0;
#endif /* CONFIG_USE_NDISUIO */
}
static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode)
{
u32 auth_mode = mode;
if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
(char *) &auth_mode, sizeof(auth_mode)) < 0) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
"OID_802_11_AUTHENTICATION_MODE (%d)",
(int) auth_mode);
return -1;
}
return 0;
}
static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv)
{
u32 auth_mode;
int res;
res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE,
(char *) &auth_mode, sizeof(auth_mode));
if (res != sizeof(auth_mode)) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
"OID_802_11_AUTHENTICATION_MODE");
return -1;
}
return auth_mode;
}
static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr)
{
u32 encr_status = encr;
if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS,
(char *) &encr_status, sizeof(encr_status)) < 0) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
"OID_802_11_ENCRYPTION_STATUS (%d)", encr);
return -1;
}
return 0;
}
static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv)
{
u32 encr;
int res;
res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS,
(char *) &encr, sizeof(encr));
if (res != sizeof(encr)) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
"OID_802_11_ENCRYPTION_STATUS");
return -1;
}
return encr;
}
static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid)
{
struct wpa_driver_ndis_data *drv = priv;
if (drv->wired) {
/*
* Report PAE group address as the "BSSID" for wired
* connection.
*/
os_memcpy(bssid, pae_group_addr, ETH_ALEN);
return 0;
}
return ndis_get_oid(drv, OID_802_11_BSSID, (char *) bssid, ETH_ALEN) <
0 ? -1 : 0;
}
static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid)
{
struct wpa_driver_ndis_data *drv = priv;
NDIS_802_11_SSID buf;
int res;
res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
if (res < 4) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID");
if (drv->wired) {
wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure "
"with a wired interface");
return 0;
}
return -1;
}
os_memcpy(ssid, buf.Ssid, buf.SsidLength);
return buf.SsidLength;
}
static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv,
const u8 *ssid, size_t ssid_len)
{
NDIS_802_11_SSID buf;
os_memset(&buf, 0, sizeof(buf));
buf.SsidLength = ssid_len;
os_memcpy(buf.Ssid, ssid, ssid_len);
/*
* Make sure radio is marked enabled here so that scan request will not
* force SSID to be changed to a random one in order to enable radio at
* that point.
*/
drv->radio_enabled = 1;
return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
}
/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off.
*/
static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv)
{
drv->radio_enabled = 0;
return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4);
}
/* Disconnect by setting SSID to random (i.e., likely not used). */
static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv)
{
char ssid[SSID_MAX_LEN];
int i;
for (i = 0; i < SSID_MAX_LEN; i++)
ssid[i] = rand() & 0xff;
return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN);
}
static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
u16 reason_code)
{
struct wpa_driver_ndis_data *drv = priv;
return wpa_driver_ndis_disconnect(drv);
}
static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
}
static int wpa_driver_ndis_scan_native80211(
struct wpa_driver_ndis_data *drv,
struct wpa_driver_scan_params *params)
{
DOT11_SCAN_REQUEST_V2 req;
int res;
os_memset(&req, 0, sizeof(req));
req.dot11BSSType = dot11_BSS_type_any;
os_memset(req.dot11BSSID, 0xff, ETH_ALEN);
req.dot11ScanType = dot11_scan_type_auto;
res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req,
sizeof(req));
eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
drv->ctx);
return res;
}
static int wpa_driver_ndis_scan(void *priv,
struct wpa_driver_scan_params *params)
{
struct wpa_driver_ndis_data *drv = priv;
int res;
if (drv->native80211)
return wpa_driver_ndis_scan_native80211(drv, params);
if (!drv->radio_enabled) {
wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first"
" scan");
if (wpa_driver_ndis_disconnect(drv) < 0) {
wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio");
}
drv->radio_enabled = 1;
}
res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, " ", 4);
eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
drv->ctx);
return res;
}
static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
{
return get_ie((const u8 *) (res + 1), res->ie_len, ie);
}
static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid(
struct wpa_scan_res *r, NDIS_802_11_SSID *ssid)
{
struct wpa_scan_res *nr;
u8 *pos;
if (wpa_scan_get_ie(r, WLAN_EID_SSID))
return r; /* SSID IE already present */
if (ssid->SsidLength == 0 || ssid->SsidLength > SSID_MAX_LEN)
return r; /* No valid SSID inside scan data */
nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength);
if (nr == NULL)
return r;
pos = ((u8 *) (nr + 1)) + nr->ie_len;
*pos++ = WLAN_EID_SSID;
*pos++ = ssid->SsidLength;
os_memcpy(pos, ssid->Ssid, ssid->SsidLength);
nr->ie_len += 2 + ssid->SsidLength;
return nr;
}
static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv)
{
struct wpa_driver_ndis_data *drv = priv;
NDIS_802_11_BSSID_LIST_EX *b;
size_t blen, count, i;
int len;
char *pos;
struct wpa_scan_results *results;
struct wpa_scan_res *r;
blen = 65535;
b = os_zalloc(blen);
if (b == NULL)
return NULL;
len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
if (len < 0) {
wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
os_free(b);
return NULL;
}
count = b->NumberOfItems;
results = os_zalloc(sizeof(*results));
if (results == NULL) {
os_free(b);
return NULL;
}
results->res = os_calloc(count, sizeof(struct wpa_scan_res *));
if (results->res == NULL) {
os_free(results);
os_free(b);
return NULL;
}
pos = (char *) &b->Bssid[0];
for (i = 0; i < count; i++) {
NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
NDIS_802_11_FIXED_IEs *fixed;
if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) {
wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d",
(int) bss->IELength);
break;
}
if (((char *) bss->IEs) + bss->IELength > (char *) b + blen) {
/*
* Some NDIS drivers have been reported to include an
* entry with an invalid IELength in scan results and
* this has crashed wpa_supplicant, so validate the
* returned value before using it.
*/
wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan "
"result IE (BSSID=" MACSTR ") IELength=%d",
MAC2STR(bss->MacAddress),
(int) bss->IELength);
break;
}
r = os_zalloc(sizeof(*r) + bss->IELength -
sizeof(NDIS_802_11_FIXED_IEs));
if (r == NULL)
break;
os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN);
r->level = (int) bss->Rssi;
r->freq = bss->Configuration.DSConfig / 1000;
fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs;
r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval);
r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities);
r->tsf = WPA_GET_LE64(fixed->Timestamp);
os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs),
bss->IELength - sizeof(NDIS_802_11_FIXED_IEs));
r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid);
results->res[results->num++] = r;
pos += bss->Length;
if (pos > (char *) b + blen)
break;
}
os_free(b);
return results;
}
static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv,
int key_idx, const u8 *addr,
const u8 *bssid, int pairwise)
{
NDIS_802_11_REMOVE_KEY rkey;
NDIS_802_11_KEY_INDEX index;
int res, res2;
os_memset(&rkey, 0, sizeof(rkey));
rkey.Length = sizeof(rkey);
rkey.KeyIndex = key_idx;
if (pairwise)
rkey.KeyIndex |= 1 << 30;
os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
sizeof(rkey));
if (!pairwise) {
index = key_idx;
res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP,
(char *) &index, sizeof(index));
} else
res2 = 0;
if (res < 0 && res2 < 0)
return -1;
return 0;
}
static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv,
int pairwise, int key_idx, int set_tx,
const u8 *key, size_t key_len)
{
NDIS_802_11_WEP *wep;
size_t len;
int res;
len = 12 + key_len;
wep = os_zalloc(len);
if (wep == NULL)
return -1;
wep->Length = len;
wep->KeyIndex = key_idx;
if (set_tx)
wep->KeyIndex |= 1 << 31;
#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */
if (pairwise)
wep->KeyIndex |= 1 << 30;
#endif
wep->KeyLength = key_len;
os_memcpy(wep->KeyMaterial, key, key_len);
wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP",
(u8 *) wep, len);
res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
os_free(wep);
return res;
}
static int wpa_driver_ndis_set_key(const char *ifname, void *priv,
enum wpa_alg alg, const u8 *addr,
int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len)
{
struct wpa_driver_ndis_data *drv = priv;
size_t len, i;
NDIS_802_11_KEY *nkey;
int res, pairwise;
u8 bssid[ETH_ALEN];
if (addr == NULL || is_broadcast_ether_addr(addr)) {
/* Group Key */
pairwise = 0;
if (wpa_driver_ndis_get_bssid(drv, bssid) < 0)
os_memset(bssid, 0xff, ETH_ALEN);
} else {
/* Pairwise Key */
pairwise = 1;
os_memcpy(bssid, addr, ETH_ALEN);
}
if (alg == WPA_ALG_NONE || key_len == 0) {
return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid,
pairwise);
}
if (alg == WPA_ALG_WEP) {
return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx,
key, key_len);
}
len = 12 + 6 + 6 + 8 + key_len;
nkey = os_zalloc(len);
if (nkey == NULL)
return -1;
nkey->Length = len;
nkey->KeyIndex = key_idx;
if (set_tx)
nkey->KeyIndex |= 1 << 31;
if (pairwise)
nkey->KeyIndex |= 1 << 30;
if (seq && seq_len)
nkey->KeyIndex |= 1 << 29;
nkey->KeyLength = key_len;
os_memcpy(nkey->BSSID, bssid, ETH_ALEN);
if (seq && seq_len) {
for (i = 0; i < seq_len; i++)
nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8);
}
if (alg == WPA_ALG_TKIP && key_len == 32) {
os_memcpy(nkey->KeyMaterial, key, 16);
os_memcpy(nkey->KeyMaterial + 16, key + 24, 8);
os_memcpy(nkey->KeyMaterial + 24, key + 16, 8);
} else {
os_memcpy(nkey->KeyMaterial, key, key_len);
}
wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY",
(u8 *) nkey, len);
res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len);
os_free(nkey);
return res;
}
static int
wpa_driver_ndis_set_key_wrapper(void *priv,
struct wpa_driver_set_key_params *params)
{
return wpa_driver_ndis_set_key(params->ifname, priv,
params->alg, params->addr,
params->key_idx, params->set_tx,
params->seq, params->seq_len,
params->key, params->key_len);
}
static int
wpa_driver_ndis_associate(void *priv,
struct wpa_driver_associate_params *params)
{
struct wpa_driver_ndis_data *drv = priv;
u32 auth_mode, encr, priv_mode, mode;
u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
drv->mode = params->mode;
/* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys,
* so static WEP keys needs to be set again after this. */
if (params->mode == IEEE80211_MODE_IBSS) {
mode = Ndis802_11IBSS;
/* Need to make sure that BSSID polling is enabled for
* IBSS mode. */
eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
drv, NULL);
} else
mode = Ndis802_11Infrastructure;
if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
(char *) &mode, sizeof(mode)) < 0) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
"OID_802_11_INFRASTRUCTURE_MODE (%d)",
(int) mode);
/* Try to continue anyway */
}
if (params->key_mgmt_suite == WPA_KEY_MGMT_NONE ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
/* Re-set WEP keys if static WEP configuration is used. */
int i;
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
continue;
wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP "
"key %d", i);
wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
bcast, i,
i == params->wep_tx_keyidx,
NULL, 0, params->wep_key[i],
params->wep_key_len[i]);
}
}
if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
if (params->auth_alg & WPA_AUTH_ALG_SHARED) {
if (params->auth_alg & WPA_AUTH_ALG_OPEN)
auth_mode = Ndis802_11AuthModeAutoSwitch;
else
auth_mode = Ndis802_11AuthModeShared;
} else
auth_mode = Ndis802_11AuthModeOpen;
priv_mode = Ndis802_11PrivFilterAcceptAll;
} else if (params->wpa_ie[0] == WLAN_EID_RSN) {
priv_mode = Ndis802_11PrivFilter8021xWEP;
if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
auth_mode = Ndis802_11AuthModeWPA2PSK;
else
auth_mode = Ndis802_11AuthModeWPA2;
#ifdef CONFIG_WPS
} else if (params->key_mgmt_suite == WPA_KEY_MGMT_WPS) {
auth_mode = Ndis802_11AuthModeOpen;
priv_mode = Ndis802_11PrivFilterAcceptAll;
if (params->wps == WPS_MODE_PRIVACY) {
- u8 dummy_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 };
+ u8 stub_key[5] = { 0x11, 0x22, 0x33, 0x44, 0x55 };
/*
* Some NDIS drivers refuse to associate in open mode
* configuration due to Privacy field mismatch, so use
* a workaround to make the configuration look like
* matching one for WPS provisioning.
*/
- wpa_printf(MSG_DEBUG, "NDIS: Set dummy WEP key as a "
+ wpa_printf(MSG_DEBUG, "NDIS: Set stub WEP key as a "
"workaround to allow driver to associate "
"for WPS");
wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
bcast, 0, 1,
- NULL, 0, dummy_key,
- sizeof(dummy_key));
+ NULL, 0, stub_key,
+ sizeof(stub_key));
}
#endif /* CONFIG_WPS */
} else {
priv_mode = Ndis802_11PrivFilter8021xWEP;
if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE)
auth_mode = Ndis802_11AuthModeWPANone;
else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
auth_mode = Ndis802_11AuthModeWPAPSK;
else
auth_mode = Ndis802_11AuthModeWPA;
}
switch (params->pairwise_suite) {
case WPA_CIPHER_CCMP:
encr = Ndis802_11Encryption3Enabled;
break;
case WPA_CIPHER_TKIP:
encr = Ndis802_11Encryption2Enabled;
break;
case WPA_CIPHER_WEP40:
case WPA_CIPHER_WEP104:
encr = Ndis802_11Encryption1Enabled;
break;
case WPA_CIPHER_NONE:
#ifdef CONFIG_WPS
if (params->wps == WPS_MODE_PRIVACY) {
encr = Ndis802_11Encryption1Enabled;
break;
}
#endif /* CONFIG_WPS */
if (params->group_suite == WPA_CIPHER_CCMP)
encr = Ndis802_11Encryption3Enabled;
else if (params->group_suite == WPA_CIPHER_TKIP)
encr = Ndis802_11Encryption2Enabled;
else
encr = Ndis802_11EncryptionDisabled;
break;
default:
#ifdef CONFIG_WPS
if (params->wps == WPS_MODE_PRIVACY) {
encr = Ndis802_11Encryption1Enabled;
break;
}
#endif /* CONFIG_WPS */
encr = Ndis802_11EncryptionDisabled;
break;
};
if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER,
(char *) &priv_mode, sizeof(priv_mode)) < 0) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
"OID_802_11_PRIVACY_FILTER (%d)",
(int) priv_mode);
/* Try to continue anyway */
}
ndis_set_auth_mode(drv, auth_mode);
ndis_set_encr_status(drv, encr);
if (params->bssid) {
ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid,
ETH_ALEN);
drv->oid_bssid_set = 1;
} else if (drv->oid_bssid_set) {
ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff",
ETH_ALEN);
drv->oid_bssid_set = 0;
}
return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len);
}
static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv)
{
int len, count, i, ret;
struct ndis_pmkid_entry *entry;
NDIS_802_11_PMKID *p;
count = 0;
entry = drv->pmkid;
while (entry) {
count++;
if (count >= drv->no_of_pmkid)
break;
entry = entry->next;
}
len = 8 + count * sizeof(BSSID_INFO);
p = os_zalloc(len);
if (p == NULL)
return -1;
p->Length = len;
p->BSSIDInfoCount = count;
entry = drv->pmkid;
for (i = 0; i < count; i++) {
os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN);
os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16);
entry = entry->next;
}
wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len);
ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len);
os_free(p);
return ret;
}
static int wpa_driver_ndis_add_pmkid(void *priv,
struct wpa_pmkid_params *params)
{
struct wpa_driver_ndis_data *drv = priv;
struct ndis_pmkid_entry *entry, *prev;
const u8 *bssid = params->bssid;
const u8 *pmkid = params->pmkid;
if (!bssid || !pmkid)
return -1;
if (drv->no_of_pmkid == 0)
return 0;
prev = NULL;
entry = drv->pmkid;
while (entry) {
if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
break;
prev = entry;
entry = entry->next;
}
if (entry) {
/* Replace existing entry for this BSSID and move it into the
* beginning of the list. */
os_memcpy(entry->pmkid, pmkid, 16);
if (prev) {
prev->next = entry->next;
entry->next = drv->pmkid;
drv->pmkid = entry;
}
} else {
entry = os_malloc(sizeof(*entry));
if (entry) {
os_memcpy(entry->bssid, bssid, ETH_ALEN);
os_memcpy(entry->pmkid, pmkid, 16);
entry->next = drv->pmkid;
drv->pmkid = entry;
}
}
return wpa_driver_ndis_set_pmkid(drv);
}
static int wpa_driver_ndis_remove_pmkid(void *priv,
struct wpa_pmkid_params *params)
{
struct wpa_driver_ndis_data *drv = priv;
struct ndis_pmkid_entry *entry, *prev;
const u8 *bssid = params->bssid;
const u8 *pmkid = params->pmkid;
if (!bssid || !pmkid)
return -1;
if (drv->no_of_pmkid == 0)
return 0;
entry = drv->pmkid;
prev = NULL;
while (entry) {
if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
os_memcmp(entry->pmkid, pmkid, 16) == 0) {
if (prev)
prev->next = entry->next;
else
drv->pmkid = entry->next;
os_free(entry);
break;
}
prev = entry;
entry = entry->next;
}
return wpa_driver_ndis_set_pmkid(drv);
}
static int wpa_driver_ndis_flush_pmkid(void *priv)
{
struct wpa_driver_ndis_data *drv = priv;
NDIS_802_11_PMKID p;
struct ndis_pmkid_entry *pmkid, *prev;
int prev_authmode, ret;
if (drv->no_of_pmkid == 0)
return 0;
pmkid = drv->pmkid;
drv->pmkid = NULL;
while (pmkid) {
prev = pmkid;
pmkid = pmkid->next;
os_free(prev);
}
/*
* Some drivers may refuse OID_802_11_PMKID if authMode is not set to
* WPA2, so change authMode temporarily, if needed.
*/
prev_authmode = ndis_get_auth_mode(drv);
if (prev_authmode != Ndis802_11AuthModeWPA2)
ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2);
os_memset(&p, 0, sizeof(p));
p.Length = 8;
p.BSSIDInfoCount = 0;
wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)",
(u8 *) &p, 8);
ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8);
if (prev_authmode != Ndis802_11AuthModeWPA2)
ndis_set_auth_mode(drv, prev_authmode);
return ret;
}
static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv)
{
char buf[512], *pos;
NDIS_802_11_ASSOCIATION_INFORMATION *ai;
int len;
union wpa_event_data data;
NDIS_802_11_BSSID_LIST_EX *b;
size_t blen, i;
len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf,
sizeof(buf));
if (len < 0) {
wpa_printf(MSG_DEBUG, "NDIS: failed to get association "
"information");
return -1;
}
if (len > sizeof(buf)) {
/* Some drivers seem to be producing incorrect length for this
* data. Limit the length to the current buffer size to avoid
* crashing in hexdump. The data seems to be otherwise valid,
* so better try to use it. */
wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association "
"information length %d", len);
len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION,
buf, sizeof(buf));
if (len < -1) {
wpa_printf(MSG_DEBUG, "NDIS: re-reading association "
"information failed");
return -1;
}
if (len > sizeof(buf)) {
wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association"
" information length %d (re-read)", len);
len = sizeof(buf);
}
}
wpa_hexdump(MSG_MSGDUMP, "NDIS: association information",
(u8 *) buf, len);
if (len < sizeof(*ai)) {
wpa_printf(MSG_DEBUG, "NDIS: too short association "
"information");
return -1;
}
ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf;
wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d "
"off_resp=%d len_req=%d len_resp=%d",
ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs,
(int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs,
(int) ai->RequestIELength, (int) ai->ResponseIELength);
if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len ||
ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) {
wpa_printf(MSG_DEBUG, "NDIS: association information - "
"IE overflow");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs",
(u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength);
wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs",
(u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength);
os_memset(&data, 0, sizeof(data));
data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs;
data.assoc_info.req_ies_len = ai->RequestIELength;
data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs;
data.assoc_info.resp_ies_len = ai->ResponseIELength;
blen = 65535;
b = os_zalloc(blen);
if (b == NULL)
goto skip_scan_results;
len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
if (len < 0) {
wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
os_free(b);
b = NULL;
goto skip_scan_results;
}
wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo",
(unsigned int) b->NumberOfItems);
pos = (char *) &b->Bssid[0];
for (i = 0; i < b->NumberOfItems; i++) {
NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 &&
bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) {
data.assoc_info.beacon_ies =
((u8 *) bss->IEs) +
sizeof(NDIS_802_11_FIXED_IEs);
data.assoc_info.beacon_ies_len =
bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs",
data.assoc_info.beacon_ies,
data.assoc_info.beacon_ies_len);
break;
}
pos += bss->Length;
if (pos > (char *) b + blen)
break;
}
skip_scan_results:
wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
os_free(b);
return 0;
}
static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_driver_ndis_data *drv = eloop_ctx;
u8 bssid[ETH_ALEN];
int poll;
if (drv->wired)
return;
if (wpa_driver_ndis_get_bssid(drv, bssid)) {
/* Disconnected */
if (!is_zero_ether_addr(drv->bssid)) {
os_memset(drv->bssid, 0, ETH_ALEN);
wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
}
} else {
/* Connected */
if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) {
os_memcpy(drv->bssid, bssid, ETH_ALEN);
wpa_driver_ndis_get_associnfo(drv);
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
}
}
/* When using integrated NDIS event receiver, we can skip BSSID
* polling when using infrastructure network. However, when using
* IBSS mode, many driver do not seem to generate connection event,
* so we need to enable BSSID polling to figure out when IBSS network
* has been formed.
*/
poll = drv->mode == IEEE80211_MODE_IBSS;
#ifndef CONFIG_NDIS_EVENTS_INTEGRATED
#ifndef _WIN32_WCE
poll = 1;
#endif /* _WIN32_WCE */
#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
if (poll) {
eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
drv, NULL);
}
}
static void wpa_driver_ndis_poll(void *priv)
{
struct wpa_driver_ndis_data *drv = priv;
eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
wpa_driver_ndis_poll_timeout(drv, NULL);
}
/* Called when driver generates Media Connect Event by calling
* NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */
void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv)
{
wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event");
if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) {
wpa_driver_ndis_get_associnfo(drv);
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
}
}
/* Called when driver generates Media Disconnect Event by calling
* NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */
void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv)
{
wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event");
os_memset(drv->bssid, 0, ETH_ALEN);
wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
}
static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv,
const u8 *data, size_t data_len)
{
NDIS_802_11_AUTHENTICATION_REQUEST *req;
int pairwise = 0, group = 0;
union wpa_event_data event;
if (data_len < sizeof(*req)) {
wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request "
"Event (len=%lu)", data_len);
return;
}
req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data;
wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: "
"Bssid " MACSTR " Flags 0x%x",
MAC2STR(req->Bssid), (int) req->Flags);
if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) ==
NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR)
pairwise = 1;
else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) ==
NDIS_802_11_AUTH_REQUEST_GROUP_ERROR)
group = 1;
if (pairwise || group) {
os_memset(&event, 0, sizeof(event));
event.michael_mic_failure.unicast = pairwise;
wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE,
&event);
}
}
static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv,
const u8 *data, size_t data_len)
{
NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid;
size_t i;
union wpa_event_data event;
if (data_len < 8) {
wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List "
"Event (len=%lu)", data_len);
return;
}
pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data;
wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d "
"NumCandidates %d",
(int) pmkid->Version, (int) pmkid->NumCandidates);
if (pmkid->Version != 1) {
wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List "
"Version %d", (int) pmkid->Version);
return;
}
if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) {
wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow");
return;
}
os_memset(&event, 0, sizeof(event));
for (i = 0; i < pmkid->NumCandidates; i++) {
PMKID_CANDIDATE *p = &pmkid->CandidateList[i];
wpa_printf(MSG_DEBUG, "NDIS: %lu: " MACSTR " Flags 0x%x",
i, MAC2STR(p->BSSID), (int) p->Flags);
os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN);
event.pmkid_candidate.index = i;
event.pmkid_candidate.preauth =
p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED;
wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE,
&event);
}
}
/* Called when driver calls NdisMIndicateStatus() with
* NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */
void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
const u8 *data, size_t data_len)
{
NDIS_802_11_STATUS_INDICATION *status;
if (data == NULL || data_len < sizeof(*status))
return;
wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication",
data, data_len);
status = (NDIS_802_11_STATUS_INDICATION *) data;
data += sizeof(status);
data_len -= sizeof(status);
switch (status->StatusType) {
case Ndis802_11StatusType_Authentication:
wpa_driver_ndis_event_auth(drv, data, data_len);
break;
case Ndis802_11StatusType_PMKID_CandidateList:
wpa_driver_ndis_event_pmkid(drv, data, data_len);
break;
default:
wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d",
(int) status->StatusType);
break;
}
}
/* Called when an adapter is added */
void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv)
{
union wpa_event_data event;
int i;
wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival");
for (i = 0; i < 30; i++) {
/* Re-open Packet32/NDISUIO connection */
wpa_driver_ndis_adapter_close(drv);
if (wpa_driver_ndis_adapter_init(drv) < 0 ||
wpa_driver_ndis_adapter_open(drv) < 0) {
wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization "
"(%d) failed", i);
os_sleep(1, 0);
} else {
wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized");
break;
}
}
os_memset(&event, 0, sizeof(event));
os_strlcpy(event.interface_status.ifname, drv->ifname,
sizeof(event.interface_status.ifname));
event.interface_status.ievent = EVENT_INTERFACE_ADDED;
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
}
/* Called when an adapter is removed */
void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv)
{
union wpa_event_data event;
wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal");
os_memset(&event, 0, sizeof(event));
os_strlcpy(event.interface_status.ifname, drv->ifname,
sizeof(event.interface_status.ifname));
event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
}
static void
wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv)
{
wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability");
if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 &&
ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) {
wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported");
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
}
if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 &&
ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) {
wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management "
"supported");
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
}
if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 &&
ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) {
wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported");
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
}
if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 &&
ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) {
wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported");
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
}
if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 &&
ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) {
wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported");
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
WPA_DRIVER_CAPA_ENC_WEP104;
}
if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 &&
ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) {
drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
}
if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 &&
ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) {
drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
}
ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled);
/* Could also verify OID_802_11_ADD_KEY error reporting and
* support for OID_802_11_ASSOCIATION_INFORMATION. */
if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA &&
drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP |
WPA_DRIVER_CAPA_ENC_CCMP)) {
wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA");
drv->has_capability = 1;
} else {
wpa_printf(MSG_DEBUG, "NDIS: no WPA support found");
}
wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
"enc 0x%x auth 0x%x",
drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
}
static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv)
{
char buf[512];
int len;
size_t i;
NDIS_802_11_CAPABILITY *c;
drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE;
len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf));
if (len < 0) {
wpa_driver_ndis_get_wpa_capability(drv);
return;
}
wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len);
c = (NDIS_802_11_CAPABILITY *) buf;
if (len < sizeof(*c) || c->Version != 2) {
wpa_printf(MSG_DEBUG, "NDIS: unsupported "
"OID_802_11_CAPABILITY data");
return;
}
wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - "
"NoOfPMKIDs %d NoOfAuthEncrPairs %d",
(int) c->NoOfPMKIDs,
(int) c->NoOfAuthEncryptPairsSupported);
drv->has_capability = 1;
drv->no_of_pmkid = c->NoOfPMKIDs;
for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) {
NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae;
ae = &c->AuthenticationEncryptionSupported[i];
if ((char *) (ae + 1) > buf + len) {
wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list "
"overflow");
break;
}
wpa_printf(MSG_MSGDUMP, "NDIS: %lu - auth %d encr %d",
i, (int) ae->AuthModeSupported,
(int) ae->EncryptStatusSupported);
switch (ae->AuthModeSupported) {
case Ndis802_11AuthModeOpen:
drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
break;
case Ndis802_11AuthModeShared:
drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
break;
case Ndis802_11AuthModeWPA:
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
break;
case Ndis802_11AuthModeWPAPSK:
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
break;
case Ndis802_11AuthModeWPA2:
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
break;
case Ndis802_11AuthModeWPA2PSK:
drv->capa.key_mgmt |=
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
break;
case Ndis802_11AuthModeWPANone:
drv->capa.key_mgmt |=
WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE;
break;
default:
break;
}
switch (ae->EncryptStatusSupported) {
case Ndis802_11Encryption1Enabled:
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40;
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104;
break;
case Ndis802_11Encryption2Enabled:
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
break;
case Ndis802_11Encryption3Enabled:
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
break;
default:
break;
}
}
wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
"enc 0x%x auth 0x%x",
drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
}
static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa)
{
struct wpa_driver_ndis_data *drv = priv;
if (!drv->has_capability)
return -1;
os_memcpy(capa, &drv->capa, sizeof(*capa));
return 0;
}
static const char * wpa_driver_ndis_get_ifname(void *priv)
{
struct wpa_driver_ndis_data *drv = priv;
return drv->ifname;
}
static const u8 * wpa_driver_ndis_get_mac_addr(void *priv)
{
struct wpa_driver_ndis_data *drv = priv;
return drv->own_addr;
}
#ifdef _WIN32_WCE
#define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512)
static void ndisuio_notification_receive(void *eloop_data, void *user_ctx)
{
struct wpa_driver_ndis_data *drv = eloop_data;
NDISUIO_DEVICE_NOTIFICATION *hdr;
u8 buf[NDISUIO_MSG_SIZE];
DWORD len, flags;
if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0,
&flags)) {
wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
"ReadMsgQueue failed: %d", (int) GetLastError());
return;
}
if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) {
wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
"Too short message (len=%d)", (int) len);
return;
}
hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf;
wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x",
(int) len, hdr->dwNotificationType);
switch (hdr->dwNotificationType) {
#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL:
wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL");
wpa_driver_ndis_event_adapter_arrival(drv);
break;
#endif
#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL:
wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL");
wpa_driver_ndis_event_adapter_removal(drv);
break;
#endif
case NDISUIO_NOTIFICATION_MEDIA_CONNECT:
wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT");
SetEvent(drv->connected_event);
wpa_driver_ndis_event_connect(drv);
break;
case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT:
ResetEvent(drv->connected_event);
wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT");
wpa_driver_ndis_event_disconnect(drv);
break;
case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION:
wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION");
#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420
wpa_driver_ndis_event_media_specific(
drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize);
#else
wpa_driver_ndis_event_media_specific(
drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer,
(size_t) hdr->uiStatusBufferSize);
#endif
break;
default:
wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x",
hdr->dwNotificationType);
break;
}
}
static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv)
{
NDISUIO_REQUEST_NOTIFICATION req;
memset(&req, 0, sizeof(req));
req.hMsgQueue = drv->event_queue;
req.dwNotificationTypes = 0;
if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
&req, sizeof(req), NULL, 0, NULL, NULL)) {
wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
"IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
(int) GetLastError());
}
if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION,
NULL, 0, NULL, 0, NULL, NULL)) {
wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
"IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d",
(int) GetLastError());
}
if (drv->event_queue) {
eloop_unregister_event(drv->event_queue,
sizeof(drv->event_queue));
CloseHandle(drv->event_queue);
drv->event_queue = NULL;
}
if (drv->connected_event) {
CloseHandle(drv->connected_event);
drv->connected_event = NULL;
}
}
static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv)
{
MSGQUEUEOPTIONS opt;
NDISUIO_REQUEST_NOTIFICATION req;
drv->connected_event =
CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
if (drv->connected_event == NULL) {
wpa_printf(MSG_INFO, "ndisuio_notification_init: "
"CreateEvent failed: %d",
(int) GetLastError());
return -1;
}
memset(&opt, 0, sizeof(opt));
opt.dwSize = sizeof(opt);
opt.dwMaxMessages = 5;
opt.cbMaxMessage = NDISUIO_MSG_SIZE;
opt.bReadAccess = TRUE;
drv->event_queue = CreateMsgQueue(NULL, &opt);
if (drv->event_queue == NULL) {
wpa_printf(MSG_INFO, "ndisuio_notification_init: "
"CreateMsgQueue failed: %d",
(int) GetLastError());
ndisuio_notification_deinit(drv);
return -1;
}
memset(&req, 0, sizeof(req));
req.hMsgQueue = drv->event_queue;
req.dwNotificationTypes =
#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL |
#endif
#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
NDISUIO_NOTIFICATION_ADAPTER_REMOVAL |
#endif
NDISUIO_NOTIFICATION_MEDIA_CONNECT |
NDISUIO_NOTIFICATION_MEDIA_DISCONNECT |
NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION;
if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
&req, sizeof(req), NULL, 0, NULL, NULL)) {
wpa_printf(MSG_INFO, "ndisuio_notification_init: "
"IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
(int) GetLastError());
ndisuio_notification_deinit(drv);
return -1;
}
eloop_register_event(drv->event_queue, sizeof(drv->event_queue),
ndisuio_notification_receive, drv, NULL);
return 0;
}
#endif /* _WIN32_WCE */
static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv)
{
#ifdef CONFIG_USE_NDISUIO
NDISUIO_QUERY_BINDING *b;
size_t blen = sizeof(*b) + 1024;
int i, error, found = 0;
DWORD written;
char name[256], desc[256], *dpos;
WCHAR *pos;
size_t j, len, dlen;
b = os_malloc(blen);
if (b == NULL)
return -1;
for (i = 0; ; i++) {
os_memset(b, 0, blen);
b->BindingIndex = i;
if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
&written, NULL)) {
error = (int) GetLastError();
if (error == ERROR_NO_MORE_ITEMS)
break;
wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
"failed: %d", error);
break;
}
pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
len = b->DeviceNameLength;
if (len >= sizeof(name))
len = sizeof(name) - 1;
for (j = 0; j < len; j++)
name[j] = (char) pos[j];
name[len] = '\0';
pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
len = b->DeviceDescrLength;
if (len >= sizeof(desc))
len = sizeof(desc) - 1;
for (j = 0; j < len; j++)
desc[j] = (char) pos[j];
desc[len] = '\0';
wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
if (os_strstr(name, drv->ifname)) {
wpa_printf(MSG_DEBUG, "NDIS: Interface name match");
found = 1;
break;
}
if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0)
{
wpa_printf(MSG_DEBUG, "NDIS: Interface description "
"match");
found = 1;
break;
}
}
if (!found) {
wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
drv->ifname);
os_free(b);
return -1;
}
os_strlcpy(drv->ifname,
os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name,
sizeof(drv->ifname));
#ifdef _WIN32_WCE
drv->adapter_name = wpa_strdup_tchar(drv->ifname);
if (drv->adapter_name == NULL) {
wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for "
"adapter name");
os_free(b);
return -1;
}
#endif /* _WIN32_WCE */
dpos = os_strstr(desc, " - ");
if (dpos)
dlen = dpos - desc;
else
dlen = os_strlen(desc);
drv->adapter_desc = os_malloc(dlen + 1);
if (drv->adapter_desc) {
os_memcpy(drv->adapter_desc, desc, dlen);
drv->adapter_desc[dlen] = '\0';
}
os_free(b);
if (drv->adapter_desc == NULL)
return -1;
wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
drv->adapter_desc);
return 0;
#else /* CONFIG_USE_NDISUIO */
PTSTR _names;
char *names, *pos, *pos2;
ULONG len;
BOOLEAN res;
#define MAX_ADAPTERS 32
char *name[MAX_ADAPTERS];
char *desc[MAX_ADAPTERS];
int num_name, num_desc, i, found_name, found_desc;
size_t dlen;
wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
PacketGetVersion());
len = 8192;
_names = os_zalloc(len);
if (_names == NULL)
return -1;
res = PacketGetAdapterNames(_names, &len);
if (!res && len > 8192) {
os_free(_names);
_names = os_zalloc(len);
if (_names == NULL)
return -1;
res = PacketGetAdapterNames(_names, &len);
}
if (!res) {
wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
"(PacketGetAdapterNames)");
os_free(_names);
return -1;
}
names = (char *) _names;
if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
"UNICODE");
/* Convert to ASCII */
pos2 = pos = names;
while (pos2 < names + len) {
if (pos2[0] == '\0' && pos2[1] == '\0' &&
pos2[2] == '\0' && pos2[3] == '\0') {
pos2 += 4;
break;
}
*pos++ = pos2[0];
pos2 += 2;
}
os_memcpy(pos + 2, names, pos - names);
pos += 2;
} else
pos = names;
num_name = 0;
while (pos < names + len) {
name[num_name] = pos;
while (*pos && pos < names + len)
pos++;
if (pos + 1 >= names + len) {
os_free(names);
return -1;
}
pos++;
num_name++;
if (num_name >= MAX_ADAPTERS) {
wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
os_free(names);
return -1;
}
if (*pos == '\0') {
wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
num_name);
pos++;
break;
}
}
num_desc = 0;
while (pos < names + len) {
desc[num_desc] = pos;
while (*pos && pos < names + len)
pos++;
if (pos + 1 >= names + len) {
os_free(names);
return -1;
}
pos++;
num_desc++;
if (num_desc >= MAX_ADAPTERS) {
wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
"descriptions");
os_free(names);
return -1;
}
if (*pos == '\0') {
wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
"found", num_name);
pos++;
break;
}
}
/*
* Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
- * descriptions. Fill in dummy descriptors to work around this.
+ * descriptions. Fill in stub descriptors to work around this.
*/
while (num_desc < num_name)
- desc[num_desc++] = "dummy description";
+ desc[num_desc++] = "stub description";
if (num_name != num_desc) {
wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
"description counts (%d != %d)",
num_name, num_desc);
os_free(names);
return -1;
}
found_name = found_desc = -1;
for (i = 0; i < num_name; i++) {
wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s",
i, name[i], desc[i]);
if (found_name == -1 && os_strstr(name[i], drv->ifname))
found_name = i;
if (found_desc == -1 &&
os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) ==
0)
found_desc = i;
}
if (found_name < 0 && found_desc >= 0) {
wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on "
"description '%s'",
name[found_desc], desc[found_desc]);
found_name = found_desc;
os_strlcpy(drv->ifname,
os_strncmp(name[found_desc], "\\Device\\NPF_", 12)
== 0 ? name[found_desc] + 12 : name[found_desc],
sizeof(drv->ifname));
}
if (found_name < 0) {
wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
drv->ifname);
os_free(names);
return -1;
}
i = found_name;
pos = os_strrchr(desc[i], '(');
if (pos) {
dlen = pos - desc[i];
pos--;
if (pos > desc[i] && *pos == ' ')
dlen--;
} else {
dlen = os_strlen(desc[i]);
}
drv->adapter_desc = os_malloc(dlen + 1);
if (drv->adapter_desc) {
os_memcpy(drv->adapter_desc, desc[i], dlen);
drv->adapter_desc[dlen] = '\0';
}
os_free(names);
if (drv->adapter_desc == NULL)
return -1;
wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
drv->adapter_desc);
return 0;
#endif /* CONFIG_USE_NDISUIO */
}
#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__)
#ifndef _WIN32_WCE
/*
* These structures are undocumented for WinXP; only WinCE version is
* documented. These would be included wzcsapi.h if it were available. Some
* changes here have been needed to make the structures match with WinXP SP2.
* It is unclear whether these work with any other version.
*/
typedef struct {
LPWSTR wszGuid;
} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY;
typedef struct {
DWORD dwNumIntfs;
PINTF_KEY_ENTRY pIntfs;
} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE;
typedef struct {
DWORD dwDataLen;
LPBYTE pData;
} RAW_DATA, *PRAW_DATA;
typedef struct {
LPWSTR wszGuid;
LPWSTR wszDescr;
ULONG ulMediaState;
ULONG ulMediaType;
ULONG ulPhysicalMediaType;
INT nInfraMode;
INT nAuthMode;
INT nWepStatus;
#ifndef _WIN32_WCE
u8 pad[2]; /* why is this needed? */
#endif /* _WIN32_WCE */
DWORD dwCtlFlags;
DWORD dwCapabilities; /* something added for WinXP SP2(?) */
RAW_DATA rdSSID;
RAW_DATA rdBSSID;
RAW_DATA rdBSSIDList;
RAW_DATA rdStSSIDList;
RAW_DATA rdCtrlData;
#ifdef UNDER_CE
BOOL bInitialized;
#endif
DWORD nWPAMCastCipher;
/* add some extra buffer for later additions since this interface is
* far from stable */
u8 later_additions[100];
} INTF_ENTRY, *PINTF_ENTRY;
#define INTF_ALL 0xffffffff
#define INTF_ALL_FLAGS 0x0000ffff
#define INTF_CTLFLAGS 0x00000010
#define INTFCTL_ENABLED 0x8000
#endif /* _WIN32_WCE */
#ifdef _WIN32_WCE
static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv)
{
HANDLE ndis;
TCHAR multi[100];
int len;
len = _tcslen(drv->adapter_name);
if (len > 80)
return -1;
ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
if (ndis == INVALID_HANDLE_VALUE) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS "
"device: %d", (int) GetLastError());
return -1;
}
len++;
memcpy(multi, drv->adapter_name, len * sizeof(TCHAR));
memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR));
len += 9;
if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER,
multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL))
{
wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER "
"failed: 0x%x", (int) GetLastError());
wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz",
(u8 *) multi, len * sizeof(TCHAR));
CloseHandle(ndis);
return -1;
}
CloseHandle(ndis);
wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO "
"protocol");
return 0;
}
#endif /* _WIN32_WCE */
static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
int enable)
{
#ifdef _WIN32_WCE
HKEY hk, hk2;
LONG ret;
DWORD i, hnd, len;
TCHAR keyname[256], devname[256];
#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig")
if (enable) {
HANDLE h;
h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL);
if (h == INVALID_HANDLE_VALUE || h == 0) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC "
"- ActivateDeviceEx failed: %d",
(int) GetLastError());
return -1;
}
wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled");
return wpa_driver_ndis_rebind_adapter(drv);
}
/*
* Unfortunately, just disabling the WZC for an interface is not enough
* to free NDISUIO for us, so need to disable and unload WZC completely
* for now when using WinCE with NDISUIO. In addition, must request
* NDISUIO protocol to be rebound to the adapter in order to free the
* NDISUIO binding that WZC hold before us.
*/
/* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk);
if (ret != ERROR_SUCCESS) {
wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) "
"failed: %d %d", (int) ret, (int) GetLastError());
return -1;
}
for (i = 0; ; i++) {
len = sizeof(keyname);
ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL,
NULL);
if (ret != ERROR_SUCCESS) {
wpa_printf(MSG_DEBUG, "NDIS: Could not find active "
"WZC - assuming it is not running.");
RegCloseKey(hk);
return -1;
}
ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2);
if (ret != ERROR_SUCCESS) {
wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) "
"failed: %d %d",
(int) ret, (int) GetLastError());
continue;
}
len = sizeof(devname);
ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL,
(LPBYTE) devname, &len);
if (ret != ERROR_SUCCESS) {
wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx("
"DEVKEY_VALNAME) failed: %d %d",
(int) ret, (int) GetLastError());
RegCloseKey(hk2);
continue;
}
if (_tcscmp(devname, WZC_DRIVER) == 0)
break;
RegCloseKey(hk2);
}
RegCloseKey(hk);
/* Found WZC - get handle to it. */
len = sizeof(hnd);
ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL,
(PUCHAR) &hnd, &len);
if (ret != ERROR_SUCCESS) {
wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) "
"failed: %d %d", (int) ret, (int) GetLastError());
RegCloseKey(hk2);
return -1;
}
RegCloseKey(hk2);
/* Deactivate WZC */
if (!DeactivateDevice((HANDLE) hnd)) {
wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d",
(int) GetLastError());
return -1;
}
wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily");
drv->wzc_disabled = 1;
return wpa_driver_ndis_rebind_adapter(drv);
#else /* _WIN32_WCE */
HMODULE hm;
DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr,
PINTFS_KEY_TABLE pIntfs);
DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
PINTF_ENTRY pIntf,
LPDWORD pdwOutFlags);
DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
PINTF_ENTRY pIntf, LPDWORD pdwOutFlags);
int ret = -1, j;
DWORD res;
INTFS_KEY_TABLE guids;
INTF_ENTRY intf;
char guid[128];
WCHAR *pos;
DWORD flags, i;
hm = LoadLibrary(TEXT("wzcsapi.dll"));
if (hm == NULL) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) "
"- WZC probably not running",
(unsigned int) GetLastError());
return -1;
}
#ifdef _WIN32_WCE
wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces");
wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface");
wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface");
#else /* _WIN32_WCE */
wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces");
wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface");
wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface");
#endif /* _WIN32_WCE */
if (wzc_enum_interf == NULL || wzc_query_interf == NULL ||
wzc_set_interf == NULL) {
wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, "
"WZCQueryInterface, or WZCSetInterface not found "
"in wzcsapi.dll");
goto fail;
}
os_memset(&guids, 0, sizeof(guids));
res = wzc_enum_interf(NULL, &guids);
if (res != 0) {
wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; "
"WZC service is apparently not running",
(int) res);
goto fail;
}
wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces",
(int) guids.dwNumIntfs);
for (i = 0; i < guids.dwNumIntfs; i++) {
pos = guids.pIntfs[i].wszGuid;
for (j = 0; j < sizeof(guid); j++) {
guid[j] = (char) *pos;
if (*pos == 0)
break;
pos++;
}
guid[sizeof(guid) - 1] = '\0';
wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'",
(int) i, guid);
if (os_strstr(drv->ifname, guid) == NULL)
continue;
wpa_printf(MSG_DEBUG, "NDIS: Current interface found from "
"WZC");
break;
}
if (i >= guids.dwNumIntfs) {
wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from "
"WZC");
goto fail;
}
os_memset(&intf, 0, sizeof(intf));
intf.wszGuid = guids.pIntfs[i].wszGuid;
/* Set flags to verify that the structure has not changed. */
intf.dwCtlFlags = -1;
flags = 0;
res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags);
if (res != 0) {
wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the "
"WZC interface: %d (0x%x)",
(int) res, (int) res);
wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
(unsigned int) GetLastError());
goto fail;
}
wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x",
(int) flags, (int) intf.dwCtlFlags);
if (intf.dwCtlFlags == -1) {
wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed "
"again - could not disable WZC");
wpa_hexdump(MSG_MSGDUMP, "NDIS: intf",
(u8 *) &intf, sizeof(intf));
goto fail;
}
if (enable) {
if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) {
wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this "
"interface");
intf.dwCtlFlags |= INTFCTL_ENABLED;
res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
&flags);
if (res != 0) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to enable "
"WZC: %d (0x%x)",
(int) res, (int) res);
wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
(unsigned int) GetLastError());
goto fail;
}
wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this "
"interface");
drv->wzc_disabled = 0;
}
} else {
if (intf.dwCtlFlags & INTFCTL_ENABLED) {
wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this "
"interface");
intf.dwCtlFlags &= ~INTFCTL_ENABLED;
res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
&flags);
if (res != 0) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to "
"disable WZC: %d (0x%x)",
(int) res, (int) res);
wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
(unsigned int) GetLastError());
goto fail;
}
wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily "
"for this interface");
drv->wzc_disabled = 1;
} else {
wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for "
"this interface");
}
}
ret = 0;
fail:
FreeLibrary(hm);
return ret;
#endif /* _WIN32_WCE */
}
#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
int enable)
{
return 0;
}
#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
#ifdef CONFIG_USE_NDISUIO
/*
* l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able
* to export this handle. This is somewhat ugly, but there is no better
* mechanism available to pass data from driver interface to l2_packet wrapper.
*/
static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
HANDLE driver_ndis_get_ndisuio_handle(void)
{
return driver_ndis_ndisuio_handle;
}
#endif /* CONFIG_USE_NDISUIO */
static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv)
{
#ifdef CONFIG_USE_NDISUIO
#ifndef _WIN32_WCE
#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio")
DWORD written;
#endif /* _WIN32_WCE */
drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
INVALID_HANDLE_VALUE);
if (drv->ndisuio == INVALID_HANDLE_VALUE) {
wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
"NDISUIO: %d", (int) GetLastError());
return -1;
}
driver_ndis_ndisuio_handle = drv->ndisuio;
#ifndef _WIN32_WCE
if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
NULL, 0, &written, NULL)) {
wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
"%d", (int) GetLastError());
CloseHandle(drv->ndisuio);
drv->ndisuio = INVALID_HANDLE_VALUE;
return -1;
}
#endif /* _WIN32_WCE */
return 0;
#else /* CONFIG_USE_NDISUIO */
return 0;
#endif /* CONFIG_USE_NDISUIO */
}
static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv)
{
#ifdef CONFIG_USE_NDISUIO
DWORD written;
#define MAX_NDIS_DEVICE_NAME_LEN 256
WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN];
size_t len, i, pos;
const char *prefix = "\\DEVICE\\";
#ifdef _WIN32_WCE
pos = 0;
#else /* _WIN32_WCE */
pos = 8;
#endif /* _WIN32_WCE */
len = pos + os_strlen(drv->ifname);
if (len >= MAX_NDIS_DEVICE_NAME_LEN)
return -1;
for (i = 0; i < pos; i++)
ifname[i] = (WCHAR) prefix[i];
for (i = pos; i < len; i++)
ifname[i] = (WCHAR) drv->ifname[i - pos];
ifname[i] = L'\0';
if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE,
ifname, len * sizeof(WCHAR), NULL, 0, &written,
NULL)) {
wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE "
"failed: %d", (int) GetLastError());
wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname",
(const u8 *) ifname, len * sizeof(WCHAR));
CloseHandle(drv->ndisuio);
drv->ndisuio = INVALID_HANDLE_VALUE;
return -1;
}
wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully");
return 0;
#else /* CONFIG_USE_NDISUIO */
char ifname[128];
os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname);
drv->adapter = PacketOpenAdapter(ifname);
if (drv->adapter == NULL) {
wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for "
"'%s'", ifname);
return -1;
}
return 0;
#endif /* CONFIG_USE_NDISUIO */
}
static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv)
{
#ifdef CONFIG_USE_NDISUIO
driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
if (drv->ndisuio != INVALID_HANDLE_VALUE)
CloseHandle(drv->ndisuio);
#else /* CONFIG_USE_NDISUIO */
if (drv->adapter)
PacketCloseAdapter(drv->adapter);
#endif /* CONFIG_USE_NDISUIO */
}
static int ndis_add_multicast(struct wpa_driver_ndis_data *drv)
{
if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST,
(const char *) pae_group_addr, ETH_ALEN) < 0) {
wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address "
"to the multicast list");
return -1;
}
return 0;
}
static void * wpa_driver_ndis_init(void *ctx, const char *ifname)
{
struct wpa_driver_ndis_data *drv;
u32 mode;
int i;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
return NULL;
drv->ctx = ctx;
/*
* Compatibility code to strip possible prefix from the GUID. Previous
* versions include \Device\NPF_ prefix for all names, but the internal
* interface name is now only the GUI. Both Packet32 and NDISUIO
* prefixes are supported.
*/
if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
ifname += 12;
else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0)
ifname += 8;
os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
if (wpa_driver_ndis_adapter_init(drv) < 0) {
os_free(drv);
return NULL;
}
if (wpa_driver_ndis_get_names(drv) < 0) {
wpa_driver_ndis_adapter_close(drv);
os_free(drv);
return NULL;
}
wpa_driver_ndis_set_wzc(drv, 0);
if (wpa_driver_ndis_adapter_open(drv) < 0) {
wpa_driver_ndis_adapter_close(drv);
os_free(drv);
return NULL;
}
if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS,
(char *) drv->own_addr, ETH_ALEN) < 0) {
wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS "
"failed");
wpa_driver_ndis_adapter_close(drv);
os_free(drv);
return NULL;
}
wpa_driver_ndis_get_capability(drv);
/* Update per interface supported AKMs */
for (i = 0; i < WPA_IF_MAX; i++)
drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
/* Make sure that the driver does not have any obsolete PMKID entries.
*/
wpa_driver_ndis_flush_pmkid(drv);
/*
* Disconnect to make sure that driver re-associates if it was
* connected.
*/
wpa_driver_ndis_disconnect(drv);
eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL);
#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail,
drv->ifname, drv->adapter_desc);
if (drv->events == NULL) {
wpa_driver_ndis_deinit(drv);
return NULL;
}
eloop_register_event(drv->event_avail, sizeof(drv->event_avail),
wpa_driver_ndis_event_pipe_cb, drv, NULL);
#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
#ifdef _WIN32_WCE
if (ndisuio_notification_init(drv) < 0) {
wpa_driver_ndis_deinit(drv);
return NULL;
}
#endif /* _WIN32_WCE */
/* Set mode here in case card was configured for ad-hoc mode
* previously. */
mode = Ndis802_11Infrastructure;
if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
(char *) &mode, sizeof(mode)) < 0) {
char buf[8];
int res;
wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
"OID_802_11_INFRASTRUCTURE_MODE (%d)",
(int) mode);
/* Try to continue anyway */
res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf,
sizeof(buf));
if (res > 0) {
wpa_printf(MSG_INFO, "NDIS: The driver seems to use "
"Native 802.11 OIDs. These are not yet "
"fully supported.");
drv->native80211 = 1;
} else if (!drv->has_capability || drv->capa.enc == 0) {
/*
* Note: This will also happen with NDIS 6 drivers with
* Vista.
*/
wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide "
"any wireless capabilities - assume it is "
"a wired interface");
drv->wired = 1;
drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED;
drv->has_capability = 1;
ndis_add_multicast(drv);
}
}
return drv;
}
static void wpa_driver_ndis_deinit(void *priv)
{
struct wpa_driver_ndis_data *drv = priv;
#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
if (drv->events) {
eloop_unregister_event(drv->event_avail,
sizeof(drv->event_avail));
ndis_events_deinit(drv->events);
}
#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
#ifdef _WIN32_WCE
ndisuio_notification_deinit(drv);
#endif /* _WIN32_WCE */
eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
wpa_driver_ndis_flush_pmkid(drv);
wpa_driver_ndis_disconnect(drv);
if (wpa_driver_ndis_radio_off(drv) < 0) {
wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn "
"radio off");
}
wpa_driver_ndis_adapter_close(drv);
if (drv->wzc_disabled)
wpa_driver_ndis_set_wzc(drv, 1);
#ifdef _WIN32_WCE
os_free(drv->adapter_name);
#endif /* _WIN32_WCE */
os_free(drv->adapter_desc);
os_free(drv);
}
static struct wpa_interface_info *
wpa_driver_ndis_get_interfaces(void *global_priv)
{
struct wpa_interface_info *iface = NULL, *niface;
#ifdef CONFIG_USE_NDISUIO
NDISUIO_QUERY_BINDING *b;
size_t blen = sizeof(*b) + 1024;
int i, error;
DWORD written;
char name[256], desc[256];
WCHAR *pos;
size_t j, len;
HANDLE ndisuio;
ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
INVALID_HANDLE_VALUE);
if (ndisuio == INVALID_HANDLE_VALUE) {
wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
"NDISUIO: %d", (int) GetLastError());
return NULL;
}
#ifndef _WIN32_WCE
if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
NULL, 0, &written, NULL)) {
wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
"%d", (int) GetLastError());
CloseHandle(ndisuio);
return NULL;
}
#endif /* _WIN32_WCE */
b = os_malloc(blen);
if (b == NULL) {
CloseHandle(ndisuio);
return NULL;
}
for (i = 0; ; i++) {
os_memset(b, 0, blen);
b->BindingIndex = i;
if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
&written, NULL)) {
error = (int) GetLastError();
if (error == ERROR_NO_MORE_ITEMS)
break;
wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
"failed: %d", error);
break;
}
pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
len = b->DeviceNameLength;
if (len >= sizeof(name))
len = sizeof(name) - 1;
for (j = 0; j < len; j++)
name[j] = (char) pos[j];
name[len] = '\0';
pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
len = b->DeviceDescrLength;
if (len >= sizeof(desc))
len = sizeof(desc) - 1;
for (j = 0; j < len; j++)
desc[j] = (char) pos[j];
desc[len] = '\0';
wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
niface = os_zalloc(sizeof(*niface));
if (niface == NULL)
break;
niface->drv_name = "ndis";
if (os_strncmp(name, "\\DEVICE\\", 8) == 0)
niface->ifname = os_strdup(name + 8);
else
niface->ifname = os_strdup(name);
if (niface->ifname == NULL) {
os_free(niface);
break;
}
niface->desc = os_strdup(desc);
niface->next = iface;
iface = niface;
}
os_free(b);
CloseHandle(ndisuio);
#else /* CONFIG_USE_NDISUIO */
PTSTR _names;
char *names, *pos, *pos2;
ULONG len;
BOOLEAN res;
char *name[MAX_ADAPTERS];
char *desc[MAX_ADAPTERS];
int num_name, num_desc, i;
wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
PacketGetVersion());
len = 8192;
_names = os_zalloc(len);
if (_names == NULL)
return NULL;
res = PacketGetAdapterNames(_names, &len);
if (!res && len > 8192) {
os_free(_names);
_names = os_zalloc(len);
if (_names == NULL)
return NULL;
res = PacketGetAdapterNames(_names, &len);
}
if (!res) {
wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
"(PacketGetAdapterNames)");
os_free(_names);
return NULL;
}
names = (char *) _names;
if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
"UNICODE");
/* Convert to ASCII */
pos2 = pos = names;
while (pos2 < names + len) {
if (pos2[0] == '\0' && pos2[1] == '\0' &&
pos2[2] == '\0' && pos2[3] == '\0') {
pos2 += 4;
break;
}
*pos++ = pos2[0];
pos2 += 2;
}
os_memcpy(pos + 2, names, pos - names);
pos += 2;
} else
pos = names;
num_name = 0;
while (pos < names + len) {
name[num_name] = pos;
while (*pos && pos < names + len)
pos++;
if (pos + 1 >= names + len) {
os_free(names);
return NULL;
}
pos++;
num_name++;
if (num_name >= MAX_ADAPTERS) {
wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
os_free(names);
return NULL;
}
if (*pos == '\0') {
wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
num_name);
pos++;
break;
}
}
num_desc = 0;
while (pos < names + len) {
desc[num_desc] = pos;
while (*pos && pos < names + len)
pos++;
if (pos + 1 >= names + len) {
os_free(names);
return NULL;
}
pos++;
num_desc++;
if (num_desc >= MAX_ADAPTERS) {
wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
"descriptions");
os_free(names);
return NULL;
}
if (*pos == '\0') {
wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
"found", num_name);
pos++;
break;
}
}
/*
* Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
- * descriptions. Fill in dummy descriptors to work around this.
+ * descriptions. Fill in stub descriptors to work around this.
*/
while (num_desc < num_name)
- desc[num_desc++] = "dummy description";
+ desc[num_desc++] = "stub description";
if (num_name != num_desc) {
wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
"description counts (%d != %d)",
num_name, num_desc);
os_free(names);
return NULL;
}
for (i = 0; i < num_name; i++) {
niface = os_zalloc(sizeof(*niface));
if (niface == NULL)
break;
niface->drv_name = "ndis";
if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0)
niface->ifname = os_strdup(name[i] + 12);
else
niface->ifname = os_strdup(name[i]);
if (niface->ifname == NULL) {
os_free(niface);
break;
}
niface->desc = os_strdup(desc[i]);
niface->next = iface;
iface = niface;
}
#endif /* CONFIG_USE_NDISUIO */
return iface;
}
static const char *ndis_drv_name = "ndis";
static const char *ndis_drv_desc = "Windows NDIS driver";
struct wpa_driver_ops wpa_driver_ndis_ops;
void driver_ndis_init_ops(void)
{
os_memset(&wpa_driver_ndis_ops, 0, sizeof(wpa_driver_ndis_ops));
wpa_driver_ndis_ops.name = ndis_drv_name;
wpa_driver_ndis_ops.desc = ndis_drv_desc;
wpa_driver_ndis_ops.get_bssid = wpa_driver_ndis_get_bssid;
wpa_driver_ndis_ops.get_ssid = wpa_driver_ndis_get_ssid;
wpa_driver_ndis_ops.set_key = wpa_driver_ndis_set_key_wrapper;
wpa_driver_ndis_ops.init = wpa_driver_ndis_init;
wpa_driver_ndis_ops.deinit = wpa_driver_ndis_deinit;
wpa_driver_ndis_ops.deauthenticate = wpa_driver_ndis_deauthenticate;
wpa_driver_ndis_ops.associate = wpa_driver_ndis_associate;
wpa_driver_ndis_ops.add_pmkid = wpa_driver_ndis_add_pmkid;
wpa_driver_ndis_ops.remove_pmkid = wpa_driver_ndis_remove_pmkid;
wpa_driver_ndis_ops.flush_pmkid = wpa_driver_ndis_flush_pmkid;
wpa_driver_ndis_ops.get_capa = wpa_driver_ndis_get_capa;
wpa_driver_ndis_ops.poll = wpa_driver_ndis_poll;
wpa_driver_ndis_ops.get_ifname = wpa_driver_ndis_get_ifname;
wpa_driver_ndis_ops.get_mac_addr = wpa_driver_ndis_get_mac_addr;
wpa_driver_ndis_ops.get_scan_results2 =
wpa_driver_ndis_get_scan_results;
wpa_driver_ndis_ops.get_interfaces = wpa_driver_ndis_get_interfaces;
wpa_driver_ndis_ops.scan2 = wpa_driver_ndis_scan;
}
diff --git a/contrib/wpa/src/drivers/driver_nl80211.c b/contrib/wpa/src/drivers/driver_nl80211.c
index 8eb033c78cf9..9a9a146f7ea1 100644
--- a/contrib/wpa/src/drivers/driver_nl80211.c
+++ b/contrib/wpa/src/drivers/driver_nl80211.c
@@ -1,12229 +1,12252 @@
/*
* Driver interaction with Linux nl80211/cfg80211
* Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <sys/types.h>
#include <fcntl.h>
#include <net/if.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#ifdef CONFIG_LIBNL3_ROUTE
#include <netlink/route/neighbour.h>
#endif /* CONFIG_LIBNL3_ROUTE */
#include <linux/rtnetlink.h>
#include <netpacket/packet.h>
#include <linux/errqueue.h>
#include "common.h"
#include "eloop.h"
#include "common/qca-vendor.h"
#include "common/qca-vendor-attr.h"
#include "common/brcm_vendor.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_common.h"
#include "netlink.h"
#include "linux_defines.h"
#include "linux_ioctl.h"
#include "radiotap.h"
#include "radiotap_iter.h"
#include "rfkill.h"
#include "driver_nl80211.h"
#ifndef NETLINK_CAP_ACK
#define NETLINK_CAP_ACK 10
#endif /* NETLINK_CAP_ACK */
/* support for extack if compilation headers are too old */
#ifndef NETLINK_EXT_ACK
#define NETLINK_EXT_ACK 11
enum nlmsgerr_attrs {
NLMSGERR_ATTR_UNUSED,
NLMSGERR_ATTR_MSG,
NLMSGERR_ATTR_OFFS,
NLMSGERR_ATTR_COOKIE,
__NLMSGERR_ATTR_MAX,
NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
};
#endif
#ifndef NLM_F_CAPPED
#define NLM_F_CAPPED 0x100
#endif
#ifndef NLM_F_ACK_TLVS
#define NLM_F_ACK_TLVS 0x200
#endif
#ifndef SOL_NETLINK
#define SOL_NETLINK 270
#endif
#ifdef ANDROID
/* system/core/libnl_2 does not include nl_socket_set_nonblocking() */
#undef nl_socket_set_nonblocking
#define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h)
#endif /* ANDROID */
static struct nl_sock * nl_create_handle(struct nl_cb *cb, const char *dbg)
{
struct nl_sock *handle;
handle = nl_socket_alloc_cb(cb);
if (handle == NULL) {
wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
"callbacks (%s)", dbg);
return NULL;
}
if (genl_connect(handle)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
"netlink (%s)", dbg);
nl_socket_free(handle);
return NULL;
}
return handle;
}
static void nl_destroy_handles(struct nl_sock **handle)
{
if (*handle == NULL)
return;
nl_socket_free(*handle);
*handle = NULL;
}
#if __WORDSIZE == 64
#define ELOOP_SOCKET_INVALID (intptr_t) 0x8888888888888889ULL
#else
#define ELOOP_SOCKET_INVALID (intptr_t) 0x88888889ULL
#endif
static void nl80211_register_eloop_read(struct nl_sock **handle,
eloop_sock_handler handler,
void *eloop_data, int persist)
{
/*
* libnl uses a pretty small buffer (32 kB that gets converted to 64 kB)
* by default. It is possible to hit that limit in some cases where
* operations are blocked, e.g., with a burst of Deauthentication frames
* to hostapd and STA entry deletion. Try to increase the buffer to make
* this less likely to occur.
*/
int err;
err = nl_socket_set_buffer_size(*handle, 262144, 0);
if (err < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Could not set nl_socket RX buffer size: %s",
nl_geterror(err));
/* continue anyway with the default (smaller) buffer */
}
nl_socket_set_nonblocking(*handle);
eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
eloop_data, *handle);
if (!persist)
*handle = (void *) (((intptr_t) *handle) ^
ELOOP_SOCKET_INVALID);
}
static void nl80211_destroy_eloop_handle(struct nl_sock **handle, int persist)
{
if (!persist)
*handle = (void *) (((intptr_t) *handle) ^
ELOOP_SOCKET_INVALID);
eloop_unregister_read_sock(nl_socket_get_fd(*handle));
nl_destroy_handles(handle);
}
static void nl80211_global_deinit(void *priv);
static void nl80211_check_global(struct nl80211_global *global);
static void wpa_driver_nl80211_deinit(struct i802_bss *bss);
static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
struct hostapd_freq_params *freq);
static int
wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
const u8 *set_addr, int first,
const char *driver_params);
static int nl80211_send_frame_cmd(struct i802_bss *bss,
unsigned int freq, unsigned int wait,
const u8 *buf, size_t buf_len,
int save_cookie,
int no_cck, int no_ack, int offchanok,
const u16 *csa_offs, size_t csa_offs_len);
static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
int report);
#define IFIDX_ANY -1
static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
int ifidx_reason);
static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
int ifidx_reason);
static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
int ifidx_reason);
static int nl80211_set_channel(struct i802_bss *bss,
struct hostapd_freq_params *freq, int set_chan);
static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
int ifindex, int disabled);
static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
int reset_mode);
static int i802_set_iface_flags(struct i802_bss *bss, int up);
static int nl80211_set_param(void *priv, const char *param);
#ifdef CONFIG_MESH
static int nl80211_put_mesh_config(struct nl_msg *msg,
struct wpa_driver_mesh_bss_params *params);
#endif /* CONFIG_MESH */
static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
u16 reason);
/* Converts nl80211_chan_width to a common format */
enum chan_width convert2width(int width)
{
switch (width) {
case NL80211_CHAN_WIDTH_20_NOHT:
return CHAN_WIDTH_20_NOHT;
case NL80211_CHAN_WIDTH_20:
return CHAN_WIDTH_20;
case NL80211_CHAN_WIDTH_40:
return CHAN_WIDTH_40;
case NL80211_CHAN_WIDTH_80:
return CHAN_WIDTH_80;
case NL80211_CHAN_WIDTH_80P80:
return CHAN_WIDTH_80P80;
case NL80211_CHAN_WIDTH_160:
return CHAN_WIDTH_160;
}
return CHAN_WIDTH_UNKNOWN;
}
int is_ap_interface(enum nl80211_iftype nlmode)
{
return nlmode == NL80211_IFTYPE_AP ||
nlmode == NL80211_IFTYPE_P2P_GO;
}
int is_sta_interface(enum nl80211_iftype nlmode)
{
return nlmode == NL80211_IFTYPE_STATION ||
nlmode == NL80211_IFTYPE_P2P_CLIENT;
}
static int is_p2p_net_interface(enum nl80211_iftype nlmode)
{
return nlmode == NL80211_IFTYPE_P2P_CLIENT ||
nlmode == NL80211_IFTYPE_P2P_GO;
}
struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
int ifindex)
{
struct i802_bss *bss;
for (bss = drv->first_bss; bss; bss = bss->next) {
if (bss->ifindex == ifindex)
return bss;
}
return NULL;
}
static int is_mesh_interface(enum nl80211_iftype nlmode)
{
return nlmode == NL80211_IFTYPE_MESH_POINT;
}
void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
{
if (drv->associated)
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
drv->associated = 0;
os_memset(drv->bssid, 0, ETH_ALEN);
drv->first_bss->freq = 0;
}
/* nl80211 code */
static int ack_handler(struct nl_msg *msg, void *arg)
{
int *err = arg;
*err = 0;
return NL_STOP;
}
struct nl80211_ack_ext_arg {
int *err;
void *ext_data;
};
static int ack_handler_cookie(struct nl_msg *msg, void *arg)
{
struct nl80211_ack_ext_arg *ext_arg = arg;
struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
u64 *cookie = ext_arg->ext_data;
struct nlattr *attrs;
size_t ack_len, attr_len;
*ext_arg->err = 0;
ack_len = sizeof(struct nlmsghdr) + sizeof(int) +
sizeof(struct nlmsghdr);
attrs = (struct nlattr *)
((u8 *) nlmsg_data(nlmsg_hdr(msg)) + sizeof(struct nlmsghdr) +
sizeof(int));
if (nlmsg_hdr(msg)->nlmsg_len <= ack_len)
return NL_STOP;
attr_len = nlmsg_hdr(msg)->nlmsg_len - ack_len;
if(!(nlmsg_hdr(msg)->nlmsg_flags & NLM_F_ACK_TLVS))
return NL_STOP;
nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, attr_len, NULL);
if (tb[NLMSGERR_ATTR_COOKIE])
*cookie = nla_get_u64(tb[NLMSGERR_ATTR_COOKIE]);
return NL_STOP;
}
static int finish_handler(struct nl_msg *msg, void *arg)
{
int *ret = arg;
*ret = 0;
return NL_SKIP;
}
static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
void *arg)
{
struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1;
int len = nlh->nlmsg_len;
struct nlattr *attrs;
struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
int *ret = arg;
int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
*ret = err->error;
if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
return NL_SKIP;
if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
ack_len += err->msg.nlmsg_len - sizeof(*nlh);
if (len <= ack_len)
return NL_STOP;
attrs = (void *) ((unsigned char *) nlh + ack_len);
len -= ack_len;
nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
if (tb[NLMSGERR_ATTR_MSG]) {
len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]),
nla_len(tb[NLMSGERR_ATTR_MSG]));
wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s",
len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG]));
}
return NL_SKIP;
}
static int no_seq_check(struct nl_msg *msg, void *arg)
{
return NL_OK;
}
static void nl80211_nlmsg_clear(struct nl_msg *msg)
{
/*
* Clear nlmsg data, e.g., to make sure key material is not left in
* heap memory for unnecessarily long time.
*/
if (msg) {
struct nlmsghdr *hdr = nlmsg_hdr(msg);
void *data = nlmsg_data(hdr);
/*
* This would use nlmsg_datalen() or the older nlmsg_len() if
* only libnl were to maintain a stable API.. Neither will work
* with all released versions, so just calculate the length
* here.
*/
int len = hdr->nlmsg_len - NLMSG_HDRLEN;
os_memset(data, 0, len);
}
}
static int send_and_recv(struct nl80211_global *global,
struct nl_sock *nl_handle, struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
void *valid_data,
int (*ack_handler_custom)(struct nl_msg *, void *),
void *ack_data)
{
struct nl_cb *cb;
int err = -ENOMEM, opt;
if (!msg)
return -ENOMEM;
cb = nl_cb_clone(global->nl_cb);
if (!cb)
goto out;
/* try to set NETLINK_EXT_ACK to 1, ignoring errors */
opt = 1;
setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
NETLINK_EXT_ACK, &opt, sizeof(opt));
/* try to set NETLINK_CAP_ACK to 1, ignoring errors */
opt = 1;
setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
NETLINK_CAP_ACK, &opt, sizeof(opt));
err = nl_send_auto_complete(nl_handle, msg);
if (err < 0) {
wpa_printf(MSG_INFO,
"nl80211: nl_send_auto_complete() failed: %s",
nl_geterror(err));
/* Need to convert libnl error code to an errno value. For now,
* just hardcode this to EBADF; the real error reason is shown
* in that error print above. */
err = -EBADF;
goto out;
}
err = 1;
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
if (ack_handler_custom) {
struct nl80211_ack_ext_arg *ext_arg = ack_data;
ext_arg->err = &err;
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM,
ack_handler_custom, ack_data);
} else {
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
}
if (valid_handler)
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
valid_handler, valid_data);
while (err > 0) {
int res = nl_recvmsgs(nl_handle, cb);
if (res == -NLE_DUMP_INTR) {
/* Most likely one of the nl80211 dump routines hit a
* case where internal results changed while the dump
* was being sent. The most common known case for this
* is scan results fetching while associated were every
* received Beacon frame from the AP may end up
* incrementing bss_generation. This
* NL80211_CMD_GET_SCAN case tries again in the caller;
* other cases (of which there are no known common ones)
* will stop and return an error. */
wpa_printf(MSG_DEBUG, "nl80211: %s; convert to -EAGAIN",
nl_geterror(res));
err = -EAGAIN;
} else if (res < 0) {
wpa_printf(MSG_INFO,
"nl80211: %s->nl_recvmsgs failed: %d (%s)",
__func__, res, nl_geterror(res));
}
}
out:
nl_cb_put(cb);
/* Always clear the message as it can potentially contain keys */
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return err;
}
int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
void *valid_data,
int (*ack_handler_custom)(struct nl_msg *, void *),
void *ack_data)
{
return send_and_recv(drv->global, drv->global->nl, msg,
valid_handler, valid_data,
ack_handler_custom, ack_data);
}
/* Use this method to mark that it is necessary to own the connection/interface
* for this operation.
* handle may be set to NULL, to get the same behavior as send_and_recv_msgs().
* set_owner can be used to mark this socket for receiving control port frames.
*/
static int send_and_recv_msgs_owner(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg,
struct nl_sock *handle, int set_owner,
int (*valid_handler)(struct nl_msg *,
void *),
void *valid_data,
int (*ack_handler_custom)(struct nl_msg *,
void *),
void *ack_data)
{
if (!msg)
return -ENOMEM;
/* Control port over nl80211 needs the flags and attributes below.
*
* The Linux kernel has initial checks for them (in nl80211.c) like:
* validate_pae_over_nl80211(...)
* or final checks like:
* dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid
*
* Final operations (e.g., disassociate) don't need to set these
* attributes, but they have to be performed on the socket, which has
* the connection owner property set in the kernel.
*/
if ((drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) &&
handle && set_owner &&
(nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_OVER_NL80211) ||
nla_put_flag(msg, NL80211_ATTR_SOCKET_OWNER) ||
nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_PREAUTH)))
return -1;
return send_and_recv(drv->global, handle ? handle : drv->global->nl,
msg, valid_handler, valid_data,
ack_handler_custom, ack_data);
}
static int
send_and_recv_msgs_connect_handle(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg, struct i802_bss *bss,
int set_owner)
{
struct nl_sock *nl_connect = get_connect_handle(bss);
if (nl_connect)
return send_and_recv_msgs_owner(drv, msg, nl_connect, set_owner,
process_bss_event, bss, NULL,
NULL);
else
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
struct nl_sock * get_connect_handle(struct i802_bss *bss)
{
if ((bss->drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) ||
bss->use_nl_connect)
return bss->nl_connect;
return NULL;
}
struct family_data {
const char *group;
int id;
};
static int family_handler(struct nl_msg *msg, void *arg)
{
struct family_data *res = arg;
struct nlattr *tb[CTRL_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *mcgrp;
int i;
nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[CTRL_ATTR_MCAST_GROUPS])
return NL_SKIP;
nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) {
struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp),
nla_len(mcgrp), NULL);
if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] ||
!tb2[CTRL_ATTR_MCAST_GRP_ID] ||
os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]),
res->group,
nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
continue;
res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
break;
};
return NL_SKIP;
}
static int nl_get_multicast_id(struct nl80211_global *global,
const char *family, const char *group)
{
struct nl_msg *msg;
int ret;
struct family_data res = { group, -ENOENT };
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
- if (!genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"),
+ if (!genlmsg_put(msg, 0, 0, global->nlctrl_id,
0, 0, CTRL_CMD_GETFAMILY, 0) ||
nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family)) {
nlmsg_free(msg);
return -1;
}
ret = send_and_recv(global, global->nl, msg, family_handler, &res,
NULL, NULL);
if (ret == 0)
ret = res.id;
return ret;
}
void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg, int flags, uint8_t cmd)
{
if (TEST_FAIL())
return NULL;
return genlmsg_put(msg, 0, 0, drv->global->nl80211_id,
0, flags, cmd, 0);
}
static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss)
{
if (bss->wdev_id_set)
return nla_put_u64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
return nla_put_u32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
}
struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd)
{
struct nl_msg *msg;
msg = nlmsg_alloc();
if (!msg)
return NULL;
if (!nl80211_cmd(bss->drv, msg, flags, cmd) ||
nl80211_set_iface_id(msg, bss) < 0) {
nlmsg_free(msg);
return NULL;
}
return msg;
}
static struct nl_msg *
nl80211_ifindex_msg_build(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg, int ifindex, int flags,
uint8_t cmd)
{
if (!msg)
return NULL;
if (!nl80211_cmd(drv, msg, flags, cmd) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex)) {
nlmsg_free(msg);
return NULL;
}
return msg;
}
static struct nl_msg *
nl80211_ifindex_msg(struct wpa_driver_nl80211_data *drv, int ifindex,
int flags, uint8_t cmd)
{
return nl80211_ifindex_msg_build(drv, nlmsg_alloc(), ifindex, flags,
cmd);
}
struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
uint8_t cmd)
{
return nl80211_ifindex_msg(drv, drv->ifindex, flags, cmd);
}
struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd)
{
return nl80211_ifindex_msg(bss->drv, bss->ifindex, flags, cmd);
}
struct wiphy_idx_data {
int wiphy_idx;
enum nl80211_iftype nlmode;
u8 *macaddr;
u8 use_4addr;
};
static int netdev_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct wiphy_idx_data *info = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb[NL80211_ATTR_WIPHY])
info->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
if (tb[NL80211_ATTR_IFTYPE])
info->nlmode = nla_get_u32(tb[NL80211_ATTR_IFTYPE]);
if (tb[NL80211_ATTR_MAC] && info->macaddr)
os_memcpy(info->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
ETH_ALEN);
if (tb[NL80211_ATTR_4ADDR])
info->use_4addr = nla_get_u8(tb[NL80211_ATTR_4ADDR]);
return NL_SKIP;
}
int nl80211_get_wiphy_index(struct i802_bss *bss)
{
struct nl_msg *msg;
struct wiphy_idx_data data = {
.wiphy_idx = -1,
.macaddr = NULL,
};
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
return -1;
if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
NULL, NULL) == 0)
return data.wiphy_idx;
return -1;
}
static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss)
{
struct nl_msg *msg;
struct wiphy_idx_data data = {
.nlmode = NL80211_IFTYPE_UNSPECIFIED,
.macaddr = NULL,
};
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
return NL80211_IFTYPE_UNSPECIFIED;
if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
NULL, NULL) == 0)
return data.nlmode;
return NL80211_IFTYPE_UNSPECIFIED;
}
static int nl80211_get_macaddr(struct i802_bss *bss)
{
struct nl_msg *msg;
struct wiphy_idx_data data = {
.macaddr = bss->addr,
};
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
return -1;
return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
NULL, NULL);
}
static int nl80211_get_4addr(struct i802_bss *bss)
{
struct nl_msg *msg;
struct wiphy_idx_data data = {
.use_4addr = 0,
};
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)) ||
send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data,
NULL, NULL))
return -1;
return data.use_4addr;
}
static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
struct nl80211_wiphy_data *w)
{
struct nl_msg *msg;
int ret;
msg = nlmsg_alloc();
if (!msg)
return -1;
if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx)) {
nlmsg_free(msg);
return -1;
}
ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL,
NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
"failed: ret=%d (%s)",
ret, strerror(-ret));
}
return ret;
}
static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle)
{
struct nl80211_wiphy_data *w = eloop_ctx;
int res;
wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available");
res = nl_recvmsgs(handle, w->nl_cb);
if (res < 0) {
wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
__func__, res);
}
}
static int process_beacon_event(struct nl_msg *msg, void *arg)
{
struct nl80211_wiphy_data *w = arg;
struct wpa_driver_nl80211_data *drv;
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb[NL80211_ATTR_MAX + 1];
union wpa_event_data event;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (gnlh->cmd != NL80211_CMD_FRAME) {
wpa_printf(MSG_DEBUG, "nl80211: Unexpected beacon event? (%d)",
gnlh->cmd);
return NL_SKIP;
}
if (!tb[NL80211_ATTR_FRAME])
return NL_SKIP;
dl_list_for_each(drv, &w->drvs, struct wpa_driver_nl80211_data,
wiphy_list) {
os_memset(&event, 0, sizeof(event));
event.rx_mgmt.frame = nla_data(tb[NL80211_ATTR_FRAME]);
event.rx_mgmt.frame_len = nla_len(tb[NL80211_ATTR_FRAME]);
wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
}
return NL_SKIP;
}
static struct nl80211_wiphy_data *
nl80211_get_wiphy_data_ap(struct i802_bss *bss)
{
static DEFINE_DL_LIST(nl80211_wiphys);
struct nl80211_wiphy_data *w;
int wiphy_idx, found = 0;
struct i802_bss *tmp_bss;
u8 channel;
if (bss->wiphy_data != NULL)
return bss->wiphy_data;
wiphy_idx = nl80211_get_wiphy_index(bss);
dl_list_for_each(w, &nl80211_wiphys, struct nl80211_wiphy_data, list) {
if (w->wiphy_idx == wiphy_idx)
goto add;
}
/* alloc new one */
w = os_zalloc(sizeof(*w));
if (w == NULL)
return NULL;
w->wiphy_idx = wiphy_idx;
dl_list_init(&w->bsss);
dl_list_init(&w->drvs);
/* Beacon frames not supported in IEEE 802.11ad */
if (ieee80211_freq_to_chan(bss->freq, &channel) !=
HOSTAPD_MODE_IEEE80211AD) {
w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!w->nl_cb) {
os_free(w);
return NULL;
}
nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
no_seq_check, NULL);
nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
process_beacon_event, w);
w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
"wiphy beacons");
if (w->nl_beacons == NULL) {
os_free(w);
return NULL;
}
if (nl80211_register_beacons(bss->drv, w)) {
nl_destroy_handles(&w->nl_beacons);
os_free(w);
return NULL;
}
nl80211_register_eloop_read(&w->nl_beacons,
nl80211_recv_beacons, w, 0);
}
dl_list_add(&nl80211_wiphys, &w->list);
add:
/* drv entry for this bss already there? */
dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
if (tmp_bss->drv == bss->drv) {
found = 1;
break;
}
}
/* if not add it */
if (!found)
dl_list_add(&w->drvs, &bss->drv->wiphy_list);
dl_list_add(&w->bsss, &bss->wiphy_list);
bss->wiphy_data = w;
return w;
}
static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
{
struct nl80211_wiphy_data *w = bss->wiphy_data;
struct i802_bss *tmp_bss;
int found = 0;
if (w == NULL)
return;
bss->wiphy_data = NULL;
dl_list_del(&bss->wiphy_list);
/* still any for this drv present? */
dl_list_for_each(tmp_bss, &w->bsss, struct i802_bss, wiphy_list) {
if (tmp_bss->drv == bss->drv) {
found = 1;
break;
}
}
/* if not remove it */
if (!found)
dl_list_del(&bss->drv->wiphy_list);
if (!dl_list_empty(&w->bsss))
return;
if (w->nl_beacons)
nl80211_destroy_eloop_handle(&w->nl_beacons, 0);
nl_cb_put(w->nl_cb);
dl_list_del(&w->list);
os_free(w);
}
static unsigned int nl80211_get_ifindex(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
return drv->ifindex;
}
static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!drv->associated)
return -1;
os_memcpy(bssid, drv->bssid, ETH_ALEN);
return 0;
}
static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!drv->associated)
return -1;
os_memcpy(ssid, drv->ssid, drv->ssid_len);
return drv->ssid_len;
}
static void wpa_driver_nl80211_event_newlink(
struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
int ifindex, const char *ifname)
{
union wpa_event_data event;
if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) {
if (if_nametoindex(drv->first_bss->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK",
drv->first_bss->ifname);
return;
}
if (!drv->if_removed)
return;
wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event",
drv->first_bss->ifname);
drv->if_removed = 0;
}
os_memset(&event, 0, sizeof(event));
event.interface_status.ifindex = ifindex;
os_strlcpy(event.interface_status.ifname, ifname,
sizeof(event.interface_status.ifname));
event.interface_status.ievent = EVENT_INTERFACE_ADDED;
if (drv)
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
else
wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
&event);
}
static void wpa_driver_nl80211_event_dellink(
struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
int ifindex, const char *ifname)
{
union wpa_event_data event;
if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) {
if (drv->if_removed) {
wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s",
ifname);
return;
}
wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1",
ifname);
drv->if_removed = 1;
} else {
wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed",
ifname);
}
os_memset(&event, 0, sizeof(event));
event.interface_status.ifindex = ifindex;
os_strlcpy(event.interface_status.ifname, ifname,
sizeof(event.interface_status.ifname));
event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
if (drv)
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
else
wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
&event);
}
static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
u8 *buf, size_t len)
{
int attrlen, rta_len;
struct rtattr *attr;
attrlen = len;
attr = (struct rtattr *) buf;
rta_len = RTA_ALIGN(sizeof(struct rtattr));
while (RTA_OK(attr, attrlen)) {
if (attr->rta_type == IFLA_IFNAME) {
if (os_strcmp(((char *) attr) + rta_len,
drv->first_bss->ifname) == 0)
return 1;
else
break;
}
attr = RTA_NEXT(attr, attrlen);
}
return 0;
}
static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
int ifindex, u8 *buf, size_t len)
{
if (drv->ifindex == ifindex)
return 1;
if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
nl80211_check_global(drv->global);
wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
"interface");
if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0)
return -1;
return 1;
}
return 0;
}
static struct wpa_driver_nl80211_data *
nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len,
int *init_failed)
{
struct wpa_driver_nl80211_data *drv;
int res;
if (init_failed)
*init_failed = 0;
dl_list_for_each(drv, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len);
if (res < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Found matching own interface, but failed to complete reinitialization");
if (init_failed)
*init_failed = 1;
return drv;
}
if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY))
return drv;
}
return NULL;
}
static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv,
int ifindex, int notify)
{
struct i802_bss *bss;
u8 addr[ETH_ALEN];
bss = get_bss_ifindex(drv, ifindex);
if (bss &&
linux_get_ifhwaddr(drv->global->ioctl_sock,
bss->ifname, addr) < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: %s: failed to re-read MAC address",
bss->ifname);
} else if (bss && os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Own MAC address on ifindex %d (%s) changed from "
MACSTR " to " MACSTR,
ifindex, bss->ifname,
MAC2STR(bss->addr), MAC2STR(addr));
os_memcpy(bss->addr, addr, ETH_ALEN);
if (notify)
wpa_supplicant_event(drv->ctx,
EVENT_INTERFACE_MAC_CHANGED, NULL);
}
}
static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
struct ifinfomsg *ifi,
u8 *buf, size_t len)
{
struct nl80211_global *global = ctx;
struct wpa_driver_nl80211_data *drv;
int attrlen;
struct rtattr *attr;
u32 brid = 0;
char namebuf[IFNAMSIZ];
char ifname[IFNAMSIZ + 1];
char extra[100], *pos, *end;
int init_failed;
extra[0] = '\0';
pos = extra;
end = pos + sizeof(extra);
ifname[0] = '\0';
attrlen = len;
attr = (struct rtattr *) buf;
while (RTA_OK(attr, attrlen)) {
switch (attr->rta_type) {
case IFLA_IFNAME:
if (RTA_PAYLOAD(attr) > IFNAMSIZ)
break;
os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr));
ifname[RTA_PAYLOAD(attr)] = '\0';
break;
case IFLA_MASTER:
brid = nla_get_u32((struct nlattr *) attr);
pos += os_snprintf(pos, end - pos, " master=%u", brid);
break;
case IFLA_WIRELESS:
pos += os_snprintf(pos, end - pos, " wext");
break;
case IFLA_OPERSTATE:
pos += os_snprintf(pos, end - pos, " operstate=%u",
nla_get_u32((struct nlattr *) attr));
break;
case IFLA_LINKMODE:
pos += os_snprintf(pos, end - pos, " linkmode=%u",
nla_get_u32((struct nlattr *) attr));
break;
}
attr = RTA_NEXT(attr, attrlen);
}
extra[sizeof(extra) - 1] = '\0';
wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
ifi->ifi_index, ifname, extra, ifi->ifi_family,
ifi->ifi_flags,
(ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
(ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed);
if (!drv)
goto event_newlink;
if (init_failed)
return; /* do not update interface state */
if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
namebuf[0] = '\0';
if (if_indextoname(ifi->ifi_index, namebuf) &&
linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
"event since interface %s is up", namebuf);
drv->ignore_if_down_event = 0;
/* Re-read MAC address as it may have changed */
nl80211_refresh_mac(drv, ifi->ifi_index, 1);
return;
}
wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
namebuf, ifname);
if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Not the main interface (%s) - do not indicate interface down",
drv->first_bss->ifname);
} else if (drv->ignore_if_down_event) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
"event generated by mode change");
drv->ignore_if_down_event = 0;
} else {
drv->if_disabled = 1;
wpa_supplicant_event(drv->ctx,
EVENT_INTERFACE_DISABLED, NULL);
/*
* Try to get drv again, since it may be removed as
* part of the EVENT_INTERFACE_DISABLED handling for
* dynamic interfaces
*/
drv = nl80211_find_drv(global, ifi->ifi_index,
buf, len, NULL);
if (!drv)
return;
}
}
if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
namebuf[0] = '\0';
if (if_indextoname(ifi->ifi_index, namebuf) &&
linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
"event since interface %s is down",
namebuf);
return;
}
wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)",
namebuf, ifname);
if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Not the main interface (%s) - do not indicate interface up",
drv->first_bss->ifname);
} else if (if_nametoindex(drv->first_bss->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
"event since interface %s does not exist",
drv->first_bss->ifname);
} else if (drv->if_removed) {
wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
"event since interface %s is marked "
"removed", drv->first_bss->ifname);
} else {
/* Re-read MAC address as it may have changed */
nl80211_refresh_mac(drv, ifi->ifi_index, 0);
drv->if_disabled = 0;
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
NULL);
}
}
/*
* Some drivers send the association event before the operup event--in
* this case, lifting operstate in wpa_driver_nl80211_set_operstate()
* fails. This will hit us when wpa_supplicant does not need to do
* IEEE 802.1X authentication
*/
if (drv->operstate == 1 &&
(ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
!(ifi->ifi_flags & IFF_RUNNING)) {
wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate");
netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
-1, IF_OPER_UP);
}
event_newlink:
if (ifname[0])
wpa_driver_nl80211_event_newlink(global, drv, ifi->ifi_index,
ifname);
if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
struct i802_bss *bss;
/* device has been added to bridge */
if (!if_indextoname(brid, namebuf)) {
wpa_printf(MSG_DEBUG,
"nl80211: Could not find bridge ifname for ifindex %u",
brid);
return;
}
wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
brid, namebuf);
add_ifidx(drv, brid, ifi->ifi_index);
for (bss = drv->first_bss; bss; bss = bss->next) {
if (os_strcmp(ifname, bss->ifname) == 0) {
os_strlcpy(bss->brname, namebuf, IFNAMSIZ);
break;
}
}
}
}
static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
struct ifinfomsg *ifi,
u8 *buf, size_t len)
{
struct nl80211_global *global = ctx;
struct wpa_driver_nl80211_data *drv;
int attrlen;
struct rtattr *attr;
u32 brid = 0;
char ifname[IFNAMSIZ + 1];
char extra[100], *pos, *end;
extra[0] = '\0';
pos = extra;
end = pos + sizeof(extra);
ifname[0] = '\0';
attrlen = len;
attr = (struct rtattr *) buf;
while (RTA_OK(attr, attrlen)) {
switch (attr->rta_type) {
case IFLA_IFNAME:
if (RTA_PAYLOAD(attr) > IFNAMSIZ)
break;
os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr));
ifname[RTA_PAYLOAD(attr)] = '\0';
break;
case IFLA_MASTER:
brid = nla_get_u32((struct nlattr *) attr);
pos += os_snprintf(pos, end - pos, " master=%u", brid);
break;
case IFLA_OPERSTATE:
pos += os_snprintf(pos, end - pos, " operstate=%u",
nla_get_u32((struct nlattr *) attr));
break;
case IFLA_LINKMODE:
pos += os_snprintf(pos, end - pos, " linkmode=%u",
nla_get_u32((struct nlattr *) attr));
break;
}
attr = RTA_NEXT(attr, attrlen);
}
extra[sizeof(extra) - 1] = '\0';
wpa_printf(MSG_DEBUG, "RTM_DELLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
ifi->ifi_index, ifname, extra, ifi->ifi_family,
ifi->ifi_flags,
(ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
(ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL);
if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
/* device has been removed from bridge */
char namebuf[IFNAMSIZ];
if (!if_indextoname(brid, namebuf)) {
wpa_printf(MSG_DEBUG,
"nl80211: Could not find bridge ifname for ifindex %u",
brid);
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Remove ifindex %u for bridge %s",
brid, namebuf);
}
del_ifidx(drv, brid, ifi->ifi_index);
}
if (ifi->ifi_family != AF_BRIDGE || !brid)
wpa_driver_nl80211_event_dellink(global, drv, ifi->ifi_index,
ifname);
}
struct nl80211_get_assoc_freq_arg {
struct wpa_driver_nl80211_data *drv;
unsigned int assoc_freq;
unsigned int ibss_freq;
u8 assoc_bssid[ETH_ALEN];
u8 assoc_ssid[SSID_MAX_LEN];
u8 assoc_ssid_len;
};
static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *bss[NL80211_BSS_MAX + 1];
static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
[NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
[NL80211_BSS_STATUS] = { .type = NLA_U32 },
};
struct nl80211_get_assoc_freq_arg *ctx = arg;
enum nl80211_bss_status status;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_BSS] ||
nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
bss_policy) ||
!bss[NL80211_BSS_STATUS])
return NL_SKIP;
status = nla_get_u32(bss[NL80211_BSS_STATUS]);
if (status == NL80211_BSS_STATUS_ASSOCIATED &&
bss[NL80211_BSS_FREQUENCY]) {
ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
ctx->assoc_freq);
}
if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
bss[NL80211_BSS_FREQUENCY]) {
ctx->ibss_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
ctx->ibss_freq);
}
if (status == NL80211_BSS_STATUS_ASSOCIATED &&
bss[NL80211_BSS_BSSID]) {
os_memcpy(ctx->assoc_bssid,
nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
wpa_printf(MSG_DEBUG, "nl80211: Associated with "
MACSTR, MAC2STR(ctx->assoc_bssid));
}
if (status == NL80211_BSS_STATUS_ASSOCIATED &&
bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
const u8 *ie, *ssid;
size_t ie_len;
ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
ssid = get_ie(ie, ie_len, WLAN_EID_SSID);
if (ssid && ssid[1] > 0 && ssid[1] <= SSID_MAX_LEN) {
ctx->assoc_ssid_len = ssid[1];
os_memcpy(ctx->assoc_ssid, ssid + 2, ssid[1]);
}
}
return NL_SKIP;
}
int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid)
{
struct nl_msg *msg;
int ret;
struct nl80211_get_assoc_freq_arg arg;
int count = 0;
try_again:
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
os_memset(&arg, 0, sizeof(arg));
arg.drv = drv;
ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
&arg, NULL, NULL);
if (ret == -EAGAIN) {
count++;
if (count >= 10) {
wpa_printf(MSG_INFO,
"nl80211: Failed to receive consistent scan result dump for get_assoc_ssid");
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to receive consistent scan result dump for get_assoc_ssid - try again");
goto try_again;
}
}
if (ret == 0) {
os_memcpy(ssid, arg.assoc_ssid, arg.assoc_ssid_len);
return arg.assoc_ssid_len;
}
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)",
ret, strerror(-ret));
return ret;
}
unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
{
struct nl_msg *msg;
int ret;
struct nl80211_get_assoc_freq_arg arg;
int count = 0;
try_again:
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
os_memset(&arg, 0, sizeof(arg));
arg.drv = drv;
ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
&arg, NULL, NULL);
if (ret == -EAGAIN) {
count++;
if (count >= 10) {
wpa_printf(MSG_INFO,
"nl80211: Failed to receive consistent scan result dump for get_assoc_freq");
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to receive consistent scan result dump for get_assoc_freq - try again");
goto try_again;
}
}
if (ret == 0) {
unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
arg.ibss_freq : arg.assoc_freq;
wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
"associated BSS from scan results: %u MHz", freq);
if (freq)
drv->assoc_freq = freq;
return drv->assoc_freq;
}
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
"(%s)", ret, strerror(-ret));
return drv->assoc_freq;
}
static int get_link_signal(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
[NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
[NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 },
};
struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
[NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
};
struct wpa_signal_info *sig_change = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_STA_INFO] ||
nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
tb[NL80211_ATTR_STA_INFO], policy))
return NL_SKIP;
if (!sinfo[NL80211_STA_INFO_SIGNAL])
return NL_SKIP;
sig_change->current_signal =
(s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
sig_change->avg_signal =
(s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]);
else
sig_change->avg_signal = 0;
if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
sig_change->avg_beacon_signal =
(s8)
nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
else
sig_change->avg_beacon_signal = 0;
if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
sinfo[NL80211_STA_INFO_TX_BITRATE],
rate_policy)) {
sig_change->current_txrate = 0;
} else {
if (rinfo[NL80211_RATE_INFO_BITRATE]) {
sig_change->current_txrate =
nla_get_u16(rinfo[
NL80211_RATE_INFO_BITRATE]) * 100;
}
}
}
return NL_SKIP;
}
int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
struct wpa_signal_info *sig)
{
struct nl_msg *msg;
sig->current_signal = -WPA_INVALID_NOISE;
sig->current_txrate = 0;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid)) {
nlmsg_free(msg);
return -ENOBUFS;
}
return send_and_recv_msgs(drv, msg, get_link_signal, sig, NULL, NULL);
}
static int get_link_noise(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
};
struct wpa_signal_info *sig_change = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_SURVEY_INFO]) {
wpa_printf(MSG_DEBUG, "nl80211: survey data missing!");
return NL_SKIP;
}
if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
tb[NL80211_ATTR_SURVEY_INFO],
survey_policy)) {
wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested "
"attributes!");
return NL_SKIP;
}
if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
return NL_SKIP;
if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
sig_change->frequency)
return NL_SKIP;
if (!sinfo[NL80211_SURVEY_INFO_NOISE])
return NL_SKIP;
sig_change->current_noise =
(s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
return NL_SKIP;
}
int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
struct wpa_signal_info *sig_change)
{
struct nl_msg *msg;
sig_change->current_noise = WPA_INVALID_NOISE;
sig_change->frequency = drv->assoc_freq;
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
return send_and_recv_msgs(drv, msg, get_link_noise, sig_change,
NULL, NULL);
}
static int get_channel_info(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1] = { 0 };
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct wpa_channel_info *chan_info = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
os_memset(chan_info, 0, sizeof(struct wpa_channel_info));
chan_info->chanwidth = CHAN_WIDTH_UNKNOWN;
if (tb[NL80211_ATTR_WIPHY_FREQ])
chan_info->frequency =
nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
if (tb[NL80211_ATTR_CHANNEL_WIDTH])
chan_info->chanwidth = convert2width(
nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
enum nl80211_channel_type ct =
nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
switch (ct) {
case NL80211_CHAN_HT40MINUS:
chan_info->sec_channel = -1;
break;
case NL80211_CHAN_HT40PLUS:
chan_info->sec_channel = 1;
break;
default:
chan_info->sec_channel = 0;
break;
}
}
if (tb[NL80211_ATTR_CENTER_FREQ1])
chan_info->center_frq1 =
nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
if (tb[NL80211_ATTR_CENTER_FREQ2])
chan_info->center_frq2 =
nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
if (chan_info->center_frq2) {
u8 seg1_idx = 0;
if (ieee80211_freq_to_chan(chan_info->center_frq2, &seg1_idx) !=
NUM_HOSTAPD_MODES)
chan_info->seg1_idx = seg1_idx;
}
return NL_SKIP;
}
static int nl80211_channel_info(void *priv, struct wpa_channel_info *ci)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
return send_and_recv_msgs(drv, msg, get_channel_info, ci, NULL, NULL);
}
static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
void *handle)
{
struct nl_cb *cb = eloop_ctx;
int res;
wpa_printf(MSG_MSGDUMP, "nl80211: Event message available");
res = nl_recvmsgs(handle, cb);
if (res < 0) {
wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
__func__, res);
}
}
/**
* wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
* @priv: driver_nl80211 private data
* @alpha2_arg: country to which to switch to
* Returns: 0 on success, -1 on failure
*
* This asks nl80211 to set the regulatory domain for given
* country ISO / IEC alpha2.
*/
static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
char alpha2[3];
struct nl_msg *msg;
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
alpha2[0] = alpha2_arg[0];
alpha2[1] = alpha2_arg[1];
alpha2[2] = '\0';
if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG) ||
nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, alpha2)) {
nlmsg_free(msg);
return -EINVAL;
}
if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL))
return -EINVAL;
return 0;
}
static int nl80211_get_country(struct nl_msg *msg, void *arg)
{
char *alpha2 = arg;
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) {
wpa_printf(MSG_DEBUG, "nl80211: No country information available");
return NL_SKIP;
}
os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3);
return NL_SKIP;
}
static int wpa_driver_nl80211_get_country(void *priv, char *alpha2)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
alpha2[0] = '\0';
ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2,
NULL, NULL);
if (!alpha2[0])
ret = -1;
return ret;
}
static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
{
int ret;
global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
if (global->nl_cb == NULL) {
wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
"callbacks");
return -1;
}
global->nl = nl_create_handle(global->nl_cb, "nl");
if (global->nl == NULL)
goto err;
global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
if (global->nl80211_id < 0) {
wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
"found");
goto err;
}
+ global->nlctrl_id = genl_ctrl_resolve(global->nl, "nlctrl");
+ if (global->nlctrl_id < 0) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: 'nlctrl' generic netlink not found");
+ goto err;
+ }
+
global->nl_event = nl_create_handle(global->nl_cb, "event");
if (global->nl_event == NULL)
goto err;
ret = nl_get_multicast_id(global, "nl80211", "scan");
if (ret >= 0)
ret = nl_socket_add_membership(global->nl_event, ret);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
"membership for scan events: %d (%s)",
ret, nl_geterror(ret));
goto err;
}
ret = nl_get_multicast_id(global, "nl80211", "mlme");
if (ret >= 0)
ret = nl_socket_add_membership(global->nl_event, ret);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
"membership for mlme events: %d (%s)",
ret, nl_geterror(ret));
goto err;
}
ret = nl_get_multicast_id(global, "nl80211", "regulatory");
if (ret >= 0)
ret = nl_socket_add_membership(global->nl_event, ret);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
"membership for regulatory events: %d (%s)",
ret, nl_geterror(ret));
/* Continue without regulatory events */
}
ret = nl_get_multicast_id(global, "nl80211", "vendor");
if (ret >= 0)
ret = nl_socket_add_membership(global->nl_event, ret);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
"membership for vendor events: %d (%s)",
ret, nl_geterror(ret));
/* Continue without vendor events */
}
nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
no_seq_check, NULL);
nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
process_global_event, global);
nl80211_register_eloop_read(&global->nl_event,
wpa_driver_nl80211_event_receive,
global->nl_cb, 0);
return 0;
err:
nl_destroy_handles(&global->nl_event);
nl_destroy_handles(&global->nl);
nl_cb_put(global->nl_cb);
global->nl_cb = NULL;
return -1;
}
static void nl80211_check_global(struct nl80211_global *global)
{
struct nl_sock *handle;
const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL };
int ret;
unsigned int i;
/*
* Try to re-add memberships to handle case of cfg80211 getting reloaded
* and all registration having been cleared.
*/
handle = (void *) (((intptr_t) global->nl_event) ^
ELOOP_SOCKET_INVALID);
for (i = 0; groups[i]; i++) {
ret = nl_get_multicast_id(global, "nl80211", groups[i]);
if (ret >= 0)
ret = nl_socket_add_membership(handle, ret);
if (ret < 0) {
wpa_printf(MSG_INFO,
"nl80211: Could not re-add multicast membership for %s events: %d (%s)",
groups[i], ret, nl_geterror(ret));
}
}
}
static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
{
struct wpa_driver_nl80211_data *drv = ctx;
wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
/*
* rtnetlink ifdown handler will report interfaces other than the P2P
* Device interface as disabled.
*/
if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
}
static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
{
struct wpa_driver_nl80211_data *drv = ctx;
wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
if (i802_set_iface_flags(drv->first_bss, 1)) {
wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
"after rfkill unblock");
return;
}
if (is_p2p_net_interface(drv->nlmode))
nl80211_disable_11b_rates(drv, drv->ifindex, 1);
/*
* rtnetlink ifup handler will report interfaces other than the P2P
* Device interface as enabled.
*/
if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
}
static void wpa_driver_nl80211_handle_eapol_tx_status(int sock,
void *eloop_ctx,
void *handle)
{
struct wpa_driver_nl80211_data *drv = eloop_ctx;
u8 data[2048];
struct msghdr msg;
struct iovec entry;
u8 control[512];
struct cmsghdr *cmsg;
int res, found_ee = 0, found_wifi = 0, acked = 0;
union wpa_event_data event;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &entry;
msg.msg_iovlen = 1;
entry.iov_base = data;
entry.iov_len = sizeof(data);
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
res = recvmsg(sock, &msg, MSG_ERRQUEUE);
/* if error or not fitting 802.3 header, return */
if (res < 14)
return;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_WIFI_STATUS) {
int *ack;
found_wifi = 1;
ack = (void *)CMSG_DATA(cmsg);
acked = *ack;
}
if (cmsg->cmsg_level == SOL_PACKET &&
cmsg->cmsg_type == PACKET_TX_TIMESTAMP) {
struct sock_extended_err *err =
(struct sock_extended_err *)CMSG_DATA(cmsg);
if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS)
found_ee = 1;
}
}
if (!found_ee || !found_wifi)
return;
memset(&event, 0, sizeof(event));
event.eapol_tx_status.dst = data;
event.eapol_tx_status.data = data + 14;
event.eapol_tx_status.data_len = res - 14;
event.eapol_tx_status.ack = acked;
wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
}
static int nl80211_init_connect_handle(struct i802_bss *bss)
{
if (bss->nl_connect) {
wpa_printf(MSG_DEBUG,
"nl80211: Connect handle already created (nl_connect=%p)",
bss->nl_connect);
return -1;
}
bss->nl_connect = nl_create_handle(bss->nl_cb, "connect");
if (!bss->nl_connect)
return -1;
nl80211_register_eloop_read(&bss->nl_connect,
wpa_driver_nl80211_event_receive,
bss->nl_cb, 1);
return 0;
}
static int nl80211_init_bss(struct i802_bss *bss)
{
bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!bss->nl_cb)
return -1;
nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
no_seq_check, NULL);
nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
process_bss_event, bss);
nl80211_init_connect_handle(bss);
return 0;
}
static void nl80211_destroy_bss(struct i802_bss *bss)
{
nl_cb_put(bss->nl_cb);
bss->nl_cb = NULL;
if (bss->nl_connect)
nl80211_destroy_eloop_handle(&bss->nl_connect, 1);
}
static void
wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv)
{
struct rfkill_config *rcfg;
if (drv->rfkill)
return;
rcfg = os_zalloc(sizeof(*rcfg));
if (!rcfg)
return;
rcfg->ctx = drv;
/* rfkill uses netdev sysfs for initialization. However, P2P Device is
* not associated with a netdev, so use the name of some other interface
* sharing the same wiphy as the P2P Device interface.
*
* Note: This is valid, as a P2P Device interface is always dynamically
* created and is created only once another wpa_s interface was added.
*/
if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) {
struct nl80211_global *global = drv->global;
struct wpa_driver_nl80211_data *tmp1;
dl_list_for_each(tmp1, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
if (drv == tmp1 || drv->wiphy_idx != tmp1->wiphy_idx ||
!tmp1->rfkill)
continue;
wpa_printf(MSG_DEBUG,
"nl80211: Use (%s) to initialize P2P Device rfkill",
tmp1->first_bss->ifname);
os_strlcpy(rcfg->ifname, tmp1->first_bss->ifname,
sizeof(rcfg->ifname));
break;
}
} else {
os_strlcpy(rcfg->ifname, drv->first_bss->ifname,
sizeof(rcfg->ifname));
}
rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
drv->rfkill = rfkill_init(rcfg);
if (!drv->rfkill) {
wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
os_free(rcfg);
}
}
static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
void *global_priv, int hostapd,
const u8 *set_addr,
const char *driver_params)
{
struct wpa_driver_nl80211_data *drv;
struct i802_bss *bss;
if (global_priv == NULL)
return NULL;
drv = os_zalloc(sizeof(*drv));
if (drv == NULL)
return NULL;
drv->global = global_priv;
drv->ctx = ctx;
drv->hostapd = !!hostapd;
drv->eapol_sock = -1;
/*
* There is no driver capability flag for this, so assume it is
* supported and disable this on first attempt to use if the driver
* rejects the command due to missing support.
*/
drv->set_rekey_offload = 1;
drv->num_if_indices = ARRAY_SIZE(drv->default_if_indices);
drv->if_indices = drv->default_if_indices;
drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
if (!drv->first_bss) {
os_free(drv);
return NULL;
}
bss = drv->first_bss;
bss->drv = drv;
bss->ctx = ctx;
os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
drv->monitor_ifidx = -1;
drv->monitor_sock = -1;
drv->eapol_tx_sock = -1;
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
if (nl80211_init_bss(bss))
goto failed;
if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
goto failed;
if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS) {
drv->control_port_ap = 1;
goto skip_wifi_status;
}
drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
if (drv->eapol_tx_sock < 0)
goto failed;
if (drv->data_tx_status) {
int enabled = 1;
if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS,
&enabled, sizeof(enabled)) < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: wifi status sockopt failed: %s",
strerror(errno));
drv->data_tx_status = 0;
if (!drv->use_monitor)
drv->capa.flags &=
~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
} else {
eloop_register_read_sock(
drv->eapol_tx_sock,
wpa_driver_nl80211_handle_eapol_tx_status,
drv, NULL);
}
}
skip_wifi_status:
if (drv->global) {
nl80211_check_global(drv->global);
dl_list_add(&drv->global->interfaces, &drv->list);
drv->in_interface_list = 1;
}
return bss;
failed:
wpa_driver_nl80211_deinit(bss);
return NULL;
}
/**
* wpa_driver_nl80211_init - Initialize nl80211 driver interface
* @ctx: context to be used when calling wpa_supplicant functions,
* e.g., wpa_supplicant_event()
* @ifname: interface name, e.g., wlan0
* @global_priv: private driver global data from global_init()
* Returns: Pointer to private data, %NULL on failure
*/
static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
void *global_priv)
{
return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL,
NULL);
}
static int nl80211_register_frame(struct i802_bss *bss,
struct nl_sock *nl_handle,
u16 type, const u8 *match, size_t match_len,
bool multicast)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
char buf[30];
buf[0] = '\0';
wpa_snprintf_hex(buf, sizeof(buf), match, match_len);
wpa_printf(MSG_DEBUG,
"nl80211: Register frame type=0x%x (%s) nl_handle=%p match=%s multicast=%d",
type, fc2str(type), nl_handle, buf, multicast);
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REGISTER_FRAME)) ||
(multicast && nla_put_flag(msg, NL80211_ATTR_RECEIVE_MULTICAST)) ||
nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, type) ||
nla_put(msg, NL80211_ATTR_FRAME_MATCH, match_len, match)) {
nlmsg_free(msg);
return -1;
}
ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL,
NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
"failed (type=%u): ret=%d (%s)",
type, ret, strerror(-ret));
wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
match, match_len);
}
return ret;
}
static int nl80211_alloc_mgmt_handle(struct i802_bss *bss)
{
if (bss->nl_mgmt) {
wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting "
"already on! (nl_mgmt=%p)", bss->nl_mgmt);
return -1;
}
bss->nl_mgmt = nl_create_handle(bss->nl_cb, "mgmt");
if (bss->nl_mgmt == NULL)
return -1;
return 0;
}
static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss)
{
nl80211_register_eloop_read(&bss->nl_mgmt,
wpa_driver_nl80211_event_receive,
bss->nl_cb, 0);
}
static int nl80211_register_action_frame(struct i802_bss *bss,
const u8 *match, size_t match_len)
{
u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
return nl80211_register_frame(bss, bss->nl_mgmt,
type, match, match_len, false);
}
static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
int ret = 0;
if (nl80211_alloc_mgmt_handle(bss))
return -1;
wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
"handle %p", bss->nl_mgmt);
if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
/* register for any AUTH message */
nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0, false);
} else if ((drv->capa.flags & WPA_DRIVER_FLAGS_SAE) &&
!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
/* register for SAE Authentication frames */
nl80211_register_frame(bss, bss->nl_mgmt, type,
(u8 *) "\x03\x00", 2, false);
}
#ifdef CONFIG_PASN
/* register for PASN Authentication frames */
if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
nl80211_register_frame(bss, bss->nl_mgmt, type,
(u8 *) "\x07\x00", 2, false))
ret = -1;
#endif /* CONFIG_PASN */
#ifdef CONFIG_INTERWORKING
/* QoS Map Configure */
if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0)
ret = -1;
#endif /* CONFIG_INTERWORKING */
#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP)
/* GAS Initial Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
ret = -1;
/* GAS Initial Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0)
ret = -1;
/* GAS Comeback Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0)
ret = -1;
/* GAS Comeback Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0)
ret = -1;
/* Protected GAS Initial Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0a", 2) < 0)
ret = -1;
/* Protected GAS Initial Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0b", 2) < 0)
ret = -1;
/* Protected GAS Comeback Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0c", 2) < 0)
ret = -1;
/* Protected GAS Comeback Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0)
ret = -1;
#endif /* CONFIG_P2P || CONFIG_INTERWORKING || CONFIG_DPP */
#ifdef CONFIG_P2P
/* P2P Public Action */
if (nl80211_register_action_frame(bss,
(u8 *) "\x04\x09\x50\x6f\x9a\x09",
6) < 0)
ret = -1;
/* P2P Action */
if (nl80211_register_action_frame(bss,
(u8 *) "\x7f\x50\x6f\x9a\x09",
5) < 0)
ret = -1;
#endif /* CONFIG_P2P */
#ifdef CONFIG_DPP
/* DPP Public Action */
if (nl80211_register_action_frame(bss,
(u8 *) "\x04\x09\x50\x6f\x9a\x1a",
6) < 0)
ret = -1;
#endif /* CONFIG_DPP */
#ifdef CONFIG_OCV
/* SA Query Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x08\x00", 2) < 0)
ret = -1;
#endif /* CONFIG_OCV */
/* SA Query Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
ret = -1;
#ifdef CONFIG_TDLS
if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
/* TDLS Discovery Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) <
0)
ret = -1;
}
#endif /* CONFIG_TDLS */
#ifdef CONFIG_FST
/* FST Action frames */
if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
ret = -1;
#endif /* CONFIG_FST */
/* FT Action frames */
if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
ret = -1;
else if (!drv->has_driver_key_mgmt) {
int i;
/* Update supported AKMs only if the driver doesn't advertize
* any AKM capabilities. */
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
/* Update per interface supported AKMs */
for (i = 0; i < WPA_IF_MAX; i++)
drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
}
/* WNM - BSS Transition Management Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
ret = -1;
/* WNM-Sleep Mode Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
ret = -1;
#ifdef CONFIG_WNM
/* WNM - Collocated Interference Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x0b", 2) < 0)
ret = -1;
#endif /* CONFIG_WNM */
#ifdef CONFIG_HS20
/* WNM-Notification */
if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x1a", 2) < 0)
ret = -1;
#endif /* CONFIG_HS20 */
/* WMM-AC ADDTS Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x11\x01", 2) < 0)
ret = -1;
/* WMM-AC DELTS */
if (nl80211_register_action_frame(bss, (u8 *) "\x11\x02", 2) < 0)
ret = -1;
/* Radio Measurement - Neighbor Report Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0)
ret = -1;
/* Radio Measurement - Radio Measurement Request */
if (!drv->no_rrm &&
nl80211_register_action_frame(bss, (u8 *) "\x05\x00", 2) < 0)
ret = -1;
/* Radio Measurement - Link Measurement Request */
if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) &&
(nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0))
ret = -1;
+ /* Robust AV SCS Response */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x13\x01", 2) < 0)
+ ret = -1;
+
/* Robust AV MSCS Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x13\x05", 2) < 0)
ret = -1;
+ /* Protected QoS Management Action frame */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x7e\x50\x6f\x9a\x1a",
+ 5) < 0)
+ ret = -1;
+
nl80211_mgmt_handle_register_eloop(bss);
return ret;
}
static int nl80211_mgmt_subscribe_mesh(struct i802_bss *bss)
{
int ret = 0;
if (nl80211_alloc_mgmt_handle(bss))
return -1;
wpa_printf(MSG_DEBUG,
"nl80211: Subscribe to mgmt frames with mesh handle %p",
bss->nl_mgmt);
/* Auth frames for mesh SAE */
if (nl80211_register_frame(bss, bss->nl_mgmt,
(WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_AUTH << 4),
NULL, 0, false) < 0)
ret = -1;
/* Mesh peering open */
if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x01", 2) < 0)
ret = -1;
/* Mesh peering confirm */
if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x02", 2) < 0)
ret = -1;
/* Mesh peering close */
if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x03", 2) < 0)
ret = -1;
nl80211_mgmt_handle_register_eloop(bss);
return ret;
}
static int nl80211_register_spurious_class3(struct i802_bss *bss)
{
struct nl_msg *msg;
int ret;
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL,
NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
"failed: ret=%d (%s)",
ret, strerror(-ret));
}
return ret;
}
static int nl80211_action_subscribe_ap(struct i802_bss *bss)
{
int ret = 0;
/* Public Action frames */
if (nl80211_register_action_frame(bss, (u8 *) "\x04", 1) < 0)
ret = -1;
/* RRM Measurement Report */
if (nl80211_register_action_frame(bss, (u8 *) "\x05\x01", 2) < 0)
ret = -1;
/* RRM Link Measurement Report */
if (nl80211_register_action_frame(bss, (u8 *) "\x05\x03", 2) < 0)
ret = -1;
/* RRM Neighbor Report Request */
if (nl80211_register_action_frame(bss, (u8 *) "\x05\x04", 2) < 0)
ret = -1;
/* FT Action frames */
if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
ret = -1;
/* SA Query */
if (nl80211_register_action_frame(bss, (u8 *) "\x08", 1) < 0)
ret = -1;
/* Protected Dual of Public Action */
if (nl80211_register_action_frame(bss, (u8 *) "\x09", 1) < 0)
ret = -1;
/* WNM */
if (nl80211_register_action_frame(bss, (u8 *) "\x0a", 1) < 0)
ret = -1;
/* WMM */
if (nl80211_register_action_frame(bss, (u8 *) "\x11", 1) < 0)
ret = -1;
#ifdef CONFIG_FST
/* FST Action frames */
if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
ret = -1;
#endif /* CONFIG_FST */
/* Vendor-specific */
if (nl80211_register_action_frame(bss, (u8 *) "\x7f", 1) < 0)
ret = -1;
return ret;
}
static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss)
{
static const int stypes[] = {
WLAN_FC_STYPE_AUTH,
WLAN_FC_STYPE_ASSOC_REQ,
WLAN_FC_STYPE_REASSOC_REQ,
WLAN_FC_STYPE_DISASSOC,
WLAN_FC_STYPE_DEAUTH,
WLAN_FC_STYPE_PROBE_REQ,
/* Beacon doesn't work as mac80211 doesn't currently allow
* it, but it wouldn't really be the right thing anyway as
* it isn't per interface ... maybe just dump the scan
* results periodically for OLBC?
*/
/* WLAN_FC_STYPE_BEACON, */
};
unsigned int i;
if (nl80211_alloc_mgmt_handle(bss))
return -1;
wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
"handle %p", bss->nl_mgmt);
for (i = 0; i < ARRAY_SIZE(stypes); i++) {
if (nl80211_register_frame(bss, bss->nl_mgmt,
(WLAN_FC_TYPE_MGMT << 2) |
(stypes[i] << 4),
NULL, 0, false) < 0) {
goto out_err;
}
}
if (nl80211_action_subscribe_ap(bss))
goto out_err;
if (nl80211_register_spurious_class3(bss))
goto out_err;
nl80211_mgmt_handle_register_eloop(bss);
return 0;
out_err:
nl_destroy_handles(&bss->nl_mgmt);
return -1;
}
static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
{
if (nl80211_alloc_mgmt_handle(bss))
return -1;
wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
"handle %p (device SME)", bss->nl_mgmt);
if (nl80211_action_subscribe_ap(bss))
goto out_err;
if (bss->drv->device_ap_sme) {
u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
/* Register for all Authentication frames */
if (nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0,
false) < 0)
wpa_printf(MSG_DEBUG,
"nl80211: Failed to subscribe to handle Authentication frames - SAE offload may not work");
}
nl80211_mgmt_handle_register_eloop(bss);
return 0;
out_err:
nl_destroy_handles(&bss->nl_mgmt);
return -1;
}
static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
{
if (bss->nl_mgmt == NULL)
return;
wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
"(%s)", bss->nl_mgmt, reason);
nl80211_destroy_eloop_handle(&bss->nl_mgmt, 0);
nl80211_put_wiphy_data_ap(bss);
}
static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
{
wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
}
static void nl80211_del_p2pdev(struct i802_bss *bss)
{
struct nl_msg *msg;
int ret;
msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE);
ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
bss->ifname, (long long unsigned int) bss->wdev_id,
strerror(-ret));
}
static int nl80211_set_p2pdev(struct i802_bss *bss, int start)
{
struct nl_msg *msg;
int ret;
msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE :
NL80211_CMD_STOP_P2P_DEVICE);
ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s",
start ? "Start" : "Stop",
bss->ifname, (long long unsigned int) bss->wdev_id,
strerror(-ret));
return ret;
}
static int i802_set_iface_flags(struct i802_bss *bss, int up)
{
enum nl80211_iftype nlmode;
nlmode = nl80211_get_ifmode(bss);
if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
return linux_set_iface_flags(bss->drv->global->ioctl_sock,
bss->ifname, up);
}
/* P2P Device has start/stop which is equivalent */
return nl80211_set_p2pdev(bss, up);
}
#ifdef CONFIG_TESTING_OPTIONS
static int qca_vendor_test_cmd_handler(struct nl_msg *msg, void *arg)
{
/* struct wpa_driver_nl80211_data *drv = arg; */
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
wpa_printf(MSG_DEBUG,
"nl80211: QCA vendor test command response received");
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_VENDOR_DATA]) {
wpa_printf(MSG_DEBUG, "nl80211: No vendor data attribute");
return NL_SKIP;
}
wpa_hexdump(MSG_DEBUG,
"nl80211: Received QCA vendor test command response",
nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
nla_len(tb[NL80211_ATTR_VENDOR_DATA]));
return NL_SKIP;
}
#endif /* CONFIG_TESTING_OPTIONS */
static void qca_vendor_test(struct wpa_driver_nl80211_data *drv)
{
#ifdef CONFIG_TESTING_OPTIONS
struct nl_msg *msg;
struct nlattr *params;
int ret;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_TEST) ||
!(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TEST, 123)) {
nlmsg_free(msg);
return;
}
nla_nest_end(msg, params);
ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv,
NULL, NULL);
wpa_printf(MSG_DEBUG,
"nl80211: QCA vendor test command returned %d (%s)",
ret, strerror(-ret));
#endif /* CONFIG_TESTING_OPTIONS */
}
static int
wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
const u8 *set_addr, int first,
const char *driver_params)
{
struct i802_bss *bss = drv->first_bss;
int send_rfkill_event = 0;
enum nl80211_iftype nlmode;
drv->ifindex = if_nametoindex(bss->ifname);
bss->ifindex = drv->ifindex;
bss->wdev_id = drv->global->if_add_wdevid;
bss->wdev_id_set = drv->global->if_add_wdevid_set;
bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex;
bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set;
drv->global->if_add_wdevid_set = 0;
if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
bss->static_ap = 1;
if (first &&
nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE &&
linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0)
drv->start_iface_up = 1;
if (wpa_driver_nl80211_capa(drv))
return -1;
if (driver_params && nl80211_set_param(bss, driver_params) < 0)
return -1;
wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
bss->ifname, drv->phyname);
if (set_addr &&
(linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) ||
linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
set_addr)))
return -1;
if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_STATION)
drv->start_mode_sta = 1;
if (drv->hostapd || bss->static_ap)
nlmode = NL80211_IFTYPE_AP;
else if (bss->if_dynamic ||
nl80211_get_ifmode(bss) == NL80211_IFTYPE_MESH_POINT)
nlmode = nl80211_get_ifmode(bss);
else
nlmode = NL80211_IFTYPE_STATION;
if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode");
return -1;
}
if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
nl80211_get_macaddr(bss);
wpa_driver_nl80211_drv_init_rfkill(drv);
if (!rfkill_is_blocked(drv->rfkill)) {
int ret = i802_set_iface_flags(bss, 1);
if (ret) {
wpa_printf(MSG_ERROR, "nl80211: Could not set "
"interface '%s' UP", bss->ifname);
return ret;
}
if (is_p2p_net_interface(nlmode))
nl80211_disable_11b_rates(bss->drv,
bss->drv->ifindex, 1);
if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
return ret;
} else {
wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
"interface '%s' due to rfkill", bss->ifname);
if (nlmode != NL80211_IFTYPE_P2P_DEVICE)
drv->if_disabled = 1;
send_rfkill_event = 1;
}
if (!drv->hostapd && nlmode != NL80211_IFTYPE_P2P_DEVICE)
netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
1, IF_OPER_DORMANT);
if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
bss->addr))
return -1;
os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN);
}
if (send_rfkill_event) {
eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
drv, drv->ctx);
}
if (drv->vendor_cmd_test_avail)
qca_vendor_test(drv);
return 0;
}
static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss)
{
struct nl_msg *msg;
struct wpa_driver_nl80211_data *drv = bss->drv;
wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
drv->ifindex);
nl80211_put_wiphy_data_ap(bss);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
/**
* wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
* @bss: Pointer to private nl80211 data from wpa_driver_nl80211_init()
*
* Shut down driver interface and processing of driver events. Free
* private data buffer if one was allocated in wpa_driver_nl80211_init().
*/
static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
unsigned int i;
wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d",
bss->ifname, drv->disabled_11b_rates);
bss->in_deinit = 1;
if (drv->data_tx_status)
eloop_unregister_read_sock(drv->eapol_tx_sock);
if (drv->eapol_tx_sock >= 0)
close(drv->eapol_tx_sock);
if (bss->nl_preq)
wpa_driver_nl80211_probe_req_report(bss, 0);
if (bss->added_if_into_bridge) {
if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
bss->ifname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"interface %s from bridge %s: %s",
bss->ifname, bss->brname, strerror(errno));
}
if (drv->rtnl_sk)
nl_socket_free(drv->rtnl_sk);
if (bss->added_bridge) {
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname,
0) < 0)
wpa_printf(MSG_INFO,
"nl80211: Could not set bridge %s down",
bss->brname);
if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"bridge %s: %s",
bss->brname, strerror(errno));
}
nl80211_remove_monitor_interface(drv);
if (is_ap_interface(drv->nlmode))
wpa_driver_nl80211_del_beacon(bss);
if (drv->eapol_sock >= 0) {
eloop_unregister_read_sock(drv->eapol_sock);
close(drv->eapol_sock);
}
if (drv->if_indices != drv->default_if_indices)
os_free(drv->if_indices);
if (drv->disabled_11b_rates)
nl80211_disable_11b_rates(drv, drv->ifindex, 0);
netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
IF_OPER_UP);
eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill, drv, drv->ctx);
rfkill_deinit(drv->rfkill);
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
if (!drv->start_iface_up)
(void) i802_set_iface_flags(bss, 0);
if (drv->addr_changed) {
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
0) < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Could not set interface down to restore permanent MAC address");
}
if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
drv->perm_addr) < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Could not restore permanent MAC address");
}
}
if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
if (drv->start_mode_sta)
wpa_driver_nl80211_set_mode(bss,
NL80211_IFTYPE_STATION);
nl80211_mgmt_unsubscribe(bss, "deinit");
} else {
nl80211_mgmt_unsubscribe(bss, "deinit");
nl80211_del_p2pdev(bss);
}
nl80211_destroy_bss(drv->first_bss);
os_free(drv->filter_ssids);
os_free(drv->auth_ie);
os_free(drv->auth_data);
if (drv->in_interface_list)
dl_list_del(&drv->list);
os_free(drv->extended_capa);
os_free(drv->extended_capa_mask);
for (i = 0; i < drv->num_iface_ext_capa; i++) {
os_free(drv->iface_ext_capa[i].ext_capa);
os_free(drv->iface_ext_capa[i].ext_capa_mask);
}
os_free(drv->first_bss);
#ifdef CONFIG_DRIVER_NL80211_QCA
os_free(drv->pending_roam_data);
#endif /* CONFIG_DRIVER_NL80211_QCA */
os_free(drv);
}
static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len)
{
switch (alg) {
case WPA_ALG_WEP:
if (key_len == 5)
return RSN_CIPHER_SUITE_WEP40;
return RSN_CIPHER_SUITE_WEP104;
case WPA_ALG_TKIP:
return RSN_CIPHER_SUITE_TKIP;
case WPA_ALG_CCMP:
return RSN_CIPHER_SUITE_CCMP;
case WPA_ALG_GCMP:
return RSN_CIPHER_SUITE_GCMP;
case WPA_ALG_CCMP_256:
return RSN_CIPHER_SUITE_CCMP_256;
case WPA_ALG_GCMP_256:
return RSN_CIPHER_SUITE_GCMP_256;
case WPA_ALG_BIP_CMAC_128:
return RSN_CIPHER_SUITE_AES_128_CMAC;
case WPA_ALG_BIP_GMAC_128:
return RSN_CIPHER_SUITE_BIP_GMAC_128;
case WPA_ALG_BIP_GMAC_256:
return RSN_CIPHER_SUITE_BIP_GMAC_256;
case WPA_ALG_BIP_CMAC_256:
return RSN_CIPHER_SUITE_BIP_CMAC_256;
case WPA_ALG_SMS4:
return RSN_CIPHER_SUITE_SMS4;
case WPA_ALG_KRK:
return RSN_CIPHER_SUITE_KRK;
case WPA_ALG_NONE:
wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d",
alg);
return 0;
}
wpa_printf(MSG_ERROR, "nl80211: Unsupported encryption algorithm %d",
alg);
return 0;
}
static u32 wpa_cipher_to_cipher_suite(unsigned int cipher)
{
switch (cipher) {
case WPA_CIPHER_CCMP_256:
return RSN_CIPHER_SUITE_CCMP_256;
case WPA_CIPHER_GCMP_256:
return RSN_CIPHER_SUITE_GCMP_256;
case WPA_CIPHER_CCMP:
return RSN_CIPHER_SUITE_CCMP;
case WPA_CIPHER_GCMP:
return RSN_CIPHER_SUITE_GCMP;
case WPA_CIPHER_TKIP:
return RSN_CIPHER_SUITE_TKIP;
case WPA_CIPHER_WEP104:
return RSN_CIPHER_SUITE_WEP104;
case WPA_CIPHER_WEP40:
return RSN_CIPHER_SUITE_WEP40;
case WPA_CIPHER_GTK_NOT_USED:
return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
}
return 0;
}
static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[],
int max_suites)
{
int num_suites = 0;
if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256)
suites[num_suites++] = RSN_CIPHER_SUITE_CCMP_256;
if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256)
suites[num_suites++] = RSN_CIPHER_SUITE_GCMP_256;
if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP)
suites[num_suites++] = RSN_CIPHER_SUITE_CCMP;
if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP)
suites[num_suites++] = RSN_CIPHER_SUITE_GCMP;
if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP)
suites[num_suites++] = RSN_CIPHER_SUITE_TKIP;
if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104)
suites[num_suites++] = RSN_CIPHER_SUITE_WEP104;
if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40)
suites[num_suites++] = RSN_CIPHER_SUITE_WEP40;
return num_suites;
}
static int wpa_key_mgmt_to_suites(unsigned int key_mgmt_suites, u32 suites[],
int max_suites)
{
int num_suites = 0;
#define __AKM(a, b) \
if (num_suites < max_suites && \
(key_mgmt_suites & (WPA_KEY_MGMT_ ## a))) \
suites[num_suites++] = (RSN_AUTH_KEY_MGMT_ ## b)
__AKM(IEEE8021X, UNSPEC_802_1X);
__AKM(PSK, PSK_OVER_802_1X);
__AKM(FT_IEEE8021X, FT_802_1X);
__AKM(FT_PSK, FT_PSK);
__AKM(IEEE8021X_SHA256, 802_1X_SHA256);
__AKM(PSK_SHA256, PSK_SHA256);
__AKM(SAE, SAE);
__AKM(FT_SAE, FT_SAE);
__AKM(CCKM, CCKM);
__AKM(OSEN, OSEN);
__AKM(IEEE8021X_SUITE_B, 802_1X_SUITE_B);
__AKM(IEEE8021X_SUITE_B_192, 802_1X_SUITE_B_192);
__AKM(FILS_SHA256, FILS_SHA256);
__AKM(FILS_SHA384, FILS_SHA384);
__AKM(FT_FILS_SHA256, FT_FILS_SHA256);
__AKM(FT_FILS_SHA384, FT_FILS_SHA384);
__AKM(OWE, OWE);
__AKM(DPP, DPP);
__AKM(FT_IEEE8021X_SHA384, FT_802_1X_SHA384);
#undef __AKM
return num_suites;
}
#ifdef CONFIG_DRIVER_NL80211_QCA
static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
const u8 *key, size_t key_len)
{
struct nl_msg *msg;
int ret;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
return 0;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) ||
nla_put(msg, NL80211_ATTR_VENDOR_DATA, key_len, key)) {
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return -1;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Key management set key failed: ret=%d (%s)",
ret, strerror(-ret));
}
return ret;
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv,
const u8 *key, size_t key_len,
const u8 *addr)
{
struct nl_msg *msg = NULL;
int ret;
/*
* If the authenticator address is not set, assume it is
* the current BSSID.
*/
if (!addr && drv->associated)
addr = drv->bssid;
else if (!addr)
return -1;
wpa_printf(MSG_DEBUG, "nl80211: Set PMK to the driver for " MACSTR,
MAC2STR(addr));
wpa_hexdump_key(MSG_DEBUG, "nl80211: PMK", key, key_len);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_PMK);
if (!msg ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
nla_put(msg, NL80211_ATTR_PMK, key_len, key)) {
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)",
ret, strerror(-ret));
}
return ret;
}
static int wpa_driver_nl80211_set_key(struct i802_bss *bss,
struct wpa_driver_set_key_params *params)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ifindex;
struct nl_msg *msg;
struct nl_msg *key_msg;
int ret;
int skip_set_key = 1;
const char *ifname = params->ifname;
enum wpa_alg alg = params->alg;
const u8 *addr = params->addr;
int key_idx = params->key_idx;
int set_tx = params->set_tx;
const u8 *seq = params->seq;
size_t seq_len = params->seq_len;
const u8 *key = params->key;
size_t key_len = params->key_len;
int vlan_id = params->vlan_id;
enum key_flag key_flag = params->key_flag;
/* Ignore for P2P Device */
if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
return 0;
ifindex = if_nametoindex(ifname);
wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d "
"set_tx=%d seq_len=%lu key_len=%lu key_flag=0x%x",
__func__, ifindex, ifname, alg, addr, key_idx, set_tx,
(unsigned long) seq_len, (unsigned long) key_len, key_flag);
if (check_key_flag(key_flag)) {
wpa_printf(MSG_DEBUG, "%s: invalid key_flag", __func__);
return -EINVAL;
}
#ifdef CONFIG_DRIVER_NL80211_QCA
if ((key_flag & KEY_FLAG_PMK) &&
(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
__func__);
ret = issue_key_mgmt_set_key(drv, key, key_len);
return ret;
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
if (key_flag & KEY_FLAG_PMK) {
if (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)
return nl80211_set_pmk(drv, key, key_len, addr);
/* The driver does not have any offload mechanism for PMK, so
* there is no need to configure this key. */
return 0;
}
ret = -ENOBUFS;
key_msg = nlmsg_alloc();
if (!key_msg)
return ret;
if ((key_flag & KEY_FLAG_PAIRWISE_MASK) ==
KEY_FLAG_PAIRWISE_RX_TX_MODIFY) {
wpa_printf(MSG_DEBUG,
"nl80211: SET_KEY (pairwise RX/TX modify)");
msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
if (!msg)
goto fail2;
} else if (alg == WPA_ALG_NONE && (key_flag & KEY_FLAG_RX_TX)) {
wpa_printf(MSG_DEBUG, "%s: invalid key_flag to delete key",
__func__);
ret = -EINVAL;
goto fail2;
} else if (alg == WPA_ALG_NONE) {
wpa_printf(MSG_DEBUG, "nl80211: DEL_KEY");
msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
if (!msg)
goto fail2;
} else {
u32 suite;
suite = wpa_alg_to_cipher_suite(alg, key_len);
if (!suite) {
ret = -EINVAL;
goto fail2;
}
wpa_printf(MSG_DEBUG, "nl80211: NEW_KEY");
msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY);
if (!msg)
goto fail2;
if (nla_put(key_msg, NL80211_KEY_DATA, key_len, key) ||
nla_put_u32(key_msg, NL80211_KEY_CIPHER, suite))
goto fail;
wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len);
if (seq && seq_len) {
if (nla_put(key_msg, NL80211_KEY_SEQ, seq_len, seq))
goto fail;
wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ",
seq, seq_len);
}
}
if (addr && !is_broadcast_ether_addr(addr)) {
wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr));
if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
goto fail;
if ((key_flag & KEY_FLAG_PAIRWISE_MASK) ==
KEY_FLAG_PAIRWISE_RX ||
(key_flag & KEY_FLAG_PAIRWISE_MASK) ==
KEY_FLAG_PAIRWISE_RX_TX_MODIFY) {
if (nla_put_u8(key_msg, NL80211_KEY_MODE,
key_flag == KEY_FLAG_PAIRWISE_RX ?
NL80211_KEY_NO_TX : NL80211_KEY_SET_TX))
goto fail;
} else if ((key_flag & KEY_FLAG_GROUP_MASK) ==
KEY_FLAG_GROUP_RX) {
wpa_printf(MSG_DEBUG, " RSN IBSS RX GTK");
if (nla_put_u32(key_msg, NL80211_KEY_TYPE,
NL80211_KEYTYPE_GROUP))
goto fail;
} else if (!(key_flag & KEY_FLAG_PAIRWISE)) {
wpa_printf(MSG_DEBUG,
" key_flag missing PAIRWISE when setting a pairwise key");
ret = -EINVAL;
goto fail;
} else if (alg == WPA_ALG_WEP &&
(key_flag & KEY_FLAG_RX_TX) == KEY_FLAG_RX_TX) {
wpa_printf(MSG_DEBUG, " unicast WEP key");
skip_set_key = 0;
} else {
wpa_printf(MSG_DEBUG, " pairwise key");
}
} else if ((key_flag & KEY_FLAG_PAIRWISE) ||
!(key_flag & KEY_FLAG_GROUP)) {
wpa_printf(MSG_DEBUG,
" invalid key_flag for a broadcast key");
ret = -EINVAL;
goto fail;
} else {
wpa_printf(MSG_DEBUG, " broadcast key");
if (key_flag & KEY_FLAG_DEFAULT)
skip_set_key = 0;
}
if (nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
nla_put_nested(msg, NL80211_ATTR_KEY, key_msg))
goto fail;
nl80211_nlmsg_clear(key_msg);
nlmsg_free(key_msg);
key_msg = NULL;
if (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
wpa_printf(MSG_DEBUG, "nl80211: VLAN ID %d", vlan_id);
if (nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id))
goto fail;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
ret = 0;
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s",
ret, strerror(-ret));
/*
* If we failed or don't need to set the key as default (below),
* we're done here.
*/
if (ret || skip_set_key)
return ret;
wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_SET_KEY - default key");
ret = -ENOBUFS;
key_msg = nlmsg_alloc();
if (!key_msg)
return ret;
msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
if (!msg)
goto fail2;
if (!key_msg ||
nla_put_u8(key_msg, NL80211_KEY_IDX, key_idx) ||
nla_put_flag(key_msg, wpa_alg_bip(alg) ?
(key_idx == 6 || key_idx == 7 ?
NL80211_KEY_DEFAULT_BEACON :
NL80211_KEY_DEFAULT_MGMT) :
NL80211_KEY_DEFAULT))
goto fail;
if (addr && is_broadcast_ether_addr(addr)) {
struct nlattr *types;
types = nla_nest_start(key_msg, NL80211_KEY_DEFAULT_TYPES);
if (!types ||
nla_put_flag(key_msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
goto fail;
nla_nest_end(key_msg, types);
} else if (addr) {
struct nlattr *types;
types = nla_nest_start(key_msg, NL80211_KEY_DEFAULT_TYPES);
if (!types ||
nla_put_flag(key_msg, NL80211_KEY_DEFAULT_TYPE_UNICAST))
goto fail;
nla_nest_end(key_msg, types);
}
if (nla_put_nested(msg, NL80211_ATTR_KEY, key_msg))
goto fail;
nl80211_nlmsg_clear(key_msg);
nlmsg_free(key_msg);
key_msg = NULL;
if (vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD)) {
wpa_printf(MSG_DEBUG, "nl80211: set_key default - VLAN ID %d",
vlan_id);
if (nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id))
goto fail;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG,
"nl80211: set_key default failed; err=%d %s",
ret, strerror(-ret));
return ret;
fail:
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
fail2:
nl80211_nlmsg_clear(key_msg);
nlmsg_free(key_msg);
return ret;
}
static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
int key_idx, int defkey,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len)
{
struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY);
u32 suite;
if (!key_attr)
return -1;
suite = wpa_alg_to_cipher_suite(alg, key_len);
if (!suite)
return -1;
if (defkey && wpa_alg_bip(alg)) {
if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT))
return -1;
} else if (defkey) {
if (nla_put_flag(msg, NL80211_KEY_DEFAULT))
return -1;
}
if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) ||
nla_put_u32(msg, NL80211_KEY_CIPHER, suite) ||
(seq && seq_len &&
nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) ||
nla_put(msg, NL80211_KEY_DATA, key_len, key))
return -1;
nla_nest_end(msg, key_attr);
return 0;
}
static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
struct nl_msg *msg)
{
int i, privacy = 0;
struct nlattr *nl_keys, *nl_key;
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
continue;
privacy = 1;
break;
}
if (params->wps == WPS_MODE_PRIVACY)
privacy = 1;
if (params->pairwise_suite &&
params->pairwise_suite != WPA_CIPHER_NONE)
privacy = 1;
if (!privacy)
return 0;
if (nla_put_flag(msg, NL80211_ATTR_PRIVACY))
return -ENOBUFS;
nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
if (!nl_keys)
return -ENOBUFS;
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
continue;
nl_key = nla_nest_start(msg, i);
if (!nl_key ||
nla_put(msg, NL80211_KEY_DATA, params->wep_key_len[i],
params->wep_key[i]) ||
nla_put_u32(msg, NL80211_KEY_CIPHER,
params->wep_key_len[i] == 5 ?
RSN_CIPHER_SUITE_WEP40 :
RSN_CIPHER_SUITE_WEP104) ||
nla_put_u8(msg, NL80211_KEY_IDX, i) ||
(i == params->wep_tx_keyidx &&
nla_put_flag(msg, NL80211_KEY_DEFAULT)))
return -ENOBUFS;
nla_nest_end(msg, nl_key);
}
nla_nest_end(msg, nl_keys);
return 0;
}
int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
const u8 *addr, int cmd, u16 reason_code,
int local_state_change,
struct i802_bss *bss)
{
int ret;
struct nl_msg *msg;
struct nl_sock *nl_connect = get_connect_handle(bss);
if (!(msg = nl80211_drv_msg(drv, 0, cmd)) ||
nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) ||
(addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
(local_state_change &&
nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))) {
nlmsg_free(msg);
return -1;
}
if (nl_connect)
ret = send_and_recv(drv->global, nl_connect, msg,
process_bss_event, bss, NULL, NULL);
else
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: MLME command failed: reason=%u ret=%d (%s)",
reason_code, ret, strerror(-ret));
}
return ret;
}
static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
u16 reason_code,
struct i802_bss *bss)
{
int ret;
int drv_associated = drv->associated;
wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code);
nl80211_mark_disconnected(drv);
/* Disconnect command doesn't need BSSID - it uses cached value */
ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT,
reason_code, 0, bss);
/*
* For locally generated disconnect, supplicant already generates a
* DEAUTH event, so ignore the event from NL80211.
*/
drv->ignore_next_local_disconnect = drv_associated && (ret == 0);
return ret;
}
static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss,
const u8 *addr, u16 reason_code)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret;
int drv_associated = drv->associated;
if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
nl80211_mark_disconnected(drv);
return nl80211_leave_ibss(drv, 1);
}
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
return wpa_driver_nl80211_disconnect(drv, reason_code, bss);
}
wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
__func__, MAC2STR(addr), reason_code);
nl80211_mark_disconnected(drv);
ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
reason_code, 0, bss);
/*
* For locally generated deauthenticate, supplicant already generates a
* DEAUTH event, so ignore the event from NL80211.
*/
drv->ignore_next_local_deauth = drv_associated && (ret == 0);
return ret;
}
static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_auth_params *params)
{
int i;
drv->auth_freq = params->freq;
drv->auth_alg = params->auth_alg;
drv->auth_wep_tx_keyidx = params->wep_tx_keyidx;
drv->auth_local_state_change = params->local_state_change;
drv->auth_p2p = params->p2p;
if (params->bssid)
os_memcpy(drv->auth_bssid_, params->bssid, ETH_ALEN);
else
os_memset(drv->auth_bssid_, 0, ETH_ALEN);
if (params->ssid) {
os_memcpy(drv->auth_ssid, params->ssid, params->ssid_len);
drv->auth_ssid_len = params->ssid_len;
} else
drv->auth_ssid_len = 0;
os_free(drv->auth_ie);
drv->auth_ie = NULL;
drv->auth_ie_len = 0;
if (params->ie) {
drv->auth_ie = os_malloc(params->ie_len);
if (drv->auth_ie) {
os_memcpy(drv->auth_ie, params->ie, params->ie_len);
drv->auth_ie_len = params->ie_len;
}
}
os_free(drv->auth_data);
drv->auth_data = NULL;
drv->auth_data_len = 0;
if (params->auth_data) {
drv->auth_data = os_memdup(params->auth_data,
params->auth_data_len);
if (drv->auth_data)
drv->auth_data_len = params->auth_data_len;
}
for (i = 0; i < 4; i++) {
if (params->wep_key[i] && params->wep_key_len[i] &&
params->wep_key_len[i] <= 16) {
os_memcpy(drv->auth_wep_key[i], params->wep_key[i],
params->wep_key_len[i]);
drv->auth_wep_key_len[i] = params->wep_key_len[i];
} else
drv->auth_wep_key_len[i] = 0;
}
}
static void nl80211_unmask_11b_rates(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
if (is_p2p_net_interface(drv->nlmode) || !drv->disabled_11b_rates)
return;
/*
* Looks like we failed to unmask 11b rates previously. This could
* happen, e.g., if the interface was down at the point in time when a
* P2P group was terminated.
*/
wpa_printf(MSG_DEBUG,
"nl80211: Interface %s mode is for non-P2P, but 11b rates were disabled - re-enable them",
bss->ifname);
nl80211_disable_11b_rates(drv, drv->ifindex, 0);
}
static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg)
{
if (wpa_auth_alg & WPA_AUTH_ALG_OPEN)
return NL80211_AUTHTYPE_OPEN_SYSTEM;
if (wpa_auth_alg & WPA_AUTH_ALG_SHARED)
return NL80211_AUTHTYPE_SHARED_KEY;
if (wpa_auth_alg & WPA_AUTH_ALG_LEAP)
return NL80211_AUTHTYPE_NETWORK_EAP;
if (wpa_auth_alg & WPA_AUTH_ALG_FT)
return NL80211_AUTHTYPE_FT;
if (wpa_auth_alg & WPA_AUTH_ALG_SAE)
return NL80211_AUTHTYPE_SAE;
if (wpa_auth_alg & WPA_AUTH_ALG_FILS)
return NL80211_AUTHTYPE_FILS_SK;
if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS)
return NL80211_AUTHTYPE_FILS_SK_PFS;
return NL80211_AUTHTYPE_MAX;
}
static int wpa_driver_nl80211_authenticate(
struct i802_bss *bss, struct wpa_driver_auth_params *params)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1, i;
struct nl_msg *msg;
enum nl80211_auth_type type;
enum nl80211_iftype nlmode;
int count = 0;
int is_retry;
struct wpa_driver_set_key_params p;
nl80211_unmask_11b_rates(bss);
is_retry = drv->retry_auth;
drv->retry_auth = 0;
drv->ignore_deauth_event = 0;
nl80211_mark_disconnected(drv);
os_memset(drv->auth_bssid, 0, ETH_ALEN);
if (params->bssid)
os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN);
else
os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
/* FIX: IBSS mode */
nlmode = params->p2p ?
NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
if (drv->nlmode != nlmode &&
wpa_driver_nl80211_set_mode(bss, nlmode) < 0)
return -1;
retry:
wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE);
if (!msg)
goto fail;
os_memset(&p, 0, sizeof(p));
p.ifname = bss->ifname;
p.alg = WPA_ALG_WEP;
for (i = 0; i < 4; i++) {
if (!params->wep_key[i])
continue;
p.key_idx = i;
p.set_tx = i == params->wep_tx_keyidx;
p.key = params->wep_key[i];
p.key_len = params->wep_key_len[i];
p.key_flag = i == params->wep_tx_keyidx ?
KEY_FLAG_GROUP_RX_TX_DEFAULT :
KEY_FLAG_GROUP_RX_TX;
wpa_driver_nl80211_set_key(bss, &p);
if (params->wep_tx_keyidx != i)
continue;
if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0,
params->wep_key[i], params->wep_key_len[i]))
goto fail;
}
if (params->bssid) {
wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
MAC2STR(params->bssid));
if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
goto fail;
}
if (params->freq) {
wpa_printf(MSG_DEBUG, " * freq=%d", params->freq);
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq))
goto fail;
}
if (params->ssid) {
wpa_printf(MSG_DEBUG, " * SSID=%s",
wpa_ssid_txt(params->ssid, params->ssid_len));
if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
params->ssid))
goto fail;
}
wpa_hexdump(MSG_DEBUG, " * IEs", params->ie, params->ie_len);
if (params->ie &&
nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie))
goto fail;
if (params->auth_data) {
wpa_hexdump(MSG_DEBUG, " * auth_data", params->auth_data,
params->auth_data_len);
if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->auth_data_len,
params->auth_data))
goto fail;
}
type = get_nl_auth_type(params->auth_alg);
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
if (type == NL80211_AUTHTYPE_MAX ||
nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
goto fail;
if (params->local_state_change) {
wpa_printf(MSG_DEBUG, " * Local state change only");
if (nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))
goto fail;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: MLME command failed (auth): count=%d ret=%d (%s)",
count, ret, strerror(-ret));
count++;
if ((ret == -EALREADY || ret == -EEXIST) && count == 1 &&
params->bssid && !params->local_state_change) {
/*
* mac80211 does not currently accept new
* authentication if we are already authenticated. As a
* workaround, force deauthentication and try again.
*/
wpa_printf(MSG_DEBUG, "nl80211: Retry authentication "
"after forced deauthentication");
drv->ignore_deauth_event = 1;
wpa_driver_nl80211_deauthenticate(
bss, params->bssid,
WLAN_REASON_PREV_AUTH_NOT_VALID);
nlmsg_free(msg);
goto retry;
}
if (ret == -ENOENT && params->freq && !is_retry) {
/*
* cfg80211 has likely expired the BSS entry even
* though it was previously available in our internal
* BSS table. To recover quickly, start a single
* channel scan on the specified channel.
*/
struct wpa_driver_scan_params scan;
int freqs[2];
os_memset(&scan, 0, sizeof(scan));
scan.num_ssids = 1;
if (params->ssid) {
scan.ssids[0].ssid = params->ssid;
scan.ssids[0].ssid_len = params->ssid_len;
}
freqs[0] = params->freq;
freqs[1] = 0;
scan.freqs = freqs;
wpa_printf(MSG_DEBUG, "nl80211: Trigger single "
"channel scan to refresh cfg80211 BSS "
"entry");
ret = wpa_driver_nl80211_scan(bss, &scan);
if (ret == 0) {
nl80211_copy_auth_params(drv, params);
drv->scan_for_auth = 1;
}
} else if (is_retry) {
/*
* Need to indicate this with an event since the return
* value from the retry is not delivered to core code.
*/
union wpa_event_data event;
wpa_printf(MSG_DEBUG, "nl80211: Authentication retry "
"failed");
os_memset(&event, 0, sizeof(event));
os_memcpy(event.timeout_event.addr, drv->auth_bssid_,
ETH_ALEN);
wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT,
&event);
}
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Authentication request send successfully");
}
fail:
nlmsg_free(msg);
return ret;
}
int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv)
{
struct wpa_driver_auth_params params;
struct i802_bss *bss = drv->first_bss;
int i;
wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
os_memset(&params, 0, sizeof(params));
params.freq = drv->auth_freq;
params.auth_alg = drv->auth_alg;
params.wep_tx_keyidx = drv->auth_wep_tx_keyidx;
params.local_state_change = drv->auth_local_state_change;
params.p2p = drv->auth_p2p;
if (!is_zero_ether_addr(drv->auth_bssid_))
params.bssid = drv->auth_bssid_;
if (drv->auth_ssid_len) {
params.ssid = drv->auth_ssid;
params.ssid_len = drv->auth_ssid_len;
}
params.ie = drv->auth_ie;
params.ie_len = drv->auth_ie_len;
params.auth_data = drv->auth_data;
params.auth_data_len = drv->auth_data_len;
for (i = 0; i < 4; i++) {
if (drv->auth_wep_key_len[i]) {
params.wep_key[i] = drv->auth_wep_key[i];
params.wep_key_len[i] = drv->auth_wep_key_len[i];
}
}
drv->retry_auth = 1;
return wpa_driver_nl80211_authenticate(bss, &params);
}
static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
size_t data_len, int noack,
unsigned int freq, int no_cck,
int offchanok,
unsigned int wait_time,
const u16 *csa_offs,
size_t csa_offs_len, int no_encrypt)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_mgmt *mgmt;
int encrypt = !no_encrypt;
u16 fc;
int use_cookie = 1;
int res;
mgmt = (struct ieee80211_mgmt *) data;
fc = le_to_host16(mgmt->frame_control);
wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da=" MACSTR
" noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u no_encrypt=%d fc=0x%x (%s) nlmode=%d",
MAC2STR(mgmt->da), noack, freq, no_cck, offchanok, wait_time,
no_encrypt, fc, fc2str(fc), drv->nlmode);
if ((is_sta_interface(drv->nlmode) ||
drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) &&
WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
/*
* The use of last_mgmt_freq is a bit of a hack,
* but it works due to the single-threaded nature
* of wpa_supplicant.
*/
if (freq == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d",
drv->last_mgmt_freq);
freq = drv->last_mgmt_freq;
}
wait_time = 0;
use_cookie = 0;
no_cck = 1;
offchanok = 1;
goto send_frame_cmd;
}
if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
if (freq == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d",
bss->freq);
freq = bss->freq;
}
if ((int) freq == bss->freq)
wait_time = 0;
goto send_frame_cmd;
}
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
/*
* Only one of the authentication frame types is encrypted.
* In order for static WEP encryption to work properly (i.e.,
* to not encrypt the frame), we need to tell mac80211 about
* the frames that must not be encrypted.
*/
u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3)
encrypt = 0;
}
if (is_sta_interface(drv->nlmode) &&
WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
if (freq == 0 &&
(drv->capa.flags & WPA_DRIVER_FLAGS_SAE) &&
!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
freq = nl80211_get_assoc_freq(drv);
wpa_printf(MSG_DEBUG,
"nl80211: send_mlme - Use assoc_freq=%u for external auth",
freq);
}
/* Allow off channel for PASN authentication */
if (data_len >= IEEE80211_HDRLEN + 2 &&
WPA_GET_LE16(data + IEEE80211_HDRLEN) == WLAN_AUTH_PASN &&
!offchanok) {
wpa_printf(MSG_DEBUG,
"nl80211: send_mlme: allow off channel for PASN");
offchanok = 1;
}
}
#ifdef CONFIG_PASN
if (is_sta_interface(drv->nlmode) &&
WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_DEAUTH) {
wpa_printf(MSG_DEBUG,
"nl80211: send_mlme: allow Deauthentication frame for PASN");
use_cookie = 0;
offchanok = 1;
goto send_frame_cmd;
}
#endif /* CONFIG_PASN */
if (freq == 0 && drv->nlmode == NL80211_IFTYPE_ADHOC) {
freq = nl80211_get_assoc_freq(drv);
wpa_printf(MSG_DEBUG,
"nl80211: send_mlme - Use assoc_freq=%u for IBSS",
freq);
}
if (freq == 0) {
wpa_printf(MSG_DEBUG, "nl80211: send_mlme - Use bss->freq=%u",
bss->freq);
freq = bss->freq;
}
- if (drv->use_monitor) {
+ if (drv->use_monitor && is_ap_interface(drv->nlmode)) {
wpa_printf(MSG_DEBUG,
"nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor",
freq, bss->freq);
return nl80211_send_monitor(drv, data, data_len, encrypt,
noack);
}
if (noack || WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
use_cookie = 0;
send_frame_cmd:
#ifdef CONFIG_TESTING_OPTIONS
if (no_encrypt && !encrypt && !drv->use_monitor) {
wpa_printf(MSG_DEBUG,
"nl80211: Request to send an unencrypted frame - use a monitor interface for this");
if (nl80211_create_monitor_interface(drv) < 0)
return -1;
res = nl80211_send_monitor(drv, data, data_len, encrypt,
noack);
nl80211_remove_monitor_interface(drv);
return res;
}
#endif /* CONFIG_TESTING_OPTIONS */
wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame_cmd");
res = nl80211_send_frame_cmd(bss, freq, wait_time, data, data_len,
use_cookie, no_cck, noack, offchanok,
csa_offs, csa_offs_len);
return res;
}
static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates)
{
u8 rates[NL80211_MAX_SUPP_RATES];
u8 rates_len = 0;
int i;
if (!basic_rates)
return 0;
for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
rates[rates_len++] = basic_rates[i] / 5;
return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
}
static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
int slot, int ht_opmode, int ap_isolate,
const int *basic_rates)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_BSS)) ||
(cts >= 0 &&
nla_put_u8(msg, NL80211_ATTR_BSS_CTS_PROT, cts)) ||
(preamble >= 0 &&
nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble)) ||
(slot >= 0 &&
nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot)) ||
(ht_opmode >= 0 &&
nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) ||
(ap_isolate >= 0 &&
nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)) ||
nl80211_put_basic_rates(msg, basic_rates)) {
nlmsg_free(msg);
return -ENOBUFS;
}
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
static int wpa_driver_nl80211_set_acl(void *priv,
struct hostapd_acl_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nl_msg *acl;
unsigned int i;
int ret;
size_t acl_nla_sz, acl_nlmsg_sz, nla_sz, nlmsg_sz;
if (!(drv->capa.max_acl_mac_addrs))
return -ENOTSUP;
if (params->num_mac_acl > drv->capa.max_acl_mac_addrs)
return -ENOTSUP;
wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
acl_nla_sz = nla_total_size(ETH_ALEN) * params->num_mac_acl;
acl_nlmsg_sz = nlmsg_total_size(acl_nla_sz);
acl = nlmsg_alloc_size(acl_nlmsg_sz);
if (!acl)
return -ENOMEM;
for (i = 0; i < params->num_mac_acl; i++) {
if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
nlmsg_free(acl);
return -ENOMEM;
}
}
/*
* genetlink message header (Length of user header is 0) +
* u32 attr: NL80211_ATTR_IFINDEX +
* u32 attr: NL80211_ATTR_ACL_POLICY +
* nested acl attr
*/
nla_sz = GENL_HDRLEN +
nla_total_size(4) * 2 +
nla_total_size(acl_nla_sz);
nlmsg_sz = nlmsg_total_size(nla_sz);
if (!(msg = nl80211_ifindex_msg_build(drv, nlmsg_alloc_size(nlmsg_sz),
drv->ifindex, 0,
NL80211_CMD_SET_MAC_ACL)) ||
nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) ||
nla_put_nested(msg, NL80211_ATTR_MAC_ADDRS, acl)) {
nlmsg_free(msg);
nlmsg_free(acl);
return -ENOMEM;
}
nlmsg_free(acl);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)",
ret, strerror(-ret));
}
return ret;
}
static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int)
{
if (beacon_int > 0) {
wpa_printf(MSG_DEBUG, " * beacon_int=%d", beacon_int);
return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
beacon_int);
}
return 0;
}
static int nl80211_put_dtim_period(struct nl_msg *msg, int dtim_period)
{
if (dtim_period > 0) {
wpa_printf(MSG_DEBUG, " * dtim_period=%d", dtim_period);
return nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
}
return 0;
}
#ifdef CONFIG_MESH
static int nl80211_set_mesh_config(void *priv,
struct wpa_driver_mesh_bss_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MESH_CONFIG);
if (!msg)
return -1;
ret = nl80211_put_mesh_config(msg, params);
if (ret < 0) {
nlmsg_free(msg);
return ret;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: Mesh config set failed: %d (%s)",
ret, strerror(-ret));
return ret;
}
return 0;
}
#endif /* CONFIG_MESH */
static int nl80211_put_beacon_rate(struct nl_msg *msg, u64 flags, u64 flags2,
struct wpa_driver_ap_params *params)
{
struct nlattr *bands, *band;
struct nl80211_txrate_vht vht_rate;
struct nl80211_txrate_he he_rate;
if (!params->freq ||
(params->beacon_rate == 0 &&
params->rate_type == BEACON_RATE_LEGACY))
return 0;
bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
if (!bands)
return -1;
switch (params->freq->mode) {
case HOSTAPD_MODE_IEEE80211B:
case HOSTAPD_MODE_IEEE80211G:
band = nla_nest_start(msg, NL80211_BAND_2GHZ);
break;
case HOSTAPD_MODE_IEEE80211A:
if (is_6ghz_freq(params->freq->freq))
band = nla_nest_start(msg, NL80211_BAND_6GHZ);
else
band = nla_nest_start(msg, NL80211_BAND_5GHZ);
break;
case HOSTAPD_MODE_IEEE80211AD:
band = nla_nest_start(msg, NL80211_BAND_60GHZ);
break;
default:
return 0;
}
if (!band)
return -1;
os_memset(&vht_rate, 0, sizeof(vht_rate));
os_memset(&he_rate, 0, sizeof(he_rate));
switch (params->rate_type) {
case BEACON_RATE_LEGACY:
if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY)) {
wpa_printf(MSG_INFO,
"nl80211: Driver does not support setting Beacon frame rate (legacy)");
return -1;
}
if (nla_put_u8(msg, NL80211_TXRATE_LEGACY,
(u8) params->beacon_rate / 5) ||
nla_put(msg, NL80211_TXRATE_HT, 0, NULL) ||
(params->freq->vht_enabled &&
nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
&vht_rate)))
return -1;
wpa_printf(MSG_DEBUG, " * beacon_rate = legacy:%u (* 100 kbps)",
params->beacon_rate);
break;
case BEACON_RATE_HT:
if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_HT)) {
wpa_printf(MSG_INFO,
"nl80211: Driver does not support setting Beacon frame rate (HT)");
return -1;
}
if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) ||
nla_put_u8(msg, NL80211_TXRATE_HT, params->beacon_rate) ||
(params->freq->vht_enabled &&
nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
&vht_rate)))
return -1;
wpa_printf(MSG_DEBUG, " * beacon_rate = HT-MCS %u",
params->beacon_rate);
break;
case BEACON_RATE_VHT:
if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_VHT)) {
wpa_printf(MSG_INFO,
"nl80211: Driver does not support setting Beacon frame rate (VHT)");
return -1;
}
vht_rate.mcs[0] = BIT(params->beacon_rate);
if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL))
return -1;
if (nla_put(msg, NL80211_TXRATE_HT, 0, NULL))
return -1;
if (nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
&vht_rate))
return -1;
wpa_printf(MSG_DEBUG, " * beacon_rate = VHT-MCS %u",
params->beacon_rate);
break;
case BEACON_RATE_HE:
if (!(flags2 & WPA_DRIVER_FLAGS2_BEACON_RATE_HE)) {
wpa_printf(MSG_INFO,
"nl80211: Driver does not support setting Beacon frame rate (HE)");
return -1;
}
he_rate.mcs[0] = BIT(params->beacon_rate);
if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) ||
nla_put(msg, NL80211_TXRATE_HT, 0, NULL) ||
nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
&vht_rate) ||
nla_put(msg, NL80211_TXRATE_HE, sizeof(he_rate), &he_rate))
return -1;
wpa_printf(MSG_DEBUG, " * beacon_rate = HE-MCS %u",
params->beacon_rate);
break;
}
nla_nest_end(msg, band);
nla_nest_end(msg, bands);
return 0;
}
static int nl80211_set_multicast_to_unicast(struct i802_bss *bss,
int multicast_to_unicast)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MULTICAST_TO_UNICAST);
if (!msg ||
(multicast_to_unicast &&
nla_put_flag(msg, NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED))) {
wpa_printf(MSG_ERROR,
"nl80211: Failed to build NL80211_CMD_SET_MULTICAST_TO_UNICAST msg for %s",
bss->ifname);
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
switch (ret) {
case 0:
wpa_printf(MSG_DEBUG,
"nl80211: multicast to unicast %s on interface %s",
multicast_to_unicast ? "enabled" : "disabled",
bss->ifname);
break;
case -EOPNOTSUPP:
if (!multicast_to_unicast)
break;
wpa_printf(MSG_INFO,
"nl80211: multicast to unicast not supported on interface %s",
bss->ifname);
break;
default:
wpa_printf(MSG_ERROR,
"nl80211: %s multicast to unicast failed with %d (%s) on interface %s",
multicast_to_unicast ? "enabling" : "disabling",
ret, strerror(-ret), bss->ifname);
break;
}
return ret;
}
#ifdef CONFIG_SAE
static int nl80211_put_sae_pwe(struct nl_msg *msg, int pwe)
{
u8 sae_pwe;
wpa_printf(MSG_DEBUG, "nl802111: sae_pwe=%d", pwe);
if (pwe == 0)
sae_pwe = NL80211_SAE_PWE_HUNT_AND_PECK;
else if (pwe == 1)
sae_pwe = NL80211_SAE_PWE_HASH_TO_ELEMENT;
else if (pwe == 2)
sae_pwe = NL80211_SAE_PWE_BOTH;
else if (pwe == 3)
return 0; /* special test mode */
else
return -1;
if (nla_put_u8(msg, NL80211_ATTR_SAE_PWE, sae_pwe))
return -1;
return 0;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
static int nl80211_fils_discovery(struct i802_bss *bss, struct nl_msg *msg,
struct wpa_driver_ap_params *params)
{
struct nlattr *attr;
if (!bss->drv->fils_discovery) {
wpa_printf(MSG_ERROR,
"nl80211: Driver does not support FILS Discovery frame transmission for %s",
bss->ifname);
return -1;
}
attr = nla_nest_start(msg, NL80211_ATTR_FILS_DISCOVERY);
if (!attr ||
nla_put_u32(msg, NL80211_FILS_DISCOVERY_ATTR_INT_MIN,
params->fd_min_int) ||
nla_put_u32(msg, NL80211_FILS_DISCOVERY_ATTR_INT_MAX,
params->fd_max_int) ||
(params->fd_frame_tmpl &&
nla_put(msg, NL80211_FILS_DISCOVERY_ATTR_TMPL,
params->fd_frame_tmpl_len, params->fd_frame_tmpl)))
return -1;
nla_nest_end(msg, attr);
return 0;
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211AX
static int nl80211_unsol_bcast_probe_resp(struct i802_bss *bss,
struct nl_msg *msg,
struct wpa_driver_ap_params *params)
{
struct nlattr *attr;
if (!bss->drv->unsol_bcast_probe_resp) {
wpa_printf(MSG_ERROR,
"nl80211: Driver does not support unsolicited broadcast Probe Response frame transmission for %s",
bss->ifname);
return -1;
}
wpa_printf(MSG_DEBUG,
"nl80211: Unsolicited broadcast Probe Response frame interval: %u",
params->unsol_bcast_probe_resp_interval);
attr = nla_nest_start(msg, NL80211_ATTR_UNSOL_BCAST_PROBE_RESP);
if (!attr ||
nla_put_u32(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT,
params->unsol_bcast_probe_resp_interval) ||
(params->unsol_bcast_probe_resp_tmpl &&
nla_put(msg, NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL,
params->unsol_bcast_probe_resp_tmpl_len,
params->unsol_bcast_probe_resp_tmpl)))
return -1;
nla_nest_end(msg, attr);
return 0;
}
#endif /* CONFIG_IEEE80211AX */
static int wpa_driver_nl80211_set_ap(void *priv,
struct wpa_driver_ap_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
u8 cmd = NL80211_CMD_NEW_BEACON;
int ret = -ENOBUFS;
int beacon_set;
int num_suites;
u32 suites[20], suite;
u32 ver;
#ifdef CONFIG_MESH
struct wpa_driver_mesh_bss_params mesh_params;
#endif /* CONFIG_MESH */
beacon_set = params->reenable ? 0 : bss->beacon_set;
wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
beacon_set);
if (beacon_set)
cmd = NL80211_CMD_SET_BEACON;
else if (!drv->device_ap_sme && !drv->use_monitor &&
!nl80211_get_wiphy_data_ap(bss))
return -ENOBUFS;
wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
params->head, params->head_len);
wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail",
params->tail, params->tail_len);
wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex);
wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int);
wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate);
wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type);
wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period);
wpa_printf(MSG_DEBUG, "nl80211: ssid=%s",
wpa_ssid_txt(params->ssid, params->ssid_len));
if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len,
params->head) ||
nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
params->tail) ||
nl80211_put_beacon_int(msg, params->beacon_int) ||
nl80211_put_beacon_rate(msg, drv->capa.flags, drv->capa.flags2,
params) ||
nl80211_put_dtim_period(msg, params->dtim_period) ||
nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
goto fail;
if (params->proberesp && params->proberesp_len) {
wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)",
params->proberesp, params->proberesp_len);
if (nla_put(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len,
params->proberesp))
goto fail;
}
switch (params->hide_ssid) {
case NO_SSID_HIDING:
wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use");
if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
NL80211_HIDDEN_SSID_NOT_IN_USE))
goto fail;
break;
case HIDDEN_SSID_ZERO_LEN:
wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len");
if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
NL80211_HIDDEN_SSID_ZERO_LEN))
goto fail;
break;
case HIDDEN_SSID_ZERO_CONTENTS:
wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents");
if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
NL80211_HIDDEN_SSID_ZERO_CONTENTS))
goto fail;
break;
}
wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy);
if (params->privacy &&
nla_put_flag(msg, NL80211_ATTR_PRIVACY))
goto fail;
wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs);
if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) ==
(WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) {
/* Leave out the attribute */
} else if (params->auth_algs & WPA_AUTH_ALG_SHARED) {
if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
NL80211_AUTHTYPE_SHARED_KEY))
goto fail;
} else {
if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
NL80211_AUTHTYPE_OPEN_SYSTEM))
goto fail;
}
wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version);
ver = 0;
if (params->wpa_version & WPA_PROTO_WPA)
ver |= NL80211_WPA_VERSION_1;
if (params->wpa_version & WPA_PROTO_RSN)
ver |= NL80211_WPA_VERSION_2;
if (ver &&
nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
goto fail;
wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x",
params->key_mgmt_suites);
num_suites = wpa_key_mgmt_to_suites(params->key_mgmt_suites,
suites, ARRAY_SIZE(suites));
if (num_suites > NL80211_MAX_NR_AKM_SUITES)
wpa_printf(MSG_DEBUG,
"nl80211: Not enough room for all AKM suites (num_suites=%d > NL80211_MAX_NR_AKM_SUITES)",
num_suites);
else if (num_suites &&
nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
suites))
goto fail;
if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
(!params->pairwise_ciphers ||
params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) &&
(nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
goto fail;
if (drv->device_ap_sme &&
(params->key_mgmt_suites & WPA_KEY_MGMT_SAE) &&
nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
goto fail;
wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
params->pairwise_ciphers);
num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers,
suites, ARRAY_SIZE(suites));
if (num_suites &&
nla_put(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
num_suites * sizeof(u32), suites))
goto fail;
wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x",
params->group_cipher);
suite = wpa_cipher_to_cipher_suite(params->group_cipher);
if (suite &&
nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite))
goto fail;
if (params->beacon_ies) {
wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies",
params->beacon_ies);
if (nla_put(msg, NL80211_ATTR_IE,
wpabuf_len(params->beacon_ies),
wpabuf_head(params->beacon_ies)))
goto fail;
}
if (params->proberesp_ies) {
wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies",
params->proberesp_ies);
if (nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
wpabuf_len(params->proberesp_ies),
wpabuf_head(params->proberesp_ies)))
goto fail;
}
if (params->assocresp_ies) {
wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies",
params->assocresp_ies);
if (nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
wpabuf_len(params->assocresp_ies),
wpabuf_head(params->assocresp_ies)))
goto fail;
}
if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER) {
wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d",
params->ap_max_inactivity);
if (nla_put_u16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT,
params->ap_max_inactivity))
goto fail;
}
#ifdef CONFIG_P2P
if (params->p2p_go_ctwindow > 0) {
if (drv->p2p_go_ctwindow_supported) {
wpa_printf(MSG_DEBUG, "nl80211: P2P GO ctwindow=%d",
params->p2p_go_ctwindow);
if (nla_put_u8(msg, NL80211_ATTR_P2P_CTWINDOW,
params->p2p_go_ctwindow))
goto fail;
} else {
wpa_printf(MSG_INFO,
"nl80211: Driver does not support CTWindow configuration - ignore this parameter");
}
}
#endif /* CONFIG_P2P */
if (params->pbss) {
wpa_printf(MSG_DEBUG, "nl80211: PBSS");
if (nla_put_flag(msg, NL80211_ATTR_PBSS))
goto fail;
}
if (params->ftm_responder) {
struct nlattr *ftm;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_FTM_RESPONDER)) {
ret = -ENOTSUP;
goto fail;
}
ftm = nla_nest_start(msg, NL80211_ATTR_FTM_RESPONDER);
if (!ftm ||
nla_put_flag(msg, NL80211_FTM_RESP_ATTR_ENABLED) ||
(params->lci &&
nla_put(msg, NL80211_FTM_RESP_ATTR_LCI,
wpabuf_len(params->lci),
wpabuf_head(params->lci))) ||
(params->civic &&
nla_put(msg, NL80211_FTM_RESP_ATTR_CIVICLOC,
wpabuf_len(params->civic),
wpabuf_head(params->civic))))
goto fail;
nla_nest_end(msg, ftm);
}
#ifdef CONFIG_IEEE80211AX
if (params->he_spr_ctrl) {
struct nlattr *spr;
spr = nla_nest_start(msg, NL80211_ATTR_HE_OBSS_PD);
wpa_printf(MSG_DEBUG, "nl80211: he_spr_ctrl=0x%x",
params->he_spr_ctrl);
if (!spr ||
nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_SR_CTRL,
params->he_spr_ctrl) ||
((params->he_spr_ctrl &
SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) &&
nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET,
params->he_spr_non_srg_obss_pd_max_offset)))
goto fail;
if ((params->he_spr_ctrl &
SPATIAL_REUSE_SRG_INFORMATION_PRESENT) &&
(nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET,
params->he_spr_srg_obss_pd_min_offset) ||
nla_put_u8(msg, NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET,
params->he_spr_srg_obss_pd_max_offset) ||
nla_put(msg, NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP,
sizeof(params->he_spr_bss_color_bitmap),
params->he_spr_bss_color_bitmap) ||
nla_put(msg, NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP,
sizeof(params->he_spr_partial_bssid_bitmap),
params->he_spr_partial_bssid_bitmap)))
goto fail;
nla_nest_end(msg, spr);
}
if (params->freq && params->freq->he_enabled) {
struct nlattr *bss_color;
bss_color = nla_nest_start(msg, NL80211_ATTR_HE_BSS_COLOR);
if (!bss_color ||
(params->he_bss_color_disabled &&
nla_put_flag(msg, NL80211_HE_BSS_COLOR_ATTR_DISABLED)) ||
(params->he_bss_color_partial &&
nla_put_flag(msg, NL80211_HE_BSS_COLOR_ATTR_PARTIAL)) ||
nla_put_u8(msg, NL80211_HE_BSS_COLOR_ATTR_COLOR,
params->he_bss_color))
goto fail;
nla_nest_end(msg, bss_color);
}
if (params->twt_responder) {
wpa_printf(MSG_DEBUG, "nl80211: twt_responder=%d",
params->twt_responder);
if (nla_put_flag(msg, NL80211_ATTR_TWT_RESPONDER))
goto fail;
}
if (params->unsol_bcast_probe_resp_interval &&
nl80211_unsol_bcast_probe_resp(bss, msg, params) < 0)
goto fail;
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_SAE
if (((params->key_mgmt_suites & WPA_KEY_MGMT_SAE) ||
(params->key_mgmt_suites & WPA_KEY_MGMT_FT_SAE)) &&
nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
goto fail;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
if (params->fd_max_int && nl80211_fils_discovery(bss, msg, params) < 0)
goto fail;
#endif /* CONFIG_FILS */
ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
ret, strerror(-ret));
} else {
bss->beacon_set = 1;
nl80211_set_bss(bss, params->cts_protect, params->preamble,
params->short_slot_time, params->ht_opmode,
params->isolate, params->basic_rates);
nl80211_set_multicast_to_unicast(bss,
params->multicast_to_unicast);
if (beacon_set && params->freq &&
params->freq->bandwidth != bss->bandwidth) {
wpa_printf(MSG_DEBUG,
"nl80211: Update BSS %s bandwidth: %d -> %d",
bss->ifname, bss->bandwidth,
params->freq->bandwidth);
ret = nl80211_set_channel(bss, params->freq, 1);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Frequency set failed: %d (%s)",
ret, strerror(-ret));
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Frequency set succeeded for ht2040 coex");
bss->bandwidth = params->freq->bandwidth;
}
} else if (!beacon_set && params->freq) {
/*
* cfg80211 updates the driver on frequence change in AP
* mode only at the point when beaconing is started, so
* set the initial value here.
*/
bss->bandwidth = params->freq->bandwidth;
}
}
#ifdef CONFIG_MESH
if (is_mesh_interface(drv->nlmode) && params->ht_opmode != -1) {
os_memset(&mesh_params, 0, sizeof(mesh_params));
mesh_params.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE;
mesh_params.ht_opmode = params->ht_opmode;
ret = nl80211_set_mesh_config(priv, &mesh_params);
if (ret < 0)
return ret;
}
#endif /* CONFIG_MESH */
return ret;
fail:
nlmsg_free(msg);
return ret;
}
static int nl80211_put_freq_params(struct nl_msg *msg,
const struct hostapd_freq_params *freq)
{
enum hostapd_hw_mode hw_mode;
int is_24ghz;
u8 channel;
wpa_printf(MSG_DEBUG, " * freq=%d", freq->freq);
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
return -ENOBUFS;
wpa_printf(MSG_DEBUG, " * he_enabled=%d", freq->he_enabled);
wpa_printf(MSG_DEBUG, " * vht_enabled=%d", freq->vht_enabled);
wpa_printf(MSG_DEBUG, " * ht_enabled=%d", freq->ht_enabled);
hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
hw_mode == HOSTAPD_MODE_IEEE80211B;
if (freq->vht_enabled || (freq->he_enabled && !is_24ghz)) {
enum nl80211_chan_width cw;
wpa_printf(MSG_DEBUG, " * bandwidth=%d", freq->bandwidth);
switch (freq->bandwidth) {
case 20:
cw = NL80211_CHAN_WIDTH_20;
break;
case 40:
cw = NL80211_CHAN_WIDTH_40;
break;
case 80:
if (freq->center_freq2)
cw = NL80211_CHAN_WIDTH_80P80;
else
cw = NL80211_CHAN_WIDTH_80;
break;
case 160:
cw = NL80211_CHAN_WIDTH_160;
break;
default:
return -EINVAL;
}
wpa_printf(MSG_DEBUG, " * channel_width=%d", cw);
wpa_printf(MSG_DEBUG, " * center_freq1=%d",
freq->center_freq1);
wpa_printf(MSG_DEBUG, " * center_freq2=%d",
freq->center_freq2);
if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1,
freq->center_freq1) ||
(freq->center_freq2 &&
nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2,
freq->center_freq2)))
return -ENOBUFS;
} else if (freq->ht_enabled) {
enum nl80211_channel_type ct;
wpa_printf(MSG_DEBUG, " * sec_channel_offset=%d",
freq->sec_channel_offset);
switch (freq->sec_channel_offset) {
case -1:
ct = NL80211_CHAN_HT40MINUS;
break;
case 1:
ct = NL80211_CHAN_HT40PLUS;
break;
default:
ct = NL80211_CHAN_HT20;
break;
}
wpa_printf(MSG_DEBUG, " * channel_type=%d", ct);
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
return -ENOBUFS;
} else if (freq->edmg.channels && freq->edmg.bw_config) {
wpa_printf(MSG_DEBUG,
" * EDMG configuration: channels=0x%x bw_config=%d",
freq->edmg.channels, freq->edmg.bw_config);
if (nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_CHANNELS,
freq->edmg.channels) ||
nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
freq->edmg.bw_config))
return -1;
} else {
wpa_printf(MSG_DEBUG, " * channel_type=%d",
NL80211_CHAN_NO_HT);
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
NL80211_CHAN_NO_HT))
return -ENOBUFS;
}
return 0;
}
static int nl80211_set_channel(struct i802_bss *bss,
struct hostapd_freq_params *freq, int set_chan)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
wpa_printf(MSG_DEBUG,
"nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
freq->freq, freq->ht_enabled, freq->vht_enabled, freq->he_enabled,
freq->bandwidth, freq->center_freq1, freq->center_freq2);
msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
NL80211_CMD_SET_WIPHY);
if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
nlmsg_free(msg);
return -1;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret == 0) {
bss->freq = freq->freq;
return 0;
}
wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
"%d (%s)", freq->freq, ret, strerror(-ret));
return -1;
}
static u32 sta_flags_nl80211(int flags)
{
u32 f = 0;
if (flags & WPA_STA_AUTHORIZED)
f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
if (flags & WPA_STA_WMM)
f |= BIT(NL80211_STA_FLAG_WME);
if (flags & WPA_STA_SHORT_PREAMBLE)
f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
if (flags & WPA_STA_MFP)
f |= BIT(NL80211_STA_FLAG_MFP);
if (flags & WPA_STA_TDLS_PEER)
f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
if (flags & WPA_STA_AUTHENTICATED)
f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
if (flags & WPA_STA_ASSOCIATED)
f |= BIT(NL80211_STA_FLAG_ASSOCIATED);
return f;
}
#ifdef CONFIG_MESH
static u32 sta_plink_state_nl80211(enum mesh_plink_state state)
{
switch (state) {
case PLINK_IDLE:
return NL80211_PLINK_LISTEN;
case PLINK_OPN_SNT:
return NL80211_PLINK_OPN_SNT;
case PLINK_OPN_RCVD:
return NL80211_PLINK_OPN_RCVD;
case PLINK_CNF_RCVD:
return NL80211_PLINK_CNF_RCVD;
case PLINK_ESTAB:
return NL80211_PLINK_ESTAB;
case PLINK_HOLDING:
return NL80211_PLINK_HOLDING;
case PLINK_BLOCKED:
return NL80211_PLINK_BLOCKED;
default:
wpa_printf(MSG_ERROR, "nl80211: Invalid mesh plink state %d",
state);
}
return -1;
}
#endif /* CONFIG_MESH */
static int wpa_driver_nl80211_sta_add(void *priv,
struct hostapd_sta_add_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nl80211_sta_flag_update upd;
int ret = -ENOBUFS;
if ((params->flags & WPA_STA_TDLS_PEER) &&
!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
return -EOPNOTSUPP;
wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR,
params->set ? "Set" : "Add", MAC2STR(params->addr));
msg = nl80211_bss_msg(bss, 0, params->set ? NL80211_CMD_SET_STATION :
NL80211_CMD_NEW_STATION);
if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
goto fail;
/*
* Set the below properties only in one of the following cases:
* 1. New station is added, already associated.
* 2. Set WPA_STA_TDLS_PEER station.
* 3. Set an already added unassociated station, if driver supports
* full AP client state. (Set these properties after station became
* associated will be rejected by the driver).
*/
if (!params->set || (params->flags & WPA_STA_TDLS_PEER) ||
(params->set && FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
(params->flags & WPA_STA_ASSOCIATED))) {
wpa_hexdump(MSG_DEBUG, " * supported rates",
params->supp_rates, params->supp_rates_len);
wpa_printf(MSG_DEBUG, " * capability=0x%x",
params->capability);
if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
params->supp_rates_len, params->supp_rates) ||
nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY,
params->capability))
goto fail;
if (params->ht_capabilities) {
wpa_hexdump(MSG_DEBUG, " * ht_capabilities",
(u8 *) params->ht_capabilities,
sizeof(*params->ht_capabilities));
if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY,
sizeof(*params->ht_capabilities),
params->ht_capabilities))
goto fail;
}
if (params->vht_capabilities) {
wpa_hexdump(MSG_DEBUG, " * vht_capabilities",
(u8 *) params->vht_capabilities,
sizeof(*params->vht_capabilities));
if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY,
sizeof(*params->vht_capabilities),
params->vht_capabilities))
goto fail;
}
if (params->he_capab) {
wpa_hexdump(MSG_DEBUG, " * he_capab",
params->he_capab, params->he_capab_len);
if (nla_put(msg, NL80211_ATTR_HE_CAPABILITY,
params->he_capab_len, params->he_capab))
goto fail;
}
if (params->he_6ghz_capab) {
wpa_hexdump(MSG_DEBUG, " * he_6ghz_capab",
params->he_6ghz_capab,
sizeof(*params->he_6ghz_capab));
if (nla_put(msg, NL80211_ATTR_HE_6GHZ_CAPABILITY,
sizeof(*params->he_6ghz_capab),
params->he_6ghz_capab))
goto fail;
}
if (params->ext_capab) {
wpa_hexdump(MSG_DEBUG, " * ext_capab",
params->ext_capab, params->ext_capab_len);
if (nla_put(msg, NL80211_ATTR_STA_EXT_CAPABILITY,
params->ext_capab_len, params->ext_capab))
goto fail;
}
if (is_ap_interface(drv->nlmode) &&
nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS,
params->support_p2p_ps ?
NL80211_P2P_PS_SUPPORTED :
NL80211_P2P_PS_UNSUPPORTED))
goto fail;
}
if (!params->set) {
if (params->aid) {
wpa_printf(MSG_DEBUG, " * aid=%u", params->aid);
if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid))
goto fail;
} else {
/*
* cfg80211 validates that AID is non-zero, so we have
* to make this a non-zero value for the TDLS case where
- * a dummy STA entry is used for now and for a station
+ * a stub STA entry is used for now and for a station
* that is still not associated.
*/
wpa_printf(MSG_DEBUG, " * aid=1 (%s workaround)",
(params->flags & WPA_STA_TDLS_PEER) ?
"TDLS" : "UNASSOC_STA");
if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1))
goto fail;
}
wpa_printf(MSG_DEBUG, " * listen_interval=%u",
params->listen_interval);
if (nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
params->listen_interval))
goto fail;
} else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) {
wpa_printf(MSG_DEBUG, " * peer_aid=%u", params->aid);
if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid))
goto fail;
} else if (FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
(params->flags & WPA_STA_ASSOCIATED)) {
wpa_printf(MSG_DEBUG, " * aid=%u", params->aid);
wpa_printf(MSG_DEBUG, " * listen_interval=%u",
params->listen_interval);
if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid) ||
nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
params->listen_interval))
goto fail;
}
if (params->vht_opmode_enabled) {
wpa_printf(MSG_DEBUG, " * opmode=%u", params->vht_opmode);
if (nla_put_u8(msg, NL80211_ATTR_OPMODE_NOTIF,
params->vht_opmode))
goto fail;
}
if (params->supp_channels) {
wpa_hexdump(MSG_DEBUG, " * supported channels",
params->supp_channels, params->supp_channels_len);
if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS,
params->supp_channels_len, params->supp_channels))
goto fail;
}
if (params->supp_oper_classes) {
wpa_hexdump(MSG_DEBUG, " * supported operating classes",
params->supp_oper_classes,
params->supp_oper_classes_len);
if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
params->supp_oper_classes_len,
params->supp_oper_classes))
goto fail;
}
os_memset(&upd, 0, sizeof(upd));
upd.set = sta_flags_nl80211(params->flags);
upd.mask = upd.set | sta_flags_nl80211(params->flags_mask);
/*
* If the driver doesn't support full AP client state, ignore ASSOC/AUTH
* flags, as nl80211 driver moves a new station, by default, into
* associated state.
*
* On the other hand, if the driver supports that feature and the
* station is added in unauthenticated state, set the
* authenticated/associated bits in the mask to prevent moving this
* station to associated state before it is actually associated.
*
* This is irrelevant for mesh mode where the station is added to the
* driver as authenticated already, and ASSOCIATED isn't part of the
* nl80211 API.
*/
if (!is_mesh_interface(drv->nlmode)) {
if (!FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) {
wpa_printf(MSG_DEBUG,
"nl80211: Ignore ASSOC/AUTH flags since driver doesn't support full AP client state");
upd.mask &= ~(BIT(NL80211_STA_FLAG_ASSOCIATED) |
BIT(NL80211_STA_FLAG_AUTHENTICATED));
} else if (!params->set &&
!(params->flags & WPA_STA_TDLS_PEER)) {
if (!(params->flags & WPA_STA_AUTHENTICATED))
upd.mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
if (!(params->flags & WPA_STA_ASSOCIATED))
upd.mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
}
#ifdef CONFIG_MESH
} else {
if (params->plink_state == PLINK_ESTAB && params->peer_aid) {
ret = nla_put_u16(msg, NL80211_ATTR_MESH_PEER_AID,
params->peer_aid);
if (ret)
goto fail;
}
#endif /* CONFIG_MESH */
}
wpa_printf(MSG_DEBUG, " * flags set=0x%x mask=0x%x",
upd.set, upd.mask);
if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
goto fail;
#ifdef CONFIG_MESH
if (params->plink_state &&
nla_put_u8(msg, NL80211_ATTR_STA_PLINK_STATE,
sta_plink_state_nl80211(params->plink_state)))
goto fail;
#endif /* CONFIG_MESH */
if ((!params->set || (params->flags & WPA_STA_TDLS_PEER) ||
FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) &&
(params->flags & WPA_STA_WMM)) {
struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo);
if (!wme ||
nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES,
params->qosinfo & WMM_QOSINFO_STA_AC_MASK) ||
nla_put_u8(msg, NL80211_STA_WME_MAX_SP,
(params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) &
WMM_QOSINFO_STA_SP_MASK))
goto fail;
nla_nest_end(msg, wme);
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
"result: %d (%s)", params->set ? "SET" : "NEW", ret,
strerror(-ret));
if (ret == -EEXIST)
ret = 0;
fail:
nlmsg_free(msg);
return ret;
}
static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
{
#ifdef CONFIG_LIBNL3_ROUTE
struct wpa_driver_nl80211_data *drv = bss->drv;
struct rtnl_neigh *rn;
struct nl_addr *nl_addr;
int err;
rn = rtnl_neigh_alloc();
if (!rn)
return;
rtnl_neigh_set_family(rn, AF_BRIDGE);
rtnl_neigh_set_ifindex(rn, bss->ifindex);
nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
if (!nl_addr) {
rtnl_neigh_put(rn);
return;
}
rtnl_neigh_set_lladdr(rn, nl_addr);
err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
if (err < 0) {
wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
bss->ifindex, nl_geterror(err));
} else {
wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry for "
MACSTR, MAC2STR(addr));
}
nl_addr_put(nl_addr);
rtnl_neigh_put(rn);
#endif /* CONFIG_LIBNL3_ROUTE */
}
static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr,
int deauth, u16 reason_code)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
(deauth == 0 &&
nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
WLAN_FC_STYPE_DISASSOC)) ||
(deauth == 1 &&
nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
WLAN_FC_STYPE_DEAUTH)) ||
(reason_code &&
nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code))) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR
" --> %d (%s)",
bss->ifname, MAC2STR(addr), ret, strerror(-ret));
if (drv->rtnl_sk)
rtnl_neigh_delete_fdb_entry(bss, addr);
if (ret == -ENOENT)
return 0;
return ret;
}
void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx)
{
struct nl_msg *msg;
struct wpa_driver_nl80211_data *drv2;
wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
/* stop listening for EAPOL on this interface */
dl_list_for_each(drv2, &drv->global->interfaces,
struct wpa_driver_nl80211_data, list)
{
del_ifidx(drv2, ifidx, IFIDX_ANY);
/* Remove all bridges learned for this iface */
del_ifidx(drv2, IFIDX_ANY, ifidx);
}
msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
if (send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL) == 0)
return;
wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
}
const char * nl80211_iftype_str(enum nl80211_iftype mode)
{
switch (mode) {
case NL80211_IFTYPE_ADHOC:
return "ADHOC";
case NL80211_IFTYPE_STATION:
return "STATION";
case NL80211_IFTYPE_AP:
return "AP";
case NL80211_IFTYPE_AP_VLAN:
return "AP_VLAN";
case NL80211_IFTYPE_WDS:
return "WDS";
case NL80211_IFTYPE_MONITOR:
return "MONITOR";
case NL80211_IFTYPE_MESH_POINT:
return "MESH_POINT";
case NL80211_IFTYPE_P2P_CLIENT:
return "P2P_CLIENT";
case NL80211_IFTYPE_P2P_GO:
return "P2P_GO";
case NL80211_IFTYPE_P2P_DEVICE:
return "P2P_DEVICE";
case NL80211_IFTYPE_OCB:
return "OCB";
case NL80211_IFTYPE_NAN:
return "NAN";
default:
return "unknown";
}
}
static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
const char *ifname,
enum nl80211_iftype iftype,
const u8 *addr, int wds,
int (*handler)(struct nl_msg *, void *),
void *arg)
{
struct nl_msg *msg;
int ifidx;
int ret = -ENOBUFS;
wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)",
iftype, nl80211_iftype_str(iftype));
msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_NEW_INTERFACE);
if (!msg ||
nla_put_string(msg, NL80211_ATTR_IFNAME, ifname) ||
nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype))
goto fail;
if (iftype == NL80211_IFTYPE_MONITOR) {
struct nlattr *flags;
flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS);
if (!flags ||
nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES))
goto fail;
nla_nest_end(msg, flags);
} else if (wds) {
if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
goto fail;
}
/*
* Tell cfg80211 that the interface belongs to the socket that created
* it, and the interface should be deleted when the socket is closed.
*/
if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
goto fail;
if ((addr && iftype == NL80211_IFTYPE_P2P_DEVICE) &&
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
goto fail;
ret = send_and_recv_msgs(drv, msg, handler, arg, NULL, NULL);
msg = NULL;
if (ret) {
fail:
nlmsg_free(msg);
wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
ifname, ret, strerror(-ret));
return ret;
}
if (iftype == NL80211_IFTYPE_P2P_DEVICE)
return 0;
ifidx = if_nametoindex(ifname);
wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
ifname, ifidx);
if (ifidx <= 0)
return -1;
/*
* Some virtual interfaces need to process EAPOL packets and events on
* the parent interface. This is used mainly with hostapd.
*/
if (drv->hostapd ||
iftype == NL80211_IFTYPE_AP_VLAN ||
iftype == NL80211_IFTYPE_WDS ||
iftype == NL80211_IFTYPE_MONITOR) {
/* start listening for EAPOL on this interface */
add_ifidx(drv, ifidx, IFIDX_ANY);
}
if (addr && iftype != NL80211_IFTYPE_MONITOR &&
linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) {
nl80211_remove_iface(drv, ifidx);
return -1;
}
return ifidx;
}
int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
const char *ifname, enum nl80211_iftype iftype,
const u8 *addr, int wds,
int (*handler)(struct nl_msg *, void *),
void *arg, int use_existing)
{
int ret;
ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler,
arg);
/* if error occurred and interface exists already */
if (ret == -ENFILE && if_nametoindex(ifname)) {
if (use_existing) {
wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s",
ifname);
if (addr && iftype != NL80211_IFTYPE_MONITOR &&
linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
addr) < 0 &&
(linux_set_iface_flags(drv->global->ioctl_sock,
ifname, 0) < 0 ||
linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
addr) < 0 ||
linux_set_iface_flags(drv->global->ioctl_sock,
ifname, 1) < 0))
return -1;
return -ENFILE;
}
wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
/* Try to remove the interface that was already there. */
nl80211_remove_iface(drv, if_nametoindex(ifname));
/* Try to create the interface again */
ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
wds, handler, arg);
}
if (ret >= 0 && is_p2p_net_interface(iftype)) {
wpa_printf(MSG_DEBUG,
"nl80211: Interface %s created for P2P - disable 11b rates",
ifname);
nl80211_disable_11b_rates(drv, ret, 1);
}
return ret;
}
static int nl80211_setup_ap(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d",
bss->ifname, drv->device_ap_sme, drv->use_monitor);
/*
* Disable Probe Request reporting unless we need it in this way for
* devices that include the AP SME, in the other case (unless using
* monitor iface) we'll get it through the nl_mgmt socket instead.
*/
if (!drv->device_ap_sme)
wpa_driver_nl80211_probe_req_report(bss, 0);
if (!drv->device_ap_sme && !drv->use_monitor)
if (nl80211_mgmt_subscribe_ap(bss))
return -1;
if (drv->device_ap_sme && !drv->use_monitor)
if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
wpa_printf(MSG_DEBUG,
"nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it");
if (!drv->device_ap_sme && drv->use_monitor &&
nl80211_create_monitor_interface(drv) &&
!drv->device_ap_sme)
return -1;
if (drv->device_ap_sme &&
wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
"Probe Request frame reporting in AP mode");
/* Try to survive without this */
}
return 0;
}
static void nl80211_teardown_ap(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d",
bss->ifname, drv->device_ap_sme, drv->use_monitor);
if (drv->device_ap_sme) {
wpa_driver_nl80211_probe_req_report(bss, 0);
if (!drv->use_monitor)
nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
} else if (drv->use_monitor)
nl80211_remove_monitor_interface(drv);
else
nl80211_mgmt_unsubscribe(bss, "AP teardown");
nl80211_put_wiphy_data_ap(bss);
bss->beacon_set = 0;
}
static int nl80211_tx_control_port(void *priv, const u8 *dest,
u16 proto, const u8 *buf, size_t len,
int no_encrypt)
{
struct nl80211_ack_ext_arg ext_arg;
struct i802_bss *bss = priv;
struct nl_msg *msg;
u64 cookie = 0;
int ret;
wpa_printf(MSG_DEBUG,
"nl80211: Send over control port dest=" MACSTR
" proto=0x%04x len=%u no_encrypt=%d",
MAC2STR(dest), proto, (unsigned int) len, no_encrypt);
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CONTROL_PORT_FRAME);
if (!msg ||
nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, proto) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dest) ||
nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
(no_encrypt &&
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) {
nlmsg_free(msg);
return -ENOBUFS;
}
os_memset(&ext_arg, 0, sizeof(struct nl80211_ack_ext_arg));
ext_arg.ext_data = &cookie;
ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL,
ack_handler_cookie, &ext_arg);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: tx_control_port failed: ret=%d (%s)",
ret, strerror(-ret));
} else {
struct wpa_driver_nl80211_data *drv = bss->drv;
wpa_printf(MSG_DEBUG,
"nl80211: tx_control_port cookie=0x%llx",
(long long unsigned int) cookie);
drv->eapol_tx_cookie = cookie;
}
return ret;
}
static int nl80211_send_eapol_data(struct i802_bss *bss,
const u8 *addr, const u8 *data,
size_t data_len)
{
struct sockaddr_ll ll;
int ret;
if (bss->drv->eapol_tx_sock < 0) {
wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL");
return -1;
}
os_memset(&ll, 0, sizeof(ll));
ll.sll_family = AF_PACKET;
ll.sll_ifindex = bss->ifindex;
ll.sll_protocol = htons(ETH_P_PAE);
ll.sll_halen = ETH_ALEN;
os_memcpy(ll.sll_addr, addr, ETH_ALEN);
ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0,
(struct sockaddr *) &ll, sizeof(ll));
if (ret < 0)
wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s",
strerror(errno));
return ret;
}
static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
static int wpa_driver_nl80211_hapd_send_eapol(
void *priv, const u8 *addr, const u8 *data,
size_t data_len, int encrypt, const u8 *own_addr, u32 flags)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_hdr *hdr;
size_t len;
u8 *pos;
int res;
int qos = flags & WPA_STA_WMM;
/* For now, disable EAPOL TX over control port in AP mode by default
* since it does not provide TX status notifications. */
if (drv->control_port_ap &&
(drv->capa.flags & WPA_DRIVER_FLAGS_CONTROL_PORT))
return nl80211_tx_control_port(bss, addr, ETH_P_EAPOL,
data, data_len, !encrypt);
if (drv->device_ap_sme || !drv->use_monitor)
return nl80211_send_eapol_data(bss, addr, data, data_len);
len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
data_len;
hdr = os_zalloc(len);
if (hdr == NULL) {
wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)",
(unsigned long) len);
return -1;
}
hdr->frame_control =
IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
if (encrypt)
hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
if (qos) {
hdr->frame_control |=
host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
}
memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
pos = (u8 *) (hdr + 1);
if (qos) {
/* Set highest priority in QoS header */
pos[0] = 7;
pos[1] = 0;
pos += 2;
}
memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
pos += sizeof(rfc1042_header);
WPA_PUT_BE16(pos, ETH_P_PAE);
pos += 2;
memcpy(pos, data, data_len);
res = nl80211_send_monitor(drv, hdr, len, encrypt, 0);
if (res < 0) {
wpa_printf(MSG_ERROR,
"hapd_send_eapol - packet len: %lu - failed",
(unsigned long) len);
}
os_free(hdr);
return res;
}
static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
unsigned int total_flags,
unsigned int flags_or,
unsigned int flags_and)
{
struct i802_bss *bss = priv;
struct nl_msg *msg;
struct nlattr *flags;
struct nl80211_sta_flag_update upd;
wpa_printf(MSG_DEBUG, "nl80211: Set STA flags - ifname=%s addr=" MACSTR
" total_flags=0x%x flags_or=0x%x flags_and=0x%x authorized=%d",
bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and,
!!(total_flags & WPA_STA_AUTHORIZED));
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
goto fail;
/*
* Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
* can be removed eventually.
*/
flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS);
if (!flags ||
((total_flags & WPA_STA_AUTHORIZED) &&
nla_put_flag(msg, NL80211_STA_FLAG_AUTHORIZED)) ||
((total_flags & WPA_STA_WMM) &&
nla_put_flag(msg, NL80211_STA_FLAG_WME)) ||
((total_flags & WPA_STA_SHORT_PREAMBLE) &&
nla_put_flag(msg, NL80211_STA_FLAG_SHORT_PREAMBLE)) ||
((total_flags & WPA_STA_MFP) &&
nla_put_flag(msg, NL80211_STA_FLAG_MFP)) ||
((total_flags & WPA_STA_TDLS_PEER) &&
nla_put_flag(msg, NL80211_STA_FLAG_TDLS_PEER)))
goto fail;
nla_nest_end(msg, flags);
os_memset(&upd, 0, sizeof(upd));
upd.mask = sta_flags_nl80211(flags_or | ~flags_and);
upd.set = sta_flags_nl80211(flags_or);
if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
goto fail;
return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
fail:
nlmsg_free(msg);
return -ENOBUFS;
}
static int driver_nl80211_sta_set_airtime_weight(void *priv, const u8 *addr,
unsigned int weight)
{
struct i802_bss *bss = priv;
struct nl_msg *msg;
int ret;
wpa_printf(MSG_DEBUG,
"nl80211: Set STA airtime weight - ifname=%s addr=" MACSTR
" weight=%u", bss->ifname, MAC2STR(addr), weight);
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
nla_put_u16(msg, NL80211_ATTR_AIRTIME_WEIGHT, weight))
goto fail;
ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: SET_STATION[AIRTIME_WEIGHT] failed: ret=%d (%s)",
ret, strerror(-ret));
}
return ret;
fail:
nlmsg_free(msg);
return -ENOBUFS;
}
static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params)
{
enum nl80211_iftype nlmode, old_mode;
if (params->p2p) {
wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
"group (GO)");
nlmode = NL80211_IFTYPE_P2P_GO;
} else
nlmode = NL80211_IFTYPE_AP;
old_mode = drv->nlmode;
if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) {
nl80211_remove_monitor_interface(drv);
return -1;
}
if (params->freq.freq &&
nl80211_set_channel(drv->first_bss, &params->freq, 0)) {
if (old_mode != nlmode)
wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
nl80211_remove_monitor_interface(drv);
return -1;
}
return 0;
}
static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
int reset_mode)
{
struct nl_msg *msg;
int ret;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
"(%s)", ret, strerror(-ret));
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Leave IBSS request sent successfully");
}
if (reset_mode &&
wpa_driver_nl80211_set_mode(drv->first_bss,
NL80211_IFTYPE_STATION)) {
wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
"station mode");
}
return ret;
}
static int nl80211_ht_vht_overrides(struct nl_msg *msg,
struct wpa_driver_associate_params *params)
{
if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
return -1;
if (params->htcaps && params->htcaps_mask) {
int sz = sizeof(struct ieee80211_ht_capabilities);
wpa_hexdump(MSG_DEBUG, " * htcaps", params->htcaps, sz);
wpa_hexdump(MSG_DEBUG, " * htcaps_mask",
params->htcaps_mask, sz);
if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
params->htcaps) ||
nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
params->htcaps_mask))
return -1;
}
#ifdef CONFIG_VHT_OVERRIDES
if (params->disable_vht) {
wpa_printf(MSG_DEBUG, " * VHT disabled");
if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
return -1;
}
if (params->vhtcaps && params->vhtcaps_mask) {
int sz = sizeof(struct ieee80211_vht_capabilities);
wpa_hexdump(MSG_DEBUG, " * vhtcaps", params->vhtcaps, sz);
wpa_hexdump(MSG_DEBUG, " * vhtcaps_mask",
params->vhtcaps_mask, sz);
if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
params->vhtcaps) ||
nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
params->vhtcaps_mask))
return -1;
}
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_HE_OVERRIDES
if (params->disable_he) {
wpa_printf(MSG_DEBUG, " * HE disabled");
if (nla_put_flag(msg, NL80211_ATTR_DISABLE_HE))
return -1;
}
#endif /* CONFIG_HE_OVERRIDES */
return 0;
}
static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params)
{
struct nl_msg *msg;
int ret = -1;
int count = 0;
wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, &params->freq)) {
wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
"IBSS mode");
return -1;
}
retry:
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_IBSS)) ||
params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
goto fail;
wpa_printf(MSG_DEBUG, " * SSID=%s",
wpa_ssid_txt(params->ssid, params->ssid_len));
if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
goto fail;
os_memcpy(drv->ssid, params->ssid, params->ssid_len);
drv->ssid_len = params->ssid_len;
if (nl80211_put_freq_params(msg, &params->freq) < 0 ||
nl80211_put_beacon_int(msg, params->beacon_int))
goto fail;
ret = nl80211_set_conn_keys(params, msg);
if (ret)
goto fail;
if (params->bssid && params->fixed_bssid) {
wpa_printf(MSG_DEBUG, " * BSSID=" MACSTR,
MAC2STR(params->bssid));
if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
goto fail;
}
if (params->fixed_freq) {
wpa_printf(MSG_DEBUG, " * fixed_freq");
if (nla_put_flag(msg, NL80211_ATTR_FREQ_FIXED))
goto fail;
}
if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) {
wpa_printf(MSG_DEBUG, " * control port");
if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
goto fail;
}
if (params->wpa_ie) {
wpa_hexdump(MSG_DEBUG,
" * Extra IEs for Beacon/Probe Response frames",
params->wpa_ie, params->wpa_ie_len);
if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
params->wpa_ie))
goto fail;
}
ret = nl80211_ht_vht_overrides(msg, params);
if (ret < 0)
goto fail;
ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
ret, strerror(-ret));
count++;
if (ret == -EALREADY && count == 1) {
wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
"forced leave");
nl80211_leave_ibss(drv, 0);
nlmsg_free(msg);
goto retry;
}
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Join IBSS request sent successfully");
}
fail:
nlmsg_free(msg);
return ret;
}
static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params,
struct nl_msg *msg)
{
if (params->fils_erp_username_len) {
wpa_hexdump_ascii(MSG_DEBUG, " * FILS ERP EMSKname/username",
params->fils_erp_username,
params->fils_erp_username_len);
if (nla_put(msg, NL80211_ATTR_FILS_ERP_USERNAME,
params->fils_erp_username_len,
params->fils_erp_username))
return -1;
}
if (params->fils_erp_realm_len) {
wpa_hexdump_ascii(MSG_DEBUG, " * FILS ERP Realm",
params->fils_erp_realm,
params->fils_erp_realm_len);
if (nla_put(msg, NL80211_ATTR_FILS_ERP_REALM,
params->fils_erp_realm_len, params->fils_erp_realm))
return -1;
}
if (params->fils_erp_rrk_len) {
wpa_printf(MSG_DEBUG, " * FILS ERP next seq %u",
params->fils_erp_next_seq_num);
if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM,
params->fils_erp_next_seq_num))
return -1;
wpa_printf(MSG_DEBUG, " * FILS ERP rRK (len=%lu)",
(unsigned long) params->fils_erp_rrk_len);
if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK,
params->fils_erp_rrk_len, params->fils_erp_rrk))
return -1;
}
return 0;
}
static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params,
struct nl_msg *msg)
{
if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
return -1;
if (params->bssid) {
wpa_printf(MSG_DEBUG, " * bssid=" MACSTR,
MAC2STR(params->bssid));
if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
return -1;
}
if (params->bssid_hint) {
wpa_printf(MSG_DEBUG, " * bssid_hint=" MACSTR,
MAC2STR(params->bssid_hint));
if (nla_put(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN,
params->bssid_hint))
return -1;
}
if (params->freq.freq) {
wpa_printf(MSG_DEBUG, " * freq=%d", params->freq.freq);
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
params->freq.freq))
return -1;
drv->assoc_freq = params->freq.freq;
} else
drv->assoc_freq = 0;
if (params->freq_hint) {
wpa_printf(MSG_DEBUG, " * freq_hint=%d", params->freq_hint);
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_HINT,
params->freq_hint))
return -1;
}
if (params->freq.edmg.channels && params->freq.edmg.bw_config) {
wpa_printf(MSG_DEBUG,
" * EDMG configuration: channels=0x%x bw_config=%d",
params->freq.edmg.channels,
params->freq.edmg.bw_config);
if (nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_CHANNELS,
params->freq.edmg.channels) ||
nla_put_u8(msg, NL80211_ATTR_WIPHY_EDMG_BW_CONFIG,
params->freq.edmg.bw_config))
return -1;
}
if (params->bg_scan_period >= 0) {
wpa_printf(MSG_DEBUG, " * bg scan period=%d",
params->bg_scan_period);
if (nla_put_u16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
params->bg_scan_period))
return -1;
}
if (params->ssid) {
wpa_printf(MSG_DEBUG, " * SSID=%s",
wpa_ssid_txt(params->ssid, params->ssid_len));
if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
params->ssid))
return -1;
if (params->ssid_len > sizeof(drv->ssid))
return -1;
os_memcpy(drv->ssid, params->ssid, params->ssid_len);
drv->ssid_len = params->ssid_len;
}
wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, params->wpa_ie_len);
if (params->wpa_ie &&
nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie))
return -1;
if (params->wpa_proto) {
enum nl80211_wpa_versions ver = 0;
if (params->wpa_proto & WPA_PROTO_WPA)
ver |= NL80211_WPA_VERSION_1;
if (params->wpa_proto & WPA_PROTO_RSN)
ver |= NL80211_WPA_VERSION_2;
wpa_printf(MSG_DEBUG, " * WPA Versions 0x%x", ver);
if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
return -1;
}
if (params->pairwise_suite != WPA_CIPHER_NONE) {
u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite);
wpa_printf(MSG_DEBUG, " * pairwise=0x%x", cipher);
if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
cipher))
return -1;
}
if (params->group_suite == WPA_CIPHER_GTK_NOT_USED &&
!(drv->capa.enc & WPA_DRIVER_CAPA_ENC_GTK_NOT_USED)) {
/*
* This is likely to work even though many drivers do not
* advertise support for operations without GTK.
*/
wpa_printf(MSG_DEBUG, " * skip group cipher configuration for GTK_NOT_USED due to missing driver support advertisement");
} else if (params->group_suite != WPA_CIPHER_NONE) {
u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite);
wpa_printf(MSG_DEBUG, " * group=0x%x", cipher);
if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher))
return -1;
}
if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK ||
params->key_mgmt_suite == WPA_KEY_MGMT_CCKM ||
params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 ||
params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
params->key_mgmt_suite == WPA_KEY_MGMT_DPP) {
int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
switch (params->key_mgmt_suite) {
case WPA_KEY_MGMT_CCKM:
mgmt = RSN_AUTH_KEY_MGMT_CCKM;
break;
case WPA_KEY_MGMT_IEEE8021X:
mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
break;
case WPA_KEY_MGMT_FT_IEEE8021X:
mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X;
break;
case WPA_KEY_MGMT_FT_PSK:
mgmt = RSN_AUTH_KEY_MGMT_FT_PSK;
break;
case WPA_KEY_MGMT_IEEE8021X_SHA256:
mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
break;
case WPA_KEY_MGMT_PSK_SHA256:
mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256;
break;
case WPA_KEY_MGMT_OSEN:
mgmt = RSN_AUTH_KEY_MGMT_OSEN;
break;
case WPA_KEY_MGMT_SAE:
mgmt = RSN_AUTH_KEY_MGMT_SAE;
break;
case WPA_KEY_MGMT_FT_SAE:
mgmt = RSN_AUTH_KEY_MGMT_FT_SAE;
break;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
break;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
break;
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
break;
case WPA_KEY_MGMT_FILS_SHA256:
mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256;
break;
case WPA_KEY_MGMT_FILS_SHA384:
mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384;
break;
case WPA_KEY_MGMT_FT_FILS_SHA256:
mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
break;
case WPA_KEY_MGMT_FT_FILS_SHA384:
mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
break;
case WPA_KEY_MGMT_OWE:
mgmt = RSN_AUTH_KEY_MGMT_OWE;
break;
case WPA_KEY_MGMT_DPP:
mgmt = RSN_AUTH_KEY_MGMT_DPP;
break;
case WPA_KEY_MGMT_PSK:
default:
mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
break;
}
wpa_printf(MSG_DEBUG, " * akm=0x%x", mgmt);
if (nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, mgmt))
return -1;
}
if (params->req_handshake_offload &&
(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) {
wpa_printf(MSG_DEBUG, " * WANT_1X_4WAY_HS");
if (nla_put_flag(msg, NL80211_ATTR_WANT_1X_4WAY_HS))
return -1;
}
/* Add PSK in case of 4-way handshake offload */
if (params->psk &&
(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) {
wpa_hexdump_key(MSG_DEBUG, " * PSK", params->psk, 32);
if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk))
return -1;
}
if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
return -1;
if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
(params->pairwise_suite == WPA_CIPHER_NONE ||
params->pairwise_suite == WPA_CIPHER_WEP104 ||
params->pairwise_suite == WPA_CIPHER_WEP40) &&
(nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
return -1;
if (params->rrm_used) {
u32 drv_rrm_flags = drv->capa.rrm_flags;
if ((!((drv_rrm_flags &
WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) &&
(drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) &&
!(drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) ||
nla_put_flag(msg, NL80211_ATTR_USE_RRM))
return -1;
}
if (nl80211_ht_vht_overrides(msg, params) < 0)
return -1;
if (params->p2p)
wpa_printf(MSG_DEBUG, " * P2P group");
if (params->pbss) {
wpa_printf(MSG_DEBUG, " * PBSS");
if (nla_put_flag(msg, NL80211_ATTR_PBSS))
return -1;
}
drv->connect_reassoc = 0;
if (params->prev_bssid) {
wpa_printf(MSG_DEBUG, " * prev_bssid=" MACSTR,
MAC2STR(params->prev_bssid));
if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
params->prev_bssid))
return -1;
drv->connect_reassoc = 1;
}
if ((params->auth_alg & WPA_AUTH_ALG_FILS) &&
nl80211_put_fils_connect_params(drv, params, msg) != 0)
return -1;
if ((params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) &&
(!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
return -1;
return 0;
}
static int wpa_driver_nl80211_try_connect(
struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params,
struct i802_bss *bss)
{
struct nl_msg *msg;
enum nl80211_auth_type type;
int ret;
int algs;
#ifdef CONFIG_DRIVER_NL80211_QCA
if (params->req_key_mgmt_offload && params->psk &&
(params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
ret = issue_key_mgmt_set_key(drv, params->psk, 32);
if (ret)
return ret;
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
if (!msg)
return -1;
ret = nl80211_connect_common(drv, params, msg);
if (ret)
goto fail;
if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
goto fail;
if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_OPTIONAL &&
(drv->capa.flags & WPA_DRIVER_FLAGS_MFP_OPTIONAL) &&
nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_OPTIONAL))
goto fail;
#ifdef CONFIG_SAE
if ((params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) &&
nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
goto fail;
#endif /* CONFIG_SAE */
algs = 0;
if (params->auth_alg & WPA_AUTH_ALG_OPEN)
algs++;
if (params->auth_alg & WPA_AUTH_ALG_SHARED)
algs++;
if (params->auth_alg & WPA_AUTH_ALG_LEAP)
algs++;
if (params->auth_alg & WPA_AUTH_ALG_FILS)
algs++;
if (params->auth_alg & WPA_AUTH_ALG_FT)
algs++;
if (algs > 1) {
wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic "
"selection");
goto skip_auth_type;
}
type = get_nl_auth_type(params->auth_alg);
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
if (type == NL80211_AUTHTYPE_MAX ||
nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
goto fail;
skip_auth_type:
ret = nl80211_set_conn_keys(params, msg);
if (ret)
goto fail;
ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
"(%s)", ret, strerror(-ret));
} else {
#ifdef CONFIG_DRIVER_NL80211_QCA
drv->roam_indication_done = false;
#endif /* CONFIG_DRIVER_NL80211_QCA */
wpa_printf(MSG_DEBUG,
"nl80211: Connect request send successfully");
}
fail:
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return ret;
}
static int wpa_driver_nl80211_connect(
struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params,
struct i802_bss *bss)
{
int ret;
/* Store the connection attempted bssid for future use */
if (params->bssid)
os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN);
else
os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
ret = wpa_driver_nl80211_try_connect(drv, params, bss);
if (ret == -EALREADY) {
/*
* cfg80211 does not currently accept new connections if
* we are already connected. As a workaround, force
* disconnection and try again.
*/
wpa_printf(MSG_DEBUG, "nl80211: Explicitly "
"disconnecting before reassociation "
"attempt");
if (wpa_driver_nl80211_disconnect(
drv, WLAN_REASON_PREV_AUTH_NOT_VALID, bss))
return -1;
ret = wpa_driver_nl80211_try_connect(drv, params, bss);
}
return ret;
}
static int wpa_driver_nl80211_associate(
void *priv, struct wpa_driver_associate_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1;
struct nl_msg *msg;
nl80211_unmask_11b_rates(bss);
if (params->mode == IEEE80211_MODE_AP)
return wpa_driver_nl80211_ap(drv, params);
if (params->mode == IEEE80211_MODE_IBSS)
return wpa_driver_nl80211_ibss(drv, params);
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
enum nl80211_iftype nlmode = params->p2p ?
NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
return -1;
if (params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)
bss->use_nl_connect = 1;
else
bss->use_nl_connect = 0;
return wpa_driver_nl80211_connect(drv, params, bss);
}
nl80211_mark_disconnected(drv);
wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);
if (!msg)
return -1;
ret = nl80211_connect_common(drv, params, msg);
if (ret)
goto fail;
if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
goto fail;
if (params->fils_kek) {
wpa_printf(MSG_DEBUG, " * FILS KEK (len=%u)",
(unsigned int) params->fils_kek_len);
if (nla_put(msg, NL80211_ATTR_FILS_KEK, params->fils_kek_len,
params->fils_kek))
goto fail;
}
if (params->fils_nonces) {
wpa_hexdump(MSG_DEBUG, " * FILS nonces (for AAD)",
params->fils_nonces,
params->fils_nonces_len);
if (nla_put(msg, NL80211_ATTR_FILS_NONCES,
params->fils_nonces_len, params->fils_nonces))
goto fail;
}
ret = send_and_recv_msgs_connect_handle(drv, msg, drv->first_bss, 1);
msg = NULL;
if (ret) {
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: MLME command failed (assoc): ret=%d (%s)",
ret, strerror(-ret));
nl80211_dump_scan(drv);
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Association request send successfully");
}
fail:
nlmsg_free(msg);
return ret;
}
static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
int ifindex, enum nl80211_iftype mode)
{
struct nl_msg *msg;
int ret = -ENOBUFS;
wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
ifindex, mode, nl80211_iftype_str(mode));
msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
goto fail;
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (!ret)
return 0;
fail:
nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
" %d (%s)", ifindex, mode, ret, strerror(-ret));
return ret;
}
static int wpa_driver_nl80211_set_mode_impl(
struct i802_bss *bss,
enum nl80211_iftype nlmode,
struct hostapd_freq_params *desired_freq_params)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1;
int i;
int was_ap = is_ap_interface(drv->nlmode);
int res;
int mode_switch_res;
if (TEST_FAIL())
return -1;
mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
if (mode_switch_res && nlmode == nl80211_get_ifmode(bss))
mode_switch_res = 0;
if (mode_switch_res == 0) {
drv->nlmode = nlmode;
ret = 0;
goto done;
}
if (mode_switch_res == -ENODEV)
return -1;
if (nlmode == drv->nlmode) {
wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
"requested mode - ignore error");
ret = 0;
goto done; /* Already in the requested mode */
}
/* mac80211 doesn't allow mode changes while the device is up, so
* take the device down, try to set the mode again, and bring the
* device back up.
*/
wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
"interface down");
for (i = 0; i < 10; i++) {
res = i802_set_iface_flags(bss, 0);
if (res == -EACCES || res == -ENODEV)
break;
if (res != 0) {
wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
"interface down");
os_sleep(0, 100000);
continue;
}
/*
* Setting the mode will fail for some drivers if the phy is
* on a frequency that the mode is disallowed in.
*/
if (desired_freq_params) {
res = nl80211_set_channel(bss, desired_freq_params, 0);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to set frequency on interface");
}
}
if (i == 0 && was_ap && !is_ap_interface(nlmode) &&
bss->brname[0] &&
(bss->added_if_into_bridge || bss->already_in_bridge)) {
wpa_printf(MSG_DEBUG,
"nl80211: Remove AP interface %s temporarily from the bridge %s to allow its mode to be set to STATION",
bss->ifname, bss->brname);
if (linux_br_del_if(drv->global->ioctl_sock,
bss->brname, bss->ifname) < 0)
wpa_printf(MSG_INFO,
"nl80211: Failed to remove interface %s from bridge %s: %s",
bss->ifname, bss->brname,
strerror(errno));
}
/* Try to set the mode again while the interface is down */
mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
if (mode_switch_res == -EBUSY) {
wpa_printf(MSG_DEBUG,
"nl80211: Delaying mode set while interface going down");
os_sleep(0, 100000);
continue;
}
ret = mode_switch_res;
break;
}
if (!ret) {
wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
"interface is down");
drv->nlmode = nlmode;
drv->ignore_if_down_event = 1;
}
/* Bring the interface back up */
res = linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
if (res != 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to set interface up after switching mode");
ret = -1;
}
done:
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
"from %d failed", nlmode, drv->nlmode);
return ret;
}
if (is_p2p_net_interface(nlmode)) {
wpa_printf(MSG_DEBUG,
"nl80211: Interface %s mode change to P2P - disable 11b rates",
bss->ifname);
nl80211_disable_11b_rates(drv, drv->ifindex, 1);
} else if (drv->disabled_11b_rates) {
wpa_printf(MSG_DEBUG,
"nl80211: Interface %s mode changed to non-P2P - re-enable 11b rates",
bss->ifname);
nl80211_disable_11b_rates(drv, drv->ifindex, 0);
}
if (is_ap_interface(nlmode)) {
nl80211_mgmt_unsubscribe(bss, "start AP");
/* Setup additional AP mode functionality if needed */
if (nl80211_setup_ap(bss))
return -1;
} else if (was_ap) {
/* Remove additional AP mode functionality */
nl80211_teardown_ap(bss);
} else {
nl80211_mgmt_unsubscribe(bss, "mode change");
}
if (is_mesh_interface(nlmode) &&
nl80211_mgmt_subscribe_mesh(bss))
return -1;
if (!bss->in_deinit && !is_ap_interface(nlmode) &&
!is_mesh_interface(nlmode) &&
nl80211_mgmt_subscribe_non_ap(bss) < 0)
wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
"frame processing - ignore for now");
return 0;
}
void nl80211_restore_ap_mode(struct i802_bss *bss)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int was_ap = is_ap_interface(drv->nlmode);
wpa_driver_nl80211_set_mode(bss, drv->ap_scan_as_station);
if (!was_ap && is_ap_interface(drv->ap_scan_as_station) &&
bss->brname[0] &&
(bss->added_if_into_bridge || bss->already_in_bridge)) {
wpa_printf(MSG_DEBUG,
"nl80211: Add AP interface %s back into the bridge %s",
bss->ifname, bss->brname);
if (linux_br_add_if(drv->global->ioctl_sock, bss->brname,
bss->ifname) < 0) {
wpa_printf(MSG_WARNING,
"nl80211: Failed to add interface %s into bridge %s: %s",
bss->ifname, bss->brname, strerror(errno));
}
}
drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
}
int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
enum nl80211_iftype nlmode)
{
return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL);
}
static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
struct hostapd_freq_params *freq)
{
return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC,
freq);
}
static int wpa_driver_nl80211_get_capa(void *priv,
struct wpa_driver_capa *capa)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!drv->has_capability)
return -1;
os_memcpy(capa, &drv->capa, sizeof(*capa));
if (drv->extended_capa && drv->extended_capa_mask) {
capa->extended_capa = drv->extended_capa;
capa->extended_capa_mask = drv->extended_capa_mask;
capa->extended_capa_len = drv->extended_capa_len;
}
return 0;
}
static int wpa_driver_nl80211_set_operstate(void *priv, int state)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
wpa_printf(MSG_DEBUG, "nl80211: Set %s operstate %d->%d (%s)",
bss->ifname, drv->operstate, state,
state ? "UP" : "DORMANT");
drv->operstate = state;
return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1,
state ? IF_OPER_UP : IF_OPER_DORMANT);
}
static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nl80211_sta_flag_update upd;
int ret;
if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) {
wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated");
return 0;
}
wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid));
os_memset(&upd, 0, sizeof(upd));
upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
if (authorized)
upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) ||
nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (!ret)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
ret, strerror(-ret));
return ret;
}
/* Set kernel driver on given frequency (MHz) */
static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
{
struct i802_bss *bss = priv;
return nl80211_set_channel(bss, freq, 0);
}
static inline int min_int(int a, int b)
{
if (a < b)
return a;
return b;
}
static int get_key_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
/*
* TODO: validate the key index and mac address!
* Otherwise, there's a race condition as soon as
* the kernel starts sending key notifications.
*/
if (tb[NL80211_ATTR_KEY_SEQ])
memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]),
min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6));
nl80211_nlmsg_clear(msg);
return NL_SKIP;
}
static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
int idx, u8 *seq)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0,
NL80211_CMD_GET_KEY);
if (!msg ||
(addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
nla_put_u8(msg, NL80211_ATTR_KEY_IDX, idx)) {
nlmsg_free(msg);
return -ENOBUFS;
}
memset(seq, 0, 6);
return send_and_recv_msgs(drv, msg, get_key_handler, seq, NULL, NULL);
}
static int i802_set_rts(void *priv, int rts)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
u32 val;
if (rts >= 2347 || rts == -1)
val = (u32) -1;
else
val = rts;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (!ret)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
"%d (%s)", rts, ret, strerror(-ret));
return ret;
}
static int i802_set_frag(void *priv, int frag)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
u32 val;
if (frag >= 2346 || frag == -1)
val = (u32) -1;
else
val = frag;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (!ret)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
"%d: %d (%s)", frag, ret, strerror(-ret));
return ret;
}
static int i802_flush(void *priv)
{
struct i802_bss *bss = priv;
struct nl_msg *msg;
int res;
wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
bss->ifname);
/*
* XXX: FIX! this needs to flush all VLANs too
*/
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
res = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
if (res) {
wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
"(%s)", res, strerror(-res));
}
return res;
}
static void get_sta_tid_stats(struct hostap_sta_driver_data *data,
struct nlattr *attr)
{
struct nlattr *tid_stats[NL80211_TID_STATS_MAX + 1], *tidattr;
struct nlattr *txq_stats[NL80211_TXQ_STATS_MAX + 1];
static struct nla_policy txq_stats_policy[NL80211_TXQ_STATS_MAX + 1] = {
[NL80211_TXQ_STATS_BACKLOG_BYTES] = { .type = NLA_U32 },
[NL80211_TXQ_STATS_BACKLOG_PACKETS] = { .type = NLA_U32 },
};
int rem;
nla_for_each_nested(tidattr, attr, rem) {
if (nla_parse_nested(tid_stats, NL80211_TID_STATS_MAX,
tidattr, NULL) != 0 ||
!tid_stats[NL80211_TID_STATS_TXQ_STATS] ||
nla_parse_nested(txq_stats, NL80211_TXQ_STATS_MAX,
tid_stats[NL80211_TID_STATS_TXQ_STATS],
txq_stats_policy) != 0)
continue;
/* sum the backlogs over all TIDs for station */
if (txq_stats[NL80211_TXQ_STATS_BACKLOG_BYTES])
data->backlog_bytes += nla_get_u32(
txq_stats[NL80211_TXQ_STATS_BACKLOG_BYTES]);
if (txq_stats[NL80211_TXQ_STATS_BACKLOG_PACKETS])
data->backlog_bytes += nla_get_u32(
txq_stats[NL80211_TXQ_STATS_BACKLOG_PACKETS]);
}
}
static int get_sta_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct hostap_sta_driver_data *data = arg;
struct nlattr *stats[NL80211_STA_INFO_MAX + 1];
static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
[NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
[NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 },
[NL80211_STA_INFO_RX_DURATION] = { .type = NLA_U64 },
[NL80211_STA_INFO_TX_DURATION] = { .type = NLA_U64 },
+ [NL80211_STA_INFO_CONNECTED_TIME] = { .type = NLA_U32 },
};
struct nlattr *rate[NL80211_RATE_INFO_MAX + 1];
static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
[NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
[NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 },
[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
[NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 },
};
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
/*
* TODO: validate the interface and mac address!
* Otherwise, there's a race condition as soon as
* the kernel starts sending station notifications.
*/
if (!tb[NL80211_ATTR_STA_INFO]) {
wpa_printf(MSG_DEBUG, "sta stats missing!");
return NL_SKIP;
}
if (nla_parse_nested(stats, NL80211_STA_INFO_MAX,
tb[NL80211_ATTR_STA_INFO],
stats_policy)) {
wpa_printf(MSG_DEBUG, "failed to parse nested attributes!");
return NL_SKIP;
}
if (stats[NL80211_STA_INFO_INACTIVE_TIME])
data->inactive_msec =
nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
/* For backwards compatibility, fetch the 32-bit counters first. */
if (stats[NL80211_STA_INFO_RX_BYTES])
data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]);
if (stats[NL80211_STA_INFO_TX_BYTES])
data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]);
if (stats[NL80211_STA_INFO_RX_BYTES64] &&
stats[NL80211_STA_INFO_TX_BYTES64]) {
/*
* The driver supports 64-bit counters, so use them to override
* the 32-bit values.
*/
data->rx_bytes =
nla_get_u64(stats[NL80211_STA_INFO_RX_BYTES64]);
data->tx_bytes =
nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]);
data->bytes_64bit = 1;
}
if (stats[NL80211_STA_INFO_RX_PACKETS])
data->rx_packets =
nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
if (stats[NL80211_STA_INFO_TX_PACKETS])
data->tx_packets =
nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
if (stats[NL80211_STA_INFO_RX_DURATION])
data->rx_airtime =
nla_get_u64(stats[NL80211_STA_INFO_RX_DURATION]);
if (stats[NL80211_STA_INFO_TX_DURATION])
data->tx_airtime =
nla_get_u64(stats[NL80211_STA_INFO_TX_DURATION]);
if (stats[NL80211_STA_INFO_TX_FAILED])
data->tx_retry_failed =
nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
if (stats[NL80211_STA_INFO_SIGNAL])
data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]);
if (stats[NL80211_STA_INFO_ACK_SIGNAL]) {
data->last_ack_rssi =
nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]);
data->flags |= STA_DRV_DATA_LAST_ACK_RSSI;
}
+ if (stats[NL80211_STA_INFO_CONNECTED_TIME]) {
+ data->connected_sec =
+ nla_get_u32(stats[NL80211_STA_INFO_CONNECTED_TIME]);
+ data->flags |= STA_DRV_DATA_CONN_TIME;
+ }
+
if (stats[NL80211_STA_INFO_TX_BITRATE] &&
nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
stats[NL80211_STA_INFO_TX_BITRATE],
rate_policy) == 0) {
if (rate[NL80211_RATE_INFO_BITRATE32])
data->current_tx_rate =
nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
else if (rate[NL80211_RATE_INFO_BITRATE])
data->current_tx_rate =
nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
if (rate[NL80211_RATE_INFO_MCS]) {
data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
data->flags |= STA_DRV_DATA_TX_MCS;
}
if (rate[NL80211_RATE_INFO_VHT_MCS]) {
data->tx_vhtmcs =
nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
data->flags |= STA_DRV_DATA_TX_VHT_MCS;
}
if (rate[NL80211_RATE_INFO_SHORT_GI])
data->flags |= STA_DRV_DATA_TX_SHORT_GI;
if (rate[NL80211_RATE_INFO_VHT_NSS]) {
data->tx_vht_nss =
nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
data->flags |= STA_DRV_DATA_TX_VHT_NSS;
}
}
if (stats[NL80211_STA_INFO_RX_BITRATE] &&
nla_parse_nested(rate, NL80211_RATE_INFO_MAX,
stats[NL80211_STA_INFO_RX_BITRATE],
rate_policy) == 0) {
if (rate[NL80211_RATE_INFO_BITRATE32])
data->current_rx_rate =
nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]);
else if (rate[NL80211_RATE_INFO_BITRATE])
data->current_rx_rate =
nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]);
if (rate[NL80211_RATE_INFO_MCS]) {
data->rx_mcs =
nla_get_u8(rate[NL80211_RATE_INFO_MCS]);
data->flags |= STA_DRV_DATA_RX_MCS;
}
if (rate[NL80211_RATE_INFO_VHT_MCS]) {
data->rx_vhtmcs =
nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]);
data->flags |= STA_DRV_DATA_RX_VHT_MCS;
}
if (rate[NL80211_RATE_INFO_SHORT_GI])
data->flags |= STA_DRV_DATA_RX_SHORT_GI;
if (rate[NL80211_RATE_INFO_VHT_NSS]) {
data->rx_vht_nss =
nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]);
data->flags |= STA_DRV_DATA_RX_VHT_NSS;
}
}
if (stats[NL80211_STA_INFO_TID_STATS])
get_sta_tid_stats(data, stats[NL80211_STA_INFO_TID_STATS]);
return NL_SKIP;
}
static int i802_read_sta_data(struct i802_bss *bss,
struct hostap_sta_driver_data *data,
const u8 *addr)
{
struct nl_msg *msg;
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
nlmsg_free(msg);
return -ENOBUFS;
}
return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data,
NULL, NULL);
}
static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
int cw_min, int cw_max, int burst_time)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *txq, *params;
int res;
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY);
if (!msg)
return -1;
txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
if (!txq)
goto fail;
/* We are only sending parameters for a single TXQ at a time */
params = nla_nest_start(msg, 1);
if (!params)
goto fail;
switch (queue) {
case 0:
if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO))
goto fail;
break;
case 1:
if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI))
goto fail;
break;
case 2:
if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE))
goto fail;
break;
case 3:
if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK))
goto fail;
break;
}
/* Burst time is configured in units of 0.1 msec and TXOP parameter in
* 32 usec, so need to convert the value here. */
if (nla_put_u16(msg, NL80211_TXQ_ATTR_TXOP,
(burst_time * 100 + 16) / 32) ||
nla_put_u16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min) ||
nla_put_u16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max) ||
nla_put_u8(msg, NL80211_TXQ_ATTR_AIFS, aifs))
goto fail;
nla_nest_end(msg, params);
nla_nest_end(msg, txq);
res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG,
"nl80211: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d",
queue, aifs, cw_min, cw_max, burst_time, res);
if (res == 0)
return 0;
msg = NULL;
fail:
nlmsg_free(msg);
return -1;
}
static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr,
const char *ifname, int vlan_id)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR
", ifname=%s[%d], vlan_id=%d)",
bss->ifname, if_nametoindex(bss->ifname),
MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id);
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
(vlan_id && (drv->capa.flags & WPA_DRIVER_FLAGS_VLAN_OFFLOAD) &&
nla_put_u16(msg, NL80211_ATTR_VLAN_ID, vlan_id)) ||
nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret < 0) {
wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
MAC2STR(addr), ifname, vlan_id, ret,
strerror(-ret));
}
return ret;
}
static int i802_get_inact_sec(void *priv, const u8 *addr)
{
struct hostap_sta_driver_data data;
int ret;
os_memset(&data, 0, sizeof(data));
data.inactive_msec = (unsigned long) -1;
ret = i802_read_sta_data(priv, &data, addr);
if (ret == -ENOENT)
return -ENOENT;
if (ret || data.inactive_msec == (unsigned long) -1)
return -1;
return data.inactive_msec / 1000;
}
static int i802_sta_clear_stats(void *priv, const u8 *addr)
{
#if 0
/* TODO */
#endif
return 0;
}
static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
u16 reason)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_mgmt mgmt;
u8 channel;
if (ieee80211_freq_to_chan(bss->freq, &channel) ==
HOSTAPD_MODE_IEEE80211AD) {
/* Deauthentication is not used in DMG/IEEE 802.11ad;
* disassociate the STA instead. */
return i802_sta_disassoc(priv, own_addr, addr, reason);
}
if (is_mesh_interface(drv->nlmode))
return -1;
if (drv->device_ap_sme)
return wpa_driver_nl80211_sta_remove(bss, addr, 1, reason);
memset(&mgmt, 0, sizeof(mgmt));
mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_DEAUTH);
memcpy(mgmt.da, addr, ETH_ALEN);
memcpy(mgmt.sa, own_addr, ETH_ALEN);
memcpy(mgmt.bssid, own_addr, ETH_ALEN);
mgmt.u.deauth.reason_code = host_to_le16(reason);
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.deauth), 0, 0, 0, 0,
0, NULL, 0, 0);
}
static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
u16 reason)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct ieee80211_mgmt mgmt;
if (is_mesh_interface(drv->nlmode))
return -1;
if (drv->device_ap_sme)
return wpa_driver_nl80211_sta_remove(bss, addr, 0, reason);
memset(&mgmt, 0, sizeof(mgmt));
mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_DISASSOC);
memcpy(mgmt.da, addr, ETH_ALEN);
memcpy(mgmt.sa, own_addr, ETH_ALEN);
memcpy(mgmt.bssid, own_addr, ETH_ALEN);
mgmt.u.disassoc.reason_code = host_to_le16(reason);
return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
IEEE80211_HDRLEN +
sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
0, NULL, 0, 0);
}
static void dump_ifidx(struct wpa_driver_nl80211_data *drv)
{
char buf[200], *pos, *end;
int i, res;
pos = buf;
end = pos + sizeof(buf);
for (i = 0; i < drv->num_if_indices; i++) {
if (!drv->if_indices[i].ifindex)
continue;
res = os_snprintf(pos, end - pos, " %d(%d)",
drv->if_indices[i].ifindex,
drv->if_indices[i].reason);
if (os_snprintf_error(end - pos, res))
break;
pos += res;
}
*pos = '\0';
wpa_printf(MSG_DEBUG, "nl80211: if_indices[%d]:%s",
drv->num_if_indices, buf);
}
static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
int ifidx_reason)
{
int i;
struct drv_nl80211_if_info *old;
wpa_printf(MSG_DEBUG,
"nl80211: Add own interface ifindex %d (ifidx_reason %d)",
ifidx, ifidx_reason);
if (have_ifidx(drv, ifidx, ifidx_reason)) {
wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list",
ifidx);
return;
}
for (i = 0; i < drv->num_if_indices; i++) {
if (drv->if_indices[i].ifindex == 0) {
drv->if_indices[i].ifindex = ifidx;
drv->if_indices[i].reason = ifidx_reason;
dump_ifidx(drv);
return;
}
}
if (drv->if_indices != drv->default_if_indices)
old = drv->if_indices;
else
old = NULL;
drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
sizeof(*old));
if (!drv->if_indices) {
if (!old)
drv->if_indices = drv->default_if_indices;
else
drv->if_indices = old;
wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
"interfaces");
wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
return;
}
if (!old)
os_memcpy(drv->if_indices, drv->default_if_indices,
sizeof(drv->default_if_indices));
drv->if_indices[drv->num_if_indices].ifindex = ifidx;
drv->if_indices[drv->num_if_indices].reason = ifidx_reason;
drv->num_if_indices++;
dump_ifidx(drv);
}
static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
int ifidx_reason)
{
int i;
for (i = 0; i < drv->num_if_indices; i++) {
if ((drv->if_indices[i].ifindex == ifidx ||
ifidx == IFIDX_ANY) &&
(drv->if_indices[i].reason == ifidx_reason ||
ifidx_reason == IFIDX_ANY)) {
drv->if_indices[i].ifindex = 0;
drv->if_indices[i].reason = 0;
break;
}
}
dump_ifidx(drv);
}
static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
int ifidx_reason)
{
int i;
for (i = 0; i < drv->num_if_indices; i++)
if (drv->if_indices[i].ifindex == ifidx &&
(drv->if_indices[i].reason == ifidx_reason ||
ifidx_reason == IFIDX_ANY))
return 1;
return 0;
}
static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
const char *bridge_ifname, char *ifname_wds)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
char name[IFNAMSIZ + 1];
union wpa_event_data event;
int ret;
ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
if (ret >= (int) sizeof(name))
wpa_printf(MSG_WARNING,
"nl80211: WDS interface name was truncated");
else if (ret < 0)
return ret;
if (ifname_wds)
os_strlcpy(ifname_wds, name, IFNAMSIZ + 1);
wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
" aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
if (val) {
if (!if_nametoindex(name)) {
if (nl80211_create_iface(drv, name,
NL80211_IFTYPE_AP_VLAN,
bss->addr, 1, NULL, NULL, 0) <
0)
return -1;
if (bridge_ifname &&
linux_br_add_if(drv->global->ioctl_sock,
bridge_ifname, name) < 0)
return -1;
os_memset(&event, 0, sizeof(event));
event.wds_sta_interface.sta_addr = addr;
event.wds_sta_interface.ifname = name;
event.wds_sta_interface.istatus = INTERFACE_ADDED;
wpa_supplicant_event(bss->ctx,
EVENT_WDS_STA_INTERFACE_STATUS,
&event);
}
if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
"interface %s up", name);
}
return i802_set_sta_vlan(priv, addr, name, 0);
} else {
if (bridge_ifname &&
linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
name) < 0)
wpa_printf(MSG_INFO,
"nl80211: Failed to remove interface %s from bridge %s: %s",
name, bridge_ifname, strerror(errno));
i802_set_sta_vlan(priv, addr, bss->ifname, 0);
nl80211_remove_iface(drv, if_nametoindex(name));
os_memset(&event, 0, sizeof(event));
event.wds_sta_interface.sta_addr = addr;
event.wds_sta_interface.ifname = name;
event.wds_sta_interface.istatus = INTERFACE_REMOVED;
wpa_supplicant_event(bss->ctx, EVENT_WDS_STA_INTERFACE_STATUS,
&event);
return 0;
}
}
static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
{
struct wpa_driver_nl80211_data *drv = eloop_ctx;
struct sockaddr_ll lladdr;
unsigned char buf[3000];
int len;
socklen_t fromlen = sizeof(lladdr);
len = recvfrom(sock, buf, sizeof(buf), 0,
(struct sockaddr *)&lladdr, &fromlen);
if (len < 0) {
wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s",
strerror(errno));
return;
}
if (have_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY))
drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
}
static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
struct i802_bss *bss,
const char *brname, const char *ifname)
{
int br_ifindex;
char in_br[IFNAMSIZ];
os_strlcpy(bss->brname, brname, IFNAMSIZ);
br_ifindex = if_nametoindex(brname);
if (br_ifindex == 0) {
/*
* Bridge was configured, but the bridge device does
* not exist. Try to add it now.
*/
if (linux_br_add(drv->global->ioctl_sock, brname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
"bridge interface %s: %s",
brname, strerror(errno));
return -1;
}
bss->added_bridge = 1;
br_ifindex = if_nametoindex(brname);
add_ifidx(drv, br_ifindex, drv->ifindex);
}
bss->br_ifindex = br_ifindex;
if (linux_br_get(in_br, ifname) == 0) {
if (os_strcmp(in_br, brname) == 0) {
bss->already_in_bridge = 1;
return 0; /* already in the bridge */
}
wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
"bridge %s", ifname, in_br);
if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) <
0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to "
"remove interface %s from bridge "
"%s: %s",
ifname, in_br, strerror(errno));
return -1;
}
}
wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
ifname, brname);
if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
wpa_printf(MSG_WARNING,
"nl80211: Failed to add interface %s into bridge %s: %s",
ifname, brname, strerror(errno));
/* Try to continue without the interface being in a bridge. This
* may be needed for some cases, e.g., with Open vSwitch, where
* an external component will need to handle bridge
* configuration. */
return 0;
}
bss->added_if_into_bridge = 1;
return 0;
}
static void *i802_init(struct hostapd_data *hapd,
struct wpa_init_params *params)
{
struct wpa_driver_nl80211_data *drv;
struct i802_bss *bss;
size_t i;
char master_ifname[IFNAMSIZ];
int ifindex, br_ifindex = 0;
int br_added = 0;
bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
params->global_priv, 1,
params->bssid, params->driver_params);
if (bss == NULL)
return NULL;
drv = bss->drv;
if (linux_br_get(master_ifname, params->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
params->ifname, master_ifname);
br_ifindex = if_nametoindex(master_ifname);
os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
} else if ((params->num_bridge == 0 || !params->bridge[0]) &&
linux_master_get(master_ifname, params->ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s",
params->ifname, master_ifname);
/* start listening for EAPOL on the master interface */
add_ifidx(drv, if_nametoindex(master_ifname), drv->ifindex);
/* check if master itself is under bridge */
if (linux_br_get(master_ifname, master_ifname) == 0) {
wpa_printf(MSG_DEBUG, "nl80211: which is in bridge %s",
master_ifname);
br_ifindex = if_nametoindex(master_ifname);
os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
}
} else {
master_ifname[0] = '\0';
}
bss->br_ifindex = br_ifindex;
for (i = 0; i < params->num_bridge; i++) {
if (params->bridge[i]) {
ifindex = if_nametoindex(params->bridge[i]);
if (ifindex)
add_ifidx(drv, ifindex, drv->ifindex);
if (ifindex == br_ifindex)
br_added = 1;
}
}
/* start listening for EAPOL on the default AP interface */
add_ifidx(drv, drv->ifindex, IFIDX_ANY);
if (params->num_bridge && params->bridge[0]) {
if (i802_check_bridge(drv, bss, params->bridge[0],
params->ifname) < 0)
goto failed;
if (os_strcmp(params->bridge[0], master_ifname) != 0)
br_added = 1;
}
if (!br_added && br_ifindex &&
(params->num_bridge == 0 || !params->bridge[0]))
add_ifidx(drv, br_ifindex, drv->ifindex);
#ifdef CONFIG_LIBNL3_ROUTE
if (bss->added_if_into_bridge || bss->already_in_bridge) {
int err;
drv->rtnl_sk = nl_socket_alloc();
if (drv->rtnl_sk == NULL) {
wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
goto failed;
}
err = nl_connect(drv->rtnl_sk, NETLINK_ROUTE);
if (err) {
wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
nl_geterror(err));
goto failed;
}
}
#endif /* CONFIG_LIBNL3_ROUTE */
if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
wpa_printf(MSG_DEBUG,
"nl80211: Do not open EAPOL RX socket - using control port for RX");
goto skip_eapol_sock;
}
drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
if (drv->eapol_sock < 0) {
wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s",
strerror(errno));
goto failed;
}
if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
{
wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol");
goto failed;
}
skip_eapol_sock:
if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
params->own_addr))
goto failed;
os_memcpy(drv->perm_addr, params->own_addr, ETH_ALEN);
memcpy(bss->addr, params->own_addr, ETH_ALEN);
return bss;
failed:
wpa_driver_nl80211_deinit(bss);
return NULL;
}
static void i802_deinit(void *priv)
{
struct i802_bss *bss = priv;
wpa_driver_nl80211_deinit(bss);
}
static enum nl80211_iftype wpa_driver_nl80211_if_type(
enum wpa_driver_if_type type)
{
switch (type) {
case WPA_IF_STATION:
return NL80211_IFTYPE_STATION;
case WPA_IF_P2P_CLIENT:
case WPA_IF_P2P_GROUP:
return NL80211_IFTYPE_P2P_CLIENT;
case WPA_IF_AP_VLAN:
return NL80211_IFTYPE_AP_VLAN;
case WPA_IF_AP_BSS:
return NL80211_IFTYPE_AP;
case WPA_IF_P2P_GO:
return NL80211_IFTYPE_P2P_GO;
case WPA_IF_P2P_DEVICE:
return NL80211_IFTYPE_P2P_DEVICE;
case WPA_IF_MESH:
return NL80211_IFTYPE_MESH_POINT;
default:
return -1;
}
}
static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
{
struct wpa_driver_nl80211_data *drv;
dl_list_for_each(drv, &global->interfaces,
struct wpa_driver_nl80211_data, list) {
if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0)
return 1;
}
return 0;
}
static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr)
{
unsigned int idx;
if (!drv->global)
return -1;
os_memcpy(new_addr, drv->first_bss->addr, ETH_ALEN);
for (idx = 0; idx < 64; idx++) {
new_addr[0] = drv->first_bss->addr[0] | 0x02;
new_addr[0] ^= idx << 2;
if (!nl80211_addr_in_use(drv->global, new_addr))
break;
}
if (idx == 64)
return -1;
wpa_printf(MSG_DEBUG, "nl80211: Assigned new virtual interface address "
MACSTR, MAC2STR(new_addr));
return 0;
}
struct wdev_info {
u64 wdev_id;
int wdev_id_set;
u8 macaddr[ETH_ALEN];
};
static int nl80211_wdev_handler(struct nl_msg *msg, void *arg)
{
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct wdev_info *wi = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb[NL80211_ATTR_WDEV]) {
wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
wi->wdev_id_set = 1;
}
if (tb[NL80211_ATTR_MAC])
os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
ETH_ALEN);
return NL_SKIP;
}
static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
const char *ifname, const u8 *addr,
void *bss_ctx, void **drv_priv,
char *force_ifname, u8 *if_addr,
const char *bridge, int use_existing,
int setup_ap)
{
enum nl80211_iftype nlmode;
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int ifidx;
int added = 1;
if (addr)
os_memcpy(if_addr, addr, ETH_ALEN);
nlmode = wpa_driver_nl80211_if_type(type);
if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
struct wdev_info p2pdev_info;
os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
0, nl80211_wdev_handler,
&p2pdev_info, use_existing);
if (!p2pdev_info.wdev_id_set || ifidx != 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s",
ifname);
return -1;
}
drv->global->if_add_wdevid = p2pdev_info.wdev_id;
drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set;
if (!is_zero_ether_addr(p2pdev_info.macaddr))
os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN);
wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created",
ifname,
(long long unsigned int) p2pdev_info.wdev_id);
} else {
ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
0, NULL, NULL, use_existing);
if (use_existing && ifidx == -ENFILE) {
added = 0;
ifidx = if_nametoindex(ifname);
} else if (ifidx < 0) {
return -1;
}
}
if (!addr) {
if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
os_memcpy(if_addr, bss->addr, ETH_ALEN);
else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
ifname, if_addr) < 0) {
if (added)
nl80211_remove_iface(drv, ifidx);
return -1;
}
}
if (!addr &&
(type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
type == WPA_IF_P2P_GO || type == WPA_IF_MESH ||
type == WPA_IF_STATION)) {
/* Enforce unique address */
u8 new_addr[ETH_ALEN];
if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
new_addr) < 0) {
if (added)
nl80211_remove_iface(drv, ifidx);
return -1;
}
if (nl80211_addr_in_use(drv->global, new_addr)) {
wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
"for interface %s type %d", ifname, type);
if (nl80211_vif_addr(drv, new_addr) < 0) {
if (added)
nl80211_remove_iface(drv, ifidx);
return -1;
}
if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
new_addr) < 0) {
if (added)
nl80211_remove_iface(drv, ifidx);
return -1;
}
}
os_memcpy(if_addr, new_addr, ETH_ALEN);
}
if (type == WPA_IF_AP_BSS && setup_ap) {
struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
if (new_bss == NULL) {
if (added)
nl80211_remove_iface(drv, ifidx);
return -1;
}
if (bridge &&
i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
"interface %s to a bridge %s",
ifname, bridge);
if (added)
nl80211_remove_iface(drv, ifidx);
os_free(new_bss);
return -1;
}
if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
{
if (added)
nl80211_remove_iface(drv, ifidx);
os_free(new_bss);
return -1;
}
os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
os_memcpy(new_bss->addr, if_addr, ETH_ALEN);
new_bss->ifindex = ifidx;
new_bss->drv = drv;
new_bss->next = drv->first_bss->next;
new_bss->freq = drv->first_bss->freq;
new_bss->ctx = bss_ctx;
new_bss->added_if = added;
drv->first_bss->next = new_bss;
if (drv_priv)
*drv_priv = new_bss;
nl80211_init_bss(new_bss);
/* Subscribe management frames for this WPA_IF_AP_BSS */
if (nl80211_setup_ap(new_bss))
return -1;
}
if (drv->global)
drv->global->if_add_ifindex = ifidx;
/*
* Some virtual interfaces need to process EAPOL packets and events on
* the parent interface. This is used mainly with hostapd.
*/
if (ifidx > 0 &&
(drv->hostapd ||
nlmode == NL80211_IFTYPE_AP_VLAN ||
nlmode == NL80211_IFTYPE_WDS ||
nlmode == NL80211_IFTYPE_MONITOR))
add_ifidx(drv, ifidx, IFIDX_ANY);
return 0;
}
static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
enum wpa_driver_if_type type,
const char *ifname)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ifindex = if_nametoindex(ifname);
wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d",
__func__, type, ifname, ifindex, bss->added_if);
if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex))
nl80211_remove_iface(drv, ifindex);
else if (ifindex > 0 && !bss->added_if) {
struct wpa_driver_nl80211_data *drv2;
dl_list_for_each(drv2, &drv->global->interfaces,
struct wpa_driver_nl80211_data, list) {
del_ifidx(drv2, ifindex, IFIDX_ANY);
del_ifidx(drv2, IFIDX_ANY, ifindex);
}
}
if (type != WPA_IF_AP_BSS)
return 0;
if (bss->added_if_into_bridge) {
if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
bss->ifname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"interface %s from bridge %s: %s",
bss->ifname, bss->brname, strerror(errno));
}
if (bss->added_bridge) {
if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
wpa_printf(MSG_INFO, "nl80211: Failed to remove "
"bridge %s: %s",
bss->brname, strerror(errno));
}
if (bss != drv->first_bss) {
struct i802_bss *tbss;
wpa_printf(MSG_DEBUG, "nl80211: Not the first BSS - remove it");
for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
if (tbss->next == bss) {
tbss->next = bss->next;
/* Unsubscribe management frames */
nl80211_teardown_ap(bss);
nl80211_destroy_bss(bss);
if (!bss->added_if)
i802_set_iface_flags(bss, 0);
os_free(bss);
bss = NULL;
break;
}
}
if (bss)
wpa_printf(MSG_INFO, "nl80211: %s - could not find "
"BSS %p in the list", __func__, bss);
} else {
wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
nl80211_teardown_ap(bss);
if (!bss->added_if && !drv->first_bss->next)
wpa_driver_nl80211_del_beacon(bss);
nl80211_destroy_bss(bss);
if (!bss->added_if)
i802_set_iface_flags(bss, 0);
if (drv->first_bss->next) {
drv->first_bss = drv->first_bss->next;
drv->ctx = drv->first_bss->ctx;
os_free(bss);
} else {
wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
}
}
return 0;
}
static int cookie_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
u64 *cookie = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb[NL80211_ATTR_COOKIE])
*cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
return NL_SKIP;
}
static int nl80211_send_frame_cmd(struct i802_bss *bss,
unsigned int freq, unsigned int wait,
const u8 *buf, size_t buf_len,
int save_cookie, int no_cck, int no_ack,
int offchanok, const u16 *csa_offs,
size_t csa_offs_len)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
u64 cookie;
int ret = -1;
wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
"no_ack=%d offchanok=%d",
freq, wait, no_cck, no_ack, offchanok);
wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len);
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) ||
(freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
(wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) ||
(offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
drv->test_use_roc_tx) &&
nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) ||
(no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) ||
(no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) ||
(csa_offs && nla_put(msg, NL80211_ATTR_CSA_C_OFFSETS_TX,
csa_offs_len * sizeof(u16), csa_offs)) ||
nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf))
goto fail;
cookie = 0;
ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
"(%s) (freq=%u wait=%u)", ret, strerror(-ret),
freq, wait);
} else {
wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; "
"cookie 0x%llx", no_ack ? " (no ACK)" : "",
(long long unsigned int) cookie);
if (save_cookie)
drv->send_frame_cookie = no_ack ? (u64) -1 : cookie;
if (drv->num_send_frame_cookies == MAX_SEND_FRAME_COOKIES) {
wpa_printf(MSG_DEBUG,
"nl80211: Drop oldest pending send frame cookie 0x%llx",
(long long unsigned int)
drv->send_frame_cookies[0]);
os_memmove(&drv->send_frame_cookies[0],
&drv->send_frame_cookies[1],
(MAX_SEND_FRAME_COOKIES - 1) *
sizeof(u64));
drv->num_send_frame_cookies--;
}
drv->send_frame_cookies[drv->num_send_frame_cookies] = cookie;
drv->num_send_frame_cookies++;
}
fail:
nlmsg_free(msg);
return ret;
}
static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
unsigned int freq,
unsigned int wait_time,
const u8 *dst, const u8 *src,
const u8 *bssid,
const u8 *data, size_t data_len,
int no_cck)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
int ret = -1;
u8 *buf;
struct ieee80211_hdr *hdr;
int offchanok = 1;
if (is_ap_interface(drv->nlmode) && (int) freq == bss->freq &&
bss->beacon_set)
offchanok = 0;
wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
"freq=%u MHz wait=%d ms no_cck=%d offchanok=%d)",
drv->ifindex, freq, wait_time, no_cck, offchanok);
buf = os_zalloc(24 + data_len);
if (buf == NULL)
return ret;
os_memcpy(buf + 24, data, data_len);
hdr = (struct ieee80211_hdr *) buf;
hdr->frame_control =
IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
os_memcpy(hdr->addr1, dst, ETH_ALEN);
os_memcpy(hdr->addr2, src, ETH_ALEN);
os_memcpy(hdr->addr3, bssid, ETH_ALEN);
if (os_memcmp(bss->addr, src, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "nl80211: Use random TA " MACSTR,
MAC2STR(src));
os_memcpy(bss->rand_addr, src, ETH_ALEN);
} else {
os_memset(bss->rand_addr, 0, ETH_ALEN);
}
#ifdef CONFIG_MESH
if (is_mesh_interface(drv->nlmode)) {
struct hostapd_hw_modes *modes;
u16 num_modes, flags;
u8 dfs_domain;
int i;
modes = nl80211_get_hw_feature_data(bss, &num_modes,
&flags, &dfs_domain);
if (dfs_domain != HOSTAPD_DFS_REGION_ETSI &&
ieee80211_is_dfs(bss->freq, modes, num_modes))
offchanok = 0;
if (modes) {
for (i = 0; i < num_modes; i++) {
os_free(modes[i].channels);
os_free(modes[i].rates);
}
os_free(modes);
}
}
#endif /* CONFIG_MESH */
if (is_ap_interface(drv->nlmode) &&
(!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
(int) freq == bss->freq || drv->device_ap_sme ||
!drv->use_monitor))
ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
0, freq, no_cck, offchanok,
wait_time, NULL, 0, 0);
else
ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
24 + data_len,
1, no_cck, 0, offchanok, NULL, 0);
os_free(buf);
return ret;
}
static void nl80211_frame_wait_cancel(struct i802_bss *bss, u64 cookie)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
(long long unsigned int) cookie);
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) {
nlmsg_free(msg);
return;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
"(%s)", ret, strerror(-ret));
}
static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
unsigned int i;
u64 cookie;
/* Cancel the last pending TX cookie */
nl80211_frame_wait_cancel(bss, drv->send_frame_cookie);
/*
* Cancel the other pending TX cookies, if any. This is needed since
* the driver may keep a list of all pending offchannel TX operations
* and free up the radio only once they have expired or cancelled.
*/
for (i = drv->num_send_frame_cookies; i > 0; i--) {
cookie = drv->send_frame_cookies[i - 1];
if (cookie != drv->send_frame_cookie)
nl80211_frame_wait_cancel(bss, cookie);
}
drv->num_send_frame_cookies = 0;
}
static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
unsigned int duration)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
u64 cookie;
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REMAIN_ON_CHANNEL)) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) {
nlmsg_free(msg);
return -1;
}
cookie = 0;
ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
if (ret == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
"0x%llx for freq=%u MHz duration=%u",
(long long unsigned int) cookie, freq, duration);
drv->remain_on_chan_cookie = cookie;
drv->pending_remain_on_chan = 1;
return 0;
}
wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
"(freq=%d duration=%u): %d (%s)",
freq, duration, ret, strerror(-ret));
return -1;
}
static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
if (!drv->pending_remain_on_chan) {
wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
"to cancel");
return -1;
}
wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
"0x%llx",
(long long unsigned int) drv->remain_on_chan_cookie);
msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
if (!msg ||
nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie)) {
nlmsg_free(msg);
return -1;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret == 0)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
"%d (%s)", ret, strerror(-ret));
return -1;
}
static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!report) {
if (bss->nl_preq && drv->device_ap_sme &&
is_ap_interface(drv->nlmode) && !bss->in_deinit &&
!bss->static_ap) {
/*
* Do not disable Probe Request reporting that was
* enabled in nl80211_setup_ap().
*/
wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
"Probe Request reporting nl_preq=%p while "
"in AP mode", bss->nl_preq);
} else if (bss->nl_preq) {
wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
"reporting nl_preq=%p", bss->nl_preq);
nl80211_destroy_eloop_handle(&bss->nl_preq, 0);
}
return 0;
}
if (bss->nl_preq) {
wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
"already on! nl_preq=%p", bss->nl_preq);
return 0;
}
bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq");
if (bss->nl_preq == NULL)
return -1;
wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request "
"reporting nl_preq=%p", bss->nl_preq);
if (nl80211_register_frame(bss, bss->nl_preq,
(WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_PROBE_REQ << 4),
NULL, 0, false) < 0)
goto out_err;
nl80211_register_eloop_read(&bss->nl_preq,
wpa_driver_nl80211_event_receive,
bss->nl_cb, 0);
return 0;
out_err:
nl_destroy_handles(&bss->nl_preq);
return -1;
}
static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
int ifindex, int disabled)
{
struct nl_msg *msg;
struct nlattr *bands, *band;
int ret;
wpa_printf(MSG_DEBUG,
"nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s)",
ifindex, disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" :
"no NL80211_TXRATE_LEGACY constraint");
msg = nl80211_ifindex_msg(drv, ifindex, 0,
NL80211_CMD_SET_TX_BITRATE_MASK);
if (!msg)
return -1;
bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
if (!bands)
goto fail;
/*
* Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
* else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
* rates. All 5 GHz rates are left enabled.
*/
band = nla_nest_start(msg, NL80211_BAND_2GHZ);
if (!band ||
(disabled && nla_put(msg, NL80211_TXRATE_LEGACY, 8,
"\x0c\x12\x18\x24\x30\x48\x60\x6c")))
goto fail;
nla_nest_end(msg, band);
nla_nest_end(msg, bands);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
"(%s)", ret, strerror(-ret));
} else
drv->disabled_11b_rates = disabled;
return ret;
fail:
nlmsg_free(msg);
return -1;
}
static int wpa_driver_nl80211_deinit_ap(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!is_ap_interface(drv->nlmode))
return -1;
wpa_driver_nl80211_del_beacon(bss);
bss->beacon_set = 0;
/*
* If the P2P GO interface was dynamically added, then it is
* possible that the interface change to station is not possible.
*/
if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic)
return 0;
return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
}
static int wpa_driver_nl80211_stop_ap(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
if (!is_ap_interface(drv->nlmode))
return -1;
wpa_driver_nl80211_del_beacon(bss);
bss->beacon_set = 0;
return 0;
}
static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
return -1;
/*
* If the P2P Client interface was dynamically added, then it is
* possible that the interface change to station is not possible.
*/
if (bss->if_dynamic)
return 0;
return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
}
static void wpa_driver_nl80211_resume(void *priv)
{
struct i802_bss *bss = priv;
enum nl80211_iftype nlmode = nl80211_get_ifmode(bss);
if (i802_set_iface_flags(bss, 1))
wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event");
if (is_p2p_net_interface(nlmode))
nl80211_disable_11b_rates(bss->drv, bss->drv->ifindex, 1);
}
static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *cqm;
wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
"hysteresis=%d", threshold, hysteresis);
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_CQM)) ||
!(cqm = nla_nest_start(msg, NL80211_ATTR_CQM)) ||
nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold) ||
nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis)) {
nlmsg_free(msg);
return -1;
}
nla_nest_end(msg, cqm);
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
static int get_channel_width(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct wpa_signal_info *sig_change = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
sig_change->center_frq1 = -1;
sig_change->center_frq2 = -1;
sig_change->chanwidth = CHAN_WIDTH_UNKNOWN;
if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
sig_change->chanwidth = convert2width(
nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
if (tb[NL80211_ATTR_CENTER_FREQ1])
sig_change->center_frq1 =
nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
if (tb[NL80211_ATTR_CENTER_FREQ2])
sig_change->center_frq2 =
nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
}
return NL_SKIP;
}
static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv,
struct wpa_signal_info *sig)
{
struct nl_msg *msg;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
return send_and_recv_msgs(drv, msg, get_channel_width, sig, NULL, NULL);
}
static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int res;
os_memset(si, 0, sizeof(*si));
res = nl80211_get_link_signal(drv, si);
if (res) {
if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
drv->nlmode != NL80211_IFTYPE_MESH_POINT)
return res;
si->current_signal = 0;
}
res = nl80211_get_channel_width(drv, si);
if (res != 0)
return res;
return nl80211_get_link_noise(drv, si);
}
static int nl80211_set_param(void *priv, const char *param)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
if (param == NULL)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
#ifdef CONFIG_P2P
if (os_strstr(param, "use_p2p_group_interface=1")) {
wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
"interface");
drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
}
#endif /* CONFIG_P2P */
if (os_strstr(param, "use_monitor=1"))
drv->use_monitor = 1;
if (os_strstr(param, "force_connect_cmd=1")) {
drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME;
drv->force_connect_cmd = 1;
}
if (os_strstr(param, "force_bss_selection=1"))
drv->capa.flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
if (os_strstr(param, "no_offchannel_tx=1")) {
drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
drv->test_use_roc_tx = 1;
}
if (os_strstr(param, "control_port=0")) {
drv->capa.flags &= ~WPA_DRIVER_FLAGS_CONTROL_PORT;
drv->capa.flags2 &= ~(WPA_DRIVER_FLAGS2_CONTROL_PORT_RX |
WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS);
drv->control_port_ap = 0;
}
if (os_strstr(param, "control_port_ap=1"))
drv->control_port_ap = 1;
if (os_strstr(param, "control_port_ap=0")) {
drv->capa.flags2 &= ~WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS;
drv->control_port_ap = 0;
}
if (os_strstr(param, "full_ap_client_state=0"))
drv->capa.flags &= ~WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
if (os_strstr(param, "no_rrm=1")) {
drv->no_rrm = 1;
if (!bss->in_deinit && !is_ap_interface(drv->nlmode) &&
!is_mesh_interface(drv->nlmode)) {
nl80211_mgmt_unsubscribe(bss, "no_rrm=1");
if (nl80211_mgmt_subscribe_non_ap(bss) < 0)
wpa_printf(MSG_DEBUG,
"nl80211: Failed to re-register Action frame processing - ignore for now");
}
}
return 0;
}
static void * nl80211_global_init(void *ctx)
{
struct nl80211_global *global;
struct netlink_config *cfg;
global = os_zalloc(sizeof(*global));
if (global == NULL)
return NULL;
global->ctx = ctx;
global->ioctl_sock = -1;
dl_list_init(&global->interfaces);
global->if_add_ifindex = -1;
cfg = os_zalloc(sizeof(*cfg));
if (cfg == NULL)
goto err;
cfg->ctx = global;
cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
global->netlink = netlink_init(cfg);
if (global->netlink == NULL) {
os_free(cfg);
goto err;
}
if (wpa_driver_nl80211_init_nl_global(global) < 0)
goto err;
global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (global->ioctl_sock < 0) {
wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s",
strerror(errno));
goto err;
}
return global;
err:
nl80211_global_deinit(global);
return NULL;
}
static void nl80211_global_deinit(void *priv)
{
struct nl80211_global *global = priv;
if (global == NULL)
return;
if (!dl_list_empty(&global->interfaces)) {
wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at "
"nl80211_global_deinit",
dl_list_len(&global->interfaces));
}
if (global->netlink)
netlink_deinit(global->netlink);
nl_destroy_handles(&global->nl);
if (global->nl_event)
nl80211_destroy_eloop_handle(&global->nl_event, 0);
nl_cb_put(global->nl_cb);
if (global->ioctl_sock >= 0)
close(global->ioctl_sock);
os_free(global);
}
static const char * nl80211_get_radio_name(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
return drv->phyname;
}
static int nl80211_pmkid(struct i802_bss *bss, int cmd,
struct wpa_pmkid_params *params)
{
struct nl_msg *msg;
const size_t PMK_MAX_LEN = 48; /* current cfg80211 limit */
if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
(params->pmkid &&
nla_put(msg, NL80211_ATTR_PMKID, 16, params->pmkid)) ||
(params->bssid &&
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) ||
(params->ssid_len &&
nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
(params->fils_cache_id &&
nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2,
params->fils_cache_id)) ||
(params->pmk_lifetime &&
nla_put_u32(msg, NL80211_ATTR_PMK_LIFETIME,
params->pmk_lifetime)) ||
(params->pmk_reauth_threshold &&
nla_put_u8(msg, NL80211_ATTR_PMK_REAUTH_THRESHOLD,
params->pmk_reauth_threshold)) ||
(cmd != NL80211_CMD_DEL_PMKSA &&
params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return -ENOBUFS;
}
return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
}
static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params)
{
struct i802_bss *bss = priv;
int ret;
if (params->bssid)
wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR,
MAC2STR(params->bssid));
else if (params->fils_cache_id && params->ssid_len) {
wpa_printf(MSG_DEBUG,
"nl80211: Add PMKSA for cache id %02x%02x SSID %s",
params->fils_cache_id[0], params->fils_cache_id[1],
wpa_ssid_txt(params->ssid, params->ssid_len));
}
ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params);
if (ret < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)",
ret, strerror(-ret));
}
return ret;
}
static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params)
{
struct i802_bss *bss = priv;
int ret;
if (params->bssid)
wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
MAC2STR(params->bssid));
else if (params->fils_cache_id && params->ssid_len) {
wpa_printf(MSG_DEBUG,
"nl80211: Delete PMKSA for cache id %02x%02x SSID %s",
params->fils_cache_id[0], params->fils_cache_id[1],
wpa_ssid_txt(params->ssid, params->ssid_len));
}
ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params);
if (ret < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)",
ret, strerror(-ret));
}
return ret;
}
static int nl80211_flush_pmkid(void *priv)
{
struct i802_bss *bss = priv;
struct nl_msg *msg;
wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA);
if (!msg)
return -ENOBUFS;
return send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
}
static void clean_survey_results(struct survey_results *survey_results)
{
struct freq_survey *survey, *tmp;
if (dl_list_empty(&survey_results->survey_list))
return;
dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
struct freq_survey, list) {
dl_list_del(&survey->list);
os_free(survey);
}
}
static void add_survey(struct nlattr **sinfo, u32 ifidx,
struct dl_list *survey_list)
{
struct freq_survey *survey;
survey = os_zalloc(sizeof(struct freq_survey));
if (!survey)
return;
survey->ifidx = ifidx;
survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
survey->filled = 0;
if (sinfo[NL80211_SURVEY_INFO_NOISE]) {
survey->nf = (int8_t)
nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
survey->filled |= SURVEY_HAS_NF;
}
if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) {
survey->channel_time =
nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]);
survey->filled |= SURVEY_HAS_CHAN_TIME;
}
if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) {
survey->channel_time_busy =
nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]);
survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY;
}
if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) {
survey->channel_time_rx =
nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]);
survey->filled |= SURVEY_HAS_CHAN_TIME_RX;
}
if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) {
survey->channel_time_tx =
nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]);
survey->filled |= SURVEY_HAS_CHAN_TIME_TX;
}
wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)",
survey->freq,
survey->nf,
(unsigned long int) survey->channel_time,
(unsigned long int) survey->channel_time_busy,
(unsigned long int) survey->channel_time_tx,
(unsigned long int) survey->channel_time_rx,
survey->filled);
dl_list_add_tail(survey_list, &survey->list);
}
static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq,
unsigned int freq_filter)
{
if (!freq_filter)
return 1;
return freq_filter == surveyed_freq;
}
static int survey_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
struct survey_results *survey_results;
u32 surveyed_freq = 0;
u32 ifidx;
static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
};
survey_results = (struct survey_results *) arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb[NL80211_ATTR_IFINDEX])
return NL_SKIP;
ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
if (!tb[NL80211_ATTR_SURVEY_INFO])
return NL_SKIP;
if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
tb[NL80211_ATTR_SURVEY_INFO],
survey_policy))
return NL_SKIP;
if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) {
wpa_printf(MSG_ERROR, "nl80211: Invalid survey data");
return NL_SKIP;
}
surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
if (!check_survey_ok(sinfo, surveyed_freq,
survey_results->freq_filter))
return NL_SKIP;
if (survey_results->freq_filter &&
survey_results->freq_filter != surveyed_freq) {
wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz",
surveyed_freq);
return NL_SKIP;
}
add_survey(sinfo, ifidx, &survey_results->survey_list);
return NL_SKIP;
}
static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int err;
union wpa_event_data data;
struct survey_results *survey_results;
os_memset(&data, 0, sizeof(data));
survey_results = &data.survey_results;
dl_list_init(&survey_results->survey_list);
msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
if (!msg)
return -ENOBUFS;
if (freq)
data.survey_results.freq_filter = freq;
do {
wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data");
err = send_and_recv_msgs(drv, msg, survey_handler,
survey_results, NULL, NULL);
} while (err > 0);
if (err)
wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data");
else
wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data);
clean_survey_results(survey_results);
return err;
}
static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
const u8 *kck, size_t kck_len,
const u8 *replay_ctr)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nlattr *replay_nested;
struct nl_msg *msg;
int ret;
if (!drv->set_rekey_offload)
return;
wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
!(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
(kck_len && nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck)) ||
nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
replay_ctr)) {
nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return;
}
nla_nest_end(msg, replay_nested);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret == -EOPNOTSUPP) {
wpa_printf(MSG_DEBUG,
"nl80211: Driver does not support rekey offload");
drv->set_rekey_offload = 0;
}
}
static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr,
const u8 *addr, int qos)
{
/* send data frame to poll STA and check whether
* this frame is ACKed */
struct {
struct ieee80211_hdr hdr;
u16 qos_ctl;
} STRUCT_PACKED nulldata;
size_t size;
/* Send data frame to poll STA and check whether this frame is ACKed */
os_memset(&nulldata, 0, sizeof(nulldata));
if (qos) {
nulldata.hdr.frame_control =
IEEE80211_FC(WLAN_FC_TYPE_DATA,
WLAN_FC_STYPE_QOS_NULL);
size = sizeof(nulldata);
} else {
nulldata.hdr.frame_control =
IEEE80211_FC(WLAN_FC_TYPE_DATA,
WLAN_FC_STYPE_NULLFUNC);
size = sizeof(struct ieee80211_hdr);
}
nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
0, 0, NULL, 0, 0) < 0)
wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
"send poll frame");
}
static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
int qos)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
u64 cookie;
int ret;
if (!drv->poll_command_supported) {
nl80211_send_null_frame(bss, own_addr, addr, qos);
return;
}
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_CLIENT)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
nlmsg_free(msg);
return;
}
ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie, NULL, NULL);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
MACSTR " failed: ret=%d (%s)",
MAC2STR(addr), ret, strerror(-ret));
} else {
wpa_printf(MSG_DEBUG,
"nl80211: Client probe request addr=" MACSTR
" cookie=%llu", MAC2STR(addr),
(long long unsigned int) cookie);
}
}
static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
{
struct nl_msg *msg;
int ret;
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) ||
nla_put_u32(msg, NL80211_ATTR_PS_STATE,
enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED)) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL, NULL, NULL);
if (ret < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Setting PS state %s failed: %d (%s)",
enabled ? "enabled" : "disabled",
ret, strerror(-ret));
}
return ret;
}
static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
int ctwindow)
{
struct i802_bss *bss = priv;
wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
"opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow);
if (opp_ps != -1 || ctwindow != -1) {
#ifdef ANDROID_P2P
wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow);
#else /* ANDROID_P2P */
return -1; /* Not yet supported */
#endif /* ANDROID_P2P */
}
if (legacy_ps == -1)
return 0;
if (legacy_ps != 0 && legacy_ps != 1)
return -1; /* Not yet supported */
return nl80211_set_power_save(bss, legacy_ps);
}
static int nl80211_start_radar_detection(void *priv,
struct hostapd_freq_params *freq)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, he_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
freq->freq, freq->ht_enabled, freq->vht_enabled, freq->he_enabled,
freq->bandwidth, freq->center_freq1, freq->center_freq2);
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) {
wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar "
"detection");
return -1;
}
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) ||
nl80211_put_freq_params(msg, freq) < 0) {
nlmsg_free(msg);
return -1;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret == 0)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
"%d (%s)", ret, strerror(-ret));
return -1;
}
#ifdef CONFIG_TDLS
static int nl80211_add_peer_capab(struct nl_msg *msg,
enum tdls_peer_capability capa)
{
u32 peer_capab = 0;
if (!capa)
return 0;
if (capa & TDLS_PEER_HT)
peer_capab |= NL80211_TDLS_PEER_HT;
if (capa & TDLS_PEER_VHT)
peer_capab |= NL80211_TDLS_PEER_VHT;
if (capa & TDLS_PEER_WMM)
peer_capab |= NL80211_TDLS_PEER_WMM;
if (capa & TDLS_PEER_HE)
peer_capab |= NL80211_TDLS_PEER_HE;
return nla_put_u32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY,
peer_capab);
}
static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
u8 dialog_token, u16 status_code,
u32 peer_capab, int initiator, const u8 *buf,
size_t len)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
return -EOPNOTSUPP;
if (!dst)
return -EINVAL;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) ||
nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status_code) ||
nl80211_add_peer_capab(msg, peer_capab) ||
(initiator && nla_put_flag(msg, NL80211_ATTR_TDLS_INITIATOR)) ||
nla_put(msg, NL80211_ATTR_IE, len, buf))
goto fail;
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
fail:
nlmsg_free(msg);
return -ENOBUFS;
}
static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
enum nl80211_tdls_operation nl80211_oper;
int res;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
return -EOPNOTSUPP;
switch (oper) {
case TDLS_DISCOVERY_REQ:
nl80211_oper = NL80211_TDLS_DISCOVERY_REQ;
break;
case TDLS_SETUP:
nl80211_oper = NL80211_TDLS_SETUP;
break;
case TDLS_TEARDOWN:
nl80211_oper = NL80211_TDLS_TEARDOWN;
break;
case TDLS_ENABLE_LINK:
nl80211_oper = NL80211_TDLS_ENABLE_LINK;
break;
case TDLS_DISABLE_LINK:
nl80211_oper = NL80211_TDLS_DISABLE_LINK;
break;
case TDLS_ENABLE:
return 0;
case TDLS_DISABLE:
return 0;
default:
return -EINVAL;
}
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) ||
nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) {
nlmsg_free(msg);
return -ENOBUFS;
}
res = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR
" --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res,
strerror(-res));
return res;
}
static int
nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class,
const struct hostapd_freq_params *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret = -ENOBUFS;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
return -EOPNOTSUPP;
wpa_printf(MSG_DEBUG, "nl80211: Enable TDLS channel switch " MACSTR
" oper_class=%u freq=%u",
MAC2STR(addr), oper_class, params->freq);
msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CHANNEL_SWITCH);
if (!msg ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) ||
(ret = nl80211_put_freq_params(msg, params))) {
nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch");
return ret;
}
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
static int
nl80211_tdls_disable_channel_switch(void *priv, const u8 *addr)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
return -EOPNOTSUPP;
wpa_printf(MSG_DEBUG, "nl80211: Disable TDLS channel switch " MACSTR,
MAC2STR(addr));
msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH);
if (!msg ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
nlmsg_free(msg);
wpa_printf(MSG_DEBUG,
"nl80211: Could not build TDLS cancel chan switch");
return -ENOBUFS;
}
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
#endif /* CONFIG TDLS */
static int driver_nl80211_set_key(void *priv,
struct wpa_driver_set_key_params *params)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_set_key(bss, params);
}
static int driver_nl80211_scan2(void *priv,
struct wpa_driver_scan_params *params)
{
struct i802_bss *bss = priv;
#ifdef CONFIG_DRIVER_NL80211_QCA
struct wpa_driver_nl80211_data *drv = bss->drv;
/*
* Do a vendor specific scan if possible. If only_new_results is
* set, do a normal scan since a kernel (cfg80211) BSS cache flush
* cannot be achieved through a vendor scan. The below condition may
* need to be modified if new scan flags are added in the future whose
* functionality can only be achieved through a normal scan.
*/
if (drv->scan_vendor_cmd_avail && !params->only_new_results)
return wpa_driver_nl80211_vendor_scan(bss, params);
#endif /* CONFIG_DRIVER_NL80211_QCA */
return wpa_driver_nl80211_scan(bss, params);
}
static int driver_nl80211_deauthenticate(void *priv, const u8 *addr,
u16 reason_code)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code);
}
static int driver_nl80211_authenticate(void *priv,
struct wpa_driver_auth_params *params)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_authenticate(bss, params);
}
static void driver_nl80211_deinit(void *priv)
{
struct i802_bss *bss = priv;
wpa_driver_nl80211_deinit(bss);
}
static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type,
const char *ifname)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_if_remove(bss, type, ifname);
}
static int driver_nl80211_send_mlme(void *priv, const u8 *data,
size_t data_len, int noack,
unsigned int freq,
const u16 *csa_offs, size_t csa_offs_len,
int no_encrypt, unsigned int wait)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
freq, 0, 0, wait, csa_offs,
csa_offs_len, no_encrypt);
}
static int driver_nl80211_sta_remove(void *priv, const u8 *addr)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_sta_remove(bss, addr, -1, 0);
}
static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr,
const char *ifname, int vlan_id)
{
struct i802_bss *bss = priv;
return i802_set_sta_vlan(bss, addr, ifname, vlan_id);
}
static int driver_nl80211_read_sta_data(void *priv,
struct hostap_sta_driver_data *data,
const u8 *addr)
{
struct i802_bss *bss = priv;
os_memset(data, 0, sizeof(*data));
return i802_read_sta_data(bss, data, addr);
}
static int driver_nl80211_send_action(void *priv, unsigned int freq,
unsigned int wait_time,
const u8 *dst, const u8 *src,
const u8 *bssid,
const u8 *data, size_t data_len,
int no_cck)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src,
bssid, data, data_len, no_cck);
}
static int driver_nl80211_probe_req_report(void *priv, int report)
{
struct i802_bss *bss = priv;
return wpa_driver_nl80211_probe_req_report(bss, report);
}
static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md,
const u8 *ies, size_t ies_len)
{
int ret;
struct nl_msg *msg;
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
u16 mdid = WPA_GET_LE16(md);
wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs");
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) ||
nla_put(msg, NL80211_ATTR_IE, ies_len, ies) ||
nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed "
"err=%d (%s)", ret, strerror(-ret));
}
return ret;
}
static int nl80211_update_dh_ie(void *priv, const u8 *peer_mac,
u16 reason_code, const u8 *ie, size_t ie_len)
{
int ret;
struct nl_msg *msg;
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
wpa_printf(MSG_DEBUG, "nl80211: Updating DH IE peer: " MACSTR
" reason %u", MAC2STR(peer_mac), reason_code);
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UPDATE_OWE_INFO)) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer_mac) ||
nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, reason_code) ||
(ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie))) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: update_dh_ie failed err=%d (%s)",
ret, strerror(-ret));
}
return ret;
}
static const u8 * wpa_driver_nl80211_get_macaddr(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE)
return NULL;
return bss->addr;
}
static const char * scan_state_str(enum scan_states scan_state)
{
switch (scan_state) {
case NO_SCAN:
return "NO_SCAN";
case SCAN_REQUESTED:
return "SCAN_REQUESTED";
case SCAN_STARTED:
return "SCAN_STARTED";
case SCAN_COMPLETED:
return "SCAN_COMPLETED";
case SCAN_ABORTED:
return "SCAN_ABORTED";
case SCHED_SCAN_STARTED:
return "SCHED_SCAN_STARTED";
case SCHED_SCAN_STOPPED:
return "SCHED_SCAN_STOPPED";
case SCHED_SCAN_RESULTS:
return "SCHED_SCAN_RESULTS";
}
return "??";
}
static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int res;
char *pos, *end;
struct nl_msg *msg;
char alpha2[3] = { 0, 0, 0 };
pos = buf;
end = buf + buflen;
res = os_snprintf(pos, end - pos,
"ifindex=%d\n"
"ifname=%s\n"
"brname=%s\n"
"addr=" MACSTR "\n"
"freq=%d\n"
"%s%s%s%s%s%s",
bss->ifindex,
bss->ifname,
bss->brname,
MAC2STR(bss->addr),
bss->freq,
bss->beacon_set ? "beacon_set=1\n" : "",
bss->added_if_into_bridge ?
"added_if_into_bridge=1\n" : "",
bss->already_in_bridge ? "already_in_bridge=1\n" : "",
bss->added_bridge ? "added_bridge=1\n" : "",
bss->in_deinit ? "in_deinit=1\n" : "",
bss->if_dynamic ? "if_dynamic=1\n" : "");
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
if (bss->wdev_id_set) {
res = os_snprintf(pos, end - pos, "wdev_id=%llu\n",
(unsigned long long) bss->wdev_id);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
res = os_snprintf(pos, end - pos,
"phyname=%s\n"
"perm_addr=" MACSTR "\n"
"drv_ifindex=%d\n"
"operstate=%d\n"
"scan_state=%s\n"
"auth_bssid=" MACSTR "\n"
"auth_attempt_bssid=" MACSTR "\n"
"bssid=" MACSTR "\n"
"prev_bssid=" MACSTR "\n"
"associated=%d\n"
"assoc_freq=%u\n"
"monitor_sock=%d\n"
"monitor_ifidx=%d\n"
"monitor_refcount=%d\n"
"last_mgmt_freq=%u\n"
"eapol_tx_sock=%d\n"
"%s%s%s%s%s%s%s%s%s%s%s%s%s",
drv->phyname,
MAC2STR(drv->perm_addr),
drv->ifindex,
drv->operstate,
scan_state_str(drv->scan_state),
MAC2STR(drv->auth_bssid),
MAC2STR(drv->auth_attempt_bssid),
MAC2STR(drv->bssid),
MAC2STR(drv->prev_bssid),
drv->associated,
drv->assoc_freq,
drv->monitor_sock,
drv->monitor_ifidx,
drv->monitor_refcount,
drv->last_mgmt_freq,
drv->eapol_tx_sock,
drv->ignore_if_down_event ?
"ignore_if_down_event=1\n" : "",
drv->scan_complete_events ?
"scan_complete_events=1\n" : "",
drv->disabled_11b_rates ?
"disabled_11b_rates=1\n" : "",
drv->pending_remain_on_chan ?
"pending_remain_on_chan=1\n" : "",
drv->in_interface_list ? "in_interface_list=1\n" : "",
drv->device_ap_sme ? "device_ap_sme=1\n" : "",
drv->poll_command_supported ?
"poll_command_supported=1\n" : "",
drv->data_tx_status ? "data_tx_status=1\n" : "",
drv->scan_for_auth ? "scan_for_auth=1\n" : "",
drv->retry_auth ? "retry_auth=1\n" : "",
drv->use_monitor ? "use_monitor=1\n" : "",
drv->ignore_next_local_disconnect ?
"ignore_next_local_disconnect=1\n" : "",
drv->ignore_next_local_deauth ?
"ignore_next_local_deauth=1\n" : "");
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
if (drv->has_capability) {
res = os_snprintf(pos, end - pos,
"capa.key_mgmt=0x%x\n"
"capa.enc=0x%x\n"
"capa.auth=0x%x\n"
"capa.flags=0x%llx\n"
"capa.rrm_flags=0x%x\n"
"capa.max_scan_ssids=%d\n"
"capa.max_sched_scan_ssids=%d\n"
"capa.sched_scan_supported=%d\n"
"capa.max_match_sets=%d\n"
"capa.max_remain_on_chan=%u\n"
"capa.max_stations=%u\n"
"capa.probe_resp_offloads=0x%x\n"
"capa.max_acl_mac_addrs=%u\n"
"capa.num_multichan_concurrent=%u\n"
"capa.mac_addr_rand_sched_scan_supported=%d\n"
"capa.mac_addr_rand_scan_supported=%d\n"
"capa.conc_capab=%u\n"
"capa.max_conc_chan_2_4=%u\n"
"capa.max_conc_chan_5_0=%u\n"
"capa.max_sched_scan_plans=%u\n"
"capa.max_sched_scan_plan_interval=%u\n"
"capa.max_sched_scan_plan_iterations=%u\n",
drv->capa.key_mgmt,
drv->capa.enc,
drv->capa.auth,
(unsigned long long) drv->capa.flags,
drv->capa.rrm_flags,
drv->capa.max_scan_ssids,
drv->capa.max_sched_scan_ssids,
drv->capa.sched_scan_supported,
drv->capa.max_match_sets,
drv->capa.max_remain_on_chan,
drv->capa.max_stations,
drv->capa.probe_resp_offloads,
drv->capa.max_acl_mac_addrs,
drv->capa.num_multichan_concurrent,
drv->capa.mac_addr_rand_sched_scan_supported,
drv->capa.mac_addr_rand_scan_supported,
drv->capa.conc_capab,
drv->capa.max_conc_chan_2_4,
drv->capa.max_conc_chan_5_0,
drv->capa.max_sched_scan_plans,
drv->capa.max_sched_scan_plan_interval,
drv->capa.max_sched_scan_plan_iterations);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
msg = nlmsg_alloc();
if (msg &&
nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG) &&
nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx) == 0) {
if (send_and_recv_msgs(drv, msg, nl80211_get_country,
alpha2, NULL, NULL) == 0 &&
alpha2[0]) {
res = os_snprintf(pos, end - pos, "country=%s\n",
alpha2);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
} else {
nlmsg_free(msg);
}
return pos - buf;
}
static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings)
{
if ((settings->head &&
nla_put(msg, NL80211_ATTR_BEACON_HEAD,
settings->head_len, settings->head)) ||
(settings->tail &&
nla_put(msg, NL80211_ATTR_BEACON_TAIL,
settings->tail_len, settings->tail)) ||
(settings->beacon_ies &&
nla_put(msg, NL80211_ATTR_IE,
settings->beacon_ies_len, settings->beacon_ies)) ||
(settings->proberesp_ies &&
nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
settings->proberesp_ies_len, settings->proberesp_ies)) ||
(settings->assocresp_ies &&
nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
settings->assocresp_ies_len, settings->assocresp_ies)) ||
(settings->probe_resp &&
nla_put(msg, NL80211_ATTR_PROBE_RESP,
settings->probe_resp_len, settings->probe_resp)))
return -ENOBUFS;
return 0;
}
static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
{
struct nl_msg *msg;
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nlattr *beacon_csa;
int ret = -ENOBUFS;
int csa_off_len = 0;
int i;
wpa_printf(MSG_DEBUG,
"nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d channel=%d sec_channel_offset=%d width=%d cf1=%d cf2=%d%s%s%s)",
settings->cs_count, settings->block_tx,
settings->freq_params.freq,
settings->freq_params.channel,
settings->freq_params.sec_channel_offset,
settings->freq_params.bandwidth,
settings->freq_params.center_freq1,
settings->freq_params.center_freq2,
settings->freq_params.ht_enabled ? " ht" : "",
settings->freq_params.vht_enabled ? " vht" : "",
settings->freq_params.he_enabled ? " he" : "");
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_AP_CSA)) {
wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command");
return -EOPNOTSUPP;
}
if (drv->nlmode != NL80211_IFTYPE_AP &&
drv->nlmode != NL80211_IFTYPE_P2P_GO &&
drv->nlmode != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
/*
* Remove empty counters, assuming Probe Response and Beacon frame
* counters match. This implementation assumes that there are only two
* counters.
*/
if (settings->counter_offset_beacon[0] &&
!settings->counter_offset_beacon[1]) {
csa_off_len = 1;
} else if (settings->counter_offset_beacon[1] &&
!settings->counter_offset_beacon[0]) {
csa_off_len = 1;
settings->counter_offset_beacon[0] =
settings->counter_offset_beacon[1];
settings->counter_offset_presp[0] =
settings->counter_offset_presp[1];
} else if (settings->counter_offset_beacon[1] &&
settings->counter_offset_beacon[0]) {
csa_off_len = 2;
} else {
wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided");
return -EINVAL;
}
/* Check CSA counters validity */
if (drv->capa.max_csa_counters &&
csa_off_len > drv->capa.max_csa_counters) {
wpa_printf(MSG_ERROR,
"nl80211: Too many CSA counters provided");
return -EINVAL;
}
if (!settings->beacon_csa.tail)
return -EINVAL;
for (i = 0; i < csa_off_len; i++) {
u16 csa_c_off_bcn = settings->counter_offset_beacon[i];
u16 csa_c_off_presp = settings->counter_offset_presp[i];
if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) ||
(settings->beacon_csa.tail[csa_c_off_bcn] !=
settings->cs_count))
return -EINVAL;
if (settings->beacon_csa.probe_resp &&
((settings->beacon_csa.probe_resp_len <=
csa_c_off_presp) ||
(settings->beacon_csa.probe_resp[csa_c_off_presp] !=
settings->cs_count)))
return -EINVAL;
}
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) ||
nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT,
settings->cs_count) ||
(ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
(settings->block_tx &&
nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)))
goto error;
/* beacon_after params */
ret = set_beacon_data(msg, &settings->beacon_after);
if (ret)
goto error;
/* beacon_csa params */
beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
if (!beacon_csa)
goto fail;
ret = set_beacon_data(msg, &settings->beacon_csa);
if (ret)
goto error;
if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
csa_off_len * sizeof(u16),
settings->counter_offset_beacon) ||
(settings->beacon_csa.probe_resp &&
nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
csa_off_len * sizeof(u16),
settings->counter_offset_presp)))
goto fail;
nla_nest_end(msg, beacon_csa);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)",
ret, strerror(-ret));
}
return ret;
fail:
ret = -ENOBUFS;
error:
nlmsg_free(msg);
wpa_printf(MSG_DEBUG, "nl80211: Could not build channel switch request");
return ret;
}
static int nl80211_add_ts(void *priv, u8 tsid, const u8 *addr,
u8 user_priority, u16 admitted_time)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
wpa_printf(MSG_DEBUG,
"nl80211: add_ts request: tsid=%u admitted_time=%u up=%d",
tsid, admitted_time, user_priority);
if (!is_sta_interface(drv->nlmode))
return -ENOTSUP;
msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ADD_TX_TS);
if (!msg ||
nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
nla_put_u8(msg, NL80211_ATTR_USER_PRIO, user_priority) ||
nla_put_u16(msg, NL80211_ATTR_ADMITTED_TIME, admitted_time)) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)",
ret, strerror(-ret));
return ret;
}
static int nl80211_del_ts(void *priv, u8 tsid, const u8 *addr)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: del_ts request: tsid=%u", tsid);
if (!is_sta_interface(drv->nlmode))
return -ENOTSUP;
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_TX_TS)) ||
nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)",
ret, strerror(-ret));
return ret;
}
#ifdef CONFIG_TESTING_OPTIONS
static int cmd_reply_handler(struct nl_msg *msg, void *arg)
{
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct wpabuf *buf = arg;
if (!buf)
return NL_SKIP;
if ((size_t) genlmsg_attrlen(gnlh, 0) > wpabuf_tailroom(buf)) {
wpa_printf(MSG_INFO, "nl80211: insufficient buffer space for reply");
return NL_SKIP;
}
wpabuf_put_data(buf, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0));
return NL_SKIP;
}
#endif /* CONFIG_TESTING_OPTIONS */
static int vendor_reply_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct nlattr *nl_vendor_reply, *nl;
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct wpabuf *buf = arg;
int rem;
if (!buf)
return NL_SKIP;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
nl_vendor_reply = tb[NL80211_ATTR_VENDOR_DATA];
if (!nl_vendor_reply)
return NL_SKIP;
if ((size_t) nla_len(nl_vendor_reply) > wpabuf_tailroom(buf)) {
wpa_printf(MSG_INFO, "nl80211: Vendor command: insufficient buffer space for reply");
return NL_SKIP;
}
nla_for_each_nested(nl, nl_vendor_reply, rem) {
wpabuf_put_data(buf, nla_data(nl), nla_len(nl));
}
return NL_SKIP;
}
static bool is_cmd_with_nested_attrs(unsigned int vendor_id,
unsigned int subcmd)
{
if (vendor_id != OUI_QCA)
return true;
switch (subcmd) {
case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
case QCA_NL80211_VENDOR_SUBCMD_STATS_EXT:
case QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI:
case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY:
case QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS:
case QCA_NL80211_VENDOR_SUBCMD_NAN:
return false;
default:
return true;
}
}
static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
unsigned int subcmd, const u8 *data,
size_t data_len, enum nested_attr nested_attr,
struct wpabuf *buf)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret, nla_flag;
#ifdef CONFIG_TESTING_OPTIONS
if (vendor_id == 0xffffffff) {
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
nl80211_cmd(drv, msg, 0, subcmd);
if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) <
0)
goto fail;
/* This test vendor_cmd can be used with nl80211 commands that
* need the connect nl_sock, so use the owner-setting variant
* of send_and_recv_msgs(). */
ret = send_and_recv_msgs_owner(drv, msg,
get_connect_handle(bss), 0,
cmd_reply_handler, buf,
NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
ret);
return ret;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (nested_attr == NESTED_ATTR_USED)
nla_flag = NLA_F_NESTED;
else if (nested_attr == NESTED_ATTR_UNSPECIFIED &&
is_cmd_with_nested_attrs(vendor_id, subcmd))
nla_flag = NLA_F_NESTED;
else
nla_flag = 0;
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, vendor_id) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) ||
(data &&
nla_put(msg, nla_flag | NL80211_ATTR_VENDOR_DATA,
data_len, data)))
goto fail;
ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf,
NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d",
ret);
return ret;
fail:
nlmsg_free(msg);
return -ENOBUFS;
}
static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set,
u8 qos_map_set_len)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
qos_map_set, qos_map_set_len);
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) ||
nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
return ret;
}
static int get_wowlan_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
int *wowlan_enabled = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
*wowlan_enabled = !!tb[NL80211_ATTR_WOWLAN_TRIGGERS];
return NL_SKIP;
}
static int nl80211_get_wowlan(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int wowlan_enabled;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status");
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_WOWLAN);
ret = send_and_recv_msgs(drv, msg, get_wowlan_handler, &wowlan_enabled,
NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Getting wowlan status failed");
return 0;
}
wpa_printf(MSG_DEBUG, "nl80211: wowlan is %s",
wowlan_enabled ? "enabled" : "disabled");
return wowlan_enabled;
}
static int nl80211_set_wowlan(void *priv,
const struct wowlan_triggers *triggers)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *wowlan_triggers;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan");
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_WOWLAN)) ||
!(wowlan_triggers = nla_nest_start(msg,
NL80211_ATTR_WOWLAN_TRIGGERS)) ||
(triggers->any &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
(triggers->disconnect &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
(triggers->magic_pkt &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
(triggers->gtk_rekey_failure &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
(triggers->eap_identity_req &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
(triggers->four_way_handshake &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
(triggers->rfkill_release &&
nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) {
nlmsg_free(msg);
return -ENOBUFS;
}
nla_nest_end(msg, wowlan_triggers);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret)
wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed");
return ret;
}
#ifdef CONFIG_DRIVER_NL80211_QCA
static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *params;
wpa_printf(MSG_DEBUG, "nl80211: Roaming policy: allowed=%d", allowed);
if (!drv->roaming_vendor_cmd_avail) {
wpa_printf(MSG_DEBUG,
"nl80211: Ignore roaming policy change since driver does not provide command for setting it");
return -1;
}
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_ROAMING) ||
!(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY,
allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS :
QCA_ROAMING_NOT_ALLOWED) ||
(bssid &&
nla_put(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid))) {
nlmsg_free(msg);
return -1;
}
nla_nest_end(msg, params);
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
static int nl80211_disable_fils(void *priv, int disable)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *params;
wpa_printf(MSG_DEBUG, "nl80211: Disable FILS=%d", disable);
if (!drv->set_wifi_conf_vendor_cmd_avail)
return -1;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) ||
!(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS,
disable)) {
nlmsg_free(msg);
return -1;
}
nla_nest_end(msg, params);
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */
#define WPA_SUPPLICANT_CLIENT_ID 1
static int nl80211_set_bssid_tmp_disallow(void *priv, unsigned int num_bssid,
const u8 *bssid)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *params, *nlbssids, *attr;
unsigned int i;
wpa_printf(MSG_DEBUG,
"nl80211: Set temporarily disallowed BSSIDs (num=%u)",
num_bssid);
if (!drv->roam_vendor_cmd_avail)
return -1;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_ROAM) ||
!(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD,
QCA_WLAN_VENDOR_ROAMING_SUBCMD_SET_BLACKLIST_BSSID) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID,
WPA_SUPPLICANT_CLIENT_ID) ||
nla_put_u32(msg,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID,
num_bssid))
goto fail;
nlbssids = nla_nest_start(
msg, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS);
if (!nlbssids)
goto fail;
for (i = 0; i < num_bssid; i++) {
attr = nla_nest_start(msg, i);
if (!attr)
goto fail;
if (nla_put(msg,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID,
ETH_ALEN, &bssid[i * ETH_ALEN]))
goto fail;
wpa_printf(MSG_DEBUG, "nl80211: BSSID[%u]: " MACSTR, i,
MAC2STR(&bssid[i * ETH_ALEN]));
nla_nest_end(msg, attr);
}
nla_nest_end(msg, nlbssids);
nla_nest_end(msg, params);
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
fail:
nlmsg_free(msg);
return -1;
}
static int nl80211_add_sta_node(void *priv, const u8 *addr, u16 auth_alg)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *params;
if (!drv->add_sta_node_vendor_cmd_avail)
return -EOPNOTSUPP;
wpa_printf(MSG_DEBUG, "nl80211: Add STA node");
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE) ||
!(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
(addr &&
nla_put(msg, QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_MAC_ADDR, ETH_ALEN,
addr)) ||
nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ADD_STA_NODE_AUTH_ALGO,
auth_alg)) {
nlmsg_free(msg);
wpa_printf(MSG_ERROR,
"%s: err in adding vendor_cmd and vendor_data",
__func__);
return -1;
}
nla_nest_end(msg, params);
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
static int nl80211_set_mac_addr(void *priv, const u8 *addr)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int new_addr = addr != NULL;
if (TEST_FAIL())
return -1;
if (!addr)
addr = drv->perm_addr;
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) < 0)
return -1;
if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr) < 0)
{
wpa_printf(MSG_DEBUG,
"nl80211: failed to set_mac_addr for %s to " MACSTR,
bss->ifname, MAC2STR(addr));
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
1) < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Could not restore interface UP after failed set_mac_addr");
}
return -1;
}
wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
bss->ifname, MAC2STR(addr));
drv->addr_changed = new_addr;
os_memcpy(bss->addr, addr, ETH_ALEN);
if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
{
wpa_printf(MSG_DEBUG,
"nl80211: Could not restore interface UP after set_mac_addr");
}
return 0;
}
#ifdef CONFIG_MESH
static int wpa_driver_nl80211_init_mesh(void *priv)
{
if (wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_MESH_POINT)) {
wpa_printf(MSG_INFO,
"nl80211: Failed to set interface into mesh mode");
return -1;
}
return 0;
}
static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
size_t mesh_id_len)
{
if (mesh_id) {
wpa_printf(MSG_DEBUG, " * Mesh ID (SSID)=%s",
wpa_ssid_txt(mesh_id, mesh_id_len));
return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id);
}
return 0;
}
static int nl80211_put_mesh_config(struct nl_msg *msg,
struct wpa_driver_mesh_bss_params *params)
{
struct nlattr *container;
container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
if (!container)
return -1;
if (((params->flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
params->auto_plinks)) ||
((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) &&
nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
params->max_peer_links)) ||
((params->flags & WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD) &&
nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
params->rssi_threshold)))
return -1;
/*
* Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
* the timer could disconnect stations even in that case.
*/
if ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT) &&
nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
params->peer_link_timeout)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
return -1;
}
if ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE) &&
nla_put_u16(msg, NL80211_MESHCONF_HT_OPMODE, params->ht_opmode)) {
wpa_printf(MSG_ERROR, "nl80211: Failed to set HT_OP_MODE");
return -1;
}
nla_nest_end(msg, container);
return 0;
}
static int nl80211_join_mesh(struct i802_bss *bss,
struct wpa_driver_mesh_join_params *params)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *container;
int ret = -1;
wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
if (!msg ||
nl80211_put_freq_params(msg, &params->freq) ||
nl80211_put_basic_rates(msg, params->basic_rates) ||
nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
nl80211_put_beacon_int(msg, params->beacon_int) ||
nl80211_put_dtim_period(msg, params->dtim_period))
goto fail;
wpa_printf(MSG_DEBUG, " * flags=%08X", params->flags);
if (params->handle_dfs && nla_put_flag(msg, NL80211_ATTR_HANDLE_DFS))
goto fail;
container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
if (!container)
goto fail;
if (params->ies) {
wpa_hexdump(MSG_DEBUG, " * IEs", params->ies, params->ie_len);
if (nla_put(msg, NL80211_MESH_SETUP_IE, params->ie_len,
params->ies))
goto fail;
}
/* WPA_DRIVER_MESH_FLAG_OPEN_AUTH is treated as default by nl80211 */
if (params->flags & WPA_DRIVER_MESH_FLAG_SAE_AUTH) {
if (nla_put_u8(msg, NL80211_MESH_SETUP_AUTH_PROTOCOL, 0x1) ||
nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AUTH))
goto fail;
}
if ((params->flags & WPA_DRIVER_MESH_FLAG_AMPE) &&
nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AMPE))
goto fail;
if ((params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) &&
nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_MPM))
goto fail;
nla_nest_end(msg, container);
params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT;
params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS;
if (nl80211_put_mesh_config(msg, &params->conf) < 0)
goto fail;
ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 1);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)",
ret, strerror(-ret));
goto fail;
}
ret = 0;
drv->assoc_freq = bss->freq = params->freq.freq;
wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
fail:
nlmsg_free(msg);
return ret;
}
static int
wpa_driver_nl80211_join_mesh(void *priv,
struct wpa_driver_mesh_join_params *params)
{
struct i802_bss *bss = priv;
int ret, timeout;
timeout = params->conf.peer_link_timeout;
/* Disable kernel inactivity timer */
if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM)
params->conf.peer_link_timeout = 0;
ret = nl80211_join_mesh(bss, params);
if (ret == -EINVAL && params->conf.peer_link_timeout == 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Mesh join retry for peer_link_timeout");
/*
* Old kernel does not support setting
* NL80211_MESHCONF_PLINK_TIMEOUT to zero, so set 60 seconds
* into future from peer_link_timeout.
*/
params->conf.peer_link_timeout = timeout + 60;
ret = nl80211_join_mesh(priv, params);
}
params->conf.peer_link_timeout = timeout;
return ret;
}
static int wpa_driver_nl80211_leave_mesh(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
ret = send_and_recv_msgs_connect_handle(drv, msg, bss, 0);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
ret, strerror(-ret));
} else {
wpa_printf(MSG_DEBUG,
"nl80211: mesh leave request send successfully");
}
if (drv->start_mode_sta &&
wpa_driver_nl80211_set_mode(drv->first_bss,
NL80211_IFTYPE_STATION)) {
wpa_printf(MSG_INFO,
"nl80211: Failed to set interface into station mode");
}
return ret;
}
static int nl80211_probe_mesh_link(void *priv, const u8 *addr, const u8 *eth,
size_t len)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_PROBE_MESH_LINK);
if (!msg ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
nla_put(msg, NL80211_ATTR_FRAME, len, eth)) {
nlmsg_free(msg);
return -ENOBUFS;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: mesh link probe to " MACSTR
" failed: ret=%d (%s)",
MAC2STR(addr), ret, strerror(-ret));
} else {
wpa_printf(MSG_DEBUG, "nl80211: Mesh link to " MACSTR
" probed successfully", MAC2STR(addr));
}
return ret;
}
#endif /* CONFIG_MESH */
static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
const u8 *ipaddr, int prefixlen,
const u8 *addr)
{
#ifdef CONFIG_LIBNL3_ROUTE
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct rtnl_neigh *rn;
struct nl_addr *nl_ipaddr = NULL;
struct nl_addr *nl_lladdr = NULL;
int family, addrsize;
int res;
if (!ipaddr || prefixlen == 0 || !addr)
return -EINVAL;
if (bss->br_ifindex == 0) {
wpa_printf(MSG_DEBUG,
"nl80211: bridge must be set before adding an ip neigh to it");
return -1;
}
if (!drv->rtnl_sk) {
wpa_printf(MSG_DEBUG,
"nl80211: nl_sock for NETLINK_ROUTE is not initialized");
return -1;
}
if (version == 4) {
family = AF_INET;
addrsize = 4;
} else if (version == 6) {
family = AF_INET6;
addrsize = 16;
} else {
return -EINVAL;
}
rn = rtnl_neigh_alloc();
if (rn == NULL)
return -ENOMEM;
/* set the destination ip address for neigh */
nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
if (nl_ipaddr == NULL) {
wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
res = -ENOMEM;
goto errout;
}
nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
res = rtnl_neigh_set_dst(rn, nl_ipaddr);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: neigh set destination addr failed");
goto errout;
}
/* set the corresponding lladdr for neigh */
nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
if (nl_lladdr == NULL) {
wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
res = -ENOMEM;
goto errout;
}
rtnl_neigh_set_lladdr(rn, nl_lladdr);
rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
rtnl_neigh_set_state(rn, NUD_PERMANENT);
res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: Adding bridge ip neigh failed: %s",
nl_geterror(res));
}
errout:
if (nl_lladdr)
nl_addr_put(nl_lladdr);
if (nl_ipaddr)
nl_addr_put(nl_ipaddr);
if (rn)
rtnl_neigh_put(rn);
return res;
#else /* CONFIG_LIBNL3_ROUTE */
return -1;
#endif /* CONFIG_LIBNL3_ROUTE */
}
static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
const u8 *ipaddr)
{
#ifdef CONFIG_LIBNL3_ROUTE
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct rtnl_neigh *rn;
struct nl_addr *nl_ipaddr;
int family, addrsize;
int res;
if (!ipaddr)
return -EINVAL;
if (version == 4) {
family = AF_INET;
addrsize = 4;
} else if (version == 6) {
family = AF_INET6;
addrsize = 16;
} else {
return -EINVAL;
}
if (bss->br_ifindex == 0) {
wpa_printf(MSG_DEBUG,
"nl80211: bridge must be set to delete an ip neigh");
return -1;
}
if (!drv->rtnl_sk) {
wpa_printf(MSG_DEBUG,
"nl80211: nl_sock for NETLINK_ROUTE is not initialized");
return -1;
}
rn = rtnl_neigh_alloc();
if (rn == NULL)
return -ENOMEM;
/* set the destination ip address for neigh */
nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
if (nl_ipaddr == NULL) {
wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
res = -ENOMEM;
goto errout;
}
res = rtnl_neigh_set_dst(rn, nl_ipaddr);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: neigh set destination addr failed");
goto errout;
}
rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
if (res) {
wpa_printf(MSG_DEBUG,
"nl80211: Deleting bridge ip neigh failed: %s",
nl_geterror(res));
}
errout:
if (nl_ipaddr)
nl_addr_put(nl_ipaddr);
if (rn)
rtnl_neigh_put(rn);
return res;
#else /* CONFIG_LIBNL3_ROUTE */
return -1;
#endif /* CONFIG_LIBNL3_ROUTE */
}
static int linux_write_system_file(const char *path, unsigned int val)
{
char buf[50];
int fd, len;
len = os_snprintf(buf, sizeof(buf), "%u\n", val);
if (os_snprintf_error(sizeof(buf), len))
return -1;
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
if (write(fd, buf, len) < 0) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to write Linux system file: %s with the value of %d",
path, val);
close(fd);
return -1;
}
close(fd);
return 0;
}
static const char * drv_br_port_attr_str(enum drv_br_port_attr attr)
{
switch (attr) {
case DRV_BR_PORT_ATTR_PROXYARP:
return "proxyarp_wifi";
case DRV_BR_PORT_ATTR_HAIRPIN_MODE:
return "hairpin_mode";
}
return NULL;
}
static int wpa_driver_br_port_set_attr(void *priv, enum drv_br_port_attr attr,
unsigned int val)
{
struct i802_bss *bss = priv;
char path[128];
const char *attr_txt;
attr_txt = drv_br_port_attr_str(attr);
if (attr_txt == NULL)
return -EINVAL;
os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/%s",
bss->ifname, attr_txt);
if (linux_write_system_file(path, val))
return -1;
return 0;
}
static const char * drv_br_net_param_str(enum drv_br_net_param param)
{
switch (param) {
case DRV_BR_NET_PARAM_GARP_ACCEPT:
return "arp_accept";
default:
return NULL;
}
}
static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
unsigned int val)
{
struct i802_bss *bss = priv;
char path[128];
const char *param_txt;
int ip_version = 4;
if (param == DRV_BR_MULTICAST_SNOOPING) {
os_snprintf(path, sizeof(path),
"/sys/devices/virtual/net/%s/bridge/multicast_snooping",
bss->brname);
goto set_val;
}
param_txt = drv_br_net_param_str(param);
if (param_txt == NULL)
return -EINVAL;
switch (param) {
case DRV_BR_NET_PARAM_GARP_ACCEPT:
ip_version = 4;
break;
default:
return -EINVAL;
}
os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
ip_version, bss->brname, param_txt);
set_val:
if (linux_write_system_file(path, val))
return -1;
return 0;
}
#ifdef CONFIG_DRIVER_NL80211_QCA
static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
{
switch (hw_mode) {
case HOSTAPD_MODE_IEEE80211B:
return QCA_ACS_MODE_IEEE80211B;
case HOSTAPD_MODE_IEEE80211G:
return QCA_ACS_MODE_IEEE80211G;
case HOSTAPD_MODE_IEEE80211A:
return QCA_ACS_MODE_IEEE80211A;
case HOSTAPD_MODE_IEEE80211AD:
return QCA_ACS_MODE_IEEE80211AD;
case HOSTAPD_MODE_IEEE80211ANY:
return QCA_ACS_MODE_IEEE80211ANY;
default:
return -1;
}
}
static int add_acs_ch_list(struct nl_msg *msg, const int *freq_list)
{
int num_channels = 0, num_freqs;
u8 *ch_list;
enum hostapd_hw_mode hw_mode;
int ret = 0;
int i;
if (!freq_list)
return 0;
num_freqs = int_array_len(freq_list);
ch_list = os_malloc(sizeof(u8) * num_freqs);
if (!ch_list)
return -1;
for (i = 0; i < num_freqs; i++) {
const int freq = freq_list[i];
if (freq == 0)
break;
/* Send 2.4 GHz and 5 GHz channels with
* QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST to maintain backwards
* compatibility.
*/
if (!(freq >= 2412 && freq <= 2484) &&
!(freq >= 5180 && freq <= 5900) &&
!(freq >= 5945 && freq <= 7115))
continue;
hw_mode = ieee80211_freq_to_chan(freq, &ch_list[num_channels]);
if (hw_mode != NUM_HOSTAPD_MODES)
num_channels++;
}
if (num_channels)
ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
num_channels, ch_list);
os_free(ch_list);
return ret;
}
static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
{
int i, len, ret;
u32 *freqs;
if (!freq_list)
return 0;
len = int_array_len(freq_list);
freqs = os_malloc(sizeof(u32) * len);
if (!freqs)
return -1;
for (i = 0; i < len; i++)
freqs[i] = freq_list[i];
ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
sizeof(u32) * len, freqs);
os_free(freqs);
return ret;
}
static int nl80211_qca_do_acs(struct wpa_driver_nl80211_data *drv,
struct drv_acs_params *params)
{
struct nl_msg *msg;
struct nlattr *data;
int ret;
int mode;
mode = hw_mode_to_qca_acs(params->hw_mode);
if (mode < 0)
return -1;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_DO_ACS) ||
!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, mode) ||
(params->ht_enabled &&
nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) ||
(params->ht40_enabled &&
nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) ||
(params->vht_enabled &&
nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) ||
nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
params->ch_width) ||
add_acs_ch_list(msg, params->freq_list) ||
add_acs_freq_list(msg, params->freq_list) ||
(params->edmg_enabled &&
nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_EDMG_ENABLED))) {
nlmsg_free(msg);
return -ENOBUFS;
}
nla_nest_end(msg, data);
wpa_printf(MSG_DEBUG,
"nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d EDMG: %d",
params->hw_mode, params->ht_enabled, params->ht40_enabled,
params->vht_enabled, params->ch_width, params->edmg_enabled);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to invoke driver ACS function: %s",
strerror(-ret));
}
return ret;
}
static int nl80211_set_band(void *priv, u32 band_mask)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *data;
int ret;
enum qca_set_band qca_band_value;
u32 qca_band_mask = QCA_SETBAND_AUTO;
if (!drv->setband_vendor_cmd_avail ||
(band_mask > (WPA_SETBAND_2G | WPA_SETBAND_5G | WPA_SETBAND_6G)))
return -1;
if (band_mask & WPA_SETBAND_5G)
qca_band_mask |= QCA_SETBAND_5G;
if (band_mask & WPA_SETBAND_2G)
qca_band_mask |= QCA_SETBAND_2G;
if (band_mask & WPA_SETBAND_6G)
qca_band_mask |= QCA_SETBAND_6G;
/*
* QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE is a legacy interface hence make
* it suite to its values (AUTO/5G/2G) for backwards compatibility.
*/
qca_band_value = ((qca_band_mask & QCA_SETBAND_5G) &&
(qca_band_mask & QCA_SETBAND_2G)) ?
QCA_SETBAND_AUTO :
qca_band_mask & ~QCA_SETBAND_6G;
wpa_printf(MSG_DEBUG,
"nl80211: QCA_BAND_MASK = 0x%x, QCA_BAND_VALUE = %d",
qca_band_mask, qca_band_value);
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_SETBAND) ||
!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE,
qca_band_value) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SETBAND_MASK,
qca_band_mask)) {
nlmsg_free(msg);
return -ENOBUFS;
}
nla_nest_end(msg, data);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Driver setband function failed: %s",
strerror(-ret));
}
return ret;
}
struct nl80211_pcl {
unsigned int num;
unsigned int *freq_list;
};
static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nl80211_pcl *param = arg;
struct nlattr *nl_vend, *attr;
enum qca_iface_type iface_type;
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
unsigned int num, max_num;
u32 *freqs;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
if (!nl_vend)
return NL_SKIP;
nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
nla_data(nl_vend), nla_len(nl_vend), NULL);
attr = tb_vendor[
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE];
if (!attr) {
wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found");
param->num = 0;
return NL_SKIP;
}
iface_type = (enum qca_iface_type) nla_get_u32(attr);
wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d",
iface_type);
attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
if (!attr) {
wpa_printf(MSG_ERROR,
"nl80211: preferred_freq_list couldn't be found");
param->num = 0;
return NL_SKIP;
}
/*
* param->num has the maximum number of entries for which there
* is room in the freq_list provided by the caller.
*/
freqs = nla_data(attr);
max_num = nla_len(attr) / sizeof(u32);
if (max_num > param->num)
max_num = param->num;
for (num = 0; num < max_num; num++)
param->freq_list[num] = freqs[num];
param->num = num;
return NL_SKIP;
}
static int nl80211_get_pref_freq_list(void *priv,
enum wpa_driver_if_type if_type,
unsigned int *num,
unsigned int *freq_list)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
unsigned int i;
struct nlattr *params;
struct nl80211_pcl param;
enum qca_iface_type iface_type;
if (!drv->get_pref_freq_list)
return -1;
switch (if_type) {
case WPA_IF_STATION:
iface_type = QCA_IFACE_TYPE_STA;
break;
case WPA_IF_AP_BSS:
iface_type = QCA_IFACE_TYPE_AP;
break;
case WPA_IF_P2P_GO:
iface_type = QCA_IFACE_TYPE_P2P_GO;
break;
case WPA_IF_P2P_CLIENT:
iface_type = QCA_IFACE_TYPE_P2P_CLIENT;
break;
case WPA_IF_IBSS:
iface_type = QCA_IFACE_TYPE_IBSS;
break;
case WPA_IF_TDLS:
iface_type = QCA_IFACE_TYPE_TDLS;
break;
default:
return -1;
}
param.num = *num;
param.freq_list = freq_list;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST) ||
!(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
nla_put_u32(msg,
QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
iface_type)) {
wpa_printf(MSG_ERROR,
"%s: err in adding vendor_cmd and vendor_data",
__func__);
nlmsg_free(msg);
return -1;
}
nla_nest_end(msg, params);
os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param,
NULL, NULL);
if (ret) {
wpa_printf(MSG_ERROR,
"%s: err in send_and_recv_msgs", __func__);
return ret;
}
*num = param.num;
for (i = 0; i < *num; i++) {
wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d",
i, freq_list[i]);
}
return 0;
}
static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret;
struct nlattr *params;
if (!drv->set_prob_oper_freq)
return -1;
wpa_printf(MSG_DEBUG,
"nl80211: Set P2P probable operating freq %u for ifindex %d",
freq, bss->ifindex);
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) ||
!(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
nla_put_u32(msg,
QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
QCA_IFACE_TYPE_P2P_CLIENT) ||
nla_put_u32(msg,
QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
freq)) {
wpa_printf(MSG_ERROR,
"%s: err in adding vendor_cmd and vendor_data",
__func__);
nlmsg_free(msg);
return -1;
}
nla_nest_end(msg, params);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
__func__);
return ret;
}
nlmsg_free(msg);
return 0;
}
static int nl80211_p2p_lo_start(void *priv, unsigned int freq,
unsigned int period, unsigned int interval,
unsigned int count, const u8 *device_types,
size_t dev_types_len,
const u8 *ies, size_t ies_len)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *container;
int ret;
wpa_printf(MSG_DEBUG,
"nl80211: Start P2P Listen offload: freq=%u, period=%u, interval=%u, count=%u",
freq, period, interval, count);
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
return -1;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START))
goto fail;
container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
if (!container)
goto fail;
if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL,
freq) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD,
period) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL,
interval) ||
nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT,
count) ||
nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES,
dev_types_len, device_types) ||
nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE,
ies_len, ies))
goto fail;
nla_nest_end(msg, container);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to send P2P Listen offload vendor command");
goto fail;
}
return 0;
fail:
nlmsg_free(msg);
return -1;
}
static int nl80211_p2p_lo_stop(void *priv)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
wpa_printf(MSG_DEBUG, "nl80211: Stop P2P Listen offload");
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
return -1;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP)) {
nlmsg_free(msg);
return -1;
}
return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
}
static int nl80211_set_tdls_mode(void *priv, int tdls_external_control)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *params;
int ret;
u32 tdls_mode;
wpa_printf(MSG_DEBUG,
"nl80211: Set TDKS mode: tdls_external_control=%d",
tdls_external_control);
if (tdls_external_control == 1)
tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT |
QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL;
else
tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS))
goto fail;
params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
if (!params)
goto fail;
if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE,
tdls_mode))
goto fail;
nla_nest_end(msg, params);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: Set TDLS mode failed: ret=%d (%s)",
ret, strerror(-ret));
goto fail;
}
return 0;
fail:
nlmsg_free(msg);
return -1;
}
#ifdef CONFIG_MBO
static enum mbo_transition_reject_reason
nl80211_mbo_reject_reason_mapping(enum qca_wlan_btm_candidate_status status)
{
switch (status) {
case QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED:
return MBO_TRANSITION_REJECT_REASON_FRAME_LOSS;
case QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED:
return MBO_TRANSITION_REJECT_REASON_DELAY;
case QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY:
return MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY;
case QCA_STATUS_REJECT_LOW_RSSI:
return MBO_TRANSITION_REJECT_REASON_RSSI;
case QCA_STATUS_REJECT_HIGH_INTERFERENCE:
return MBO_TRANSITION_REJECT_REASON_INTERFERENCE;
case QCA_STATUS_REJECT_UNKNOWN:
default:
return MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
}
}
static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate,
struct nlattr *tb[], int num)
{
enum qca_wlan_btm_candidate_status status;
char buf[50];
os_memcpy(candidate->bssid,
nla_data(tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]),
ETH_ALEN);
status = nla_get_u32(
tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]);
candidate->is_accept = status == QCA_STATUS_ACCEPT;
candidate->reject_reason = nl80211_mbo_reject_reason_mapping(status);
if (candidate->is_accept)
os_snprintf(buf, sizeof(buf), "Accepted");
else
os_snprintf(buf, sizeof(buf),
"Rejected, Reject_reason: %d",
candidate->reject_reason);
wpa_printf(MSG_DEBUG, "nl80211: BSSID[%d]: " MACSTR " %s",
num, MAC2STR(candidate->bssid), buf);
}
static int
nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg)
{
struct wpa_bss_candidate_info *info = arg;
struct candidate_list *candidate = info->candidates;
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1];
static struct nla_policy policy[
QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] = {
[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = {
.minlen = ETH_ALEN
},
[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = {
.type = NLA_U32,
},
};
struct nlattr *attr;
int rem;
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
u8 num;
num = info->num; /* number of candidates sent to driver */
info->num = 0;
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb_msg[NL80211_ATTR_VENDOR_DATA] ||
nla_parse_nested(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
tb_msg[NL80211_ATTR_VENDOR_DATA], NULL) ||
!tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO])
return NL_SKIP;
wpa_printf(MSG_DEBUG,
"nl80211: WNM Candidate list received from driver");
nla_for_each_nested(attr,
tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO],
rem) {
if (info->num >= num ||
nla_parse_nested(
tb, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX,
attr, policy) ||
!tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] ||
!tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS])
break;
nl80211_parse_btm_candidate_info(candidate, tb, info->num);
candidate++;
info->num++;
}
return NL_SKIP;
}
static struct wpa_bss_candidate_info *
nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *attr, *attr1, *attr2;
struct wpa_bss_candidate_info *info;
u8 i;
int ret;
u8 *pos;
if (!drv->fetch_bss_trans_status)
return NULL;
info = os_zalloc(sizeof(*info));
if (!info)
return NULL;
/* Allocate memory for number of candidates sent to driver */
info->candidates = os_calloc(params->n_candidates,
sizeof(*info->candidates));
if (!info->candidates) {
os_free(info);
return NULL;
}
/* Copy the number of candidates being sent to driver. This is used in
* nl80211_get_bss_transition_status_handler() to limit the number of
* candidates that can be populated in info->candidates and will be
* later overwritten with the actual number of candidates received from
* the driver.
*/
info->num = params->n_candidates;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS))
goto fail;
attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
if (!attr)
goto fail;
if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON,
params->mbo_transition_reason))
goto fail;
attr1 = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO);
if (!attr1)
goto fail;
wpa_printf(MSG_DEBUG,
"nl80211: WNM Candidate list info sending to driver: mbo_transition_reason: %d n_candidates: %d",
params->mbo_transition_reason, params->n_candidates);
pos = params->bssid;
for (i = 0; i < params->n_candidates; i++) {
wpa_printf(MSG_DEBUG, "nl80211: BSSID[%d]: " MACSTR, i,
MAC2STR(pos));
attr2 = nla_nest_start(msg, i);
if (!attr2 ||
nla_put(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID,
ETH_ALEN, pos))
goto fail;
pos += ETH_ALEN;
nla_nest_end(msg, attr2);
}
nla_nest_end(msg, attr1);
nla_nest_end(msg, attr);
ret = send_and_recv_msgs(drv, msg,
nl80211_get_bss_transition_status_handler,
info, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: WNM Get BSS transition status failed: ret=%d (%s)",
ret, strerror(-ret));
goto fail;
}
return info;
fail:
nlmsg_free(msg);
os_free(info->candidates);
os_free(info);
return NULL;
}
/**
* nl80211_ignore_assoc_disallow - Configure driver to ignore assoc_disallow
* @priv: Pointer to private driver data from wpa_driver_nl80211_init()
* @ignore_assoc_disallow: 0 to not ignore, 1 to ignore
* Returns: 0 on success, -1 on failure
*/
static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
struct nlattr *attr;
int ret = -1;
if (!drv->set_wifi_conf_vendor_cmd_avail)
return -1;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
goto fail;
attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
if (!attr)
goto fail;
wpa_printf(MSG_DEBUG, "nl80211: Set ignore_assoc_disallow %d",
ignore_disallow);
if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED,
ignore_disallow))
goto fail;
nla_nest_end(msg, attr);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: Set ignore_assoc_disallow failed: ret=%d (%s)",
ret, strerror(-ret));
goto fail;
}
fail:
nlmsg_free(msg);
return ret;
}
#endif /* CONFIG_MBO */
#endif /* CONFIG_DRIVER_NL80211_QCA */
#ifdef CONFIG_DRIVER_NL80211_BRCM
static int wpa_driver_do_broadcom_acs(struct wpa_driver_nl80211_data *drv,
struct drv_acs_params *params)
{
struct nl_msg *msg;
struct nlattr *data;
int freq_list_len;
int ret = -1;
freq_list_len = int_array_len(params->freq_list);
wpa_printf(MSG_DEBUG, "%s: freq_list_len=%d",
__func__, freq_list_len);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
if (!msg ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_BRCM) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
BRCM_VENDOR_SCMD_ACS) ||
!(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HW_MODE, params->hw_mode) ||
nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT_ENABLED,
params->ht_enabled) ||
nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_HT40_ENABLED,
params->ht40_enabled) ||
nla_put_u8(msg, BRCM_VENDOR_ATTR_ACS_VHT_ENABLED,
params->vht_enabled) ||
nla_put_u16(msg, BRCM_VENDOR_ATTR_ACS_CHWIDTH, params->ch_width) ||
(freq_list_len > 0 &&
nla_put(msg, BRCM_VENDOR_ATTR_ACS_FREQ_LIST,
sizeof(int) * freq_list_len, params->freq_list)))
goto fail;
nla_nest_end(msg, data);
wpa_printf(MSG_DEBUG,
"nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d",
params->hw_mode, params->ht_enabled, params->ht40_enabled,
params->vht_enabled, params->ch_width);
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: BRCM Failed to invoke driver ACS function: %s",
strerror(errno));
}
msg = NULL;
fail:
nlmsg_free(msg);
return ret;
}
#endif /* CONFIG_DRIVER_NL80211_BRCM */
static int nl80211_do_acs(void *priv, struct drv_acs_params *params)
{
#if defined(CONFIG_DRIVER_NL80211_QCA) || defined(CONFIG_DRIVER_NL80211_BRCM)
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
#endif /* CONFIG_DRIVER_NL80211_QCA || CONFIG_DRIVER_NL80211_BRCM */
#ifdef CONFIG_DRIVER_NL80211_QCA
if (drv->qca_do_acs)
return nl80211_qca_do_acs(drv, params);
#endif /* CONFIG_DRIVER_NL80211_QCA */
#ifdef CONFIG_DRIVER_NL80211_BRCM
if (drv->brcm_do_acs)
return wpa_driver_do_broadcom_acs(drv, params);
#endif /* CONFIG_DRIVER_NL80211_BRCM */
return -1;
}
static int nl80211_write_to_file(const char *name, unsigned int val)
{
int fd, len;
char tmp[128];
int ret = 0;
fd = open(name, O_RDWR);
if (fd < 0) {
int level;
/*
* Flags may not exist on older kernels, or while we're tearing
* down a disappearing device.
*/
if (errno == ENOENT) {
ret = 0;
level = MSG_DEBUG;
} else {
ret = -1;
level = MSG_ERROR;
}
wpa_printf(level, "nl80211: Failed to open %s: %s",
name, strerror(errno));
return ret;
}
len = os_snprintf(tmp, sizeof(tmp), "%u\n", val);
len = write(fd, tmp, len);
if (len < 0) {
ret = -1;
wpa_printf(MSG_ERROR, "nl80211: Failed to write to %s: %s",
name, strerror(errno));
}
close(fd);
return ret;
}
static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags)
{
struct i802_bss *bss = priv;
char path[128];
int ret;
/* P2P-Device has no netdev that can (or should) be configured here */
if (nl80211_get_ifmode(bss) == NL80211_IFTYPE_P2P_DEVICE)
return 0;
wpa_printf(MSG_DEBUG, "nl80211: Data frame filter flags=0x%x",
filter_flags);
/* Configure filtering of unicast frame encrypted using GTK */
ret = os_snprintf(path, sizeof(path),
"/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast",
bss->ifname);
if (os_snprintf_error(sizeof(path), ret))
return -1;
ret = nl80211_write_to_file(path,
!!(filter_flags &
WPA_DATA_FRAME_FILTER_FLAG_GTK));
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: Failed to set IPv4 unicast in multicast filter");
return ret;
}
os_snprintf(path, sizeof(path),
"/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast",
bss->ifname);
ret = nl80211_write_to_file(path,
!!(filter_flags &
WPA_DATA_FRAME_FILTER_FLAG_GTK));
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: Failed to set IPv6 unicast in multicast filter");
return ret;
}
/* Configure filtering of unicast frame encrypted using GTK */
os_snprintf(path, sizeof(path),
"/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp",
bss->ifname);
ret = nl80211_write_to_file(path,
!!(filter_flags &
WPA_DATA_FRAME_FILTER_FLAG_ARP));
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: Failed set gratuitous ARP filter");
return ret;
}
/* Configure filtering of IPv6 NA frames */
os_snprintf(path, sizeof(path),
"/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na",
bss->ifname);
ret = nl80211_write_to_file(path,
!!(filter_flags &
WPA_DATA_FRAME_FILTER_FLAG_NA));
if (ret) {
wpa_printf(MSG_ERROR,
"nl80211: Failed to set unsolicited NA filter");
return ret;
}
return 0;
}
static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type,
const u8 **ext_capa, const u8 **ext_capa_mask,
unsigned int *ext_capa_len)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
enum nl80211_iftype nlmode;
unsigned int i;
if (!ext_capa || !ext_capa_mask || !ext_capa_len)
return -1;
nlmode = wpa_driver_nl80211_if_type(type);
/* By default, use the per-radio values */
*ext_capa = drv->extended_capa;
*ext_capa_mask = drv->extended_capa_mask;
*ext_capa_len = drv->extended_capa_len;
/* Replace the default value if a per-interface type value exists */
for (i = 0; i < drv->num_iface_ext_capa; i++) {
if (nlmode == drv->iface_ext_capa[i].iftype) {
*ext_capa = drv->iface_ext_capa[i].ext_capa;
*ext_capa_mask = drv->iface_ext_capa[i].ext_capa_mask;
*ext_capa_len = drv->iface_ext_capa[i].ext_capa_len;
break;
}
}
return 0;
}
static int nl80211_update_connection_params(
void *priv, struct wpa_driver_associate_params *params,
enum wpa_drv_update_connect_params_mask mask)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret = -1;
enum nl80211_auth_type type;
/* Update Connection Params is intended for drivers that implement
* internal SME and expect these updated connection params from
* wpa_supplicant. Do not send this request for the drivers using
* SME from wpa_supplicant.
*/
if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
return 0;
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS);
if (!msg)
goto fail;
wpa_printf(MSG_DEBUG, "nl80211: Update connection params (ifindex=%d)",
drv->ifindex);
if ((mask & WPA_DRV_UPDATE_ASSOC_IES) && params->wpa_ie) {
if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
params->wpa_ie))
goto fail;
wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie,
params->wpa_ie_len);
}
if (mask & WPA_DRV_UPDATE_AUTH_TYPE) {
type = get_nl_auth_type(params->auth_alg);
if (type == NL80211_AUTHTYPE_MAX ||
nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
goto fail;
wpa_printf(MSG_DEBUG, " * Auth Type %d", type);
}
if ((mask & WPA_DRV_UPDATE_FILS_ERP_INFO) &&
nl80211_put_fils_connect_params(drv, params, msg))
goto fail;
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret)
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: Update connect params command failed: ret=%d (%s)",
ret, strerror(-ret));
fail:
nlmsg_free(msg);
return ret;
}
static int nl80211_send_external_auth_status(void *priv,
struct external_auth *params)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg = NULL;
int ret = -1;
/* External auth command/status is intended for drivers that implement
* internal SME but want to offload authentication processing (e.g.,
* SAE) to hostapd/wpa_supplicant. Do not send the status to drivers
* which do not support AP SME or use wpa_supplicant/hostapd SME.
*/
if ((is_ap_interface(drv->nlmode) && !bss->drv->device_ap_sme) ||
(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
return -1;
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: External auth status: %u", params->status);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH);
if (!msg ||
nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) ||
(params->ssid && params->ssid_len &&
nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
(params->pmkid &&
nla_put(msg, NL80211_ATTR_PMKID, PMKID_LEN, params->pmkid)) ||
(params->bssid &&
nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)))
goto fail;
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret) {
wpa_printf(MSG_DEBUG,
"nl80211: External Auth status update failed: ret=%d (%s)",
ret, strerror(-ret));
goto fail;
}
fail:
nlmsg_free(msg);
return ret;
}
static int nl80211_set_4addr_mode(void *priv, const char *bridge_ifname,
int val)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
int ret = -ENOBUFS;
wpa_printf(MSG_DEBUG, "nl80211: %s 4addr mode (bridge_ifname: %s)",
val ? "Enable" : "Disable", bridge_ifname);
msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
if (!msg || nla_put_u8(msg, NL80211_ATTR_4ADDR, val))
goto fail;
if (bridge_ifname[0] && bss->added_if_into_bridge && !val) {
if (linux_br_del_if(drv->global->ioctl_sock,
bridge_ifname, bss->ifname)) {
wpa_printf(MSG_ERROR,
"nl80211: Failed to remove interface %s from bridge %s",
bss->ifname, bridge_ifname);
return -1;
}
bss->added_if_into_bridge = 0;
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
msg = NULL;
if (ret && val && nl80211_get_4addr(bss) == 1) {
wpa_printf(MSG_DEBUG,
"nl80211: 4addr mode was already enabled");
ret = 0;
}
if (!ret) {
if (bridge_ifname[0] && val &&
i802_check_bridge(drv, bss, bridge_ifname, bss->ifname) < 0)
return -1;
return 0;
}
fail:
nlmsg_free(msg);
wpa_printf(MSG_ERROR, "nl80211: Failed to enable/disable 4addr");
return ret;
}
#ifdef CONFIG_DPP
static int nl80211_dpp_listen(void *priv, bool enable)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
struct nl_sock *handle;
if (!drv->multicast_registrations || !bss->nl_mgmt)
return 0; /* cannot do more than hope broadcast RX works */
wpa_printf(MSG_DEBUG,
"nl80211: Update DPP Public Action frame registration (%s multicast RX)",
enable ? "enable" : "disable");
handle = (void *) (((intptr_t) bss->nl_mgmt) ^ ELOOP_SOCKET_INVALID);
return nl80211_register_frame(bss, handle, type,
(u8 *) "\x04\x09\x50\x6f\x9a\x1a", 6,
enable);
}
#endif /* CONFIG_DPP */
#ifdef CONFIG_TESTING_OPTIONS
static int testing_nl80211_register_frame(void *priv, u16 type,
const u8 *match, size_t match_len,
bool multicast)
{
struct i802_bss *bss = priv;
struct nl_sock *handle;
if (!bss->nl_mgmt)
return -1;
handle = (void *) (((intptr_t) bss->nl_mgmt) ^ ELOOP_SOCKET_INVALID);
return nl80211_register_frame(bss, handle, type, match, match_len,
multicast);
}
#endif /* CONFIG_TESTING_OPTIONS */
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
.get_bssid = wpa_driver_nl80211_get_bssid,
.get_ssid = wpa_driver_nl80211_get_ssid,
.set_key = driver_nl80211_set_key,
.scan2 = driver_nl80211_scan2,
.sched_scan = wpa_driver_nl80211_sched_scan,
.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
.abort_scan = wpa_driver_nl80211_abort_scan,
.deauthenticate = driver_nl80211_deauthenticate,
.authenticate = driver_nl80211_authenticate,
.associate = wpa_driver_nl80211_associate,
.global_init = nl80211_global_init,
.global_deinit = nl80211_global_deinit,
.init2 = wpa_driver_nl80211_init,
.deinit = driver_nl80211_deinit,
.get_capa = wpa_driver_nl80211_get_capa,
.set_operstate = wpa_driver_nl80211_set_operstate,
.set_supp_port = wpa_driver_nl80211_set_supp_port,
.set_country = wpa_driver_nl80211_set_country,
.get_country = wpa_driver_nl80211_get_country,
.set_ap = wpa_driver_nl80211_set_ap,
.set_acl = wpa_driver_nl80211_set_acl,
.if_add = wpa_driver_nl80211_if_add,
.if_remove = driver_nl80211_if_remove,
.send_mlme = driver_nl80211_send_mlme,
.get_hw_feature_data = nl80211_get_hw_feature_data,
.sta_add = wpa_driver_nl80211_sta_add,
.sta_remove = driver_nl80211_sta_remove,
.tx_control_port = nl80211_tx_control_port,
.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
.sta_set_airtime_weight = driver_nl80211_sta_set_airtime_weight,
.hapd_init = i802_init,
.hapd_deinit = i802_deinit,
.set_wds_sta = i802_set_wds_sta,
.get_seqnum = i802_get_seqnum,
.flush = i802_flush,
.get_inact_sec = i802_get_inact_sec,
.sta_clear_stats = i802_sta_clear_stats,
.set_rts = i802_set_rts,
.set_frag = i802_set_frag,
.set_tx_queue_params = i802_set_tx_queue_params,
.set_sta_vlan = driver_nl80211_set_sta_vlan,
.sta_deauth = i802_sta_deauth,
.sta_disassoc = i802_sta_disassoc,
.read_sta_data = driver_nl80211_read_sta_data,
.set_freq = i802_set_freq,
.send_action = driver_nl80211_send_action,
.send_action_cancel_wait = wpa_driver_nl80211_send_action_cancel_wait,
.remain_on_channel = wpa_driver_nl80211_remain_on_channel,
.cancel_remain_on_channel =
wpa_driver_nl80211_cancel_remain_on_channel,
.probe_req_report = driver_nl80211_probe_req_report,
.deinit_ap = wpa_driver_nl80211_deinit_ap,
.deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
.resume = wpa_driver_nl80211_resume,
.signal_monitor = nl80211_signal_monitor,
.signal_poll = nl80211_signal_poll,
.channel_info = nl80211_channel_info,
.set_param = nl80211_set_param,
.get_radio_name = nl80211_get_radio_name,
.add_pmkid = nl80211_add_pmkid,
.remove_pmkid = nl80211_remove_pmkid,
.flush_pmkid = nl80211_flush_pmkid,
.set_rekey_info = nl80211_set_rekey_info,
.poll_client = nl80211_poll_client,
.set_p2p_powersave = nl80211_set_p2p_powersave,
.start_dfs_cac = nl80211_start_radar_detection,
.stop_ap = wpa_driver_nl80211_stop_ap,
#ifdef CONFIG_TDLS
.send_tdls_mgmt = nl80211_send_tdls_mgmt,
.tdls_oper = nl80211_tdls_oper,
.tdls_enable_channel_switch = nl80211_tdls_enable_channel_switch,
.tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch,
#endif /* CONFIG_TDLS */
.update_ft_ies = wpa_driver_nl80211_update_ft_ies,
.update_dh_ie = nl80211_update_dh_ie,
.get_mac_addr = wpa_driver_nl80211_get_macaddr,
.get_survey = wpa_driver_nl80211_get_survey,
.status = wpa_driver_nl80211_status,
.switch_channel = nl80211_switch_channel,
#ifdef ANDROID_P2P
.set_noa = wpa_driver_set_p2p_noa,
.get_noa = wpa_driver_get_p2p_noa,
.set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
#endif /* ANDROID_P2P */
#ifdef ANDROID
#ifndef ANDROID_LIB_STUB
.driver_cmd = wpa_driver_nl80211_driver_cmd,
#endif /* !ANDROID_LIB_STUB */
#endif /* ANDROID */
.vendor_cmd = nl80211_vendor_cmd,
.set_qos_map = nl80211_set_qos_map,
.get_wowlan = nl80211_get_wowlan,
.set_wowlan = nl80211_set_wowlan,
.set_mac_addr = nl80211_set_mac_addr,
#ifdef CONFIG_MESH
.init_mesh = wpa_driver_nl80211_init_mesh,
.join_mesh = wpa_driver_nl80211_join_mesh,
.leave_mesh = wpa_driver_nl80211_leave_mesh,
.probe_mesh_link = nl80211_probe_mesh_link,
#endif /* CONFIG_MESH */
.br_add_ip_neigh = wpa_driver_br_add_ip_neigh,
.br_delete_ip_neigh = wpa_driver_br_delete_ip_neigh,
.br_port_set_attr = wpa_driver_br_port_set_attr,
.br_set_net_param = wpa_driver_br_set_net_param,
.add_tx_ts = nl80211_add_ts,
.del_tx_ts = nl80211_del_ts,
.get_ifindex = nl80211_get_ifindex,
#ifdef CONFIG_DRIVER_NL80211_QCA
.roaming = nl80211_roaming,
.disable_fils = nl80211_disable_fils,
.set_band = nl80211_set_band,
.get_pref_freq_list = nl80211_get_pref_freq_list,
.set_prob_oper_freq = nl80211_set_prob_oper_freq,
.p2p_lo_start = nl80211_p2p_lo_start,
.p2p_lo_stop = nl80211_p2p_lo_stop,
.set_default_scan_ies = nl80211_set_default_scan_ies,
.set_tdls_mode = nl80211_set_tdls_mode,
#ifdef CONFIG_MBO
.get_bss_transition_status = nl80211_get_bss_transition_status,
.ignore_assoc_disallow = nl80211_ignore_assoc_disallow,
#endif /* CONFIG_MBO */
.set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow,
.add_sta_node = nl80211_add_sta_node,
#endif /* CONFIG_DRIVER_NL80211_QCA */
.do_acs = nl80211_do_acs,
.configure_data_frame_filters = nl80211_configure_data_frame_filters,
.get_ext_capab = nl80211_get_ext_capab,
.update_connect_params = nl80211_update_connection_params,
.send_external_auth_status = nl80211_send_external_auth_status,
.set_4addr_mode = nl80211_set_4addr_mode,
#ifdef CONFIG_DPP
.dpp_listen = nl80211_dpp_listen,
#endif /* CONFIG_DPP */
#ifdef CONFIG_TESTING_OPTIONS
.register_frame = testing_nl80211_register_frame,
#endif /* CONFIG_TESTING_OPTIONS */
};
diff --git a/contrib/wpa/src/drivers/driver_nl80211.h b/contrib/wpa/src/drivers/driver_nl80211.h
index 9d61c1d6930f..80d4564721ad 100644
--- a/contrib/wpa/src/drivers/driver_nl80211.h
+++ b/contrib/wpa/src/drivers/driver_nl80211.h
@@ -1,335 +1,336 @@
/*
* Driver interaction with Linux nl80211/cfg80211 - definitions
* Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
* Copyright (c) 2003-2004, Instant802 Networks, Inc.
* Copyright (c) 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef DRIVER_NL80211_H
#define DRIVER_NL80211_H
#include "nl80211_copy.h"
#include "utils/list.h"
#include "driver.h"
#ifndef NL_CAPABILITY_VERSION_3_5_0
#define nla_nest_start(msg, attrtype) \
nla_nest_start(msg, NLA_F_NESTED | (attrtype))
#endif
struct nl80211_global {
void *ctx;
struct dl_list interfaces;
int if_add_ifindex;
u64 if_add_wdevid;
int if_add_wdevid_set;
struct netlink_data *netlink;
struct nl_cb *nl_cb;
struct nl_sock *nl;
int nl80211_id;
+ int nlctrl_id;
int ioctl_sock; /* socket for ioctl() use */
struct nl_sock *nl_event;
};
struct nl80211_wiphy_data {
struct dl_list list;
struct dl_list bsss;
struct dl_list drvs;
struct nl_sock *nl_beacons;
struct nl_cb *nl_cb;
int wiphy_idx;
};
struct i802_bss {
struct wpa_driver_nl80211_data *drv;
struct i802_bss *next;
int ifindex;
int br_ifindex;
u64 wdev_id;
char ifname[IFNAMSIZ + 1];
char brname[IFNAMSIZ];
unsigned int beacon_set:1;
unsigned int added_if_into_bridge:1;
unsigned int already_in_bridge:1;
unsigned int added_bridge:1;
unsigned int in_deinit:1;
unsigned int wdev_id_set:1;
unsigned int added_if:1;
unsigned int static_ap:1;
unsigned int use_nl_connect:1;
u8 addr[ETH_ALEN];
int freq;
int bandwidth;
int if_dynamic;
void *ctx;
struct nl_sock *nl_preq, *nl_mgmt, *nl_connect;
struct nl_cb *nl_cb;
struct nl80211_wiphy_data *wiphy_data;
struct dl_list wiphy_list;
u8 rand_addr[ETH_ALEN];
};
struct drv_nl80211_if_info {
int ifindex;
/* the AP/AP_VLAN iface that is in this bridge */
int reason;
};
struct wpa_driver_nl80211_data {
struct nl80211_global *global;
struct dl_list list;
struct dl_list wiphy_list;
char phyname[32];
unsigned int wiphy_idx;
u8 perm_addr[ETH_ALEN];
void *ctx;
int ifindex;
int if_removed;
int if_disabled;
int ignore_if_down_event;
struct rfkill_data *rfkill;
struct wpa_driver_capa capa;
u8 *extended_capa, *extended_capa_mask;
unsigned int extended_capa_len;
struct drv_nl80211_ext_capa {
enum nl80211_iftype iftype;
u8 *ext_capa, *ext_capa_mask;
unsigned int ext_capa_len;
} iface_ext_capa[NL80211_IFTYPE_MAX];
unsigned int num_iface_ext_capa;
int has_capability;
int has_driver_key_mgmt;
int operstate;
int scan_complete_events;
enum scan_states {
NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED,
SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED,
SCHED_SCAN_RESULTS
} scan_state;
u8 auth_bssid[ETH_ALEN];
u8 auth_attempt_bssid[ETH_ALEN];
u8 bssid[ETH_ALEN];
u8 prev_bssid[ETH_ALEN];
int associated;
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
enum nl80211_iftype nlmode;
enum nl80211_iftype ap_scan_as_station;
unsigned int assoc_freq;
int monitor_sock;
int monitor_ifidx;
int monitor_refcount;
unsigned int disabled_11b_rates:1;
unsigned int pending_remain_on_chan:1;
unsigned int in_interface_list:1;
unsigned int device_ap_sme:1;
unsigned int poll_command_supported:1;
unsigned int data_tx_status:1;
unsigned int scan_for_auth:1;
unsigned int retry_auth:1;
unsigned int use_monitor:1;
unsigned int ignore_next_local_disconnect:1;
unsigned int ignore_next_local_deauth:1;
unsigned int hostapd:1;
unsigned int start_mode_sta:1;
unsigned int start_iface_up:1;
unsigned int test_use_roc_tx:1;
unsigned int ignore_deauth_event:1;
unsigned int vendor_cmd_test_avail:1;
unsigned int roaming_vendor_cmd_avail:1;
unsigned int dfs_vendor_cmd_avail:1;
unsigned int have_low_prio_scan:1;
unsigned int force_connect_cmd:1;
unsigned int addr_changed:1;
unsigned int get_features_vendor_cmd_avail:1;
unsigned int set_rekey_offload:1;
unsigned int p2p_go_ctwindow_supported:1;
unsigned int setband_vendor_cmd_avail:1;
unsigned int get_pref_freq_list:1;
unsigned int set_prob_oper_freq:1;
unsigned int scan_vendor_cmd_avail:1;
unsigned int connect_reassoc:1;
unsigned int set_wifi_conf_vendor_cmd_avail:1;
unsigned int fetch_bss_trans_status:1;
unsigned int roam_vendor_cmd_avail:1;
unsigned int add_sta_node_vendor_cmd_avail:1;
unsigned int control_port_ap:1;
unsigned int multicast_registrations:1;
unsigned int no_rrm:1;
unsigned int get_sta_info_vendor_cmd_avail:1;
unsigned int fils_discovery:1;
unsigned int unsol_bcast_probe_resp:1;
unsigned int qca_do_acs:1;
unsigned int brcm_do_acs:1;
u64 vendor_scan_cookie;
u64 remain_on_chan_cookie;
u64 send_frame_cookie;
#define MAX_SEND_FRAME_COOKIES 20
u64 send_frame_cookies[MAX_SEND_FRAME_COOKIES];
unsigned int num_send_frame_cookies;
u64 eapol_tx_cookie;
unsigned int last_mgmt_freq;
struct wpa_driver_scan_filter *filter_ssids;
size_t num_filter_ssids;
struct i802_bss *first_bss;
int eapol_tx_sock;
int eapol_sock; /* socket for EAPOL frames */
struct nl_sock *rtnl_sk; /* nl_sock for NETLINK_ROUTE */
struct drv_nl80211_if_info default_if_indices[16];
struct drv_nl80211_if_info *if_indices;
int num_if_indices;
/* From failed authentication command */
int auth_freq;
u8 auth_bssid_[ETH_ALEN];
u8 auth_ssid[SSID_MAX_LEN];
size_t auth_ssid_len;
int auth_alg;
u8 *auth_ie;
size_t auth_ie_len;
u8 *auth_data;
size_t auth_data_len;
u8 auth_wep_key[4][16];
size_t auth_wep_key_len[4];
int auth_wep_tx_keyidx;
int auth_local_state_change;
int auth_p2p;
/*
* Tells whether the last scan issued from wpa_supplicant was a normal
* scan (NL80211_CMD_TRIGGER_SCAN) or a vendor scan
* (NL80211_CMD_VENDOR). 0 if no pending scan request.
*/
int last_scan_cmd;
#ifdef CONFIG_DRIVER_NL80211_QCA
bool roam_indication_done;
u8 *pending_roam_data;
size_t pending_roam_data_len;
struct os_reltime pending_roam_ind_time;
#endif /* CONFIG_DRIVER_NL80211_QCA */
};
struct nl_msg;
void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg, int flags, uint8_t cmd);
struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd);
struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
uint8_t cmd);
struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg,
int (*valid_handler)(struct nl_msg *, void *),
void *valid_data,
int (*ack_handler_custom)(struct nl_msg *, void *),
void *ack_data);
struct nl_sock * get_connect_handle(struct i802_bss *bss);
int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
const char *ifname, enum nl80211_iftype iftype,
const u8 *addr, int wds,
int (*handler)(struct nl_msg *, void *),
void *arg, int use_existing);
void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid);
enum chan_width convert2width(int width);
void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
int ifindex);
int is_ap_interface(enum nl80211_iftype nlmode);
int is_sta_interface(enum nl80211_iftype nlmode);
int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
struct wpa_signal_info *sig);
int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
struct wpa_signal_info *sig_change);
int nl80211_get_wiphy_index(struct i802_bss *bss);
int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
enum nl80211_iftype nlmode);
int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
const u8 *addr, int cmd, u16 reason_code,
int local_state_change,
struct i802_bss *bss);
int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
const void *data, size_t len,
int encrypt, int noack);
int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
struct hostapd_hw_modes *
nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
u8 *dfs_domain);
int process_global_event(struct nl_msg *msg, void *arg);
int process_bss_event(struct nl_msg *msg, void *arg);
const char * nl80211_iftype_str(enum nl80211_iftype mode);
void nl80211_restore_ap_mode(struct i802_bss *bss);
#ifdef ANDROID
int android_nl_socket_set_nonblocking(struct nl_sock *handle);
int android_pno_start(struct i802_bss *bss,
struct wpa_driver_scan_params *params);
int android_pno_stop(struct i802_bss *bss);
extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
size_t buf_len);
extern int wpa_driver_nl80211_driver_event(struct wpa_driver_nl80211_data *drv,
u32 vendor_id, u32 subcmd,
u8 *data, size_t len);
#ifdef ANDROID_P2P
int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration);
int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len);
int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow);
int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
const struct wpabuf *proberesp,
const struct wpabuf *assocresp);
#endif /* ANDROID_P2P */
#endif /* ANDROID */
/* driver_nl80211_scan.c */
void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
int wpa_driver_nl80211_scan(struct i802_bss *bss,
struct wpa_driver_scan_params *params);
int wpa_driver_nl80211_sched_scan(void *priv,
struct wpa_driver_scan_params *params);
int wpa_driver_nl80211_stop_sched_scan(void *priv);
struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie);
int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
struct wpa_driver_scan_params *params);
int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len);
#endif /* DRIVER_NL80211_H */
diff --git a/contrib/wpa/src/drivers/driver_nl80211_capa.c b/contrib/wpa/src/drivers/driver_nl80211_capa.c
index cd596e311e59..83868b78e6f0 100644
--- a/contrib/wpa/src/drivers/driver_nl80211_capa.c
+++ b/contrib/wpa/src/drivers/driver_nl80211_capa.c
@@ -1,2512 +1,2512 @@
/*
* Driver interaction with Linux nl80211/cfg80211 - Capabilities
* Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <netlink/genl/genl.h>
#include "utils/common.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_common.h"
#include "common/qca-vendor.h"
#include "common/qca-vendor-attr.h"
#include "common/brcm_vendor.h"
#include "driver_nl80211.h"
static int protocol_feature_handler(struct nl_msg *msg, void *arg)
{
u32 *feat = arg;
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES])
*feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
return NL_SKIP;
}
static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
{
u32 feat = 0;
struct nl_msg *msg;
msg = nlmsg_alloc();
if (!msg)
return 0;
if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) {
nlmsg_free(msg);
return 0;
}
if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat,
NULL, NULL) == 0)
return feat;
return 0;
}
struct wiphy_info_data {
struct wpa_driver_nl80211_data *drv;
struct wpa_driver_capa *capa;
unsigned int num_multichan_concurrent;
unsigned int error:1;
unsigned int device_ap_sme:1;
unsigned int poll_command_supported:1;
unsigned int data_tx_status:1;
unsigned int auth_supported:1;
unsigned int connect_supported:1;
unsigned int p2p_go_supported:1;
unsigned int p2p_client_supported:1;
unsigned int p2p_go_ctwindow_supported:1;
unsigned int p2p_concurrent:1;
unsigned int channel_switch_supported:1;
unsigned int set_qos_map_supported:1;
unsigned int have_low_prio_scan:1;
unsigned int wmm_ac_supported:1;
unsigned int mac_addr_rand_scan_supported:1;
unsigned int mac_addr_rand_sched_scan_supported:1;
unsigned int update_ft_ies_supported:1;
unsigned int has_key_mgmt:1;
unsigned int has_key_mgmt_iftype:1;
};
static unsigned int probe_resp_offload_support(int supp_protocols)
{
unsigned int prot = 0;
if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
return prot;
}
static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
struct nlattr *tb)
{
struct nlattr *nl_mode;
int i;
if (tb == NULL)
return;
nla_for_each_nested(nl_mode, tb, i) {
switch (nla_type(nl_mode)) {
case NL80211_IFTYPE_AP:
info->capa->flags |= WPA_DRIVER_FLAGS_AP;
break;
case NL80211_IFTYPE_MESH_POINT:
info->capa->flags |= WPA_DRIVER_FLAGS_MESH;
break;
case NL80211_IFTYPE_ADHOC:
info->capa->flags |= WPA_DRIVER_FLAGS_IBSS;
break;
case NL80211_IFTYPE_P2P_DEVICE:
info->capa->flags |=
WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
break;
case NL80211_IFTYPE_P2P_GO:
info->p2p_go_supported = 1;
break;
case NL80211_IFTYPE_P2P_CLIENT:
info->p2p_client_supported = 1;
break;
}
}
}
static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
struct nlattr *nl_combi)
{
struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
struct nlattr *nl_limit, *nl_mode;
int err, rem_limit, rem_mode;
int combination_has_p2p = 0, combination_has_mgd = 0;
static struct nla_policy
iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
[NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
[NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
[NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
[NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
},
iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
[NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
[NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
};
err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
nl_combi, iface_combination_policy);
if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
!tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
!tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
return 0; /* broken combination */
if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS])
info->capa->flags |= WPA_DRIVER_FLAGS_RADAR;
nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS],
rem_limit) {
err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
nl_limit, iface_limit_policy);
if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES])
return 0; /* broken combination */
nla_for_each_nested(nl_mode,
tb_limit[NL80211_IFACE_LIMIT_TYPES],
rem_mode) {
int ift = nla_type(nl_mode);
if (ift == NL80211_IFTYPE_P2P_GO ||
ift == NL80211_IFTYPE_P2P_CLIENT)
combination_has_p2p = 1;
if (ift == NL80211_IFTYPE_STATION)
combination_has_mgd = 1;
}
if (combination_has_p2p && combination_has_mgd)
break;
}
if (combination_has_p2p && combination_has_mgd) {
unsigned int num_channels =
nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
info->p2p_concurrent = 1;
if (info->num_multichan_concurrent < num_channels)
info->num_multichan_concurrent = num_channels;
}
return 0;
}
static void wiphy_info_iface_comb(struct wiphy_info_data *info,
struct nlattr *tb)
{
struct nlattr *nl_combi;
int rem_combi;
if (tb == NULL)
return;
nla_for_each_nested(nl_combi, tb, rem_combi) {
if (wiphy_info_iface_comb_process(info, nl_combi) > 0)
break;
}
}
static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
struct nlattr *tb)
{
struct nlattr *nl_cmd;
int i;
if (tb == NULL)
return;
nla_for_each_nested(nl_cmd, tb, i) {
switch (nla_get_u32(nl_cmd)) {
case NL80211_CMD_AUTHENTICATE:
info->auth_supported = 1;
break;
case NL80211_CMD_CONNECT:
info->connect_supported = 1;
break;
case NL80211_CMD_START_SCHED_SCAN:
info->capa->sched_scan_supported = 1;
break;
case NL80211_CMD_PROBE_CLIENT:
info->poll_command_supported = 1;
break;
case NL80211_CMD_CHANNEL_SWITCH:
info->channel_switch_supported = 1;
break;
case NL80211_CMD_SET_QOS_MAP:
info->set_qos_map_supported = 1;
break;
case NL80211_CMD_UPDATE_FT_IES:
info->update_ft_ies_supported = 1;
break;
}
}
}
static unsigned int get_akm_suites_info(struct nlattr *tb)
{
int i, num;
unsigned int key_mgmt = 0;
u32 *akms;
if (!tb)
return 0;
num = nla_len(tb) / sizeof(u32);
akms = nla_data(tb);
for (i = 0; i < num; i++) {
switch (akms[i]) {
case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
break;
case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
break;
case RSN_AUTH_KEY_MGMT_FT_802_1X:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
break;
case RSN_AUTH_KEY_MGMT_FT_PSK:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
break;
case RSN_AUTH_KEY_MGMT_802_1X_SHA256:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256;
break;
case RSN_AUTH_KEY_MGMT_PSK_SHA256:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256;
break;
case RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_TPK_HANDSHAKE;
break;
case RSN_AUTH_KEY_MGMT_FT_SAE:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE;
break;
case RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384;
break;
case RSN_AUTH_KEY_MGMT_CCKM:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_CCKM;
break;
case RSN_AUTH_KEY_MGMT_OSEN:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OSEN;
break;
case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
break;
case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
break;
case RSN_AUTH_KEY_MGMT_OWE:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
break;
case RSN_AUTH_KEY_MGMT_DPP:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
break;
case RSN_AUTH_KEY_MGMT_FILS_SHA256:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
break;
case RSN_AUTH_KEY_MGMT_FILS_SHA384:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
break;
case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
break;
case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
break;
case RSN_AUTH_KEY_MGMT_SAE:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
break;
}
}
return key_mgmt;
}
static void get_iface_akm_suites_info(struct wiphy_info_data *info,
struct nlattr *nl_akms)
{
struct nlattr *tb[NL80211_IFTYPE_AKM_ATTR_MAX + 1];
struct nlattr *nl_iftype;
unsigned int key_mgmt;
int i;
if (!nl_akms)
return;
nla_parse(tb, NL80211_IFTYPE_AKM_ATTR_MAX,
nla_data(nl_akms), nla_len(nl_akms), NULL);
if (!tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES] ||
!tb[NL80211_IFTYPE_AKM_ATTR_SUITES])
return;
info->has_key_mgmt_iftype = 1;
key_mgmt = get_akm_suites_info(tb[NL80211_IFTYPE_AKM_ATTR_SUITES]);
nla_for_each_nested(nl_iftype, tb[NL80211_IFTYPE_AKM_ATTR_IFTYPES], i) {
switch (nla_type(nl_iftype)) {
case NL80211_IFTYPE_ADHOC:
info->drv->capa.key_mgmt_iftype[WPA_IF_IBSS] = key_mgmt;
break;
case NL80211_IFTYPE_STATION:
info->drv->capa.key_mgmt_iftype[WPA_IF_STATION] =
key_mgmt;
break;
case NL80211_IFTYPE_AP:
info->drv->capa.key_mgmt_iftype[WPA_IF_AP_BSS] =
key_mgmt;
break;
case NL80211_IFTYPE_AP_VLAN:
info->drv->capa.key_mgmt_iftype[WPA_IF_AP_VLAN] =
key_mgmt;
break;
case NL80211_IFTYPE_MESH_POINT:
info->drv->capa.key_mgmt_iftype[WPA_IF_MESH] = key_mgmt;
break;
case NL80211_IFTYPE_P2P_CLIENT:
info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_CLIENT] =
key_mgmt;
break;
case NL80211_IFTYPE_P2P_GO:
info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_GO] =
key_mgmt;
break;
case NL80211_IFTYPE_P2P_DEVICE:
info->drv->capa.key_mgmt_iftype[WPA_IF_P2P_DEVICE] =
key_mgmt;
break;
case NL80211_IFTYPE_NAN:
info->drv->capa.key_mgmt_iftype[WPA_IF_NAN] = key_mgmt;
break;
}
wpa_printf(MSG_DEBUG, "nl80211: %s supported key_mgmt 0x%x",
nl80211_iftype_str(nla_type(nl_iftype)),
key_mgmt);
}
}
static void wiphy_info_iftype_akm_suites(struct wiphy_info_data *info,
struct nlattr *tb)
{
struct nlattr *nl_if;
int rem_if;
if (!tb)
return;
nla_for_each_nested(nl_if, tb, rem_if)
get_iface_akm_suites_info(info, nl_if);
}
static void wiphy_info_akm_suites(struct wiphy_info_data *info,
struct nlattr *tb)
{
if (!tb)
return;
info->has_key_mgmt = 1;
info->capa->key_mgmt = get_akm_suites_info(tb);
wpa_printf(MSG_DEBUG, "nl80211: wiphy supported key_mgmt 0x%x",
info->capa->key_mgmt);
}
static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
struct nlattr *tb)
{
int i, num;
u32 *ciphers;
if (tb == NULL)
return;
num = nla_len(tb) / sizeof(u32);
ciphers = nla_data(tb);
for (i = 0; i < num; i++) {
u32 c = ciphers[i];
wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d",
c >> 24, (c >> 16) & 0xff,
(c >> 8) & 0xff, c & 0xff);
switch (c) {
case RSN_CIPHER_SUITE_CCMP_256:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
break;
case RSN_CIPHER_SUITE_GCMP_256:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
break;
case RSN_CIPHER_SUITE_CCMP:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
break;
case RSN_CIPHER_SUITE_GCMP:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
break;
case RSN_CIPHER_SUITE_TKIP:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
break;
case RSN_CIPHER_SUITE_WEP104:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
break;
case RSN_CIPHER_SUITE_WEP40:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
break;
case RSN_CIPHER_SUITE_AES_128_CMAC:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
break;
case RSN_CIPHER_SUITE_BIP_GMAC_128:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
break;
case RSN_CIPHER_SUITE_BIP_GMAC_256:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
break;
case RSN_CIPHER_SUITE_BIP_CMAC_256:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
break;
case RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED:
info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
break;
}
}
}
static void wiphy_info_max_roc(struct wpa_driver_capa *capa,
struct nlattr *tb)
{
if (tb)
capa->max_remain_on_chan = nla_get_u32(tb);
}
static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
struct nlattr *ext_setup)
{
if (tdls == NULL)
return;
wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
if (ext_setup) {
wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
}
}
static int ext_feature_isset(const u8 *ext_features, int ext_features_len,
enum nl80211_ext_feature_index ftidx)
{
u8 ft_byte;
if ((int) ftidx / 8 >= ext_features_len)
return 0;
ft_byte = ext_features[ftidx / 8];
return (ft_byte & BIT(ftidx % 8)) != 0;
}
static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
struct nlattr *tb)
{
struct wpa_driver_capa *capa = info->capa;
u8 *ext_features;
int len;
if (tb == NULL)
return;
ext_features = nla_data(tb);
len = nla_len(tb);
if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_VHT_IBSS))
capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM))
capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM;
if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_FILS_STA))
capa->flags |= WPA_DRIVER_FLAGS_SUPPORT_FILS;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_BEACON_RATE_LEGACY))
capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_BEACON_RATE_HT))
capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_HT;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_BEACON_RATE_VHT))
capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_VHT;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_BEACON_RATE_HE))
capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_RATE_HE;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_SET_SCAN_DWELL))
capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_SCAN_START_TIME) &&
ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_BSS_PARENT_TSF) &&
ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_SET_SCAN_DWELL))
capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA))
capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI))
capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_FILS_SK_OFFLOAD))
capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK))
capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X))
capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_MFP_OPTIONAL))
capa->flags |= WPA_DRIVER_FLAGS_MFP_OPTIONAL;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_DFS_OFFLOAD))
capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
#ifdef CONFIG_MBO
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) &&
ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) &&
ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) &&
ext_feature_isset(
ext_features, len,
NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION))
capa->flags |= WPA_DRIVER_FLAGS_OCE_STA;
#endif /* CONFIG_MBO */
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
capa->flags |= WPA_DRIVER_FLAGS_FTM_RESPONDER;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211))
capa->flags |= WPA_DRIVER_FLAGS_CONTROL_PORT;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_CONTROL_PORT_NO_PREAUTH))
capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_RX;
if (ext_feature_isset(
ext_features, len,
NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS))
capa->flags2 |= WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_VLAN_OFFLOAD))
capa->flags |= WPA_DRIVER_FLAGS_VLAN_OFFLOAD;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_CAN_REPLACE_PTK0))
capa->flags |= WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_BEACON_PROTECTION))
capa->flags |= WPA_DRIVER_FLAGS_BEACON_PROTECTION;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_EXT_KEY_ID))
capa->flags |= WPA_DRIVER_FLAGS_EXTENDED_KEY_ID;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS))
info->drv->multicast_registrations = 1;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_FILS_DISCOVERY))
info->drv->fils_discovery = 1;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP))
info->drv->unsol_bcast_probe_resp = 1;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
capa->flags2 |= WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION))
capa->flags2 |= WPA_DRIVER_FLAGS2_OCV;
}
static void wiphy_info_feature_flags(struct wiphy_info_data *info,
struct nlattr *tb)
{
u32 flags;
struct wpa_driver_capa *capa = info->capa;
if (tb == NULL)
return;
flags = nla_get_u32(tb);
if (flags & NL80211_FEATURE_SK_TX_STATUS)
info->data_tx_status = 1;
if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
if (flags & NL80211_FEATURE_SAE)
capa->flags |= WPA_DRIVER_FLAGS_SAE;
if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) {
wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch");
capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH;
}
if (flags & NL80211_FEATURE_P2P_GO_CTWIN)
info->p2p_go_ctwindow_supported = 1;
if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
info->have_low_prio_scan = 1;
if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)
info->mac_addr_rand_scan_supported = 1;
if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR)
info->mac_addr_rand_sched_scan_supported = 1;
if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
info->wmm_ac_supported = 1;
if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES)
capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES;
if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES)
capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES;
if (flags & NL80211_FEATURE_QUIET)
capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET;
if (flags & NL80211_FEATURE_TX_POWER_INSERTION)
capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION;
if (flags & NL80211_FEATURE_HT_IBSS)
capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE)
capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
}
static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
struct nlattr *tb)
{
u32 protocols;
if (tb == NULL)
return;
protocols = nla_get_u32(tb);
wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP "
"mode");
capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
capa->probe_resp_offloads = probe_resp_offload_support(protocols);
}
static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
struct nlattr *tb)
{
struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1];
if (tb == NULL)
return;
if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG,
tb, NULL))
return;
if (triggers[NL80211_WOWLAN_TRIG_ANY])
capa->wowlan_triggers.any = 1;
if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT])
capa->wowlan_triggers.disconnect = 1;
if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT])
capa->wowlan_triggers.magic_pkt = 1;
if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
capa->wowlan_triggers.gtk_rekey_failure = 1;
if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
capa->wowlan_triggers.eap_identity_req = 1;
if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
capa->wowlan_triggers.four_way_handshake = 1;
if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
capa->wowlan_triggers.rfkill_release = 1;
}
static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv,
struct nlattr *tb)
{
int rem = 0, i;
struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr;
if (!tb || drv->num_iface_ext_capa == NL80211_IFTYPE_MAX)
return;
nla_for_each_nested(attr, tb, rem) {
unsigned int len;
struct drv_nl80211_ext_capa *capa;
nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr),
nla_len(attr), NULL);
if (!tb1[NL80211_ATTR_IFTYPE] ||
!tb1[NL80211_ATTR_EXT_CAPA] ||
!tb1[NL80211_ATTR_EXT_CAPA_MASK])
continue;
capa = &drv->iface_ext_capa[drv->num_iface_ext_capa];
capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]);
wpa_printf(MSG_DEBUG,
"nl80211: Driver-advertised extended capabilities for interface type %s",
nl80211_iftype_str(capa->iftype));
len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]);
capa->ext_capa = os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
len);
if (!capa->ext_capa)
goto err;
capa->ext_capa_len = len;
wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities",
capa->ext_capa, capa->ext_capa_len);
len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]);
capa->ext_capa_mask =
os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]),
len);
if (!capa->ext_capa_mask)
goto err;
wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask",
capa->ext_capa_mask, capa->ext_capa_len);
drv->num_iface_ext_capa++;
if (drv->num_iface_ext_capa == NL80211_IFTYPE_MAX)
break;
}
return;
err:
/* Cleanup allocated memory on error */
for (i = 0; i < NL80211_IFTYPE_MAX; i++) {
os_free(drv->iface_ext_capa[i].ext_capa);
drv->iface_ext_capa[i].ext_capa = NULL;
os_free(drv->iface_ext_capa[i].ext_capa_mask);
drv->iface_ext_capa[i].ext_capa_mask = NULL;
drv->iface_ext_capa[i].ext_capa_len = 0;
}
drv->num_iface_ext_capa = 0;
}
static int wiphy_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct wiphy_info_data *info = arg;
struct wpa_driver_capa *capa = info->capa;
struct wpa_driver_nl80211_data *drv = info->drv;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb[NL80211_ATTR_WIPHY])
drv->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
if (tb[NL80211_ATTR_WIPHY_NAME])
os_strlcpy(drv->phyname,
nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
sizeof(drv->phyname));
if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
capa->max_scan_ssids =
nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
capa->max_sched_scan_ssids =
nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] &&
tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] &&
tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) {
capa->max_sched_scan_plans =
nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]);
capa->max_sched_scan_plan_interval =
nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]);
capa->max_sched_scan_plan_iterations =
nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
}
if (tb[NL80211_ATTR_MAX_MATCH_SETS])
capa->max_match_sets =
nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
if (tb[NL80211_ATTR_MAC_ACL_MAX])
capa->max_acl_mac_addrs =
nla_get_u32(tb[NL80211_ATTR_MAC_ACL_MAX]);
wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
wiphy_info_akm_suites(info, tb[NL80211_ATTR_AKM_SUITES]);
wiphy_info_iftype_akm_suites(info, tb[NL80211_ATTR_IFTYPE_AKM_SUITES]);
if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
"off-channel TX");
capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
}
if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
}
wiphy_info_max_roc(capa,
tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
if (tb[NL80211_ATTR_DEVICE_AP_SME])
info->device_ap_sme = 1;
wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]);
wiphy_info_probe_resp_offload(capa,
tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] &&
drv->extended_capa == NULL) {
drv->extended_capa =
os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
if (drv->extended_capa) {
os_memcpy(drv->extended_capa,
nla_data(tb[NL80211_ATTR_EXT_CAPA]),
nla_len(tb[NL80211_ATTR_EXT_CAPA]));
drv->extended_capa_len =
nla_len(tb[NL80211_ATTR_EXT_CAPA]);
wpa_hexdump(MSG_DEBUG,
"nl80211: Driver-advertised extended capabilities (default)",
drv->extended_capa, drv->extended_capa_len);
}
drv->extended_capa_mask =
os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
if (drv->extended_capa_mask) {
os_memcpy(drv->extended_capa_mask,
nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
wpa_hexdump(MSG_DEBUG,
"nl80211: Driver-advertised extended capabilities mask (default)",
drv->extended_capa_mask,
drv->extended_capa_len);
} else {
os_free(drv->extended_capa);
drv->extended_capa = NULL;
drv->extended_capa_len = 0;
}
}
wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]);
if (tb[NL80211_ATTR_VENDOR_DATA]) {
struct nlattr *nl;
int rem;
nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) {
struct nl80211_vendor_cmd_info *vinfo;
if (nla_len(nl) != sizeof(*vinfo)) {
wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
continue;
}
vinfo = nla_data(nl);
if (vinfo->vendor_id == OUI_QCA) {
switch (vinfo->subcmd) {
case QCA_NL80211_VENDOR_SUBCMD_TEST:
drv->vendor_cmd_test_avail = 1;
break;
#ifdef CONFIG_DRIVER_NL80211_QCA
case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
drv->roaming_vendor_cmd_avail = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
drv->dfs_vendor_cmd_avail = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
drv->get_features_vendor_cmd_avail = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST:
drv->get_pref_freq_list = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
drv->set_prob_oper_freq = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
drv->capa.flags |=
WPA_DRIVER_FLAGS_ACS_OFFLOAD;
drv->qca_do_acs = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
drv->setband_vendor_cmd_avail = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
drv->scan_vendor_cmd_avail = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION:
drv->set_wifi_conf_vendor_cmd_avail = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS:
drv->fetch_bss_trans_status = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_ROAM:
drv->roam_vendor_cmd_avail = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_ADD_STA_NODE:
drv->add_sta_node_vendor_cmd_avail = 1;
break;
case QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO:
drv->get_sta_info_vendor_cmd_avail = 1;
break;
#endif /* CONFIG_DRIVER_NL80211_QCA */
}
#ifdef CONFIG_DRIVER_NL80211_BRCM
} else if (vinfo->vendor_id == OUI_BRCM) {
switch (vinfo->subcmd) {
case BRCM_VENDOR_SCMD_ACS:
drv->capa.flags |=
WPA_DRIVER_FLAGS_ACS_OFFLOAD;
wpa_printf(MSG_DEBUG,
"Enabled BRCM ACS");
drv->brcm_do_acs = 1;
break;
}
#endif /* CONFIG_DRIVER_NL80211_BRCM */
}
wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
vinfo->vendor_id, vinfo->subcmd);
}
}
if (tb[NL80211_ATTR_VENDOR_EVENTS]) {
struct nlattr *nl;
int rem;
nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) {
struct nl80211_vendor_cmd_info *vinfo;
if (nla_len(nl) != sizeof(*vinfo)) {
wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
continue;
}
vinfo = nla_data(nl);
wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
vinfo->vendor_id, vinfo->subcmd);
}
}
wiphy_info_wowlan_triggers(capa,
tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]);
if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA])
capa->max_stations =
nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
if (tb[NL80211_ATTR_MAX_CSA_COUNTERS])
capa->max_csa_counters =
nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]);
if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG])
capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY;
return NL_SKIP;
}
static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
struct wiphy_info_data *info)
{
u32 feat;
struct nl_msg *msg;
int flags = 0;
os_memset(info, 0, sizeof(*info));
info->capa = &drv->capa;
info->drv = drv;
feat = get_nl80211_protocol_features(drv);
if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
flags = NLM_F_DUMP;
msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY);
if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
nlmsg_free(msg);
return -1;
}
if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info, NULL, NULL))
return -1;
if (info->auth_supported)
drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
else if (!info->connect_supported) {
wpa_printf(MSG_INFO, "nl80211: Driver does not support "
"authentication/association or connect commands");
info->error = 1;
}
if (info->p2p_go_supported && info->p2p_client_supported)
drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
if (info->p2p_concurrent) {
wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
"interface (driver advertised support)");
drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
}
if (info->num_multichan_concurrent > 1) {
wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
"concurrent (driver advertised support)");
drv->capa.num_multichan_concurrent =
info->num_multichan_concurrent;
}
if (drv->capa.flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
wpa_printf(MSG_DEBUG, "nl80211: use P2P_DEVICE support");
/* default to 5000 since early versions of mac80211 don't set it */
if (!drv->capa.max_remain_on_chan)
drv->capa.max_remain_on_chan = 5000;
drv->capa.wmm_ac_supported = info->wmm_ac_supported;
drv->capa.mac_addr_rand_sched_scan_supported =
info->mac_addr_rand_sched_scan_supported;
drv->capa.mac_addr_rand_scan_supported =
info->mac_addr_rand_scan_supported;
if (info->channel_switch_supported) {
drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
if (!drv->capa.max_csa_counters)
drv->capa.max_csa_counters = 1;
}
if (!drv->capa.max_sched_scan_plans) {
drv->capa.max_sched_scan_plans = 1;
drv->capa.max_sched_scan_plan_interval = UINT32_MAX;
drv->capa.max_sched_scan_plan_iterations = 0;
}
if (info->update_ft_ies_supported)
drv->capa.flags |= WPA_DRIVER_FLAGS_UPDATE_FT_IES;
return 0;
}
#ifdef CONFIG_DRIVER_NL80211_QCA
static int dfs_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
int *dfs_capability_ptr = arg;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (tb[NL80211_ATTR_VENDOR_DATA]) {
struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
nla_data(nl_vend), nla_len(nl_vend), NULL);
if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) {
u32 val;
val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]);
wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u",
val);
*dfs_capability_ptr = val;
}
}
return NL_SKIP;
}
static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
{
struct nl_msg *msg;
int dfs_capability = 0;
int ret;
if (!drv->dfs_vendor_cmd_avail)
return;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)) {
nlmsg_free(msg);
return;
}
ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability,
NULL, NULL);
if (!ret && dfs_capability)
drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
}
struct features_info {
u8 *flags;
size_t flags_len;
struct wpa_driver_capa *capa;
};
static int features_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct features_info *info = arg;
struct nlattr *nl_vend, *attr;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
if (nl_vend) {
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
nla_data(nl_vend), nla_len(nl_vend), NULL);
attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS];
if (attr) {
int len = nla_len(attr);
info->flags = os_malloc(len);
if (info->flags != NULL) {
os_memcpy(info->flags, nla_data(attr), len);
info->flags_len = len;
}
}
attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA];
if (attr)
info->capa->conc_capab = nla_get_u32(attr);
attr = tb_vendor[
QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND];
if (attr)
info->capa->max_conc_chan_2_4 = nla_get_u32(attr);
attr = tb_vendor[
QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND];
if (attr)
info->capa->max_conc_chan_5_0 = nla_get_u32(attr);
}
return NL_SKIP;
}
static int check_feature(enum qca_wlan_vendor_features feature,
struct features_info *info)
{
size_t idx = feature / 8;
return (idx < info->flags_len) &&
(info->flags[idx] & BIT(feature % 8));
}
static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
{
struct nl_msg *msg;
struct features_info info;
int ret;
if (!drv->get_features_vendor_cmd_avail)
return;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) {
nlmsg_free(msg);
return;
}
os_memset(&info, 0, sizeof(info));
info.capa = &drv->capa;
ret = send_and_recv_msgs(drv, msg, features_info_handler, &info,
NULL, NULL);
if (ret || !info.flags)
return;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
&info))
drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_AP, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON;
os_free(info.flags);
}
#endif /* CONFIG_DRIVER_NL80211_QCA */
int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
{
struct wiphy_info_data info;
int i;
if (wpa_driver_nl80211_get_info(drv, &info))
return -1;
if (info.error)
return -1;
drv->has_capability = 1;
drv->has_driver_key_mgmt = info.has_key_mgmt | info.has_key_mgmt_iftype;
/* Fallback to hardcoded defaults if the driver does nott advertize any
* AKM capabilities. */
if (!drv->has_driver_key_mgmt) {
drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
WPA_DRIVER_CAPA_KEY_MGMT_OWE |
WPA_DRIVER_CAPA_KEY_MGMT_DPP;
if (drv->capa.enc & (WPA_DRIVER_CAPA_ENC_CCMP_256 |
WPA_DRIVER_CAPA_ENC_GCMP_256))
drv->capa.key_mgmt |=
WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
if (drv->capa.flags & WPA_DRIVER_FLAGS_SME)
drv->capa.key_mgmt |=
WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
WPA_DRIVER_CAPA_KEY_MGMT_SAE;
else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
drv->capa.key_mgmt |=
WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
}
if (!info.has_key_mgmt_iftype) {
/* If the driver does not advertize per interface AKM
* capabilities, consider all interfaces to support default AKMs
* in key_mgmt. */
for (i = 0; i < WPA_IF_MAX; i++)
drv->capa.key_mgmt_iftype[i] = drv->capa.key_mgmt;
} else if (info.has_key_mgmt_iftype && !info.has_key_mgmt) {
/* If the driver advertizes only per interface supported AKMs
* but does not advertize per wiphy AKM capabilities, consider
* the default key_mgmt as a mask of per interface supported
* AKMs. */
drv->capa.key_mgmt = 0;
for (i = 0; i < WPA_IF_MAX; i++)
drv->capa.key_mgmt |= drv->capa.key_mgmt_iftype[i];
} else if (info.has_key_mgmt_iftype && info.has_key_mgmt) {
/* If the driver advertizes AKM capabilities both per wiphy and
* per interface, consider the interfaces for which per
* interface AKM capabilities were not received to support the
* default key_mgmt capabilities.
*/
for (i = 0; i < WPA_IF_MAX; i++)
if (!drv->capa.key_mgmt_iftype[i])
drv->capa.key_mgmt_iftype[i] =
drv->capa.key_mgmt;
}
drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
- drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_VALID_ERROR_CODES;
drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
/*
* As all cfg80211 drivers must support cases where the AP interface is
* removed without the knowledge of wpa_supplicant/hostapd, e.g., in
* case that the user space daemon has crashed, they must be able to
* cleanup all stations and key entries in the AP tear down flow. Thus,
* this flag can/should always be set for cfg80211 drivers.
*/
drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT;
if (!info.device_ap_sme) {
drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
drv->capa.flags2 |= WPA_DRIVER_FLAGS2_AP_SME;
/*
* No AP SME is currently assumed to also indicate no AP MLME
* in the driver/firmware.
*/
drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME;
}
drv->device_ap_sme = info.device_ap_sme;
drv->poll_command_supported = info.poll_command_supported;
drv->data_tx_status = info.data_tx_status;
drv->p2p_go_ctwindow_supported = info.p2p_go_ctwindow_supported;
if (info.set_qos_map_supported)
drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
drv->have_low_prio_scan = info.have_low_prio_scan;
/*
* If poll command and tx status are supported, mac80211 is new enough
* to have everything we need to not need monitor interfaces.
*/
drv->use_monitor = !info.device_ap_sme &&
(!info.poll_command_supported || !info.data_tx_status);
/*
* If we aren't going to use monitor interfaces, but the
* driver doesn't support data TX status, we won't get TX
* status for EAPOL frames.
*/
if (!drv->use_monitor && !info.data_tx_status)
drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
#ifdef CONFIG_DRIVER_NL80211_QCA
if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD))
qca_nl80211_check_dfs_capa(drv);
qca_nl80211_get_features(drv);
/*
* To enable offchannel simultaneous support in wpa_supplicant, the
* underlying driver needs to support the same along with offchannel TX.
* Offchannel TX support is needed since remain_on_channel and
* action_tx use some common data structures and hence cannot be
* scheduled simultaneously.
*/
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
#endif /* CONFIG_DRIVER_NL80211_QCA */
wpa_printf(MSG_DEBUG,
"nl80211: key_mgmt=0x%x enc=0x%x auth=0x%x flags=0x%llx rrm_flags=0x%x probe_resp_offloads=0x%x max_stations=%u max_remain_on_chan=%u max_scan_ssids=%d",
drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth,
(unsigned long long) drv->capa.flags, drv->capa.rrm_flags,
drv->capa.probe_resp_offloads, drv->capa.max_stations,
drv->capa.max_remain_on_chan, drv->capa.max_scan_ssids);
return 0;
}
struct phy_info_arg {
u16 *num_modes;
struct hostapd_hw_modes *modes;
int last_mode, last_chan_idx;
int failed;
u8 dfs_domain;
};
static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
struct nlattr *ampdu_factor,
struct nlattr *ampdu_density,
struct nlattr *mcs_set)
{
if (capa)
mode->ht_capab = nla_get_u16(capa);
if (ampdu_factor)
mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03;
if (ampdu_density)
mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2;
if (mcs_set && nla_len(mcs_set) >= 16) {
u8 *mcs;
mcs = nla_data(mcs_set);
os_memcpy(mode->mcs_set, mcs, 16);
}
}
static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
struct nlattr *capa,
struct nlattr *mcs_set)
{
if (capa)
mode->vht_capab = nla_get_u32(capa);
if (mcs_set && nla_len(mcs_set) >= 8) {
u8 *mcs;
mcs = nla_data(mcs_set);
os_memcpy(mode->vht_mcs_set, mcs, 8);
}
}
static int phy_info_edmg_capa(struct hostapd_hw_modes *mode,
struct nlattr *bw_config,
struct nlattr *channels)
{
if (!bw_config || !channels)
return NL_OK;
mode->edmg.bw_config = nla_get_u8(bw_config);
mode->edmg.channels = nla_get_u8(channels);
if (!mode->edmg.channels || !mode->edmg.bw_config)
return NL_STOP;
return NL_OK;
}
static int cw2ecw(unsigned int cw)
{
int bit;
if (cw == 0)
return 0;
for (bit = 1; cw != 1; bit++)
cw >>= 1;
return bit;
}
static void phy_info_freq(struct hostapd_hw_modes *mode,
struct hostapd_channel_data *chan,
struct nlattr *tb_freq[])
{
u8 channel;
os_memset(chan, 0, sizeof(*chan));
chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
chan->flag = 0;
chan->allowed_bw = ~0;
chan->dfs_cac_ms = 0;
if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
chan->chan = channel;
else
wpa_printf(MSG_DEBUG,
"nl80211: No channel number found for frequency %u MHz",
chan->freq);
if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
chan->flag |= HOSTAPD_CHAN_DISABLED;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
chan->flag |= HOSTAPD_CHAN_NO_IR;
if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
chan->flag |= HOSTAPD_CHAN_RADAR;
if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY])
chan->flag |= HOSTAPD_CHAN_INDOOR_ONLY;
if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT])
chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ])
chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_10;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_20;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40P;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40M;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80;
if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160;
if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
enum nl80211_dfs_state state =
nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
switch (state) {
case NL80211_DFS_USABLE:
chan->flag |= HOSTAPD_CHAN_DFS_USABLE;
break;
case NL80211_DFS_AVAILABLE:
chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE;
break;
case NL80211_DFS_UNAVAILABLE:
chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE;
break;
}
}
if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) {
chan->dfs_cac_ms = nla_get_u32(
tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
}
chan->wmm_rules_valid = 0;
if (tb_freq[NL80211_FREQUENCY_ATTR_WMM]) {
static struct nla_policy wmm_policy[NL80211_WMMR_MAX + 1] = {
[NL80211_WMMR_CW_MIN] = { .type = NLA_U16 },
[NL80211_WMMR_CW_MAX] = { .type = NLA_U16 },
[NL80211_WMMR_AIFSN] = { .type = NLA_U8 },
[NL80211_WMMR_TXOP] = { .type = NLA_U16 },
};
static const u8 wmm_map[4] = {
[NL80211_AC_BE] = WMM_AC_BE,
[NL80211_AC_BK] = WMM_AC_BK,
[NL80211_AC_VI] = WMM_AC_VI,
[NL80211_AC_VO] = WMM_AC_VO,
};
struct nlattr *nl_wmm;
struct nlattr *tb_wmm[NL80211_WMMR_MAX + 1];
int rem_wmm, ac, count = 0;
nla_for_each_nested(nl_wmm, tb_freq[NL80211_FREQUENCY_ATTR_WMM],
rem_wmm) {
if (nla_parse_nested(tb_wmm, NL80211_WMMR_MAX, nl_wmm,
wmm_policy)) {
wpa_printf(MSG_DEBUG,
"nl80211: Failed to parse WMM rules attribute");
return;
}
if (!tb_wmm[NL80211_WMMR_CW_MIN] ||
!tb_wmm[NL80211_WMMR_CW_MAX] ||
!tb_wmm[NL80211_WMMR_AIFSN] ||
!tb_wmm[NL80211_WMMR_TXOP]) {
wpa_printf(MSG_DEBUG,
"nl80211: Channel is missing WMM rule attribute");
return;
}
ac = nl_wmm->nla_type;
if ((unsigned int) ac >= ARRAY_SIZE(wmm_map)) {
wpa_printf(MSG_DEBUG,
"nl80211: Invalid AC value %d", ac);
return;
}
ac = wmm_map[ac];
chan->wmm_rules[ac].min_cwmin =
cw2ecw(nla_get_u16(
tb_wmm[NL80211_WMMR_CW_MIN]));
chan->wmm_rules[ac].min_cwmax =
cw2ecw(nla_get_u16(
tb_wmm[NL80211_WMMR_CW_MAX]));
chan->wmm_rules[ac].min_aifs =
nla_get_u8(tb_wmm[NL80211_WMMR_AIFSN]);
chan->wmm_rules[ac].max_txop =
nla_get_u16(tb_wmm[NL80211_WMMR_TXOP]) / 32;
count++;
}
/* Set valid flag if all the AC rules are present */
if (count == WMM_AC_NUM)
chan->wmm_rules_valid = 1;
}
}
static int phy_info_freqs(struct phy_info_arg *phy_info,
struct hostapd_hw_modes *mode, struct nlattr *tb)
{
static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
[NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
[NL80211_FREQUENCY_ATTR_NO_10MHZ] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_NO_20MHZ] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG },
};
int new_channels = 0;
struct hostapd_channel_data *channel;
struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
struct nlattr *nl_freq;
int rem_freq, idx;
if (tb == NULL)
return NL_OK;
nla_for_each_nested(nl_freq, tb, rem_freq) {
nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
nla_data(nl_freq), nla_len(nl_freq), freq_policy);
if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
continue;
new_channels++;
}
channel = os_realloc_array(mode->channels,
mode->num_channels + new_channels,
sizeof(struct hostapd_channel_data));
if (!channel)
return NL_STOP;
mode->channels = channel;
mode->num_channels += new_channels;
idx = phy_info->last_chan_idx;
nla_for_each_nested(nl_freq, tb, rem_freq) {
nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
nla_data(nl_freq), nla_len(nl_freq), freq_policy);
if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
continue;
phy_info_freq(mode, &mode->channels[idx], tb_freq);
idx++;
}
phy_info->last_chan_idx = idx;
return NL_OK;
}
static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
{
static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
[NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] =
{ .type = NLA_FLAG },
};
struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
struct nlattr *nl_rate;
int rem_rate, idx;
if (tb == NULL)
return NL_OK;
nla_for_each_nested(nl_rate, tb, rem_rate) {
nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
nla_data(nl_rate), nla_len(nl_rate),
rate_policy);
if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
continue;
mode->num_rates++;
}
mode->rates = os_calloc(mode->num_rates, sizeof(int));
if (!mode->rates)
return NL_STOP;
idx = 0;
nla_for_each_nested(nl_rate, tb, rem_rate) {
nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
nla_data(nl_rate), nla_len(nl_rate),
rate_policy);
if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
continue;
mode->rates[idx] = nla_get_u32(
tb_rate[NL80211_BITRATE_ATTR_RATE]);
idx++;
}
return NL_OK;
}
static void phy_info_iftype_copy(struct he_capabilities *he_capab,
enum ieee80211_op_mode opmode,
struct nlattr **tb, struct nlattr **tb_flags)
{
enum nl80211_iftype iftype;
size_t len;
switch (opmode) {
case IEEE80211_MODE_INFRA:
iftype = NL80211_IFTYPE_STATION;
break;
case IEEE80211_MODE_IBSS:
iftype = NL80211_IFTYPE_ADHOC;
break;
case IEEE80211_MODE_AP:
iftype = NL80211_IFTYPE_AP;
break;
case IEEE80211_MODE_MESH:
iftype = NL80211_IFTYPE_MESH_POINT;
break;
default:
return;
}
if (!nla_get_flag(tb_flags[iftype]))
return;
he_capab->he_supported = 1;
if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]) {
len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]);
if (len > sizeof(he_capab->phy_cap))
len = sizeof(he_capab->phy_cap);
os_memcpy(he_capab->phy_cap,
nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]),
len);
}
if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]) {
len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]);
if (len > sizeof(he_capab->mac_cap))
len = sizeof(he_capab->mac_cap);
os_memcpy(he_capab->mac_cap,
nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC]),
len);
}
if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]) {
len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]);
if (len > sizeof(he_capab->mcs))
len = sizeof(he_capab->mcs);
os_memcpy(he_capab->mcs,
nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET]),
len);
}
if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]) {
len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]);
if (len > sizeof(he_capab->ppet))
len = sizeof(he_capab->ppet);
os_memcpy(&he_capab->ppet,
nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]),
len);
}
if (tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]) {
u16 capa;
capa = nla_get_u16(tb[NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA]);
he_capab->he_6ghz_capa = le_to_host16(capa);
}
}
static int phy_info_iftype(struct hostapd_hw_modes *mode,
struct nlattr *nl_iftype)
{
struct nlattr *tb[NL80211_BAND_IFTYPE_ATTR_MAX + 1];
struct nlattr *tb_flags[NL80211_IFTYPE_MAX + 1];
unsigned int i;
nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX,
nla_data(nl_iftype), nla_len(nl_iftype), NULL);
if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES])
return NL_STOP;
if (nla_parse_nested(tb_flags, NL80211_IFTYPE_MAX,
tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES], NULL))
return NL_STOP;
for (i = 0; i < IEEE80211_MODE_NUM; i++)
phy_info_iftype_copy(&mode->he_capab[i], i, tb, tb_flags);
return NL_OK;
}
static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
{
struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
struct hostapd_hw_modes *mode;
int ret;
if (phy_info->last_mode != nl_band->nla_type) {
mode = os_realloc_array(phy_info->modes,
*phy_info->num_modes + 1,
sizeof(*mode));
if (!mode) {
phy_info->failed = 1;
return NL_STOP;
}
phy_info->modes = mode;
mode = &phy_info->modes[*(phy_info->num_modes)];
os_memset(mode, 0, sizeof(*mode));
mode->mode = NUM_HOSTAPD_MODES;
mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN;
/*
* Unsupported VHT MCS stream is defined as value 3, so the VHT
* MCS RX/TX map must be initialized with 0xffff to mark all 8
* possible streams as unsupported. This will be overridden if
* driver advertises VHT support.
*/
mode->vht_mcs_set[0] = 0xff;
mode->vht_mcs_set[1] = 0xff;
mode->vht_mcs_set[4] = 0xff;
mode->vht_mcs_set[5] = 0xff;
*(phy_info->num_modes) += 1;
phy_info->last_mode = nl_band->nla_type;
phy_info->last_chan_idx = 0;
} else
mode = &phy_info->modes[*(phy_info->num_modes) - 1];
nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
nla_len(nl_band), NULL);
phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA],
tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR],
tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY],
tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
ret = phy_info_edmg_capa(mode,
tb_band[NL80211_BAND_ATTR_EDMG_BW_CONFIG],
tb_band[NL80211_BAND_ATTR_EDMG_CHANNELS]);
if (ret == NL_OK)
ret = phy_info_freqs(phy_info, mode,
tb_band[NL80211_BAND_ATTR_FREQS]);
if (ret == NL_OK)
ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
if (ret != NL_OK) {
phy_info->failed = 1;
return ret;
}
if (tb_band[NL80211_BAND_ATTR_IFTYPE_DATA]) {
struct nlattr *nl_iftype;
int rem_band;
nla_for_each_nested(nl_iftype,
tb_band[NL80211_BAND_ATTR_IFTYPE_DATA],
rem_band) {
ret = phy_info_iftype(mode, nl_iftype);
if (ret != NL_OK)
return ret;
}
}
return NL_OK;
}
static int phy_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct phy_info_arg *phy_info = arg;
struct nlattr *nl_band;
int rem_band;
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
return NL_SKIP;
nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
{
int res = phy_info_band(phy_info, nl_band);
if (res != NL_OK)
return res;
}
return NL_SKIP;
}
static struct hostapd_hw_modes *
wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
u16 *num_modes)
{
u16 m;
struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
int i, mode11g_idx = -1;
/* heuristic to set up modes */
for (m = 0; m < *num_modes; m++) {
if (!modes[m].num_channels)
continue;
if (modes[m].channels[0].freq < 2000) {
modes[m].num_channels = 0;
continue;
} else if (modes[m].channels[0].freq < 4000) {
modes[m].mode = HOSTAPD_MODE_IEEE80211B;
for (i = 0; i < modes[m].num_rates; i++) {
if (modes[m].rates[i] > 200) {
modes[m].mode = HOSTAPD_MODE_IEEE80211G;
break;
}
}
} else if (modes[m].channels[0].freq > 50000)
modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
else
modes[m].mode = HOSTAPD_MODE_IEEE80211A;
}
/* Remove unsupported bands */
m = 0;
while (m < *num_modes) {
if (modes[m].mode == NUM_HOSTAPD_MODES) {
wpa_printf(MSG_DEBUG,
"nl80211: Remove unsupported mode");
os_free(modes[m].channels);
os_free(modes[m].rates);
if (m + 1 < *num_modes)
os_memmove(&modes[m], &modes[m + 1],
sizeof(struct hostapd_hw_modes) *
(*num_modes - (m + 1)));
(*num_modes)--;
continue;
}
m++;
}
/* If only 802.11g mode is included, use it to construct matching
* 802.11b mode data. */
for (m = 0; m < *num_modes; m++) {
if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
return modes; /* 802.11b already included */
if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
mode11g_idx = m;
}
if (mode11g_idx < 0)
return modes; /* 2.4 GHz band not supported at all */
nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes));
if (nmodes == NULL)
return modes; /* Could not add 802.11b mode */
mode = &nmodes[*num_modes];
os_memset(mode, 0, sizeof(*mode));
(*num_modes)++;
modes = nmodes;
mode->mode = HOSTAPD_MODE_IEEE80211B;
mode11g = &modes[mode11g_idx];
mode->num_channels = mode11g->num_channels;
mode->channels = os_memdup(mode11g->channels,
mode11g->num_channels *
sizeof(struct hostapd_channel_data));
if (mode->channels == NULL) {
(*num_modes)--;
return modes; /* Could not add 802.11b mode */
}
mode->num_rates = 0;
mode->rates = os_malloc(4 * sizeof(int));
if (mode->rates == NULL) {
os_free(mode->channels);
(*num_modes)--;
return modes; /* Could not add 802.11b mode */
}
for (i = 0; i < mode11g->num_rates; i++) {
if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
continue;
mode->rates[mode->num_rates] = mode11g->rates[i];
mode->num_rates++;
if (mode->num_rates == 4)
break;
}
if (mode->num_rates == 0) {
os_free(mode->channels);
os_free(mode->rates);
(*num_modes)--;
return modes; /* No 802.11b rates */
}
wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
"information");
return modes;
}
static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start,
int end)
{
int c;
for (c = 0; c < mode->num_channels; c++) {
struct hostapd_channel_data *chan = &mode->channels[c];
if (chan->freq - 10 >= start && chan->freq + 10 <= end)
chan->flag |= HOSTAPD_CHAN_HT40;
}
}
static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start,
int end)
{
int c;
for (c = 0; c < mode->num_channels; c++) {
struct hostapd_channel_data *chan = &mode->channels[c];
if (!(chan->flag & HOSTAPD_CHAN_HT40))
continue;
if (chan->freq - 30 >= start && chan->freq - 10 <= end)
chan->flag |= HOSTAPD_CHAN_HT40MINUS;
if (chan->freq + 10 >= start && chan->freq + 30 <= end)
chan->flag |= HOSTAPD_CHAN_HT40PLUS;
}
}
static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp,
struct phy_info_arg *results)
{
u16 m;
for (m = 0; m < *results->num_modes; m++) {
int c;
struct hostapd_hw_modes *mode = &results->modes[m];
for (c = 0; c < mode->num_channels; c++) {
struct hostapd_channel_data *chan = &mode->channels[c];
if ((u32) chan->freq - 10 >= start &&
(u32) chan->freq + 10 <= end)
chan->max_tx_power = max_eirp;
}
}
}
static void nl80211_reg_rule_ht40(u32 start, u32 end,
struct phy_info_arg *results)
{
u16 m;
for (m = 0; m < *results->num_modes; m++) {
if (!(results->modes[m].ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
continue;
nl80211_set_ht40_mode(&results->modes[m], start, end);
}
}
static void nl80211_reg_rule_sec(struct nlattr *tb[],
struct phy_info_arg *results)
{
u32 start, end, max_bw;
u16 m;
if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
return;
start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
if (max_bw < 20)
return;
for (m = 0; m < *results->num_modes; m++) {
if (!(results->modes[m].ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
continue;
nl80211_set_ht40_mode_sec(&results->modes[m], start, end);
}
}
static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
int end, int max_bw)
{
int c;
for (c = 0; c < mode->num_channels; c++) {
struct hostapd_channel_data *chan = &mode->channels[c];
if (chan->freq - 10 >= start && chan->freq + 70 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_10_70;
if (chan->freq - 30 >= start && chan->freq + 50 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_30_50;
if (chan->freq - 50 >= start && chan->freq + 30 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_50_30;
if (chan->freq - 70 >= start && chan->freq + 10 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_70_10;
if (max_bw >= 160) {
if (chan->freq - 10 >= start && chan->freq + 150 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_10_150;
if (chan->freq - 30 >= start && chan->freq + 130 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_30_130;
if (chan->freq - 50 >= start && chan->freq + 110 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_50_110;
if (chan->freq - 70 >= start && chan->freq + 90 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_70_90;
if (chan->freq - 90 >= start && chan->freq + 70 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_90_70;
if (chan->freq - 110 >= start && chan->freq + 50 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_110_50;
if (chan->freq - 130 >= start && chan->freq + 30 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_130_30;
if (chan->freq - 150 >= start && chan->freq + 10 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_150_10;
}
}
}
static void nl80211_reg_rule_vht(struct nlattr *tb[],
struct phy_info_arg *results)
{
u32 start, end, max_bw;
u16 m;
if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
return;
start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
if (max_bw < 80)
return;
for (m = 0; m < *results->num_modes; m++) {
if (!(results->modes[m].ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
continue;
/* TODO: use a real VHT support indication */
if (!results->modes[m].vht_capab)
continue;
nl80211_set_vht_mode(&results->modes[m], start, end, max_bw);
}
}
static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region,
u8 *dfs_domain)
{
if (region == NL80211_DFS_FCC)
*dfs_domain = HOSTAPD_DFS_REGION_FCC;
else if (region == NL80211_DFS_ETSI)
*dfs_domain = HOSTAPD_DFS_REGION_ETSI;
else if (region == NL80211_DFS_JP)
*dfs_domain = HOSTAPD_DFS_REGION_JP;
else
*dfs_domain = 0;
}
static const char * dfs_domain_name(enum nl80211_dfs_regions region)
{
switch (region) {
case NL80211_DFS_UNSET:
return "DFS-UNSET";
case NL80211_DFS_FCC:
return "DFS-FCC";
case NL80211_DFS_ETSI:
return "DFS-ETSI";
case NL80211_DFS_JP:
return "DFS-JP";
default:
return "DFS-invalid";
}
}
static int nl80211_get_reg(struct nl_msg *msg, void *arg)
{
struct phy_info_arg *results = arg;
struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
struct nlattr *nl_rule;
struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
int rem_rule;
static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
[NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
[NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
[NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
[NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
[NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
};
nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
!tb_msg[NL80211_ATTR_REG_RULES]) {
wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
"available");
return NL_SKIP;
}
if (tb_msg[NL80211_ATTR_DFS_REGION]) {
enum nl80211_dfs_regions dfs_domain;
dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
nl80211_set_dfs_domain(dfs_domain, &results->dfs_domain);
wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
(char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
dfs_domain_name(dfs_domain));
} else {
wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
(char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
}
nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
{
u32 start, end, max_eirp = 0, max_bw = 0, flags = 0;
nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
nla_data(nl_rule), nla_len(nl_rule), reg_policy);
if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL)
continue;
start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100;
if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW])
max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS])
flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s",
start, end, max_bw, max_eirp,
flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "",
flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "",
flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "",
flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" :
"",
flags & NL80211_RRF_DFS ? " (DFS)" : "",
flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "",
flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "",
flags & NL80211_RRF_NO_IR ? " (no IR)" : "");
if (max_bw >= 40)
nl80211_reg_rule_ht40(start, end, results);
if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
nl80211_reg_rule_max_eirp(start, end, max_eirp,
results);
}
nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
{
nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
nla_data(nl_rule), nla_len(nl_rule), reg_policy);
nl80211_reg_rule_sec(tb_rule, results);
}
nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
{
nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
nla_data(nl_rule), nla_len(nl_rule), reg_policy);
nl80211_reg_rule_vht(tb_rule, results);
}
return NL_SKIP;
}
static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
struct phy_info_arg *results)
{
struct nl_msg *msg;
msg = nlmsg_alloc();
if (!msg)
return -ENOMEM;
nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) {
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) {
nlmsg_free(msg);
return -1;
}
}
return send_and_recv_msgs(drv, msg, nl80211_get_reg, results,
NULL, NULL);
}
static const char * modestr(enum hostapd_hw_mode mode)
{
switch (mode) {
case HOSTAPD_MODE_IEEE80211B:
return "802.11b";
case HOSTAPD_MODE_IEEE80211G:
return "802.11g";
case HOSTAPD_MODE_IEEE80211A:
return "802.11a";
case HOSTAPD_MODE_IEEE80211AD:
return "802.11ad";
default:
return "?";
}
}
static void nl80211_dump_chan_list(struct hostapd_hw_modes *modes,
u16 num_modes)
{
int i;
if (!modes)
return;
for (i = 0; i < num_modes; i++) {
struct hostapd_hw_modes *mode = &modes[i];
char str[200];
char *pos = str;
char *end = pos + sizeof(str);
int j, res;
for (j = 0; j < mode->num_channels; j++) {
struct hostapd_channel_data *chan = &mode->channels[j];
res = os_snprintf(pos, end - pos, " %d%s%s%s",
chan->freq,
(chan->flag & HOSTAPD_CHAN_DISABLED) ?
"[DISABLED]" : "",
(chan->flag & HOSTAPD_CHAN_NO_IR) ?
"[NO_IR]" : "",
(chan->flag & HOSTAPD_CHAN_RADAR) ?
"[RADAR]" : "");
if (os_snprintf_error(end - pos, res))
break;
pos += res;
}
*pos = '\0';
wpa_printf(MSG_DEBUG, "nl80211: Mode IEEE %s:%s",
modestr(mode->mode), str);
}
}
struct hostapd_hw_modes *
nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
u8 *dfs_domain)
{
u32 feat;
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
int nl_flags = 0;
struct nl_msg *msg;
struct phy_info_arg result = {
.num_modes = num_modes,
.modes = NULL,
.last_mode = -1,
.failed = 0,
.dfs_domain = 0,
};
*num_modes = 0;
*flags = 0;
*dfs_domain = 0;
feat = get_nl80211_protocol_features(drv);
if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
nl_flags = NLM_F_DUMP;
if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) ||
nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
nlmsg_free(msg);
return NULL;
}
if (send_and_recv_msgs(drv, msg, phy_info_handler, &result,
NULL, NULL) == 0) {
struct hostapd_hw_modes *modes;
nl80211_set_regulatory_flags(drv, &result);
if (result.failed) {
int i;
for (i = 0; result.modes && i < *num_modes; i++) {
os_free(result.modes[i].channels);
os_free(result.modes[i].rates);
}
os_free(result.modes);
*num_modes = 0;
return NULL;
}
*dfs_domain = result.dfs_domain;
modes = wpa_driver_nl80211_postprocess_modes(result.modes,
num_modes);
nl80211_dump_chan_list(modes, *num_modes);
return modes;
}
return NULL;
}
diff --git a/contrib/wpa/src/eap_peer/eap_proxy_dummy.c b/contrib/wpa/src/eap_peer/eap_proxy_dummy.c
index 2cc05c92cdfc..181e8cc74e8c 100644
--- a/contrib/wpa/src/eap_peer/eap_proxy_dummy.c
+++ b/contrib/wpa/src/eap_peer/eap_proxy_dummy.c
@@ -1,94 +1,94 @@
/*
- * EAP proxy - dummy implementation for build testing
+ * EAP proxy - stub implementation for build testing
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "eap_proxy.h"
struct eap_proxy_sm *
eap_proxy_init(void *eapol_ctx, const struct eapol_callbacks *eapol_cb,
void *msg_ctx)
{
return NULL;
}
void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy)
{
}
int eap_proxy_key_available(struct eap_proxy_sm *sm)
{
return 0;
}
const u8 * eap_proxy_get_eapKeyData(struct eap_proxy_sm *sm, size_t *len)
{
return NULL;
}
struct wpabuf * eap_proxy_get_eapRespData(struct eap_proxy_sm *sm)
{
return NULL;
}
int eap_proxy_sm_step(struct eap_proxy_sm *sm, struct eap_sm *eap_sm)
{
return 0;
}
enum eap_proxy_status
eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData,
int eapReqDataLen)
{
return EAP_PROXY_FAILURE;
}
int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
int verbose)
{
return 0;
}
int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num,
char *imsi_buf, size_t *imsi_len)
{
return -1;
}
int eap_proxy_notify_config(struct eap_proxy_sm *sm,
struct eap_peer_config *config)
{
return -1;
}
u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len)
{
return NULL;
}
u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len)
{
return NULL;
}
void eap_proxy_sm_abort(struct eap_proxy_sm *sm)
{
}
diff --git a/contrib/wpa/src/eap_peer/eap_teap.c b/contrib/wpa/src/eap_peer/eap_teap.c
index e8cc7844ce5f..bc7f6f4f5abe 100644
--- a/contrib/wpa/src/eap_peer/eap_teap.c
+++ b/contrib/wpa/src/eap_peer/eap_teap.c
@@ -1,2146 +1,2146 @@
/*
* EAP peer method: EAP-TEAP (RFC 7170)
* Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/tls.h"
#include "eap_common/eap_teap_common.h"
#include "eap_i.h"
#include "eap_tls_common.h"
#include "eap_config.h"
#include "eap_teap_pac.h"
#ifdef EAP_TEAP_DYNAMIC
#include "eap_teap_pac.c"
#endif /* EAP_TEAP_DYNAMIC */
static void eap_teap_deinit(struct eap_sm *sm, void *priv);
struct eap_teap_data {
struct eap_ssl_data ssl;
u8 teap_version; /* Negotiated version */
u8 received_version; /* Version number received during negotiation */
u16 tls_cs;
const struct eap_method *phase2_method;
void *phase2_priv;
int phase2_success;
int inner_method_done;
int iresult_verified;
int result_success_done;
int on_tx_completion;
struct eap_method_type phase2_type;
struct eap_method_type *phase2_types;
size_t num_phase2_types;
int resuming; /* starting a resumed session */
#define EAP_TEAP_PROV_UNAUTH 1
#define EAP_TEAP_PROV_AUTH 2
int provisioning_allowed; /* Allowed PAC provisioning modes */
int provisioning; /* doing PAC provisioning (not the normal auth) */
int anon_provisioning; /* doing anonymous (unauthenticated)
* provisioning */
int session_ticket_used;
int test_outer_tlvs;
u8 key_data[EAP_TEAP_KEY_LEN];
u8 *session_id;
size_t id_len;
u8 emsk[EAP_EMSK_LEN];
int success;
struct eap_teap_pac *pac;
struct eap_teap_pac *current_pac;
size_t max_pac_list_len;
int use_pac_binary_format;
u8 simck_msk[EAP_TEAP_SIMCK_LEN];
u8 simck_emsk[EAP_TEAP_SIMCK_LEN];
int simck_idx;
int cmk_emsk_available;
struct wpabuf *pending_phase2_req;
struct wpabuf *pending_resp;
struct wpabuf *server_outer_tlvs;
struct wpabuf *peer_outer_tlvs;
};
static int eap_teap_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
const u8 *client_random,
const u8 *server_random,
u8 *master_secret)
{
struct eap_teap_data *data = ctx;
wpa_printf(MSG_DEBUG, "EAP-TEAP: SessionTicket callback");
if (!master_secret) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: SessionTicket failed - fall back to full TLS handshake");
data->session_ticket_used = 0;
if (data->provisioning_allowed) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Try to provision a new PAC-Key");
data->provisioning = 1;
data->current_pac = NULL;
}
return 0;
}
wpa_hexdump(MSG_DEBUG, "EAP-TEAP: SessionTicket", ticket, len);
if (!data->current_pac) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: No PAC-Key available for using SessionTicket");
data->session_ticket_used = 0;
return 0;
}
/* EAP-TEAP uses PAC-Key as the TLS master_secret */
os_memcpy(master_secret, data->current_pac->pac_key,
EAP_TEAP_PAC_KEY_LEN);
data->session_ticket_used = 1;
return 1;
}
static void eap_teap_parse_phase1(struct eap_teap_data *data,
const char *phase1)
{
const char *pos;
pos = os_strstr(phase1, "teap_provisioning=");
if (pos) {
data->provisioning_allowed = atoi(pos + 18);
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Automatic PAC provisioning mode: %d",
data->provisioning_allowed);
}
pos = os_strstr(phase1, "teap_max_pac_list_len=");
if (pos) {
data->max_pac_list_len = atoi(pos + 22);
if (data->max_pac_list_len == 0)
data->max_pac_list_len = 1;
wpa_printf(MSG_DEBUG, "EAP-TEAP: Maximum PAC list length: %lu",
(unsigned long) data->max_pac_list_len);
}
if (os_strstr(phase1, "teap_pac_format=binary")) {
data->use_pac_binary_format = 1;
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Using binary format for PAC list");
}
#ifdef CONFIG_TESTING_OPTIONS
if (os_strstr(phase1, "teap_test_outer_tlvs=1"))
data->test_outer_tlvs = 1;
#endif /* CONFIG_TESTING_OPTIONS */
}
static void * eap_teap_init(struct eap_sm *sm)
{
struct eap_teap_data *data;
struct eap_peer_config *config = eap_get_config(sm);
if (!config)
return NULL;
data = os_zalloc(sizeof(*data));
if (!data)
return NULL;
data->teap_version = EAP_TEAP_VERSION;
data->max_pac_list_len = 10;
if (config->phase1)
eap_teap_parse_phase1(data, config->phase1);
if ((data->provisioning_allowed & EAP_TEAP_PROV_AUTH) &&
!config->cert.ca_cert && !config->cert.ca_path) {
/* Prevent PAC provisioning without mutual authentication
* (either by validating server certificate or by suitable
* inner EAP method). */
wpa_printf(MSG_INFO,
"EAP-TEAP: Disable authenticated provisioning due to no ca_cert/ca_path");
data->provisioning_allowed &= ~EAP_TEAP_PROV_AUTH;
}
if (eap_peer_select_phase2_methods(config, "auth=",
&data->phase2_types,
&data->num_phase2_types, 0) < 0) {
eap_teap_deinit(sm, data);
return NULL;
}
data->phase2_type.vendor = EAP_VENDOR_IETF;
data->phase2_type.method = EAP_TYPE_NONE;
config->teap_anon_dh = !!(data->provisioning_allowed &
EAP_TEAP_PROV_UNAUTH);
if (eap_peer_tls_ssl_init(sm, &data->ssl, config, EAP_TYPE_TEAP)) {
wpa_printf(MSG_INFO, "EAP-TEAP: Failed to initialize SSL");
eap_teap_deinit(sm, data);
return NULL;
}
if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
eap_teap_session_ticket_cb,
data) < 0) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Failed to set SessionTicket callback");
eap_teap_deinit(sm, data);
return NULL;
}
if (!config->pac_file) {
wpa_printf(MSG_INFO, "EAP-TEAP: No PAC file configured");
eap_teap_deinit(sm, data);
return NULL;
}
if (data->use_pac_binary_format &&
eap_teap_load_pac_bin(sm, &data->pac, config->pac_file) < 0) {
wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file");
eap_teap_deinit(sm, data);
return NULL;
}
if (!data->use_pac_binary_format &&
eap_teap_load_pac(sm, &data->pac, config->pac_file) < 0) {
wpa_printf(MSG_INFO, "EAP-TEAP: Failed to load PAC file");
eap_teap_deinit(sm, data);
return NULL;
}
eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len);
return data;
}
static void eap_teap_clear(struct eap_teap_data *data)
{
forced_memzero(data->key_data, EAP_TEAP_KEY_LEN);
forced_memzero(data->emsk, EAP_EMSK_LEN);
os_free(data->session_id);
data->session_id = NULL;
wpabuf_free(data->pending_phase2_req);
data->pending_phase2_req = NULL;
wpabuf_free(data->pending_resp);
data->pending_resp = NULL;
wpabuf_free(data->server_outer_tlvs);
data->server_outer_tlvs = NULL;
wpabuf_free(data->peer_outer_tlvs);
data->peer_outer_tlvs = NULL;
forced_memzero(data->simck_msk, EAP_TEAP_SIMCK_LEN);
forced_memzero(data->simck_emsk, EAP_TEAP_SIMCK_LEN);
}
static void eap_teap_deinit(struct eap_sm *sm, void *priv)
{
struct eap_teap_data *data = priv;
struct eap_teap_pac *pac, *prev;
if (!data)
return;
if (data->phase2_priv && data->phase2_method)
data->phase2_method->deinit(sm, data->phase2_priv);
eap_teap_clear(data);
os_free(data->phase2_types);
eap_peer_tls_ssl_deinit(sm, &data->ssl);
pac = data->pac;
prev = NULL;
while (pac) {
prev = pac;
pac = pac->next;
eap_teap_free_pac(prev);
}
os_free(data);
}
static int eap_teap_derive_msk(struct eap_teap_data *data)
{
/* FIX: RFC 7170 does not describe whether MSK or EMSK based S-IMCK[j]
* is used in this derivation */
if (eap_teap_derive_eap_msk(data->tls_cs, data->simck_msk,
data->key_data) < 0 ||
eap_teap_derive_eap_emsk(data->tls_cs, data->simck_msk,
data->emsk) < 0)
return -1;
data->success = 1;
return 0;
}
static int eap_teap_derive_key_auth(struct eap_sm *sm,
struct eap_teap_data *data)
{
int res;
/* RFC 7170, Section 5.1 */
res = tls_connection_export_key(sm->ssl_ctx, data->ssl.conn,
TEAP_TLS_EXPORTER_LABEL_SKS, NULL, 0,
data->simck_msk, EAP_TEAP_SIMCK_LEN);
if (res)
return res;
wpa_hexdump_key(MSG_DEBUG,
"EAP-TEAP: session_key_seed (S-IMCK[0])",
data->simck_msk, EAP_TEAP_SIMCK_LEN);
os_memcpy(data->simck_emsk, data->simck_msk, EAP_TEAP_SIMCK_LEN);
data->simck_idx = 0;
return 0;
}
static int eap_teap_init_phase2_method(struct eap_sm *sm,
struct eap_teap_data *data)
{
data->inner_method_done = 0;
data->iresult_verified = 0;
data->phase2_method =
eap_peer_get_eap_method(data->phase2_type.vendor,
data->phase2_type.method);
if (!data->phase2_method)
return -1;
sm->init_phase2 = 1;
data->phase2_priv = data->phase2_method->init(sm);
sm->init_phase2 = 0;
return data->phase2_priv == NULL ? -1 : 0;
}
static int eap_teap_select_phase2_method(struct eap_teap_data *data,
int vendor, enum eap_type type)
{
size_t i;
/* TODO: TNC with anonymous provisioning; need to require both
* completed inner EAP authentication (EAP-pwd or EAP-EKE) and TNC */
if (data->anon_provisioning &&
!eap_teap_allowed_anon_prov_phase2_method(vendor, type)) {
wpa_printf(MSG_INFO,
"EAP-TEAP: EAP type %u:%u not allowed during unauthenticated provisioning",
vendor, type);
return -1;
}
#ifdef EAP_TNC
if (vendor == EAP_VENDOR_IETF && type == EAP_TYPE_TNC) {
data->phase2_type.vendor = EAP_VENDOR_IETF;
data->phase2_type.method = EAP_TYPE_TNC;
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Selected Phase 2 EAP vendor %d method %d for TNC",
data->phase2_type.vendor,
data->phase2_type.method);
return 0;
}
#endif /* EAP_TNC */
for (i = 0; i < data->num_phase2_types; i++) {
if (data->phase2_types[i].vendor != vendor ||
data->phase2_types[i].method != type)
continue;
data->phase2_type.vendor = data->phase2_types[i].vendor;
data->phase2_type.method = data->phase2_types[i].method;
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Selected Phase 2 EAP vendor %d method %d",
data->phase2_type.vendor,
data->phase2_type.method);
break;
}
if (vendor != data->phase2_type.vendor ||
type != data->phase2_type.method ||
(vendor == EAP_VENDOR_IETF && type == EAP_TYPE_NONE))
return -1;
return 0;
}
static void eap_teap_deinit_inner_eap(struct eap_sm *sm,
struct eap_teap_data *data)
{
if (!data->phase2_priv || !data->phase2_method)
return;
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Phase 2 EAP sequence - deinitialize previous method");
data->phase2_method->deinit(sm, data->phase2_priv);
data->phase2_method = NULL;
data->phase2_priv = NULL;
data->phase2_type.vendor = EAP_VENDOR_IETF;
data->phase2_type.method = EAP_TYPE_NONE;
}
static int eap_teap_phase2_request(struct eap_sm *sm,
struct eap_teap_data *data,
struct eap_method_ret *ret,
struct eap_hdr *hdr,
struct wpabuf **resp)
{
size_t len = be_to_host16(hdr->length);
u8 *pos;
struct eap_method_ret iret;
struct eap_peer_config *config = eap_get_config(sm);
struct wpabuf msg;
int vendor = EAP_VENDOR_IETF;
enum eap_type method;
if (len <= sizeof(struct eap_hdr)) {
wpa_printf(MSG_INFO,
"EAP-TEAP: too short Phase 2 request (len=%lu)",
(unsigned long) len);
return -1;
}
pos = (u8 *) (hdr + 1);
method = *pos;
if (method == EAP_TYPE_EXPANDED) {
if (len < sizeof(struct eap_hdr) + 8) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Too short Phase 2 request (expanded header) (len=%lu)",
(unsigned long) len);
return -1;
}
vendor = WPA_GET_BE24(pos + 1);
method = WPA_GET_BE32(pos + 4);
}
wpa_printf(MSG_DEBUG, "EAP-TEAP: Phase 2 Request: type=%u:%u",
vendor, method);
if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_IDENTITY) {
eap_teap_deinit_inner_eap(sm, data);
*resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
return 0;
}
if (data->phase2_priv && data->phase2_method &&
(vendor != data->phase2_type.vendor ||
method != data->phase2_type.method))
eap_teap_deinit_inner_eap(sm, data);
if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
data->phase2_type.method == EAP_TYPE_NONE &&
eap_teap_select_phase2_method(data, vendor, method) < 0) {
if (eap_peer_tls_phase2_nak(data->phase2_types,
data->num_phase2_types,
hdr, resp))
return -1;
return 0;
}
if ((!data->phase2_priv && eap_teap_init_phase2_method(sm, data) < 0) ||
!data->phase2_method) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Failed to initialize Phase 2 EAP method %u:%u",
vendor, method);
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
return -1;
}
os_memset(&iret, 0, sizeof(iret));
wpabuf_set(&msg, hdr, len);
*resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
&msg);
if (iret.methodState == METHOD_DONE)
data->inner_method_done = 1;
if (!(*resp) ||
(iret.methodState == METHOD_DONE &&
iret.decision == DECISION_FAIL)) {
/* Wait for protected indication of failure */
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_FAIL;
} else if ((iret.methodState == METHOD_DONE ||
iret.methodState == METHOD_MAY_CONT) &&
(iret.decision == DECISION_UNCOND_SUCC ||
iret.decision == DECISION_COND_SUCC)) {
data->phase2_success = 1;
}
if (!(*resp) && config &&
(config->pending_req_identity || config->pending_req_password ||
config->pending_req_otp || config->pending_req_new_password ||
config->pending_req_sim)) {
wpabuf_free(data->pending_phase2_req);
data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
} else if (!(*resp))
return -1;
return 0;
}
static struct wpabuf * eap_teap_tlv_nak(int vendor_id, int tlv_type)
{
struct wpabuf *buf;
struct teap_tlv_nak *nak;
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Add NAK TLV (Vendor-Id %u NAK-Type %u)",
vendor_id, tlv_type);
buf = wpabuf_alloc(sizeof(*nak));
if (!buf)
return NULL;
nak = wpabuf_put(buf, sizeof(*nak));
nak->tlv_type = host_to_be16(TEAP_TLV_MANDATORY | TEAP_TLV_NAK);
nak->length = host_to_be16(6);
nak->vendor_id = host_to_be32(vendor_id);
nak->nak_type = host_to_be16(tlv_type);
return buf;
}
static struct wpabuf * eap_teap_tlv_pac_ack(void)
{
struct wpabuf *buf;
struct teap_tlv_result *res;
struct teap_tlv_pac_ack *ack;
buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack));
if (!buf)
return NULL;
wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (ack)");
ack = wpabuf_put(buf, sizeof(*ack));
ack->tlv_type = host_to_be16(TEAP_TLV_PAC | TEAP_TLV_MANDATORY);
ack->length = host_to_be16(sizeof(*ack) - sizeof(struct teap_tlv_hdr));
ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
ack->pac_len = host_to_be16(2);
ack->result = host_to_be16(TEAP_STATUS_SUCCESS);
return buf;
}
static struct wpabuf * eap_teap_add_identity_type(struct eap_sm *sm,
struct wpabuf *msg)
{
struct wpabuf *tlv;
tlv = eap_teap_tlv_identity_type(sm->use_machine_cred ?
TEAP_IDENTITY_TYPE_MACHINE :
TEAP_IDENTITY_TYPE_USER);
return wpabuf_concat(msg, tlv);
}
static struct wpabuf * eap_teap_process_eap_payload_tlv(
struct eap_sm *sm, struct eap_teap_data *data,
struct eap_method_ret *ret,
u8 *eap_payload_tlv, size_t eap_payload_tlv_len,
enum teap_identity_types req_id_type)
{
struct eap_hdr *hdr;
struct wpabuf *resp = NULL;
if (eap_payload_tlv_len < sizeof(*hdr)) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: too short EAP Payload TLV (len=%lu)",
(unsigned long) eap_payload_tlv_len);
return NULL;
}
hdr = (struct eap_hdr *) eap_payload_tlv;
if (be_to_host16(hdr->length) > eap_payload_tlv_len) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: EAP packet overflow in EAP Payload TLV");
return NULL;
}
if (hdr->code != EAP_CODE_REQUEST) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Unexpected code=%d in Phase 2 EAP header",
hdr->code);
return NULL;
}
if (eap_teap_phase2_request(sm, data, ret, hdr, &resp)) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Phase 2 Request processing failed");
return NULL;
}
resp = eap_teap_tlv_eap_payload(resp);
if (req_id_type)
resp = eap_teap_add_identity_type(sm, resp);
return resp;
}
static struct wpabuf * eap_teap_process_basic_auth_req(
struct eap_sm *sm, struct eap_teap_data *data,
u8 *basic_auth_req, size_t basic_auth_req_len,
enum teap_identity_types req_id_type)
{
const u8 *identity, *password;
size_t identity_len, password_len, plen;
struct wpabuf *resp;
wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: Basic-Password-Auth-Req prompt",
basic_auth_req, basic_auth_req_len);
/* TODO: send over control interface */
identity = eap_get_config_identity(sm, &identity_len);
password = eap_get_config_password(sm, &password_len);
if (!identity || !password ||
identity_len > 255 || password_len > 255) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: No username/password suitable for Basic-Password-Auth");
return eap_teap_tlv_nak(0, TEAP_TLV_BASIC_PASSWORD_AUTH_REQ);
}
plen = 1 + identity_len + 1 + password_len;
resp = wpabuf_alloc(sizeof(struct teap_tlv_hdr) + plen);
if (!resp)
return NULL;
eap_teap_put_tlv_hdr(resp, TEAP_TLV_BASIC_PASSWORD_AUTH_RESP, plen);
wpabuf_put_u8(resp, identity_len);
wpabuf_put_data(resp, identity, identity_len);
wpabuf_put_u8(resp, password_len);
wpabuf_put_data(resp, password, password_len);
wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TEAP: Basic-Password-Auth-Resp",
resp);
if (req_id_type)
resp = eap_teap_add_identity_type(sm, resp);
/* Assume this succeeds so that Result TLV(Success) from the server can
* be used to terminate TEAP. */
data->phase2_success = 1;
return resp;
}
static int
eap_teap_validate_crypto_binding(struct eap_teap_data *data,
const struct teap_tlv_crypto_binding *cb)
{
u8 flags, subtype;
subtype = cb->subtype & 0x0f;
flags = cb->subtype >> 4;
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
cb->version, cb->received_version, flags, subtype);
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
cb->nonce, sizeof(cb->nonce));
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
cb->emsk_compound_mac, sizeof(cb->emsk_compound_mac));
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
cb->msk_compound_mac, sizeof(cb->msk_compound_mac));
if (cb->version != EAP_TEAP_VERSION ||
cb->received_version != data->received_version ||
subtype != TEAP_CRYPTO_BINDING_SUBTYPE_REQUEST ||
flags < 1 || flags > 3) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Invalid Version/Flags/Sub-Type in Crypto-Binding TLV: Version %u Received Version %u Flags %u Sub-Type %u",
cb->version, cb->received_version, flags, subtype);
return -1;
}
if (cb->nonce[EAP_TEAP_NONCE_LEN - 1] & 0x01) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Invalid Crypto-Binding TLV Nonce in request");
return -1;
}
return 0;
}
static int eap_teap_write_crypto_binding(
struct eap_teap_data *data,
struct teap_tlv_crypto_binding *rbind,
const struct teap_tlv_crypto_binding *cb,
const u8 *cmk_msk, const u8 *cmk_emsk)
{
u8 subtype, flags;
rbind->tlv_type = host_to_be16(TEAP_TLV_MANDATORY |
TEAP_TLV_CRYPTO_BINDING);
rbind->length = host_to_be16(sizeof(*rbind) -
sizeof(struct teap_tlv_hdr));
rbind->version = EAP_TEAP_VERSION;
rbind->received_version = data->received_version;
/* FIX: RFC 7170 is not clear on which Flags value to use when
* Crypto-Binding TLV is used with Basic-Password-Auth */
flags = cmk_emsk ? TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC :
TEAP_CRYPTO_BINDING_MSK_CMAC;
subtype = TEAP_CRYPTO_BINDING_SUBTYPE_RESPONSE;
rbind->subtype = (flags << 4) | subtype;
os_memcpy(rbind->nonce, cb->nonce, sizeof(cb->nonce));
inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
os_memset(rbind->emsk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
os_memset(rbind->msk_compound_mac, 0, EAP_TEAP_COMPOUND_MAC_LEN);
if (eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs,
data->peer_outer_tlvs, cmk_msk,
rbind->msk_compound_mac) < 0)
return -1;
if (cmk_emsk &&
eap_teap_compound_mac(data->tls_cs, rbind, data->server_outer_tlvs,
data->peer_outer_tlvs, cmk_emsk,
rbind->emsk_compound_mac) < 0)
return -1;
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Reply Crypto-Binding TLV: Version %u Received Version %u Flags %u SubType %u",
rbind->version, rbind->received_version, flags, subtype);
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Nonce",
rbind->nonce, sizeof(rbind->nonce));
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: EMSK Compound MAC",
rbind->emsk_compound_mac, sizeof(rbind->emsk_compound_mac));
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: MSK Compound MAC",
rbind->msk_compound_mac, sizeof(rbind->msk_compound_mac));
return 0;
}
static int eap_teap_get_cmk(struct eap_sm *sm, struct eap_teap_data *data,
u8 *cmk_msk, u8 *cmk_emsk)
{
u8 *msk = NULL, *emsk = NULL;
size_t msk_len = 0, emsk_len = 0;
int res;
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Determining CMK[%d] for Compound MAC calculation",
data->simck_idx + 1);
if (!data->phase2_method)
return eap_teap_derive_cmk_basic_pw_auth(data->tls_cs,
data->simck_msk,
cmk_msk);
if (!data->phase2_method || !data->phase2_priv) {
wpa_printf(MSG_INFO, "EAP-TEAP: Phase 2 method not available");
return -1;
}
if (data->phase2_method->isKeyAvailable &&
!data->phase2_method->isKeyAvailable(sm, data->phase2_priv)) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Phase 2 key material not available");
return -1;
}
if (data->phase2_method->isKeyAvailable &&
data->phase2_method->getKey) {
msk = data->phase2_method->getKey(sm, data->phase2_priv,
&msk_len);
if (!msk) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Could not fetch Phase 2 MSK");
return -1;
}
}
if (data->phase2_method->isKeyAvailable &&
data->phase2_method->get_emsk) {
emsk = data->phase2_method->get_emsk(sm, data->phase2_priv,
&emsk_len);
}
res = eap_teap_derive_imck(data->tls_cs,
data->simck_msk, data->simck_emsk,
msk, msk_len, emsk, emsk_len,
data->simck_msk, cmk_msk,
data->simck_emsk, cmk_emsk);
bin_clear_free(msk, msk_len);
bin_clear_free(emsk, emsk_len);
if (res == 0) {
data->simck_idx++;
if (emsk)
data->cmk_emsk_available = 1;
}
return res;
}
static int eap_teap_session_id(struct eap_teap_data *data)
{
const size_t max_id_len = 100;
int res;
os_free(data->session_id);
data->session_id = os_malloc(max_id_len);
if (!data->session_id)
return -1;
data->session_id[0] = EAP_TYPE_TEAP;
res = tls_get_tls_unique(data->ssl.conn, data->session_id + 1,
max_id_len - 1);
if (res < 0) {
os_free(data->session_id);
data->session_id = NULL;
wpa_printf(MSG_ERROR, "EAP-TEAP: Failed to derive Session-Id");
return -1;
}
data->id_len = 1 + res;
wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Derived Session-Id",
data->session_id, data->id_len);
return 0;
}
static struct wpabuf * eap_teap_process_crypto_binding(
struct eap_sm *sm, struct eap_teap_data *data,
struct eap_method_ret *ret,
const struct teap_tlv_crypto_binding *cb, size_t bind_len)
{
struct wpabuf *resp;
u8 *pos;
u8 cmk_msk[EAP_TEAP_CMK_LEN];
u8 cmk_emsk[EAP_TEAP_CMK_LEN];
const u8 *cmk_emsk_ptr = NULL;
int res;
size_t len;
u8 flags;
if (eap_teap_validate_crypto_binding(data, cb) < 0 ||
eap_teap_get_cmk(sm, data, cmk_msk, cmk_emsk) < 0)
return NULL;
/* Validate received MSK/EMSK Compound MAC */
flags = cb->subtype >> 4;
if (flags == TEAP_CRYPTO_BINDING_MSK_CMAC ||
flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) {
u8 msk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
if (eap_teap_compound_mac(data->tls_cs, cb,
data->server_outer_tlvs,
data->peer_outer_tlvs, cmk_msk,
msk_compound_mac) < 0)
return NULL;
res = os_memcmp_const(msk_compound_mac, cb->msk_compound_mac,
EAP_TEAP_COMPOUND_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Received MSK Compound MAC",
cb->msk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP,
"EAP-TEAP: Calculated MSK Compound MAC",
msk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
if (res != 0) {
wpa_printf(MSG_INFO,
"EAP-TEAP: MSK Compound MAC did not match");
return NULL;
}
}
if ((flags == TEAP_CRYPTO_BINDING_EMSK_CMAC ||
flags == TEAP_CRYPTO_BINDING_EMSK_AND_MSK_CMAC) &&
data->cmk_emsk_available) {
u8 emsk_compound_mac[EAP_TEAP_COMPOUND_MAC_LEN];
if (eap_teap_compound_mac(data->tls_cs, cb,
data->server_outer_tlvs,
data->peer_outer_tlvs, cmk_emsk,
emsk_compound_mac) < 0)
return NULL;
res = os_memcmp_const(emsk_compound_mac, cb->emsk_compound_mac,
EAP_TEAP_COMPOUND_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Received EMSK Compound MAC",
cb->emsk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
wpa_hexdump(MSG_MSGDUMP,
"EAP-TEAP: Calculated EMSK Compound MAC",
emsk_compound_mac, EAP_TEAP_COMPOUND_MAC_LEN);
if (res != 0) {
wpa_printf(MSG_INFO,
"EAP-TEAP: EMSK Compound MAC did not match");
return NULL;
}
cmk_emsk_ptr = cmk_emsk;
}
if (flags == TEAP_CRYPTO_BINDING_EMSK_CMAC &&
!data->cmk_emsk_available) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Server included only EMSK Compound MAC, but no locally generated inner EAP EMSK to validate this");
return NULL;
}
/*
* Compound MAC was valid, so authentication succeeded. Reply with
* crypto binding to allow server to complete authentication.
*/
len = sizeof(struct teap_tlv_crypto_binding);
resp = wpabuf_alloc(len);
if (!resp)
return NULL;
if (data->phase2_success && eap_teap_derive_msk(data) < 0) {
wpa_printf(MSG_INFO, "EAP-TEAP: Failed to generate MSK");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
data->phase2_success = 0;
wpabuf_free(resp);
return NULL;
}
if (data->phase2_success && eap_teap_session_id(data) < 0) {
wpabuf_free(resp);
return NULL;
}
pos = wpabuf_put(resp, sizeof(struct teap_tlv_crypto_binding));
if (eap_teap_write_crypto_binding(
data, (struct teap_tlv_crypto_binding *) pos,
cb, cmk_msk, cmk_emsk_ptr) < 0) {
wpabuf_free(resp);
return NULL;
}
return resp;
}
static void eap_teap_parse_pac_tlv(struct eap_teap_pac *entry, int type,
u8 *pos, size_t len, int *pac_key_found)
{
switch (type & 0x7fff) {
case PAC_TYPE_PAC_KEY:
wpa_hexdump_key(MSG_DEBUG, "EAP-TEAP: PAC-Key", pos, len);
if (len != EAP_TEAP_PAC_KEY_LEN) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Invalid PAC-Key length %lu",
(unsigned long) len);
break;
}
*pac_key_found = 1;
os_memcpy(entry->pac_key, pos, len);
break;
case PAC_TYPE_PAC_OPAQUE:
wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Opaque", pos, len);
entry->pac_opaque = pos;
entry->pac_opaque_len = len;
break;
case PAC_TYPE_PAC_INFO:
wpa_hexdump(MSG_DEBUG, "EAP-TEAP: PAC-Info", pos, len);
entry->pac_info = pos;
entry->pac_info_len = len;
break;
default:
wpa_printf(MSG_DEBUG, "EAP-TEAP: Ignored unknown PAC type %d",
type);
break;
}
}
static int eap_teap_process_pac_tlv(struct eap_teap_pac *entry,
u8 *pac, size_t pac_len)
{
struct pac_attr_hdr *hdr;
u8 *pos;
size_t left, len;
int type, pac_key_found = 0;
pos = pac;
left = pac_len;
while (left > sizeof(*hdr)) {
hdr = (struct pac_attr_hdr *) pos;
type = be_to_host16(hdr->type);
len = be_to_host16(hdr->len);
pos += sizeof(*hdr);
left -= sizeof(*hdr);
if (len > left) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: PAC TLV overrun (type=%d len=%lu left=%lu)",
type, (unsigned long) len,
(unsigned long) left);
return -1;
}
eap_teap_parse_pac_tlv(entry, type, pos, len, &pac_key_found);
pos += len;
left -= len;
}
if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: PAC TLV does not include all the required fields");
return -1;
}
return 0;
}
static int eap_teap_parse_pac_info(struct eap_teap_pac *entry, int type,
u8 *pos, size_t len)
{
u16 pac_type;
u32 lifetime;
struct os_time now;
switch (type & 0x7fff) {
case PAC_TYPE_CRED_LIFETIME:
if (len != 4) {
wpa_hexdump(MSG_DEBUG,
"EAP-TEAP: PAC-Info - Invalid CRED_LIFETIME length - ignored",
pos, len);
return 0;
}
/*
* This is not currently saved separately in PAC files since
* the server can automatically initiate PAC update when
* needed. Anyway, the information is available from PAC-Info
* dump if it is needed for something in the future.
*/
lifetime = WPA_GET_BE32(pos);
os_get_time(&now);
wpa_printf(MSG_DEBUG,
"EAP-TEAP: PAC-Info - CRED_LIFETIME %d (%d days)",
lifetime, (lifetime - (u32) now.sec) / 86400);
break;
case PAC_TYPE_A_ID:
wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - A-ID",
pos, len);
entry->a_id = pos;
entry->a_id_len = len;
break;
case PAC_TYPE_I_ID:
wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - I-ID",
pos, len);
entry->i_id = pos;
entry->i_id_len = len;
break;
case PAC_TYPE_A_ID_INFO:
wpa_hexdump_ascii(MSG_DEBUG, "EAP-TEAP: PAC-Info - A-ID-Info",
pos, len);
entry->a_id_info = pos;
entry->a_id_info_len = len;
break;
case PAC_TYPE_PAC_TYPE:
/* RFC 7170, Section 4.2.12.6 - PAC-Type TLV */
if (len != 2) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Invalid PAC-Type length %lu (expected 2)",
(unsigned long) len);
wpa_hexdump_ascii(MSG_DEBUG,
"EAP-TEAP: PAC-Info - PAC-Type",
pos, len);
return -1;
}
pac_type = WPA_GET_BE16(pos);
if (pac_type != PAC_TYPE_TUNNEL_PAC) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Unsupported PAC Type %d",
pac_type);
return -1;
}
wpa_printf(MSG_DEBUG, "EAP-TEAP: PAC-Info - PAC-Type %d",
pac_type);
entry->pac_type = pac_type;
break;
default:
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Ignored unknown PAC-Info type %d", type);
break;
}
return 0;
}
static int eap_teap_process_pac_info(struct eap_teap_pac *entry)
{
struct pac_attr_hdr *hdr;
u8 *pos;
size_t left, len;
int type;
/* RFC 7170, Section 4.2.12.4 */
/* PAC-Type defaults to Tunnel PAC (Type 1) */
entry->pac_type = PAC_TYPE_TUNNEL_PAC;
pos = entry->pac_info;
left = entry->pac_info_len;
while (left > sizeof(*hdr)) {
hdr = (struct pac_attr_hdr *) pos;
type = be_to_host16(hdr->type);
len = be_to_host16(hdr->len);
pos += sizeof(*hdr);
left -= sizeof(*hdr);
if (len > left) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: PAC-Info overrun (type=%d len=%lu left=%lu)",
type, (unsigned long) len,
(unsigned long) left);
return -1;
}
if (eap_teap_parse_pac_info(entry, type, pos, len) < 0)
return -1;
pos += len;
left -= len;
}
if (!entry->a_id || !entry->a_id_info) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: PAC-Info does not include all the required fields");
return -1;
}
return 0;
}
static struct wpabuf * eap_teap_process_pac(struct eap_sm *sm,
struct eap_teap_data *data,
struct eap_method_ret *ret,
u8 *pac, size_t pac_len)
{
struct eap_peer_config *config = eap_get_config(sm);
struct eap_teap_pac entry;
os_memset(&entry, 0, sizeof(entry));
if (eap_teap_process_pac_tlv(&entry, pac, pac_len) ||
eap_teap_process_pac_info(&entry))
return NULL;
eap_teap_add_pac(&data->pac, &data->current_pac, &entry);
eap_teap_pac_list_truncate(data->pac, data->max_pac_list_len);
if (data->use_pac_binary_format)
eap_teap_save_pac_bin(sm, data->pac, config->pac_file);
else
eap_teap_save_pac(sm, data->pac, config->pac_file);
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Send PAC-Acknowledgement - %s initiated provisioning completed successfully",
data->provisioning ? "peer" : "server");
return eap_teap_tlv_pac_ack();
}
static int eap_teap_parse_decrypted(struct wpabuf *decrypted,
struct eap_teap_tlv_parse *tlv,
struct wpabuf **resp)
{
u16 tlv_type;
int mandatory, res;
size_t len;
u8 *pos, *end;
os_memset(tlv, 0, sizeof(*tlv));
/* Parse TLVs from the decrypted Phase 2 data */
pos = wpabuf_mhead(decrypted);
end = pos + wpabuf_len(decrypted);
while (end - pos >= 4) {
mandatory = pos[0] & 0x80;
tlv_type = WPA_GET_BE16(pos) & 0x3fff;
pos += 2;
len = WPA_GET_BE16(pos);
pos += 2;
if (len > (size_t) (end - pos)) {
wpa_printf(MSG_INFO, "EAP-TEAP: TLV overflow");
return -1;
}
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Received Phase 2: TLV type %u (%s) length %u%s",
tlv_type, eap_teap_tlv_type_str(tlv_type),
(unsigned int) len,
mandatory ? " (mandatory)" : "");
res = eap_teap_parse_tlv(tlv, tlv_type, pos, len);
if (res == -2)
break;
if (res < 0) {
if (mandatory) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: NAK unknown mandatory TLV type %u",
tlv_type);
*resp = eap_teap_tlv_nak(0, tlv_type);
break;
}
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Ignore unknown optional TLV type %u",
tlv_type);
}
pos += len;
}
return 0;
}
static struct wpabuf * eap_teap_pac_request(void)
{
struct wpabuf *req;
struct teap_tlv_request_action *act;
struct teap_tlv_hdr *pac;
struct teap_attr_pac_type *type;
req = wpabuf_alloc(sizeof(*act) + sizeof(*pac) + sizeof(*type));
if (!req)
return NULL;
wpa_printf(MSG_DEBUG, "EAP-TEAP: Add Request Action TLV (Process TLV)");
act = wpabuf_put(req, sizeof(*act));
act->tlv_type = host_to_be16(TEAP_TLV_REQUEST_ACTION);
act->length = host_to_be16(2);
act->status = TEAP_STATUS_SUCCESS;
act->action = TEAP_REQUEST_ACTION_PROCESS_TLV;
wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC TLV (PAC-Type = Tunnel)");
pac = wpabuf_put(req, sizeof(*pac));
pac->tlv_type = host_to_be16(TEAP_TLV_PAC);
pac->length = host_to_be16(sizeof(*type));
type = wpabuf_put(req, sizeof(*type));
type->type = host_to_be16(PAC_TYPE_PAC_TYPE);
type->length = host_to_be16(2);
type->pac_type = host_to_be16(PAC_TYPE_TUNNEL_PAC);
return req;
}
static int eap_teap_process_decrypted(struct eap_sm *sm,
struct eap_teap_data *data,
struct eap_method_ret *ret,
u8 identifier,
struct wpabuf *decrypted,
struct wpabuf **out_data)
{
struct wpabuf *resp = NULL, *tmp;
struct eap_teap_tlv_parse tlv;
int failed = 0;
enum teap_error_codes error = 0;
int iresult_added = 0;
if (eap_teap_parse_decrypted(decrypted, &tlv, &resp) < 0) {
/* Parsing failed - no response available */
return 0;
}
if (resp) {
/* Parsing rejected the message - send out an error response */
goto send_resp;
}
if (tlv.result == TEAP_STATUS_FAILURE) {
/* Server indicated failure - respond similarly per
* RFC 7170, 3.6.3. This authentication exchange cannot succeed
* and will be terminated with a cleartext EAP Failure. */
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Server rejected authentication");
resp = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
goto send_resp;
}
if (tlv.iresult == TEAP_STATUS_SUCCESS && !tlv.crypto_binding) {
/* Intermediate-Result TLV indicating success, but no
* Crypto-Binding TLV */
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Intermediate-Result TLV indicating success, but no Crypto-Binding TLV");
failed = 1;
error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
goto done;
}
if (!data->iresult_verified && !data->result_success_done &&
tlv.result == TEAP_STATUS_SUCCESS && !tlv.crypto_binding) {
/* Result TLV indicating success, but no Crypto-Binding TLV */
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Result TLV indicating success, but no Crypto-Binding TLV");
failed = 1;
error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
goto done;
}
if (tlv.iresult != TEAP_STATUS_SUCCESS &&
tlv.iresult != TEAP_STATUS_FAILURE &&
data->inner_method_done) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Inner EAP method exchange completed, but no Intermediate-Result TLV included");
failed = 1;
error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
goto done;
}
if (tlv.identity_type == TEAP_IDENTITY_TYPE_MACHINE) {
struct eap_peer_config *config = eap_get_config(sm);
sm->use_machine_cred = config && config->machine_identity &&
config->machine_identity_len;
} else if (tlv.identity_type) {
sm->use_machine_cred = 0;
}
if (tlv.identity_type) {
struct eap_peer_config *config = eap_get_config(sm);
os_free(data->phase2_types);
data->phase2_types = NULL;
data->num_phase2_types = 0;
if (config &&
eap_peer_select_phase2_methods(config, "auth=",
&data->phase2_types,
&data->num_phase2_types,
sm->use_machine_cred) < 0) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Failed to update Phase 2 EAP types");
failed = 1;
goto done;
}
}
if (tlv.basic_auth_req) {
tmp = eap_teap_process_basic_auth_req(sm, data,
tlv.basic_auth_req,
tlv.basic_auth_req_len,
tlv.identity_type);
if (!tmp)
failed = 1;
resp = wpabuf_concat(resp, tmp);
} else if (tlv.eap_payload_tlv) {
tmp = eap_teap_process_eap_payload_tlv(sm, data, ret,
tlv.eap_payload_tlv,
tlv.eap_payload_tlv_len,
tlv.identity_type);
if (!tmp)
failed = 1;
resp = wpabuf_concat(resp, tmp);
if (tlv.iresult == TEAP_STATUS_SUCCESS ||
tlv.iresult == TEAP_STATUS_FAILURE) {
tmp = eap_teap_tlv_result(failed ?
TEAP_STATUS_FAILURE :
TEAP_STATUS_SUCCESS, 1);
resp = wpabuf_concat(resp, tmp);
if (tlv.iresult == TEAP_STATUS_FAILURE)
failed = 1;
iresult_added = 1;
}
}
if (tlv.crypto_binding) {
if (tlv.iresult != TEAP_STATUS_SUCCESS &&
tlv.result != TEAP_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Unexpected Crypto-Binding TLV without Result TLV or Intermediate-Result TLV indicating success");
failed = 1;
error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
goto done;
}
tmp = eap_teap_process_crypto_binding(sm, data, ret,
tlv.crypto_binding,
tlv.crypto_binding_len);
if (!tmp) {
failed = 1;
error = TEAP_ERROR_TUNNEL_COMPROMISE_ERROR;
} else {
resp = wpabuf_concat(resp, tmp);
if (tlv.result == TEAP_STATUS_SUCCESS && !failed)
data->result_success_done = 1;
if (tlv.iresult == TEAP_STATUS_SUCCESS && !failed) {
data->inner_method_done = 0;
data->iresult_verified = 1;
}
}
}
if (data->result_success_done && data->session_ticket_used &&
eap_teap_derive_msk(data) == 0) {
/* Assume the server might accept authentication without going
* through inner authentication. */
wpa_printf(MSG_DEBUG,
"EAP-TEAP: PAC used - server may decide to skip inner authentication");
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_COND_SUCC;
} else if (data->result_success_done &&
tls_connection_get_own_cert_used(data->ssl.conn) &&
eap_teap_derive_msk(data) == 0) {
/* Assume the server might accept authentication without going
* through inner authentication. */
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Client certificate used - server may decide to skip inner authentication");
ret->methodState = METHOD_MAY_CONT;
ret->decision = DECISION_COND_SUCC;
}
if (tlv.pac) {
if (tlv.result == TEAP_STATUS_SUCCESS) {
tmp = eap_teap_process_pac(sm, data, ret,
tlv.pac, tlv.pac_len);
resp = wpabuf_concat(resp, tmp);
} else {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: PAC TLV without Result TLV acknowledging success");
failed = 1;
error = TEAP_ERROR_UNEXPECTED_TLVS_EXCHANGED;
}
}
if (!data->current_pac && data->provisioning && !failed && !tlv.pac &&
tlv.crypto_binding &&
(!data->anon_provisioning ||
(data->phase2_success && data->phase2_method &&
data->phase2_method->vendor == 0 &&
eap_teap_allowed_anon_prov_cipher_suite(data->tls_cs) &&
eap_teap_allowed_anon_prov_phase2_method(
data->phase2_method->vendor,
data->phase2_method->method))) &&
(tlv.iresult == TEAP_STATUS_SUCCESS ||
tlv.result == TEAP_STATUS_SUCCESS)) {
/*
* Need to request Tunnel PAC when using authenticated
* provisioning.
*/
wpa_printf(MSG_DEBUG, "EAP-TEAP: Request Tunnel PAC");
tmp = eap_teap_pac_request();
resp = wpabuf_concat(resp, tmp);
}
done:
if (failed) {
tmp = eap_teap_tlv_result(TEAP_STATUS_FAILURE, 0);
resp = wpabuf_concat(tmp, resp);
if (error != 0) {
tmp = eap_teap_tlv_error(error);
resp = wpabuf_concat(tmp, resp);
}
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
} else if (tlv.result == TEAP_STATUS_SUCCESS) {
tmp = eap_teap_tlv_result(TEAP_STATUS_SUCCESS, 0);
resp = wpabuf_concat(tmp, resp);
}
if ((tlv.iresult == TEAP_STATUS_SUCCESS ||
tlv.iresult == TEAP_STATUS_FAILURE) && !iresult_added) {
tmp = eap_teap_tlv_result((!failed && data->phase2_success) ?
TEAP_STATUS_SUCCESS :
TEAP_STATUS_FAILURE, 1);
resp = wpabuf_concat(tmp, resp);
}
if (resp && tlv.result == TEAP_STATUS_SUCCESS && !failed &&
(tlv.crypto_binding || data->iresult_verified) &&
data->phase2_success) {
/* Successfully completed Phase 2 */
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Authentication completed successfully");
ret->methodState = METHOD_MAY_CONT;
data->on_tx_completion = data->provisioning ?
METHOD_MAY_CONT : METHOD_DONE;
ret->decision = DECISION_UNCOND_SUCC;
}
if (!resp) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: No recognized TLVs - send empty response packet");
resp = wpabuf_alloc(1);
}
send_resp:
if (!resp)
return 0;
wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: Encrypting Phase 2 data", resp);
if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TEAP,
data->teap_version, identifier,
resp, out_data)) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Failed to encrypt a Phase 2 frame");
}
wpabuf_free(resp);
return 0;
}
static int eap_teap_decrypt(struct eap_sm *sm, struct eap_teap_data *data,
struct eap_method_ret *ret, u8 identifier,
const struct wpabuf *in_data,
struct wpabuf **out_data)
{
struct wpabuf *in_decrypted;
int res;
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Received %lu bytes encrypted data for Phase 2",
(unsigned long) wpabuf_len(in_data));
if (data->pending_phase2_req) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Pending Phase 2 request - skip decryption and use old data");
/* Clear TLS reassembly state. */
eap_peer_tls_reset_input(&data->ssl);
in_decrypted = data->pending_phase2_req;
data->pending_phase2_req = NULL;
goto continue_req;
}
if (wpabuf_len(in_data) == 0) {
/* Received TLS ACK - requesting more fragments */
res = eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TEAP,
data->teap_version,
identifier, NULL, out_data);
if (res == 0 && !data->ssl.tls_out &&
data->on_tx_completion) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Mark authentication completed at full TX of fragments");
ret->methodState = data->on_tx_completion;
data->on_tx_completion = 0;
ret->decision = DECISION_UNCOND_SUCC;
}
return res;
}
res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
if (res)
return res;
continue_req:
wpa_hexdump_buf(MSG_MSGDUMP, "EAP-TEAP: Decrypted Phase 2 TLV(s)",
in_decrypted);
if (wpabuf_len(in_decrypted) < 4) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Too short Phase 2 TLV frame (len=%lu)",
(unsigned long) wpabuf_len(in_decrypted));
wpabuf_free(in_decrypted);
return -1;
}
res = eap_teap_process_decrypted(sm, data, ret, identifier,
in_decrypted, out_data);
wpabuf_free(in_decrypted);
return res;
}
static void eap_teap_select_pac(struct eap_teap_data *data,
const u8 *a_id, size_t a_id_len)
{
if (!a_id)
return;
data->current_pac = eap_teap_get_pac(data->pac, a_id, a_id_len,
PAC_TYPE_TUNNEL_PAC);
if (data->current_pac) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: PAC found for this A-ID (PAC-Type %d)",
data->current_pac->pac_type);
wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TEAP: A-ID-Info",
data->current_pac->a_id_info,
data->current_pac->a_id_info_len);
}
}
static int eap_teap_use_pac_opaque(struct eap_sm *sm,
struct eap_teap_data *data,
struct eap_teap_pac *pac)
{
u8 *tlv;
size_t tlv_len, olen;
struct teap_tlv_hdr *ehdr;
wpa_printf(MSG_DEBUG, "EAP-TEAP: Add PAC-Opaque TLS extension");
olen = pac->pac_opaque_len;
tlv_len = sizeof(*ehdr) + olen;
tlv = os_malloc(tlv_len);
if (tlv) {
ehdr = (struct teap_tlv_hdr *) tlv;
ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE);
ehdr->length = host_to_be16(olen);
os_memcpy(ehdr + 1, pac->pac_opaque, olen);
}
if (!tlv ||
tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
TLS_EXT_PAC_OPAQUE,
tlv, tlv_len) < 0) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Failed to add PAC-Opaque TLS extension");
os_free(tlv);
return -1;
}
os_free(tlv);
return 0;
}
static int eap_teap_clear_pac_opaque_ext(struct eap_sm *sm,
struct eap_teap_data *data)
{
if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Failed to remove PAC-Opaque TLS extension");
return -1;
}
return 0;
}
static int eap_teap_process_start(struct eap_sm *sm,
struct eap_teap_data *data, u8 flags,
const u8 *pos, size_t left)
{
const u8 *a_id = NULL;
size_t a_id_len = 0;
/* TODO: Support (mostly theoretical) case of TEAP/Start request being
* fragmented */
/* EAP-TEAP version negotiation (RFC 7170, Section 3.2) */
data->received_version = flags & EAP_TLS_VERSION_MASK;
wpa_printf(MSG_DEBUG, "EAP-TEAP: Start (server ver=%u, own ver=%u)",
data->received_version, data->teap_version);
if (data->received_version < 1) {
/* Version 1 was the first defined version, so reject 0 */
wpa_printf(MSG_INFO,
"EAP-TEAP: Server used unknown TEAP version %u",
data->received_version);
return -1;
}
if (data->received_version < data->teap_version)
data->teap_version = data->received_version;
wpa_printf(MSG_DEBUG, "EAP-TEAP: Using TEAP version %d",
data->teap_version);
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Start message payload", pos, left);
/* Parse Authority-ID TLV from Outer TLVs, if present */
if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) {
const u8 *outer_pos, *outer_end;
u32 outer_tlv_len;
if (left < 4) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Not enough room for the Outer TLV Length field");
return -1;
}
outer_tlv_len = WPA_GET_BE32(pos);
pos += 4;
left -= 4;
if (outer_tlv_len > left) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Truncated Outer TLVs field (Outer TLV Length: %u; remaining buffer: %u)",
outer_tlv_len, (unsigned int) left);
return -1;
}
outer_pos = pos + left - outer_tlv_len;
outer_end = outer_pos + outer_tlv_len;
wpa_hexdump(MSG_MSGDUMP, "EAP-TEAP: Start message Outer TLVs",
outer_pos, outer_tlv_len);
wpabuf_free(data->server_outer_tlvs);
data->server_outer_tlvs = wpabuf_alloc_copy(outer_pos,
outer_tlv_len);
if (!data->server_outer_tlvs)
return -1;
left -= outer_tlv_len;
if (left > 0) {
wpa_hexdump(MSG_INFO,
"EAP-TEAP: Unexpected TLS Data in Start message",
pos, left);
return -1;
}
while (outer_pos < outer_end) {
u16 tlv_type, tlv_len;
if (outer_end - outer_pos < 4) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Truncated Outer TLV header");
return -1;
}
tlv_type = WPA_GET_BE16(outer_pos);
outer_pos += 2;
tlv_len = WPA_GET_BE16(outer_pos);
outer_pos += 2;
/* Outer TLVs are required to be optional, so no need to
* check the M flag */
tlv_type &= TEAP_TLV_TYPE_MASK;
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Outer TLV: Type=%u Length=%u",
tlv_type, tlv_len);
if (outer_end - outer_pos < tlv_len) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Truncated Outer TLV (Type %u)",
tlv_type);
return -1;
}
if (tlv_type == TEAP_TLV_AUTHORITY_ID) {
wpa_hexdump(MSG_DEBUG, "EAP-TEAP: Authority-ID",
outer_pos, tlv_len);
if (a_id) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Multiple Authority-ID TLVs in TEAP/Start");
return -1;
}
a_id = outer_pos;
a_id_len = tlv_len;
} else {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Ignore unknown Outer TLV (Type %u)",
tlv_type);
}
outer_pos += tlv_len;
}
} else if (left > 0) {
wpa_hexdump(MSG_INFO,
"EAP-TEAP: Unexpected TLS Data in Start message",
pos, left);
return -1;
}
eap_teap_select_pac(data, a_id, a_id_len);
if (data->resuming && data->current_pac) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Trying to resume session - do not add PAC-Opaque to TLS ClientHello");
if (eap_teap_clear_pac_opaque_ext(sm, data) < 0)
return -1;
} else if (data->current_pac) {
/*
* PAC found for the A-ID and we are not resuming an old
* session, so add PAC-Opaque extension to ClientHello.
*/
if (eap_teap_use_pac_opaque(sm, data, data->current_pac) < 0)
return -1;
} else if (data->provisioning_allowed) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: No PAC found - starting provisioning");
if (eap_teap_clear_pac_opaque_ext(sm, data) < 0)
return -1;
data->provisioning = 1;
}
return 0;
}
#ifdef CONFIG_TESTING_OPTIONS
-static struct wpabuf * eap_teap_add_dummy_outer_tlvs(struct eap_teap_data *data,
- struct wpabuf *resp)
+static struct wpabuf * eap_teap_add_stub_outer_tlvs(struct eap_teap_data *data,
+ struct wpabuf *resp)
{
struct wpabuf *resp2;
u16 len;
const u8 *pos;
u8 flags;
wpabuf_free(data->peer_outer_tlvs);
data->peer_outer_tlvs = wpabuf_alloc(4 + 4);
if (!data->peer_outer_tlvs) {
wpabuf_free(resp);
return NULL;
}
- /* Outer TLVs (dummy Vendor-Specific TLV for testing) */
+ /* Outer TLVs (stub Vendor-Specific TLV for testing) */
wpabuf_put_be16(data->peer_outer_tlvs, TEAP_TLV_VENDOR_SPECIFIC);
wpabuf_put_be16(data->peer_outer_tlvs, 4);
wpabuf_put_be32(data->peer_outer_tlvs, EAP_VENDOR_HOSTAP);
- wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: TESTING - Add dummy Outer TLVs",
+ wpa_hexdump_buf(MSG_DEBUG, "EAP-TEAP: TESTING - Add stub Outer TLVs",
data->peer_outer_tlvs);
wpa_hexdump_buf(MSG_DEBUG,
"EAP-TEAP: TEAP/Start response before modification",
resp);
resp2 = wpabuf_alloc(wpabuf_len(resp) + 4 +
wpabuf_len(data->peer_outer_tlvs));
if (!resp2) {
wpabuf_free(resp);
return NULL;
}
pos = wpabuf_head(resp);
wpabuf_put_u8(resp2, *pos++); /* Code */
wpabuf_put_u8(resp2, *pos++); /* Identifier */
len = WPA_GET_BE16(pos);
pos += 2;
wpabuf_put_be16(resp2, len + 4 + wpabuf_len(data->peer_outer_tlvs));
wpabuf_put_u8(resp2, *pos++); /* Type */
/* Flags | Ver (with Outer TLV length included flag set to 1) */
flags = *pos++;
if (flags & (EAP_TEAP_FLAGS_OUTER_TLV_LEN |
EAP_TLS_FLAGS_LENGTH_INCLUDED)) {
wpa_printf(MSG_INFO,
"EAP-TEAP: Cannot add Outer TLVs for testing");
wpabuf_free(resp);
wpabuf_free(resp2);
return NULL;
}
flags |= EAP_TEAP_FLAGS_OUTER_TLV_LEN;
wpabuf_put_u8(resp2, flags);
/* Outer TLV Length */
wpabuf_put_be32(resp2, wpabuf_len(data->peer_outer_tlvs));
/* TLS Data */
wpabuf_put_data(resp2, pos, wpabuf_len(resp) - 6);
wpabuf_put_buf(resp2, data->peer_outer_tlvs); /* Outer TLVs */
wpabuf_free(resp);
wpa_hexdump_buf(MSG_DEBUG,
"EAP-TEAP: TEAP/Start response after modification",
resp2);
return resp2;
}
#endif /* CONFIG_TESTING_OPTIONS */
static struct wpabuf * eap_teap_process(struct eap_sm *sm, void *priv,
struct eap_method_ret *ret,
const struct wpabuf *reqData)
{
const struct eap_hdr *req;
size_t left;
int res;
u8 flags, id;
struct wpabuf *resp;
const u8 *pos;
struct eap_teap_data *data = priv;
struct wpabuf msg;
pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TEAP, ret,
reqData, &left, &flags);
if (!pos)
return NULL;
req = wpabuf_head(reqData);
id = req->identifier;
if (flags & EAP_TLS_FLAGS_START) {
if (eap_teap_process_start(sm, data, flags, pos, left) < 0)
return NULL;
/* Outer TLVs are not used in further packet processing and
* there cannot be TLS Data in this TEAP/Start message, so
* enforce that by ignoring whatever data might remain in the
* buffer. */
left = 0;
} else if (flags & EAP_TEAP_FLAGS_OUTER_TLV_LEN) {
/* TODO: RFC 7170, Section 4.3.1 indicates that the unexpected
* Outer TLVs MUST be ignored instead of ignoring the full
* message. */
wpa_printf(MSG_INFO,
"EAP-TEAP: Outer TLVs present in non-Start message -> ignore message");
return NULL;
}
wpabuf_set(&msg, pos, left);
resp = NULL;
if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
!data->resuming) {
/* Process tunneled (encrypted) phase 2 data. */
res = eap_teap_decrypt(sm, data, ret, id, &msg, &resp);
if (res < 0) {
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
/*
* Ack possible Alert that may have caused failure in
* decryption.
*/
res = 1;
}
} else {
if (sm->waiting_ext_cert_check && data->pending_resp) {
struct eap_peer_config *config = eap_get_config(sm);
if (config->pending_ext_cert_check ==
EXT_CERT_CHECK_GOOD) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: External certificate check succeeded - continue handshake");
resp = data->pending_resp;
data->pending_resp = NULL;
sm->waiting_ext_cert_check = 0;
return resp;
}
if (config->pending_ext_cert_check ==
EXT_CERT_CHECK_BAD) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: External certificate check failed - force authentication failure");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
sm->waiting_ext_cert_check = 0;
return NULL;
}
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Continuing to wait external server certificate validation");
return NULL;
}
/* Continue processing TLS handshake (phase 1). */
res = eap_peer_tls_process_helper(sm, &data->ssl,
EAP_TYPE_TEAP,
data->teap_version, id, &msg,
&resp);
if (res < 0) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: TLS processing failed");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
return resp;
}
if (sm->waiting_ext_cert_check) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Waiting external server certificate validation");
wpabuf_free(data->pending_resp);
data->pending_resp = resp;
return NULL;
}
if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
char cipher[80];
wpa_printf(MSG_DEBUG,
"EAP-TEAP: TLS done, proceed to Phase 2");
data->tls_cs =
tls_connection_get_cipher_suite(data->ssl.conn);
wpa_printf(MSG_DEBUG,
"EAP-TEAP: TLS cipher suite 0x%04x",
data->tls_cs);
if (data->provisioning &&
(!(data->provisioning_allowed &
EAP_TEAP_PROV_AUTH) ||
tls_get_cipher(sm->ssl_ctx, data->ssl.conn,
cipher, sizeof(cipher)) < 0 ||
os_strstr(cipher, "ADH-") ||
os_strstr(cipher, "anon"))) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Using anonymous (unauthenticated) provisioning");
data->anon_provisioning = 1;
} else {
data->anon_provisioning = 0;
}
data->resuming = 0;
if (eap_teap_derive_key_auth(sm, data) < 0) {
wpa_printf(MSG_DEBUG,
"EAP-TEAP: Could not derive keys");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
wpabuf_free(resp);
return NULL;
}
}
if (res == 2) {
/*
* Application data included in the handshake message.
*/
wpabuf_free(data->pending_phase2_req);
data->pending_phase2_req = resp;
resp = NULL;
res = eap_teap_decrypt(sm, data, ret, id, &msg, &resp);
}
}
if (res == 1) {
wpabuf_free(resp);
return eap_peer_tls_build_ack(id, EAP_TYPE_TEAP,
data->teap_version);
}
#ifdef CONFIG_TESTING_OPTIONS
if (data->test_outer_tlvs && res == 0 && resp &&
(flags & EAP_TLS_FLAGS_START) && wpabuf_len(resp) >= 6)
- resp = eap_teap_add_dummy_outer_tlvs(data, resp);
+ resp = eap_teap_add_stub_outer_tlvs(data, resp);
#endif /* CONFIG_TESTING_OPTIONS */
return resp;
}
#if 0 /* TODO */
static bool eap_teap_has_reauth_data(struct eap_sm *sm, void *priv)
{
struct eap_teap_data *data = priv;
return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
}
static void eap_teap_deinit_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_teap_data *data = priv;
if (data->phase2_priv && data->phase2_method &&
data->phase2_method->deinit_for_reauth)
data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
eap_teap_clear(data);
}
static void * eap_teap_init_for_reauth(struct eap_sm *sm, void *priv)
{
struct eap_teap_data *data = priv;
if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
eap_teap_deinit(sm, data);
return NULL;
}
if (data->phase2_priv && data->phase2_method &&
data->phase2_method->init_for_reauth)
data->phase2_method->init_for_reauth(sm, data->phase2_priv);
data->phase2_success = 0;
data->inner_method_done = 0;
data->result_success_done = 0;
data->iresult_verified = 0;
data->done_on_tx_completion = 0;
data->resuming = 1;
data->provisioning = 0;
data->anon_provisioning = 0;
data->simck_idx = 0;
return priv;
}
#endif
static int eap_teap_get_status(struct eap_sm *sm, void *priv, char *buf,
size_t buflen, int verbose)
{
struct eap_teap_data *data = priv;
int len, ret;
len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
if (data->phase2_method) {
ret = os_snprintf(buf + len, buflen - len,
"EAP-TEAP Phase 2 method=%s\n",
data->phase2_method->name);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
}
return len;
}
static bool eap_teap_isKeyAvailable(struct eap_sm *sm, void *priv)
{
struct eap_teap_data *data = priv;
return data->success;
}
static u8 * eap_teap_getKey(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_teap_data *data = priv;
u8 *key;
if (!data->success)
return NULL;
key = os_memdup(data->key_data, EAP_TEAP_KEY_LEN);
if (!key)
return NULL;
*len = EAP_TEAP_KEY_LEN;
return key;
}
static u8 * eap_teap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_teap_data *data = priv;
u8 *id;
if (!data->success || !data->session_id)
return NULL;
id = os_memdup(data->session_id, data->id_len);
if (!id)
return NULL;
*len = data->id_len;
return id;
}
static u8 * eap_teap_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_teap_data *data = priv;
u8 *key;
if (!data->success)
return NULL;
key = os_memdup(data->emsk, EAP_EMSK_LEN);
if (!key)
return NULL;
*len = EAP_EMSK_LEN;
return key;
}
int eap_peer_teap_register(void)
{
struct eap_method *eap;
eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_TEAP, "TEAP");
if (!eap)
return -1;
eap->init = eap_teap_init;
eap->deinit = eap_teap_deinit;
eap->process = eap_teap_process;
eap->isKeyAvailable = eap_teap_isKeyAvailable;
eap->getKey = eap_teap_getKey;
eap->getSessionId = eap_teap_get_session_id;
eap->get_status = eap_teap_get_status;
#if 0 /* TODO */
eap->has_reauth_data = eap_teap_has_reauth_data;
eap->deinit_for_reauth = eap_teap_deinit_for_reauth;
eap->init_for_reauth = eap_teap_init_for_reauth;
#endif
eap->get_emsk = eap_teap_get_emsk;
return eap_peer_method_register(eap);
}
diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.h b/contrib/wpa/src/eap_peer/eap_tls_common.h
index 183b7de00ae5..9ac00121f664 100644
--- a/contrib/wpa/src/eap_peer/eap_tls_common.h
+++ b/contrib/wpa/src/eap_peer/eap_tls_common.h
@@ -1,140 +1,140 @@
/*
* EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
* Copyright (c) 2004-2009, 2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef EAP_TLS_COMMON_H
#define EAP_TLS_COMMON_H
/**
* struct eap_ssl_data - TLS data for EAP methods
*/
struct eap_ssl_data {
/**
* conn - TLS connection context data from tls_connection_init()
*/
struct tls_connection *conn;
/**
* tls_out - TLS message to be sent out in fragments
*/
struct wpabuf *tls_out;
/**
* tls_out_pos - The current position in the outgoing TLS message
*/
size_t tls_out_pos;
/**
* tls_out_limit - Maximum fragment size for outgoing TLS messages
*/
size_t tls_out_limit;
/**
* tls_in - Received TLS message buffer for re-assembly
*/
struct wpabuf *tls_in;
/**
* tls_in_left - Number of remaining bytes in the incoming TLS message
*/
size_t tls_in_left;
/**
* tls_in_total - Total number of bytes in the incoming TLS message
*/
size_t tls_in_total;
/**
* phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
*/
int phase2;
/**
* include_tls_length - Whether the TLS length field is included even
* if the TLS data is not fragmented
*/
int include_tls_length;
/**
* eap - EAP state machine allocated with eap_peer_sm_init()
*/
struct eap_sm *eap;
/**
* ssl_ctx - TLS library context to use for the connection
*/
void *ssl_ctx;
/**
* eap_type - EAP method used in Phase 1
* (EAP_TYPE_TLS/PEAP/TTLS/FAST/TEAP)
*/
u8 eap_type;
/**
* tls_v13 - Whether TLS v1.3 or newer is used
*/
int tls_v13;
};
/* EAP TLS Flags */
#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
#define EAP_TLS_FLAGS_START 0x20
#define EAP_TEAP_FLAGS_OUTER_TLV_LEN 0x10
#define EAP_TLS_VERSION_MASK 0x07
/* could be up to 128 bytes, but only the first 64 bytes are used */
#define EAP_TLS_KEY_LEN 64
-/* dummy type used as a flag for UNAUTH-TLS */
+/* stub type used as a flag for UNAUTH-TLS */
#define EAP_UNAUTH_TLS_TYPE 255
#define EAP_WFA_UNAUTH_TLS_TYPE 254
int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
struct eap_peer_config *config, u8 eap_type);
void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
const char *label, const u8 *context,
size_t context_len, size_t len);
u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
struct eap_ssl_data *data, u8 eap_type,
size_t *len);
int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
enum eap_type eap_type, int peap_version,
u8 id, const struct wpabuf *in_data,
struct wpabuf **out_data);
struct wpabuf * eap_peer_tls_build_ack(u8 id, enum eap_type eap_type,
int peap_version);
int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
char *buf, size_t buflen, int verbose);
const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
struct eap_ssl_data *data,
enum eap_type eap_type,
struct eap_method_ret *ret,
const struct wpabuf *reqData,
size_t *len, u8 *flags);
void eap_peer_tls_reset_input(struct eap_ssl_data *data);
void eap_peer_tls_reset_output(struct eap_ssl_data *data);
int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
const struct wpabuf *in_data,
struct wpabuf **in_decrypted);
int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
enum eap_type eap_type, int peap_version, u8 id,
const struct wpabuf *in_data,
struct wpabuf **out_data);
int eap_peer_select_phase2_methods(struct eap_peer_config *config,
const char *prefix,
struct eap_method_type **types,
size_t *num_types, int use_machine_cred);
int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
struct eap_hdr *hdr, struct wpabuf **resp);
#endif /* EAP_TLS_COMMON_H */
diff --git a/contrib/wpa/src/eap_server/eap_tls_common.h b/contrib/wpa/src/eap_server/eap_tls_common.h
index b0b736123016..b0723a1fa492 100644
--- a/contrib/wpa/src/eap_server/eap_tls_common.h
+++ b/contrib/wpa/src/eap_server/eap_tls_common.h
@@ -1,101 +1,101 @@
/*
* EAP-TLS/PEAP/TTLS/FAST server common functions
* Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef EAP_TLS_COMMON_H
#define EAP_TLS_COMMON_H
/**
* struct eap_ssl_data - TLS data for EAP methods
*/
struct eap_ssl_data {
/**
* conn - TLS connection context data from tls_connection_init()
*/
struct tls_connection *conn;
/**
* tls_out - TLS message to be sent out in fragments
*/
struct wpabuf *tls_out;
/**
* tls_out_pos - The current position in the outgoing TLS message
*/
size_t tls_out_pos;
/**
* tls_out_limit - Maximum fragment size for outgoing TLS messages
*/
size_t tls_out_limit;
/**
* tls_in - Received TLS message buffer for re-assembly
*/
struct wpabuf *tls_in;
/**
* phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
*/
int phase2;
/**
* eap - EAP state machine allocated with eap_server_sm_init()
*/
struct eap_sm *eap;
enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state;
struct wpabuf tmpbuf;
/**
* tls_v13 - Whether TLS v1.3 or newer is used
*/
int tls_v13;
};
/* EAP TLS Flags */
#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
#define EAP_TLS_FLAGS_START 0x20
#define EAP_TEAP_FLAGS_OUTER_TLV_LEN 0x10
#define EAP_TLS_VERSION_MASK 0x07
/* could be up to 128 bytes, but only the first 64 bytes are used */
#define EAP_TLS_KEY_LEN 64
-/* dummy type used as a flag for UNAUTH-TLS */
+/* stub type used as a flag for UNAUTH-TLS */
#define EAP_UNAUTH_TLS_TYPE 255
#define EAP_WFA_UNAUTH_TLS_TYPE 254
struct wpabuf * eap_tls_msg_alloc(enum eap_type type, size_t payload_len,
u8 code, u8 identifier);
int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
int verify_peer, int eap_type);
void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
const char *label, const u8 *context,
size_t context_len, size_t len);
u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
struct eap_ssl_data *data, u8 eap_type,
size_t *len);
struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
int eap_type, int version, u8 id);
struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version);
int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data);
struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
struct eap_ssl_data *data,
const struct wpabuf *plain);
int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
struct wpabuf *respData, void *priv, int eap_type,
int (*proc_version)(struct eap_sm *sm, void *priv,
int peer_version),
void (*proc_msg)(struct eap_sm *sm, void *priv,
const struct wpabuf *respData));
#endif /* EAP_TLS_COMMON_H */
diff --git a/contrib/wpa/src/l2_packet/l2_packet_none.c b/contrib/wpa/src/l2_packet/l2_packet_none.c
index bc7a4e82d672..6783d7391cb0 100644
--- a/contrib/wpa/src/l2_packet/l2_packet_none.c
+++ b/contrib/wpa/src/l2_packet/l2_packet_none.c
@@ -1,137 +1,137 @@
/*
- * WPA Supplicant - Layer2 packet handling example with dummy functions
+ * WPA Supplicant - Layer2 packet handling example with stub functions
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This file can be used as a starting point for layer2 packet implementation.
*/
#include "includes.h"
#include "common.h"
#include "eloop.h"
#include "l2_packet.h"
struct l2_packet_data {
char ifname[17];
u8 own_addr[ETH_ALEN];
void (*rx_callback)(void *ctx, const u8 *src_addr,
const u8 *buf, size_t len);
void *rx_callback_ctx;
int l2_hdr; /* whether to include layer 2 (Ethernet) header data
* buffers */
int fd;
};
int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
{
os_memcpy(addr, l2->own_addr, ETH_ALEN);
return 0;
}
int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
const u8 *buf, size_t len)
{
if (l2 == NULL)
return -1;
/*
* TODO: Send frame (may need different implementation depending on
* whether l2->l2_hdr is set).
*/
return 0;
}
static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
struct l2_packet_data *l2 = eloop_ctx;
u8 buf[2300];
int res;
/* TODO: receive frame (e.g., recv() using sock */
buf[0] = 0;
res = 0;
l2->rx_callback(l2->rx_callback_ctx, NULL /* TODO: src addr */,
buf, res);
}
struct l2_packet_data * l2_packet_init(
const char *ifname, const u8 *own_addr, unsigned short protocol,
void (*rx_callback)(void *ctx, const u8 *src_addr,
const u8 *buf, size_t len),
void *rx_callback_ctx, int l2_hdr)
{
struct l2_packet_data *l2;
l2 = os_zalloc(sizeof(struct l2_packet_data));
if (l2 == NULL)
return NULL;
os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
l2->rx_callback = rx_callback;
l2->rx_callback_ctx = rx_callback_ctx;
l2->l2_hdr = l2_hdr;
/*
* TODO: open connection for receiving frames
*/
l2->fd = -1;
if (rx_callback && l2->fd >= 0)
eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
return l2;
}
struct l2_packet_data * l2_packet_init_bridge(
const char *br_ifname, const char *ifname, const u8 *own_addr,
unsigned short protocol,
void (*rx_callback)(void *ctx, const u8 *src_addr,
const u8 *buf, size_t len),
void *rx_callback_ctx, int l2_hdr)
{
return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
rx_callback_ctx, l2_hdr);
}
void l2_packet_deinit(struct l2_packet_data *l2)
{
if (l2 == NULL)
return;
if (l2->fd >= 0) {
eloop_unregister_read_sock(l2->fd);
/* TODO: close connection */
}
os_free(l2);
}
int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
{
/* TODO: get interface IP address */
return -1;
}
void l2_packet_notify_auth_start(struct l2_packet_data *l2)
{
/* This function can be left empty */
}
int l2_packet_set_packet_filter(struct l2_packet_data *l2,
enum l2_packet_filter_type type)
{
return -1;
}
diff --git a/contrib/wpa/src/p2p/p2p.c b/contrib/wpa/src/p2p/p2p.c
index 9ac505735cbb..598a449c11b2 100644
--- a/contrib/wpa/src/p2p/p2p.c
+++ b/contrib/wpa/src/p2p/p2p.c
@@ -1,5651 +1,5653 @@
/*
* Wi-Fi Direct - P2P module
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "eloop.h"
#include "common/defs.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "crypto/sha256.h"
#include "crypto/crypto.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
#include "p2p.h"
static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx);
static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev);
static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
const u8 *sa, const u8 *data, size_t len,
int rx_freq);
static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
const u8 *sa, const u8 *data,
size_t len);
static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx);
static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx);
/*
* p2p_scan recovery timeout
*
* Many drivers are using 30 second timeout on scan results. Allow a bit larger
* timeout for this to avoid hitting P2P timeout unnecessarily.
*/
#define P2P_SCAN_TIMEOUT 35
/**
* P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
* entries will be removed
*/
#ifndef P2P_PEER_EXPIRATION_AGE
#define P2P_PEER_EXPIRATION_AGE 60
#endif /* P2P_PEER_EXPIRATION_AGE */
void p2p_expire_peers(struct p2p_data *p2p)
{
struct p2p_device *dev, *n;
struct os_reltime now;
size_t i;
os_get_reltime(&now);
dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec)
continue;
if (dev == p2p->go_neg_peer) {
/*
* GO Negotiation is in progress with the peer, so
* don't expire the peer entry until GO Negotiation
* fails or times out.
*/
continue;
}
if (p2p->cfg->go_connected &&
p2p->cfg->go_connected(p2p->cfg->cb_ctx,
dev->info.p2p_device_addr)) {
/*
* We are connected as a client to a group in which the
* peer is the GO, so do not expire the peer entry.
*/
os_get_reltime(&dev->last_seen);
continue;
}
for (i = 0; i < p2p->num_groups; i++) {
if (p2p_group_is_client_connected(
p2p->groups[i], dev->info.p2p_device_addr))
break;
}
if (i < p2p->num_groups) {
/*
* The peer is connected as a client in a group where
* we are the GO, so do not expire the peer entry.
*/
os_get_reltime(&dev->last_seen);
continue;
}
p2p_dbg(p2p, "Expiring old peer entry " MACSTR,
MAC2STR(dev->info.p2p_device_addr));
dl_list_del(&dev->list);
p2p_device_free(p2p, dev);
}
}
static const char * p2p_state_txt(int state)
{
switch (state) {
case P2P_IDLE:
return "IDLE";
case P2P_SEARCH:
return "SEARCH";
case P2P_CONNECT:
return "CONNECT";
case P2P_CONNECT_LISTEN:
return "CONNECT_LISTEN";
case P2P_GO_NEG:
return "GO_NEG";
case P2P_LISTEN_ONLY:
return "LISTEN_ONLY";
case P2P_WAIT_PEER_CONNECT:
return "WAIT_PEER_CONNECT";
case P2P_WAIT_PEER_IDLE:
return "WAIT_PEER_IDLE";
case P2P_SD_DURING_FIND:
return "SD_DURING_FIND";
case P2P_PROVISIONING:
return "PROVISIONING";
case P2P_PD_DURING_FIND:
return "PD_DURING_FIND";
case P2P_INVITE:
return "INVITE";
case P2P_INVITE_LISTEN:
return "INVITE_LISTEN";
default:
return "?";
}
}
const char * p2p_get_state_txt(struct p2p_data *p2p)
{
return p2p_state_txt(p2p->state);
}
struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p)
{
return p2p ? p2p->p2ps_adv_list : NULL;
}
void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr)
{
if (p2p && intended_addr)
os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN);
}
u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
{
struct p2p_device *dev = NULL;
if (!addr || !p2p)
return 0;
dev = p2p_get_device(p2p, addr);
if (dev)
return dev->wps_prov_info;
else
return 0;
}
void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr)
{
struct p2p_device *dev = NULL;
if (!addr || !p2p)
return;
dev = p2p_get_device(p2p, addr);
if (dev)
dev->wps_prov_info = 0;
}
void p2p_set_state(struct p2p_data *p2p, int new_state)
{
p2p_dbg(p2p, "State %s -> %s",
p2p_state_txt(p2p->state), p2p_state_txt(new_state));
p2p->state = new_state;
if (new_state == P2P_IDLE && p2p->pending_channel) {
p2p_dbg(p2p, "Apply change in listen channel");
p2p->cfg->reg_class = p2p->pending_reg_class;
p2p->cfg->channel = p2p->pending_channel;
p2p->pending_reg_class = 0;
p2p->pending_channel = 0;
}
}
void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec)
{
p2p_dbg(p2p, "Set timeout (state=%s): %u.%06u sec",
p2p_state_txt(p2p->state), sec, usec);
eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL);
}
void p2p_clear_timeout(struct p2p_data *p2p)
{
p2p_dbg(p2p, "Clear timeout (state=%s)", p2p_state_txt(p2p->state));
eloop_cancel_timeout(p2p_state_timeout, p2p, NULL);
}
void p2p_go_neg_failed(struct p2p_data *p2p, int status)
{
struct p2p_go_neg_results res;
struct p2p_device *peer = p2p->go_neg_peer;
if (!peer)
return;
eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
if (p2p->state != P2P_SEARCH) {
/*
* Clear timeouts related to GO Negotiation if no new p2p_find
* has been started.
*/
p2p_clear_timeout(p2p);
p2p_set_state(p2p, P2P_IDLE);
}
peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
peer->wps_method = WPS_NOT_READY;
peer->oob_pw_id = 0;
wpabuf_free(peer->go_neg_conf);
peer->go_neg_conf = NULL;
p2p->go_neg_peer = NULL;
os_memset(&res, 0, sizeof(res));
res.status = status;
os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
}
static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc)
{
unsigned int r, tu;
int freq;
struct wpabuf *ies;
p2p_dbg(p2p, "Starting short listen state (state=%s)",
p2p_state_txt(p2p->state));
if (p2p->pending_listen_freq) {
/* We have a pending p2p_listen request */
p2p_dbg(p2p, "p2p_listen command pending already");
return;
}
freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel);
if (freq < 0) {
p2p_dbg(p2p, "Unknown regulatory class/channel");
return;
}
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
r = 0;
tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) +
p2p->min_disc_int) * 100;
if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu)
tu = p2p->max_disc_tu;
if (!dev_disc && tu < 100)
tu = 100; /* Need to wait in non-device discovery use cases */
if (p2p->cfg->max_listen && 1024 * tu / 1000 > p2p->cfg->max_listen)
tu = p2p->cfg->max_listen * 1000 / 1024;
if (tu == 0) {
p2p_dbg(p2p, "Skip listen state since duration was 0 TU");
p2p_set_timeout(p2p, 0, 0);
return;
}
ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
if (ies == NULL)
return;
p2p->pending_listen_freq = freq;
p2p->pending_listen_sec = 0;
p2p->pending_listen_usec = 1024 * tu;
if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000,
ies) < 0) {
p2p_dbg(p2p, "Failed to start listen mode");
p2p->pending_listen_freq = 0;
}
wpabuf_free(ies);
}
int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
{
int freq;
struct wpabuf *ies;
p2p_dbg(p2p, "Going to listen(only) state");
if (p2p->pending_listen_freq) {
/* We have a pending p2p_listen request */
p2p_dbg(p2p, "p2p_listen command pending already");
return -1;
}
freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel);
if (freq < 0) {
p2p_dbg(p2p, "Unknown regulatory class/channel");
return -1;
}
p2p->pending_listen_sec = timeout / 1000;
p2p->pending_listen_usec = (timeout % 1000) * 1000;
if (p2p->p2p_scan_running) {
if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) {
p2p_dbg(p2p, "p2p_scan running - connect is already pending - skip listen");
return 0;
}
p2p_dbg(p2p, "p2p_scan running - delay start of listen state");
p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN;
return 0;
}
ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
if (ies == NULL)
return -1;
p2p->pending_listen_freq = freq;
if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) {
p2p_dbg(p2p, "Failed to start listen mode");
p2p->pending_listen_freq = 0;
wpabuf_free(ies);
return -1;
}
wpabuf_free(ies);
p2p_set_state(p2p, P2P_LISTEN_ONLY);
return 0;
}
static void p2p_device_clear_reported(struct p2p_data *p2p)
{
struct p2p_device *dev;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
dev->flags &= ~P2P_DEV_REPORTED;
dev->sd_reqs = 0;
}
}
/**
* p2p_get_device - Fetch a peer entry
* @p2p: P2P module context from p2p_init()
* @addr: P2P Device Address of the peer
* Returns: Pointer to the device entry or %NULL if not found
*/
struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr)
{
struct p2p_device *dev;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
if (os_memcmp(dev->info.p2p_device_addr, addr, ETH_ALEN) == 0)
return dev;
}
return NULL;
}
/**
* p2p_get_device_interface - Fetch a peer entry based on P2P Interface Address
* @p2p: P2P module context from p2p_init()
* @addr: P2P Interface Address of the peer
* Returns: Pointer to the device entry or %NULL if not found
*/
struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
const u8 *addr)
{
struct p2p_device *dev;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
if (os_memcmp(dev->interface_addr, addr, ETH_ALEN) == 0)
return dev;
}
return NULL;
}
/**
* p2p_create_device - Create a peer entry
* @p2p: P2P module context from p2p_init()
* @addr: P2P Device Address of the peer
* Returns: Pointer to the device entry or %NULL on failure
*
* If there is already an entry for the peer, it will be returned instead of
* creating a new one.
*/
static struct p2p_device * p2p_create_device(struct p2p_data *p2p,
const u8 *addr)
{
struct p2p_device *dev, *oldest = NULL;
size_t count = 0;
dev = p2p_get_device(p2p, addr);
if (dev)
return dev;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
count++;
if (oldest == NULL ||
os_reltime_before(&dev->last_seen, &oldest->last_seen))
oldest = dev;
}
if (count + 1 > p2p->cfg->max_peers && oldest) {
p2p_dbg(p2p,
"Remove oldest peer entry to make room for a new peer "
MACSTR, MAC2STR(oldest->info.p2p_device_addr));
dl_list_del(&oldest->list);
p2p_device_free(p2p, oldest);
}
dev = os_zalloc(sizeof(*dev));
if (dev == NULL)
return NULL;
dl_list_add(&p2p->devices, &dev->list);
os_memcpy(dev->info.p2p_device_addr, addr, ETH_ALEN);
return dev;
}
static void p2p_copy_client_info(struct p2p_device *dev,
struct p2p_client_info *cli)
{
p2p_copy_filter_devname(dev->info.device_name,
sizeof(dev->info.device_name),
cli->dev_name, cli->dev_name_len);
dev->info.dev_capab = cli->dev_capab;
dev->info.config_methods = cli->config_methods;
os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8);
dev->info.wps_sec_dev_type_list_len = 8 * cli->num_sec_dev_types;
if (dev->info.wps_sec_dev_type_list_len > WPS_SEC_DEV_TYPE_MAX_LEN)
dev->info.wps_sec_dev_type_list_len = WPS_SEC_DEV_TYPE_MAX_LEN;
os_memcpy(dev->info.wps_sec_dev_type_list, cli->sec_dev_types,
dev->info.wps_sec_dev_type_list_len);
}
static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
const u8 *go_interface_addr, int freq,
const u8 *gi, size_t gi_len,
struct os_reltime *rx_time)
{
struct p2p_group_info info;
size_t c;
struct p2p_device *dev;
if (gi == NULL)
return 0;
if (p2p_group_info_parse(gi, gi_len, &info) < 0)
return -1;
/*
* Clear old data for this group; if the devices are still in the
* group, the information will be restored in the loop following this.
*/
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
if (os_memcmp(dev->member_in_go_iface, go_interface_addr,
ETH_ALEN) == 0) {
os_memset(dev->member_in_go_iface, 0, ETH_ALEN);
os_memset(dev->member_in_go_dev, 0, ETH_ALEN);
}
}
for (c = 0; c < info.num_clients; c++) {
struct p2p_client_info *cli = &info.client[c];
if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr,
ETH_ALEN) == 0)
continue; /* ignore our own entry */
dev = p2p_get_device(p2p, cli->p2p_device_addr);
if (dev) {
if (dev->flags & (P2P_DEV_GROUP_CLIENT_ONLY |
P2P_DEV_PROBE_REQ_ONLY)) {
/*
* Update information since we have not
* received this directly from the client.
*/
p2p_copy_client_info(dev, cli);
} else {
/*
* Need to update P2P Client Discoverability
* flag since it is valid only in P2P Group
* Info attribute.
*/
dev->info.dev_capab &=
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
dev->info.dev_capab |=
cli->dev_capab &
P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
}
if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
}
} else {
dev = p2p_create_device(p2p, cli->p2p_device_addr);
if (dev == NULL)
continue;
dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
p2p_copy_client_info(dev, cli);
dev->oper_freq = freq;
p2p->cfg->dev_found(p2p->cfg->cb_ctx,
dev->info.p2p_device_addr,
&dev->info, 1);
dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
}
os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
ETH_ALEN);
os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
os_memcpy(dev->member_in_go_iface, go_interface_addr,
ETH_ALEN);
dev->flags |= P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT;
}
return 0;
}
static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev,
int probe_req, const struct p2p_message *msg)
{
os_memcpy(dev->info.device_name, msg->device_name,
sizeof(dev->info.device_name));
if (msg->manufacturer &&
msg->manufacturer_len < sizeof(dev->info.manufacturer)) {
os_memset(dev->info.manufacturer, 0,
sizeof(dev->info.manufacturer));
os_memcpy(dev->info.manufacturer, msg->manufacturer,
msg->manufacturer_len);
}
if (msg->model_name &&
msg->model_name_len < sizeof(dev->info.model_name)) {
os_memset(dev->info.model_name, 0,
sizeof(dev->info.model_name));
os_memcpy(dev->info.model_name, msg->model_name,
msg->model_name_len);
}
if (msg->model_number &&
msg->model_number_len < sizeof(dev->info.model_number)) {
os_memset(dev->info.model_number, 0,
sizeof(dev->info.model_number));
os_memcpy(dev->info.model_number, msg->model_number,
msg->model_number_len);
}
if (msg->serial_number &&
msg->serial_number_len < sizeof(dev->info.serial_number)) {
os_memset(dev->info.serial_number, 0,
sizeof(dev->info.serial_number));
os_memcpy(dev->info.serial_number, msg->serial_number,
msg->serial_number_len);
}
if (msg->pri_dev_type)
os_memcpy(dev->info.pri_dev_type, msg->pri_dev_type,
sizeof(dev->info.pri_dev_type));
else if (msg->wps_pri_dev_type)
os_memcpy(dev->info.pri_dev_type, msg->wps_pri_dev_type,
sizeof(dev->info.pri_dev_type));
if (msg->wps_sec_dev_type_list) {
os_memcpy(dev->info.wps_sec_dev_type_list,
msg->wps_sec_dev_type_list,
msg->wps_sec_dev_type_list_len);
dev->info.wps_sec_dev_type_list_len =
msg->wps_sec_dev_type_list_len;
}
if (msg->capability) {
/*
* P2P Client Discoverability bit is reserved in all frames
* that use this function, so do not change its value here.
*/
dev->info.dev_capab &= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
dev->info.dev_capab |= msg->capability[0] &
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
dev->info.group_capab = msg->capability[1];
}
if (msg->ext_listen_timing) {
dev->ext_listen_period = WPA_GET_LE16(msg->ext_listen_timing);
dev->ext_listen_interval =
WPA_GET_LE16(msg->ext_listen_timing + 2);
}
if (!probe_req) {
u16 new_config_methods;
new_config_methods = msg->config_methods ?
msg->config_methods : msg->wps_config_methods;
if (new_config_methods &&
dev->info.config_methods != new_config_methods) {
p2p_dbg(p2p, "Update peer " MACSTR
" config_methods 0x%x -> 0x%x",
MAC2STR(dev->info.p2p_device_addr),
dev->info.config_methods,
new_config_methods);
dev->info.config_methods = new_config_methods;
}
}
}
static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies,
size_t ies_len)
{
const u8 *pos, *end;
u8 id, len;
wpabuf_free(dev->info.vendor_elems);
dev->info.vendor_elems = NULL;
end = ies + ies_len;
for (pos = ies; end - pos > 1; pos += len) {
id = *pos++;
len = *pos++;
if (len > end - pos)
break;
if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3)
continue;
if (len >= 4) {
u32 type = WPA_GET_BE32(pos);
if (type == WPA_IE_VENDOR_TYPE ||
type == WMM_IE_VENDOR_TYPE ||
type == WPS_IE_VENDOR_TYPE ||
type == P2P_IE_VENDOR_TYPE ||
type == WFD_IE_VENDOR_TYPE)
continue;
}
/* Unknown vendor element - make raw IE data available */
if (wpabuf_resize(&dev->info.vendor_elems, 2 + len) < 0)
break;
wpabuf_put_data(dev->info.vendor_elems, pos - 2, 2 + len);
if (wpabuf_size(dev->info.vendor_elems) > 2000)
break;
}
}
static int p2p_compare_wfd_info(struct p2p_device *dev,
const struct p2p_message *msg)
{
if (dev->info.wfd_subelems && msg->wfd_subelems) {
if (dev->info.wfd_subelems->used != msg->wfd_subelems->used)
return 1;
return os_memcmp(dev->info.wfd_subelems->buf,
msg->wfd_subelems->buf,
dev->info.wfd_subelems->used);
}
if (dev->info.wfd_subelems || msg->wfd_subelems)
return 1;
return 0;
}
/**
* p2p_add_device - Add peer entries based on scan results or P2P frames
* @p2p: P2P module context from p2p_init()
* @addr: Source address of Beacon or Probe Response frame (may be either
* P2P Device Address or P2P Interface Address)
* @level: Signal level (signal strength of the received frame from the peer)
* @freq: Frequency on which the Beacon or Probe Response frame was received
* @rx_time: Time when the result was received
* @ies: IEs from the Beacon or Probe Response frame
* @ies_len: Length of ies buffer in octets
* @scan_res: Whether this was based on scan results
* Returns: 0 on success, -1 on failure
*
* If the scan result is for a GO, the clients in the group will also be added
* to the peer table. This function can also be used with some other frames
* like Provision Discovery Request that contains P2P Capability and P2P Device
* Info attributes.
*/
int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
struct os_reltime *rx_time, int level, const u8 *ies,
size_t ies_len, int scan_res)
{
struct p2p_device *dev;
struct p2p_message msg;
const u8 *p2p_dev_addr;
int wfd_changed;
int dev_name_changed;
int i;
struct os_reltime time_now;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_ies(ies, ies_len, &msg)) {
p2p_dbg(p2p, "Failed to parse P2P IE for a device entry");
p2p_parse_free(&msg);
return -1;
}
if (msg.p2p_device_addr)
p2p_dev_addr = msg.p2p_device_addr;
else if (msg.device_id)
p2p_dev_addr = msg.device_id;
else {
p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id");
p2p_parse_free(&msg);
return -1;
}
if (!is_zero_ether_addr(p2p->peer_filter) &&
os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) {
p2p_dbg(p2p, "Do not add peer filter for " MACSTR
" due to peer filter", MAC2STR(p2p_dev_addr));
p2p_parse_free(&msg);
return 0;
}
dev = p2p_create_device(p2p, p2p_dev_addr);
if (dev == NULL) {
p2p_parse_free(&msg);
return -1;
}
if (rx_time == NULL) {
os_get_reltime(&time_now);
rx_time = &time_now;
}
/*
* Update the device entry only if the new peer
* entry is newer than the one previously stored, or if
* the device was previously seen as a P2P Client in a group
* and the new entry isn't older than a threshold.
*/
if (dev->last_seen.sec > 0 &&
os_reltime_before(rx_time, &dev->last_seen) &&
(!(dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT) ||
os_reltime_expired(&dev->last_seen, rx_time,
P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD))) {
p2p_dbg(p2p,
"Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u flags=0x%x)",
(unsigned int) rx_time->sec,
(unsigned int) rx_time->usec,
(unsigned int) dev->last_seen.sec,
(unsigned int) dev->last_seen.usec,
dev->flags);
p2p_parse_free(&msg);
return -1;
}
os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY |
P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT);
if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
os_memcpy(dev->interface_addr, addr, ETH_ALEN);
if (msg.ssid &&
msg.ssid[1] <= sizeof(dev->oper_ssid) &&
(msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
!= 0)) {
os_memcpy(dev->oper_ssid, msg.ssid + 2, msg.ssid[1]);
dev->oper_ssid_len = msg.ssid[1];
}
wpabuf_free(dev->info.p2ps_instance);
dev->info.p2ps_instance = NULL;
if (msg.adv_service_instance && msg.adv_service_instance_len)
dev->info.p2ps_instance = wpabuf_alloc_copy(
msg.adv_service_instance, msg.adv_service_instance_len);
if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
*msg.ds_params >= 1 && *msg.ds_params <= 14) {
int ds_freq;
if (*msg.ds_params == 14)
ds_freq = 2484;
else
ds_freq = 2407 + *msg.ds_params * 5;
if (freq != ds_freq) {
p2p_dbg(p2p, "Update Listen frequency based on DS Parameter Set IE: %d -> %d MHz",
freq, ds_freq);
freq = ds_freq;
}
}
if (dev->listen_freq && dev->listen_freq != freq && scan_res) {
p2p_dbg(p2p, "Update Listen frequency based on scan results ("
MACSTR " %d -> %d MHz (DS param %d)",
MAC2STR(dev->info.p2p_device_addr), dev->listen_freq,
freq, msg.ds_params ? *msg.ds_params : -1);
}
if (scan_res) {
dev->listen_freq = freq;
if (msg.group_info)
dev->oper_freq = freq;
}
dev->info.level = level;
dev_name_changed = os_strncmp(dev->info.device_name, msg.device_name,
WPS_DEV_NAME_MAX_LEN) != 0;
p2p_copy_wps_info(p2p, dev, 0, &msg);
for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
wpabuf_free(dev->info.wps_vendor_ext[i]);
dev->info.wps_vendor_ext[i] = NULL;
}
for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
if (msg.wps_vendor_ext[i] == NULL)
break;
dev->info.wps_vendor_ext[i] = wpabuf_alloc_copy(
msg.wps_vendor_ext[i], msg.wps_vendor_ext_len[i]);
if (dev->info.wps_vendor_ext[i] == NULL)
break;
}
wfd_changed = p2p_compare_wfd_info(dev, &msg);
if (wfd_changed) {
wpabuf_free(dev->info.wfd_subelems);
if (msg.wfd_subelems)
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
else
dev->info.wfd_subelems = NULL;
}
if (scan_res) {
p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
msg.group_info, msg.group_info_len,
rx_time);
}
p2p_parse_free(&msg);
p2p_update_peer_vendor_elems(dev, ies, ies_len);
if (dev->flags & P2P_DEV_REPORTED && !wfd_changed &&
!dev_name_changed &&
(!msg.adv_service_instance ||
(dev->flags & P2P_DEV_P2PS_REPORTED)))
return 0;
p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)",
freq, (unsigned int) rx_time->sec,
(unsigned int) rx_time->usec);
if (dev->flags & P2P_DEV_USER_REJECTED) {
p2p_dbg(p2p, "Do not report rejected device");
return 0;
}
if (dev->info.config_methods == 0 &&
(freq == 2412 || freq == 2437 || freq == 2462)) {
/*
* If we have only seen a Beacon frame from a GO, we do not yet
* know what WPS config methods it supports. Since some
* applications use config_methods value from P2P-DEVICE-FOUND
* events, postpone reporting this peer until we've fully
* discovered its capabilities.
*
* At least for now, do this only if the peer was detected on
* one of the social channels since that peer can be easily be
* found again and there are no limitations of having to use
* passive scan on this channels, so this can be done through
* Probe Response frame that includes the config_methods
* information.
*/
p2p_dbg(p2p, "Do not report peer " MACSTR
" with unknown config methods", MAC2STR(addr));
return 0;
}
p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
!(dev->flags & P2P_DEV_REPORTED_ONCE));
dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
if (msg.adv_service_instance)
dev->flags |= P2P_DEV_P2PS_REPORTED;
return 0;
}
static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
{
int i;
if (p2p->go_neg_peer == dev) {
/*
* If GO Negotiation is in progress, report that it has failed.
*/
p2p_go_neg_failed(p2p, -1);
}
if (p2p->invite_peer == dev)
p2p->invite_peer = NULL;
if (p2p->sd_peer == dev)
p2p->sd_peer = NULL;
if (p2p->pending_client_disc_go == dev)
p2p->pending_client_disc_go = NULL;
/* dev_lost() device, but only if it was previously dev_found() */
if (dev->flags & P2P_DEV_REPORTED_ONCE)
p2p->cfg->dev_lost(p2p->cfg->cb_ctx,
dev->info.p2p_device_addr);
for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
wpabuf_free(dev->info.wps_vendor_ext[i]);
dev->info.wps_vendor_ext[i] = NULL;
}
wpabuf_free(dev->info.wfd_subelems);
wpabuf_free(dev->info.vendor_elems);
wpabuf_free(dev->go_neg_conf);
wpabuf_free(dev->info.p2ps_instance);
os_free(dev);
}
static int p2p_get_next_prog_freq(struct p2p_data *p2p)
{
struct p2p_channels *c;
struct p2p_reg_class *cla;
size_t cl, ch;
int found = 0;
u8 reg_class;
u8 channel;
int freq;
c = &p2p->cfg->channels;
for (cl = 0; cl < c->reg_classes; cl++) {
cla = &c->reg_class[cl];
if (cla->reg_class != p2p->last_prog_scan_class)
continue;
for (ch = 0; ch < cla->channels; ch++) {
if (cla->channel[ch] == p2p->last_prog_scan_chan) {
found = 1;
break;
}
}
if (found)
break;
}
if (!found) {
/* Start from beginning */
reg_class = c->reg_class[0].reg_class;
channel = c->reg_class[0].channel[0];
} else {
/* Pick the next channel */
ch++;
if (ch == cla->channels) {
cl++;
if (cl == c->reg_classes)
cl = 0;
ch = 0;
}
reg_class = c->reg_class[cl].reg_class;
channel = c->reg_class[cl].channel[ch];
}
freq = p2p_channel_to_freq(reg_class, channel);
p2p_dbg(p2p, "Next progressive search channel: reg_class %u channel %u -> %d MHz",
reg_class, channel, freq);
p2p->last_prog_scan_class = reg_class;
p2p->last_prog_scan_chan = channel;
if (freq == 2412 || freq == 2437 || freq == 2462)
return 0; /* No need to add social channels */
return freq;
}
static void p2p_search(struct p2p_data *p2p)
{
int freq = 0;
enum p2p_scan_type type;
u16 pw_id = DEV_PW_DEFAULT;
int res;
if (p2p->drv_in_listen) {
p2p_dbg(p2p, "Driver is still in Listen state - wait for it to end before continuing");
return;
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
if (p2p->find_pending_full &&
(p2p->find_type == P2P_FIND_PROGRESSIVE ||
p2p->find_type == P2P_FIND_START_WITH_FULL)) {
type = P2P_SCAN_FULL;
p2p_dbg(p2p, "Starting search (pending full scan)");
p2p->find_pending_full = 0;
} else if ((p2p->find_type == P2P_FIND_PROGRESSIVE &&
(freq = p2p_get_next_prog_freq(p2p)) > 0) ||
(p2p->find_type == P2P_FIND_START_WITH_FULL &&
(freq = p2p->find_specified_freq) > 0)) {
type = P2P_SCAN_SOCIAL_PLUS_ONE;
p2p_dbg(p2p, "Starting search (+ freq %u)", freq);
} else {
type = P2P_SCAN_SOCIAL;
p2p_dbg(p2p, "Starting search");
}
res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
p2p->num_req_dev_types, p2p->req_dev_types,
p2p->find_dev_id, pw_id, p2p->include_6ghz);
if (res < 0) {
p2p_dbg(p2p, "Scan request schedule failed");
p2p_continue_find(p2p);
}
}
static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct p2p_data *p2p = eloop_ctx;
p2p_dbg(p2p, "Find timeout -> stop");
p2p_stop_find(p2p);
}
void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status)
{
if (status != 0) {
p2p_dbg(p2p, "Scan request failed");
/* Do continue find even for the first p2p_find_scan */
p2p_continue_find(p2p);
} else {
p2p_dbg(p2p, "Running p2p_scan");
p2p->p2p_scan_running = 1;
eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
p2p, NULL);
}
}
static int p2p_run_after_scan(struct p2p_data *p2p)
{
struct p2p_device *dev;
enum p2p_after_scan op;
op = p2p->start_after_scan;
p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
switch (op) {
case P2P_AFTER_SCAN_NOTHING:
break;
case P2P_AFTER_SCAN_LISTEN:
p2p_dbg(p2p, "Start previously requested Listen state");
p2p_listen(p2p, p2p->pending_listen_sec * 1000 +
p2p->pending_listen_usec / 1000);
return 1;
case P2P_AFTER_SCAN_CONNECT:
p2p_dbg(p2p, "Start previously requested connect with " MACSTR,
MAC2STR(p2p->after_scan_peer));
dev = p2p_get_device(p2p, p2p->after_scan_peer);
if (dev == NULL) {
p2p_dbg(p2p, "Peer not known anymore");
break;
}
p2p_connect_send(p2p, dev);
return 1;
}
return 0;
}
static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct p2p_data *p2p = eloop_ctx;
int running;
p2p_dbg(p2p, "p2p_scan timeout (running=%d)", p2p->p2p_scan_running);
running = p2p->p2p_scan_running;
/* Make sure we recover from missed scan results callback */
p2p->p2p_scan_running = 0;
if (running)
p2p_run_after_scan(p2p);
}
static void p2p_free_req_dev_types(struct p2p_data *p2p)
{
p2p->num_req_dev_types = 0;
os_free(p2p->req_dev_types);
p2p->req_dev_types = NULL;
}
static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash)
{
u8 buf[SHA256_MAC_LEN];
char str_buf[256];
const u8 *adv_array;
size_t i, adv_len;
if (!str || !hash)
return 0;
if (!str[0]) {
os_memcpy(hash, p2p->wild_card_hash, P2PS_HASH_LEN);
return 1;
}
adv_array = (u8 *) str_buf;
adv_len = os_strlen(str);
if (adv_len >= sizeof(str_buf))
return 0;
for (i = 0; i < adv_len; i++) {
if (str[i] >= 'A' && str[i] <= 'Z')
str_buf[i] = str[i] - 'A' + 'a';
else
str_buf[i] = str[i];
}
if (sha256_vector(1, &adv_array, &adv_len, buf))
return 0;
os_memcpy(hash, buf, P2PS_HASH_LEN);
return 1;
}
int p2p_find(struct p2p_data *p2p, unsigned int timeout,
enum p2p_discovery_type type,
unsigned int num_req_dev_types, const u8 *req_dev_types,
const u8 *dev_id, unsigned int search_delay,
u8 seek_count, const char **seek, int freq, bool include_6ghz)
{
int res;
struct os_reltime start;
p2p_dbg(p2p, "Starting find (type=%d)", type);
if (p2p->p2p_scan_running) {
p2p_dbg(p2p, "p2p_scan is already running");
}
p2p_free_req_dev_types(p2p);
if (req_dev_types && num_req_dev_types) {
p2p->req_dev_types = os_memdup(req_dev_types,
num_req_dev_types *
WPS_DEV_TYPE_LEN);
if (p2p->req_dev_types == NULL)
return -1;
p2p->num_req_dev_types = num_req_dev_types;
}
if (dev_id) {
os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN);
p2p->find_dev_id = p2p->find_dev_id_buf;
} else
p2p->find_dev_id = NULL;
p2p->include_6ghz = p2p_wfd_enabled(p2p) && include_6ghz;
if (seek_count == 0 || !seek) {
/* Not an ASP search */
p2p->p2ps_seek = 0;
} else if (seek_count == 1 && seek && (!seek[0] || !seek[0][0])) {
/*
* An empty seek string means no hash values, but still an ASP
* search.
*/
p2p_dbg(p2p, "ASP search");
p2p->p2ps_seek_count = 0;
p2p->p2ps_seek = 1;
} else if (seek && seek_count <= P2P_MAX_QUERY_HASH) {
u8 buf[P2PS_HASH_LEN];
int i, count = 0;
for (i = 0; i < seek_count; i++) {
if (!p2ps_gen_hash(p2p, seek[i], buf))
continue;
p2p_dbg(p2p, "Seek service %s hash " MACSTR,
seek[i], MAC2STR(buf));
os_memcpy(&p2p->p2ps_seek_hash[count * P2PS_HASH_LEN],
buf, P2PS_HASH_LEN);
count++;
}
p2p->p2ps_seek_count = count;
p2p->p2ps_seek = 1;
} else {
p2p->p2ps_seek_count = 0;
p2p->p2ps_seek = 1;
}
/* Special case to perform wildcard search */
if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) {
p2p->p2ps_seek_count = 1;
os_memcpy(&p2p->p2ps_seek_hash, p2p->wild_card_hash,
P2PS_HASH_LEN);
}
p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
p2p_clear_timeout(p2p);
if (p2p->pending_listen_freq) {
p2p_dbg(p2p, "Clear pending_listen_freq for p2p_find");
p2p->pending_listen_freq = 0;
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
p2p->find_pending_full = 0;
p2p->find_type = type;
if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480)
p2p->find_specified_freq = freq;
else
p2p->find_specified_freq = 0;
p2p_device_clear_reported(p2p);
os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN);
p2p_set_state(p2p, P2P_SEARCH);
p2p->search_delay = search_delay;
p2p->in_search_delay = 0;
eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
p2p->last_p2p_find_timeout = timeout;
if (timeout)
eloop_register_timeout(timeout, 0, p2p_find_timeout,
p2p, NULL);
os_get_reltime(&start);
switch (type) {
case P2P_FIND_START_WITH_FULL:
if (freq > 0) {
/*
* Start with the specified channel and then move to
* scans for social channels and this specific channel.
*/
res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx,
P2P_SCAN_SPECIFIC, freq,
p2p->num_req_dev_types,
p2p->req_dev_types, dev_id,
DEV_PW_DEFAULT,
p2p->include_6ghz);
break;
}
/* fall through */
case P2P_FIND_PROGRESSIVE:
res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
p2p->num_req_dev_types,
p2p->req_dev_types, dev_id,
DEV_PW_DEFAULT, p2p->include_6ghz);
break;
case P2P_FIND_ONLY_SOCIAL:
res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0,
p2p->num_req_dev_types,
p2p->req_dev_types, dev_id,
DEV_PW_DEFAULT, p2p->include_6ghz);
break;
default:
return -1;
}
if (!res)
p2p->find_start = start;
if (res != 0 && p2p->p2p_scan_running) {
p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
/* wait for the previous p2p_scan to complete */
if (type == P2P_FIND_PROGRESSIVE ||
(type == P2P_FIND_START_WITH_FULL && freq == 0))
p2p->find_pending_full = 1;
res = 0; /* do not report failure */
} else if (res != 0) {
p2p_dbg(p2p, "Failed to start p2p_scan");
p2p_set_state(p2p, P2P_IDLE);
eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
}
return res;
}
void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
{
p2p_dbg(p2p, "Stopping find");
eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
p2p_clear_timeout(p2p);
if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
p2p->p2ps_seek_count = 0;
p2p_set_state(p2p, P2P_IDLE);
p2p_free_req_dev_types(p2p);
p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
if (p2p->go_neg_peer)
p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
p2p->go_neg_peer = NULL;
p2p->sd_peer = NULL;
p2p->invite_peer = NULL;
p2p_stop_listen_for_freq(p2p, freq);
p2p->send_action_in_progress = 0;
}
void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
{
- if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
+ if (freq > 0 &&
+ ((p2p->drv_in_listen == freq && p2p->in_listen) ||
+ p2p->pending_listen_freq == (unsigned int) freq)) {
p2p_dbg(p2p, "Skip stop_listen since we are on correct channel for response");
return;
}
if (p2p->in_listen) {
p2p->in_listen = 0;
p2p_clear_timeout(p2p);
}
if (p2p->drv_in_listen) {
/*
* The driver may not deliver callback to p2p_listen_end()
* when the operation gets canceled, so clear the internal
* variable that is tracking driver state.
*/
p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen);
p2p->drv_in_listen = 0;
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
}
void p2p_stop_listen(struct p2p_data *p2p)
{
if (p2p->state != P2P_LISTEN_ONLY) {
p2p_dbg(p2p, "Skip stop_listen since not in listen_only state.");
return;
}
p2p_stop_listen_for_freq(p2p, 0);
p2p_set_state(p2p, P2P_IDLE);
}
void p2p_stop_find(struct p2p_data *p2p)
{
p2p->pending_listen_freq = 0;
p2p_stop_find_for_freq(p2p, 0);
}
static int p2p_prepare_channel_pref(struct p2p_data *p2p,
unsigned int force_freq,
unsigned int pref_freq, int go)
{
u8 op_class, op_channel;
unsigned int freq = force_freq ? force_freq : pref_freq;
p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d",
force_freq, pref_freq, go);
if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) {
p2p_dbg(p2p, "Unsupported frequency %u MHz", freq);
return -1;
}
if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) &&
(go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class,
op_channel))) {
p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P",
freq, op_class, op_channel);
return -1;
}
p2p->op_reg_class = op_class;
p2p->op_channel = op_channel;
if (force_freq) {
p2p->channels.reg_classes = 1;
p2p->channels.reg_class[0].channels = 1;
p2p->channels.reg_class[0].reg_class = p2p->op_reg_class;
p2p->channels.reg_class[0].channel[0] = p2p->op_channel;
} else {
p2p_copy_channels(&p2p->channels, &p2p->cfg->channels,
p2p->allow_6ghz);
}
return 0;
}
static void p2p_prepare_channel_best(struct p2p_data *p2p)
{
u8 op_class, op_channel;
const int op_classes_5ghz[] = { 124, 125, 115, 0 };
const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
const int op_classes_vht[] = { 128, 0 };
const int op_classes_edmg[] = { 181, 182, 183, 0 };
const int op_classes_6ghz[] = { 131, 0 };
p2p_dbg(p2p, "Prepare channel best");
if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
p2p_supported_freq(p2p, p2p->best_freq_overall) &&
p2p_freq_to_channel(p2p->best_freq_overall, &op_class, &op_channel)
== 0) {
p2p_dbg(p2p, "Select best overall channel as operating channel preference");
p2p->op_reg_class = op_class;
p2p->op_channel = op_channel;
} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 &&
p2p_supported_freq(p2p, p2p->best_freq_5) &&
p2p_freq_to_channel(p2p->best_freq_5, &op_class, &op_channel)
== 0) {
p2p_dbg(p2p, "Select best 5 GHz channel as operating channel preference");
p2p->op_reg_class = op_class;
p2p->op_channel = op_channel;
} else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 &&
p2p_supported_freq(p2p, p2p->best_freq_24) &&
p2p_freq_to_channel(p2p->best_freq_24, &op_class,
&op_channel) == 0) {
p2p_dbg(p2p, "Select best 2.4 GHz channel as operating channel preference");
p2p->op_reg_class = op_class;
p2p->op_channel = op_channel;
} else if (p2p->cfg->num_pref_chan > 0 &&
p2p_channels_includes(&p2p->cfg->channels,
p2p->cfg->pref_chan[0].op_class,
p2p->cfg->pref_chan[0].chan)) {
p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference");
p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class;
p2p->op_channel = p2p->cfg->pref_chan[0].chan;
} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_edmg,
&p2p->op_reg_class, &p2p->op_channel) ==
0) {
p2p_dbg(p2p, "Select possible EDMG channel (op_class %u channel %u) as operating channel preference",
p2p->op_reg_class, p2p->op_channel);
} else if (p2p->allow_6ghz &&
(p2p_channel_select(&p2p->cfg->channels, op_classes_6ghz,
&p2p->op_reg_class, &p2p->op_channel) ==
0)) {
p2p_dbg(p2p, "Select possible 6 GHz channel (op_class %u channel %u) as operating channel preference",
p2p->op_reg_class, p2p->op_channel);
} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht,
&p2p->op_reg_class, &p2p->op_channel) ==
0) {
p2p_dbg(p2p, "Select possible VHT channel (op_class %u channel %u) as operating channel preference",
p2p->op_reg_class, p2p->op_channel);
} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_ht40,
&p2p->op_reg_class, &p2p->op_channel) ==
0) {
p2p_dbg(p2p, "Select possible HT40 channel (op_class %u channel %u) as operating channel preference",
p2p->op_reg_class, p2p->op_channel);
} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_5ghz,
&p2p->op_reg_class, &p2p->op_channel) ==
0) {
p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference",
p2p->op_reg_class, p2p->op_channel);
} else if (p2p_channels_includes(&p2p->cfg->channels,
p2p->cfg->op_reg_class,
p2p->cfg->op_channel)) {
p2p_dbg(p2p, "Select pre-configured channel as operating channel preference");
p2p->op_reg_class = p2p->cfg->op_reg_class;
p2p->op_channel = p2p->cfg->op_channel;
} else if (p2p_channel_random_social(&p2p->cfg->channels,
&p2p->op_reg_class,
&p2p->op_channel,
NULL, NULL) == 0) {
p2p_dbg(p2p, "Select random available social channel (op_class %u channel %u) as operating channel preference",
p2p->op_reg_class, p2p->op_channel);
} else {
/* Select any random available channel from the first available
* operating class */
p2p_channel_select(&p2p->cfg->channels, NULL,
&p2p->op_reg_class,
&p2p->op_channel);
p2p_dbg(p2p, "Select random available channel %d from operating class %d as operating channel preference",
p2p->op_channel, p2p->op_reg_class);
}
p2p_copy_channels(&p2p->channels, &p2p->cfg->channels, p2p->allow_6ghz);
}
/**
* p2p_prepare_channel - Select operating channel for GO Negotiation or P2PS PD
* @p2p: P2P module context from p2p_init()
* @dev: Selected peer device
* @force_freq: Forced frequency in MHz or 0 if not forced
* @pref_freq: Preferred frequency in MHz or 0 if no preference
* @go: Whether the local end will be forced to be GO
* Returns: 0 on success, -1 on failure (channel not supported for P2P)
*
* This function is used to do initial operating channel selection for GO
* Negotiation prior to having received peer information or for P2PS PD
* signalling. The selected channel may be further optimized in
* p2p_reselect_channel() once the peer information is available.
*/
int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
unsigned int force_freq, unsigned int pref_freq, int go)
{
p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d",
force_freq, pref_freq, go);
if (force_freq || pref_freq) {
if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) <
0)
return -1;
} else {
p2p_prepare_channel_best(p2p);
}
p2p_channels_dump(p2p, "prepared channels", &p2p->channels);
if (go)
p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq);
else if (!force_freq)
p2p_channels_union_inplace(&p2p->channels,
&p2p->cfg->cli_channels);
p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels);
p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s",
p2p->op_reg_class, p2p->op_channel,
force_freq ? " (forced)" : "");
if (force_freq)
dev->flags |= P2P_DEV_FORCE_FREQ;
else
dev->flags &= ~P2P_DEV_FORCE_FREQ;
return 0;
}
static void p2p_set_dev_persistent(struct p2p_device *dev,
int persistent_group)
{
switch (persistent_group) {
case 0:
dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP |
P2P_DEV_PREFER_PERSISTENT_RECONN);
break;
case 1:
dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN;
break;
case 2:
dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP |
P2P_DEV_PREFER_PERSISTENT_RECONN;
break;
}
}
int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
enum p2p_wps_method wps_method,
int go_intent, const u8 *own_interface_addr,
unsigned int force_freq, int persistent_group,
const u8 *force_ssid, size_t force_ssid_len,
int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id)
{
struct p2p_device *dev;
p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR
" GO Intent=%d Intended Interface Address=" MACSTR
" wps_method=%d persistent_group=%d pd_before_go_neg=%d "
"oob_pw_id=%u allow_6ghz=%d",
MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
wps_method, persistent_group, pd_before_go_neg, oob_pw_id,
p2p->allow_6ghz);
dev = p2p_get_device(p2p, peer_addr);
if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
p2p_dbg(p2p, "Cannot connect to unknown P2P Device " MACSTR,
MAC2STR(peer_addr));
return -1;
}
if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
go_intent == 15) < 0)
return -1;
if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
if (!(dev->info.dev_capab &
P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR
" that is in a group and is not discoverable",
MAC2STR(peer_addr));
return -1;
}
if (dev->oper_freq <= 0) {
p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR
" with incomplete information",
MAC2STR(peer_addr));
return -1;
}
/*
* First, try to connect directly. If the peer does not
* acknowledge frames, assume it is sleeping and use device
* discoverability via the GO at that point.
*/
}
p2p->ssid_set = 0;
if (force_ssid) {
wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
force_ssid, force_ssid_len);
os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
p2p->ssid_len = force_ssid_len;
p2p->ssid_set = 1;
}
dev->flags &= ~P2P_DEV_NOT_YET_READY;
dev->flags &= ~P2P_DEV_USER_REJECTED;
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
if (pd_before_go_neg)
dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG;
else {
dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
/*
* Assign dialog token and tie breaker here to use the same
* values in each retry within the same GO Negotiation exchange.
*/
dev->dialog_token++;
if (dev->dialog_token == 0)
dev->dialog_token = 1;
dev->tie_breaker = p2p->next_tie_breaker;
p2p->next_tie_breaker = !p2p->next_tie_breaker;
}
dev->connect_reqs = 0;
dev->go_neg_req_sent = 0;
dev->go_state = UNKNOWN_GO;
p2p_set_dev_persistent(dev, persistent_group);
p2p->go_intent = go_intent;
os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
if (p2p->state != P2P_IDLE)
p2p_stop_find(p2p);
dev->wps_method = wps_method;
dev->oob_pw_id = oob_pw_id;
dev->status = P2P_SC_SUCCESS;
if (p2p->p2p_scan_running) {
p2p_dbg(p2p, "p2p_scan running - delay connect send");
p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT;
os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN);
return 0;
}
return p2p_connect_send(p2p, dev);
}
int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
enum p2p_wps_method wps_method,
int go_intent, const u8 *own_interface_addr,
unsigned int force_freq, int persistent_group,
const u8 *force_ssid, size_t force_ssid_len,
unsigned int pref_freq, u16 oob_pw_id)
{
struct p2p_device *dev;
p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR
" GO Intent=%d Intended Interface Address=" MACSTR
" wps_method=%d persistent_group=%d oob_pw_id=%u allow_6ghz=%d",
MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
wps_method, persistent_group, oob_pw_id, p2p->allow_6ghz);
dev = p2p_get_device(p2p, peer_addr);
if (dev == NULL) {
p2p_dbg(p2p, "Cannot authorize unknown P2P Device " MACSTR,
MAC2STR(peer_addr));
return -1;
}
if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent ==
15) < 0)
return -1;
p2p->ssid_set = 0;
if (force_ssid) {
wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
force_ssid, force_ssid_len);
os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
p2p->ssid_len = force_ssid_len;
p2p->ssid_set = 1;
}
dev->flags &= ~P2P_DEV_NOT_YET_READY;
dev->flags &= ~P2P_DEV_USER_REJECTED;
dev->go_neg_req_sent = 0;
dev->go_state = UNKNOWN_GO;
p2p_set_dev_persistent(dev, persistent_group);
p2p->go_intent = go_intent;
os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
dev->wps_method = wps_method;
dev->oob_pw_id = oob_pw_id;
dev->status = P2P_SC_SUCCESS;
return 0;
}
void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
struct p2p_device *dev, struct p2p_message *msg)
{
os_get_reltime(&dev->last_seen);
p2p_copy_wps_info(p2p, dev, 0, msg);
if (msg->listen_channel) {
int freq;
freq = p2p_channel_to_freq(msg->listen_channel[3],
msg->listen_channel[4]);
if (freq < 0) {
p2p_dbg(p2p, "Unknown peer Listen channel: "
"country=%c%c(0x%02x) reg_class=%u channel=%u",
msg->listen_channel[0],
msg->listen_channel[1],
msg->listen_channel[2],
msg->listen_channel[3],
msg->listen_channel[4]);
} else {
p2p_dbg(p2p, "Update peer " MACSTR
" Listen channel: %u -> %u MHz",
MAC2STR(dev->info.p2p_device_addr),
dev->listen_freq, freq);
dev->listen_freq = freq;
}
}
if (msg->wfd_subelems) {
wpabuf_free(dev->info.wfd_subelems);
dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems);
}
if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
p2p_dbg(p2p, "Completed device entry based on data from GO Negotiation Request");
} else {
p2p_dbg(p2p, "Created device entry based on GO Neg Req: "
MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' "
"listen_freq=%d",
MAC2STR(dev->info.p2p_device_addr),
dev->info.dev_capab, dev->info.group_capab,
dev->info.device_name, dev->listen_freq);
}
dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY;
if (dev->flags & P2P_DEV_USER_REJECTED) {
p2p_dbg(p2p, "Do not report rejected device");
return;
}
p2p->cfg->dev_found(p2p->cfg->cb_ctx, addr, &dev->info,
!(dev->flags & P2P_DEV_REPORTED_ONCE));
dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
}
void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len)
{
os_memcpy(ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
p2p_random((char *) &ssid[P2P_WILDCARD_SSID_LEN], 2);
os_memcpy(&ssid[P2P_WILDCARD_SSID_LEN + 2],
p2p->cfg->ssid_postfix, p2p->cfg->ssid_postfix_len);
*ssid_len = P2P_WILDCARD_SSID_LEN + 2 + p2p->cfg->ssid_postfix_len;
}
int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
{
if (p2p->ssid_set) {
os_memcpy(params->ssid, p2p->ssid, p2p->ssid_len);
params->ssid_len = p2p->ssid_len;
} else {
p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
}
p2p->ssid_set = 0;
p2p_random(params->passphrase, p2p->cfg->passphrase_len);
return 0;
}
void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
{
struct p2p_go_neg_results res;
int go = peer->go_state == LOCAL_GO;
struct p2p_channels intersection;
p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)",
MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer");
os_memset(&res, 0, sizeof(res));
res.role_go = go;
os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
res.wps_method = peer->wps_method;
if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
res.persistent_group = 2;
else
res.persistent_group = 1;
}
if (go) {
/* Setup AP mode for WPS provisioning */
res.freq = p2p_channel_to_freq(p2p->op_reg_class,
p2p->op_channel);
os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
res.ssid_len = p2p->ssid_len;
p2p_random(res.passphrase, p2p->cfg->passphrase_len);
} else {
res.freq = peer->oper_freq;
if (p2p->ssid_len) {
os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
res.ssid_len = p2p->ssid_len;
}
}
p2p_channels_dump(p2p, "own channels", &p2p->channels);
p2p_channels_dump(p2p, "peer channels", &peer->channels);
p2p_channels_intersect(&p2p->channels, &peer->channels,
&intersection);
if (go) {
p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq);
p2p_channels_dump(p2p, "intersection after no-GO removal",
&intersection);
}
p2p_channels_to_freqs(&intersection, res.freq_list,
P2P_MAX_CHANNELS);
res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout;
p2p_clear_timeout(p2p);
p2p->ssid_set = 0;
peer->go_neg_req_sent = 0;
peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
peer->wps_method = WPS_NOT_READY;
peer->oob_pw_id = 0;
wpabuf_free(peer->go_neg_conf);
peer->go_neg_conf = NULL;
p2p_set_state(p2p, P2P_PROVISIONING);
p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
}
static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
p2p_dbg(p2p, "RX P2P Public Action from " MACSTR, MAC2STR(sa));
wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len);
if (len < 1)
return;
switch (data[0]) {
case P2P_GO_NEG_REQ:
p2p_process_go_neg_req(p2p, sa, data + 1, len - 1, rx_freq);
break;
case P2P_GO_NEG_RESP:
p2p_process_go_neg_resp(p2p, sa, data + 1, len - 1, rx_freq);
break;
case P2P_GO_NEG_CONF:
p2p_process_go_neg_conf(p2p, sa, data + 1, len - 1);
break;
case P2P_INVITATION_REQ:
p2p_process_invitation_req(p2p, sa, data + 1, len - 1,
rx_freq);
break;
case P2P_INVITATION_RESP:
p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
break;
case P2P_PROV_DISC_REQ:
p2p_process_prov_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
break;
case P2P_PROV_DISC_RESP:
p2p_process_prov_disc_resp(p2p, sa, data + 1, len - 1);
break;
case P2P_DEV_DISC_REQ:
p2p_process_dev_disc_req(p2p, sa, data + 1, len - 1, rx_freq);
break;
case P2P_DEV_DISC_RESP:
p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1);
break;
default:
p2p_dbg(p2p, "Unsupported P2P Public Action frame type %d",
data[0]);
break;
}
}
static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da,
const u8 *sa, const u8 *bssid, const u8 *data,
size_t len, int freq)
{
if (len < 1)
return;
switch (data[0]) {
case WLAN_PA_VENDOR_SPECIFIC:
data++;
len--;
if (len < 4)
return;
if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
return;
data += 4;
len -= 4;
p2p_rx_p2p_action(p2p, sa, data, len, freq);
break;
case WLAN_PA_GAS_INITIAL_REQ:
p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq);
break;
case WLAN_PA_GAS_INITIAL_RESP:
p2p_rx_gas_initial_resp(p2p, sa, data + 1, len - 1, freq);
break;
case WLAN_PA_GAS_COMEBACK_REQ:
p2p_rx_gas_comeback_req(p2p, sa, data + 1, len - 1, freq);
break;
case WLAN_PA_GAS_COMEBACK_RESP:
p2p_rx_gas_comeback_resp(p2p, sa, data + 1, len - 1, freq);
break;
}
}
void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
const u8 *bssid, u8 category,
const u8 *data, size_t len, int freq)
{
if (category == WLAN_ACTION_PUBLIC) {
p2p_rx_action_public(p2p, da, sa, bssid, data, len, freq);
return;
}
if (category != WLAN_ACTION_VENDOR_SPECIFIC)
return;
if (len < 4)
return;
if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
return;
data += 4;
len -= 4;
/* P2P action frame */
p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa));
wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len);
if (len < 1)
return;
switch (data[0]) {
case P2P_NOA:
p2p_dbg(p2p, "Received P2P Action - Notice of Absence");
/* TODO */
break;
case P2P_PRESENCE_REQ:
p2p_process_presence_req(p2p, da, sa, data + 1, len - 1, freq);
break;
case P2P_PRESENCE_RESP:
p2p_process_presence_resp(p2p, da, sa, data + 1, len - 1);
break;
case P2P_GO_DISC_REQ:
p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq);
break;
default:
p2p_dbg(p2p, "Received P2P Action - unknown type %u", data[0]);
break;
}
}
static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx)
{
struct p2p_data *p2p = eloop_ctx;
if (p2p->go_neg_peer == NULL)
return;
if (p2p->pending_listen_freq) {
p2p_dbg(p2p, "Clear pending_listen_freq for p2p_go_neg_start");
p2p->pending_listen_freq = 0;
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
p2p->go_neg_peer->status = P2P_SC_SUCCESS;
/*
* Set new timeout to make sure a previously set one does not expire
* too quickly while waiting for the GO Negotiation to complete.
*/
p2p_set_timeout(p2p, 0, 500000);
p2p_connect_send(p2p, p2p->go_neg_peer);
}
static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx)
{
struct p2p_data *p2p = eloop_ctx;
if (p2p->invite_peer == NULL)
return;
if (p2p->pending_listen_freq) {
p2p_dbg(p2p, "Clear pending_listen_freq for p2p_invite_start");
p2p->pending_listen_freq = 0;
}
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
p2p->invite_dev_pw_id);
}
static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
const u8 *ie, size_t ie_len)
{
struct p2p_message msg;
struct p2p_device *dev;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_ies(ie, ie_len, &msg) < 0 || msg.p2p_attributes == NULL)
{
p2p_parse_free(&msg);
return; /* not a P2P probe */
}
if (msg.ssid == NULL || msg.ssid[1] != P2P_WILDCARD_SSID_LEN ||
os_memcmp(msg.ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN)
!= 0) {
/* The Probe Request is not part of P2P Device Discovery. It is
* not known whether the source address of the frame is the P2P
* Device Address or P2P Interface Address. Do not add a new
* peer entry based on this frames.
*/
p2p_parse_free(&msg);
return;
}
dev = p2p_get_device(p2p, addr);
if (dev) {
if (msg.listen_channel) {
int freq;
if (dev->country[0] == 0)
os_memcpy(dev->country, msg.listen_channel, 3);
freq = p2p_channel_to_freq(msg.listen_channel[3],
msg.listen_channel[4]);
if (freq > 0 && dev->listen_freq != freq) {
p2p_dbg(p2p,
"Updated peer " MACSTR " Listen channel (Probe Request): %d -> %d MHz",
MAC2STR(addr), dev->listen_freq, freq);
dev->listen_freq = freq;
}
}
os_get_reltime(&dev->last_seen);
p2p_parse_free(&msg);
return; /* already known */
}
dev = p2p_create_device(p2p, addr);
if (dev == NULL) {
p2p_parse_free(&msg);
return;
}
os_get_reltime(&dev->last_seen);
dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
if (msg.listen_channel) {
os_memcpy(dev->country, msg.listen_channel, 3);
dev->listen_freq = p2p_channel_to_freq(msg.listen_channel[3],
msg.listen_channel[4]);
}
p2p_copy_wps_info(p2p, dev, 1, &msg);
if (msg.wfd_subelems) {
wpabuf_free(dev->info.wfd_subelems);
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
}
p2p_parse_free(&msg);
p2p_dbg(p2p, "Created device entry based on Probe Req: " MACSTR
" dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d",
MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab,
dev->info.group_capab, dev->info.device_name,
dev->listen_freq);
}
struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
const u8 *addr,
struct p2p_message *msg)
{
struct p2p_device *dev;
dev = p2p_get_device(p2p, addr);
if (dev) {
os_get_reltime(&dev->last_seen);
return dev; /* already known */
}
dev = p2p_create_device(p2p, addr);
if (dev == NULL)
return NULL;
p2p_add_dev_info(p2p, addr, dev, msg);
return dev;
}
static int dev_type_match(const u8 *dev_type, const u8 *req_dev_type)
{
if (os_memcmp(dev_type, req_dev_type, WPS_DEV_TYPE_LEN) == 0)
return 1;
if (os_memcmp(dev_type, req_dev_type, 2) == 0 &&
WPA_GET_BE32(&req_dev_type[2]) == 0 &&
WPA_GET_BE16(&req_dev_type[6]) == 0)
return 1; /* Category match with wildcard OUI/sub-category */
return 0;
}
int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
size_t num_req_dev_type)
{
size_t i;
for (i = 0; i < num_req_dev_type; i++) {
if (dev_type_match(dev_type, req_dev_type[i]))
return 1;
}
return 0;
}
/**
* p2p_match_dev_type - Match local device type with requested type
* @p2p: P2P module context from p2p_init()
* @wps: WPS TLVs from Probe Request frame (concatenated WPS IEs)
* Returns: 1 on match, 0 on mismatch
*
* This function can be used to match the Requested Device Type attribute in
* WPS IE with the local device types for deciding whether to reply to a Probe
* Request frame.
*/
int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps)
{
struct wps_parse_attr attr;
size_t i;
if (wps_parse_msg(wps, &attr))
return 1; /* assume no Requested Device Type attributes */
if (attr.num_req_dev_type == 0)
return 1; /* no Requested Device Type attributes -> match */
if (dev_type_list_match(p2p->cfg->pri_dev_type, attr.req_dev_type,
attr.num_req_dev_type))
return 1; /* Own Primary Device Type matches */
for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) {
if (dev_type_list_match(p2p->cfg->sec_dev_type[i],
attr.req_dev_type,
attr.num_req_dev_type))
return 1; /* Own Secondary Device Type matches */
}
/* No matching device type found */
return 0;
}
struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
const u8 *query_hash,
u8 query_count)
{
struct wpabuf *buf;
u8 *len;
int pw_id = -1;
size_t extra = 0;
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_probe_resp)
extra = wpabuf_len(p2p->wfd_ie_probe_resp);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
if (query_count)
extra += MAX_SVC_ADV_IE_LEN;
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
if (p2p->go_neg_peer) {
/* Advertise immediate availability of WPS credential */
pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
}
if (p2p_build_wps_ie(p2p, buf, pw_id, 1) < 0) {
p2p_dbg(p2p, "Failed to build WPS IE for Probe Response");
wpabuf_free(buf);
return NULL;
}
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_probe_resp)
wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
wpabuf_put_buf(buf,
p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
/* P2P IE */
len = p2p_buf_add_ie_hdr(buf);
p2p_buf_add_capability(buf, p2p->dev_capab &
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
if (p2p->ext_listen_interval)
p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
p2p->ext_listen_interval);
p2p_buf_add_device_info(buf, p2p, NULL);
p2p_buf_update_ie_hdr(buf, len);
if (query_count) {
p2p_buf_add_service_instance(buf, p2p, query_count, query_hash,
p2p->p2ps_adv_list);
}
return buf;
}
static int p2p_build_probe_resp_buf(struct p2p_data *p2p, struct wpabuf *buf,
struct wpabuf *ies,
const u8 *addr, int rx_freq)
{
struct ieee80211_mgmt *resp;
u8 channel, op_class;
resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
u.probe_resp.variable));
resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
(WLAN_FC_STYPE_PROBE_RESP << 4));
os_memcpy(resp->da, addr, ETH_ALEN);
os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
resp->u.probe_resp.beacon_int = host_to_le16(100);
/* hardware or low-level driver will setup seq_ctrl and timestamp */
resp->u.probe_resp.capab_info =
host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
WLAN_CAPABILITY_PRIVACY |
WLAN_CAPABILITY_SHORT_SLOT_TIME);
wpabuf_put_u8(buf, WLAN_EID_SSID);
wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
wpabuf_put_u8(buf, 8);
wpabuf_put_u8(buf, (60 / 5) | 0x80);
wpabuf_put_u8(buf, 90 / 5);
wpabuf_put_u8(buf, (120 / 5) | 0x80);
wpabuf_put_u8(buf, 180 / 5);
wpabuf_put_u8(buf, (240 / 5) | 0x80);
wpabuf_put_u8(buf, 360 / 5);
wpabuf_put_u8(buf, 480 / 5);
wpabuf_put_u8(buf, 540 / 5);
if (!rx_freq) {
channel = p2p->cfg->channel;
} else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) {
p2p_err(p2p, "Failed to convert freq to channel");
return -1;
}
wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
wpabuf_put_u8(buf, 1);
wpabuf_put_u8(buf, channel);
wpabuf_put_buf(buf, ies);
return 0;
}
static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash)
{
struct p2ps_advertisement *adv_data;
int any_wfa;
p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list);
/* Wildcard org.wi-fi.wfds matches any WFA spec defined service */
any_wfa = os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0;
adv_data = p2p->p2ps_adv_list;
while (adv_data) {
if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0)
return 1; /* exact hash match */
if (any_wfa &&
os_strncmp(adv_data->svc_name, P2PS_WILD_HASH_STR,
os_strlen(P2PS_WILD_HASH_STR)) == 0)
return 1; /* WFA service match */
adv_data = adv_data->next;
}
return 0;
}
static enum p2p_probe_req_status
p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
const u8 *bssid, const u8 *ie, size_t ie_len,
unsigned int rx_freq)
{
struct ieee802_11_elems elems;
struct wpabuf *buf;
struct p2p_message msg;
struct wpabuf *ies;
if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
ParseFailed) {
/* Ignore invalid Probe Request frames */
p2p_dbg(p2p, "Could not parse Probe Request frame - ignore it");
return P2P_PREQ_MALFORMED;
}
if (elems.p2p == NULL) {
/* not a P2P probe - ignore it */
p2p_dbg(p2p, "Not a P2P probe - ignore it");
return P2P_PREQ_NOT_P2P;
}
if (dst && !is_broadcast_ether_addr(dst) &&
os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
/* Not sent to the broadcast address or our P2P Device Address
*/
p2p_dbg(p2p, "Probe Req DA " MACSTR " not ours - ignore it",
MAC2STR(dst));
return P2P_PREQ_NOT_PROCESSED;
}
if (bssid && !is_broadcast_ether_addr(bssid)) {
/* Not sent to the Wildcard BSSID */
p2p_dbg(p2p, "Probe Req BSSID " MACSTR " not wildcard - ignore it",
MAC2STR(bssid));
return P2P_PREQ_NOT_PROCESSED;
}
if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN ||
os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
0) {
/* not using P2P Wildcard SSID - ignore */
p2p_dbg(p2p, "Probe Req not using P2P Wildcard SSID - ignore it");
return P2P_PREQ_NOT_PROCESSED;
}
if (supp_rates_11b_only(&elems)) {
/* Indicates support for 11b rates only */
p2p_dbg(p2p, "Probe Req with 11b rates only supported - ignore it");
return P2P_PREQ_NOT_P2P;
}
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_ies(ie, ie_len, &msg) < 0) {
/* Could not parse P2P attributes */
p2p_dbg(p2p, "Could not parse P2P attributes in Probe Req - ignore it");
return P2P_PREQ_NOT_P2P;
}
if (msg.service_hash && msg.service_hash_count) {
const u8 *hash = msg.service_hash;
u8 i;
int p2ps_svc_found = 0;
p2p_dbg(p2p, "in_listen=%d drv_in_listen=%d when received P2PS Probe Request at %u MHz; own Listen channel %u, pending listen freq %u MHz",
p2p->in_listen, p2p->drv_in_listen, rx_freq,
p2p->cfg->channel, p2p->pending_listen_freq);
if (!p2p->in_listen && !p2p->drv_in_listen &&
p2p->pending_listen_freq && rx_freq &&
rx_freq != p2p->pending_listen_freq) {
p2p_dbg(p2p, "Do not reply to Probe Request frame that was received on %u MHz while waiting to start Listen state on %u MHz",
rx_freq, p2p->pending_listen_freq);
p2p_parse_free(&msg);
return P2P_PREQ_NOT_LISTEN;
}
for (i = 0; i < msg.service_hash_count; i++) {
if (p2p_service_find_asp(p2p, hash)) {
p2p_dbg(p2p, "Service Hash match found: "
MACSTR, MAC2STR(hash));
p2ps_svc_found = 1;
break;
}
hash += P2PS_HASH_LEN;
}
/* Probed hash unknown */
if (!p2ps_svc_found) {
p2p_dbg(p2p, "No Service Hash match found");
p2p_parse_free(&msg);
return P2P_PREQ_NOT_PROCESSED;
}
} else {
/* This is not a P2PS Probe Request */
p2p_dbg(p2p, "No P2PS Hash in Probe Request");
if (!p2p->in_listen || !p2p->drv_in_listen) {
/* not in Listen state - ignore Probe Request */
p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
p2p->in_listen, p2p->drv_in_listen);
p2p_parse_free(&msg);
return P2P_PREQ_NOT_LISTEN;
}
}
if (msg.device_id &&
os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
/* Device ID did not match */
p2p_dbg(p2p, "Probe Req requested Device ID " MACSTR " did not match - ignore it",
MAC2STR(msg.device_id));
p2p_parse_free(&msg);
return P2P_PREQ_NOT_PROCESSED;
}
/* Check Requested Device Type match */
if (msg.wps_attributes &&
!p2p_match_dev_type(p2p, msg.wps_attributes)) {
/* No match with Requested Device Type */
p2p_dbg(p2p, "Probe Req requested Device Type did not match - ignore it");
p2p_parse_free(&msg);
return P2P_PREQ_NOT_PROCESSED;
}
if (!p2p->cfg->send_probe_resp) {
/* Response generated elsewhere */
p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response");
p2p_parse_free(&msg);
return P2P_PREQ_NOT_PROCESSED;
}
p2p_dbg(p2p, "Reply to P2P Probe Request in Listen state");
/*
* We do not really have a specific BSS that this frame is advertising,
* so build a frame that has some information in valid format. This is
* really only used for discovery purposes, not to learn exact BSS
* parameters.
*/
ies = p2p_build_probe_resp_ies(p2p, msg.service_hash,
msg.service_hash_count);
p2p_parse_free(&msg);
if (ies == NULL)
return P2P_PREQ_NOT_PROCESSED;
buf = wpabuf_alloc(200 + wpabuf_len(ies));
if (buf == NULL) {
wpabuf_free(ies);
return P2P_PREQ_NOT_PROCESSED;
}
if (p2p_build_probe_resp_buf(p2p, buf, ies, addr, rx_freq)) {
wpabuf_free(ies);
wpabuf_free(buf);
return P2P_PREQ_NOT_PROCESSED;
}
wpabuf_free(ies);
p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq);
wpabuf_free(buf);
return P2P_PREQ_PROCESSED;
}
enum p2p_probe_req_status
p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
const u8 *bssid, const u8 *ie, size_t ie_len,
unsigned int rx_freq, int p2p_lo_started)
{
enum p2p_probe_req_status res;
p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
if (p2p_lo_started) {
p2p_dbg(p2p,
"Probe Response is offloaded, do not reply Probe Request");
return P2P_PREQ_PROCESSED;
}
res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq);
if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED)
return res;
/*
* Activate a pending GO Negotiation/Invite flow if a received Probe
* Request frame is from an expected peer. Some devices may share the
* same address for P2P and non-P2P STA running simultaneously. The
* P2P_PREQ_PROCESSED and P2P_PREQ_NOT_PROCESSED p2p_reply_probe()
* return values verified above ensure we are handling a Probe Request
* frame from a P2P peer.
*/
if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
p2p->go_neg_peer &&
os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN)
== 0 &&
!(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
/* Received a Probe Request from GO Negotiation peer */
p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout");
eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL);
eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
return res;
}
if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
p2p->invite_peer &&
(p2p->invite_peer->flags & P2P_DEV_WAIT_INV_REQ_ACK) &&
os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN)
== 0) {
/* Received a Probe Request from Invite peer */
p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout");
eloop_cancel_timeout(p2p_invite_start, p2p, NULL);
eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
return res;
}
return res;
}
static int p2p_assoc_req_ie_wlan_ap(struct p2p_data *p2p, const u8 *bssid,
u8 *buf, size_t len, struct wpabuf *p2p_ie)
{
struct wpabuf *tmp;
u8 *lpos;
size_t tmplen;
int res;
u8 group_capab;
struct p2p_message msg;
if (p2p_ie == NULL)
return 0; /* WLAN AP is not a P2P manager */
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_p2p_ie(p2p_ie, &msg) < 0)
return 0;
p2p_dbg(p2p, "BSS P2P manageability %s",
msg.manageability ? "enabled" : "disabled");
if (!msg.manageability)
return 0;
/*
* (Re)Association Request - P2P IE
* P2P Capability attribute (shall be present)
* P2P Interface attribute (present if concurrent device and
* P2P Management is enabled)
*/
tmp = wpabuf_alloc(200);
if (tmp == NULL)
return -1;
lpos = p2p_buf_add_ie_hdr(tmp);
group_capab = 0;
if (p2p->num_groups > 0) {
group_capab |= P2P_GROUP_CAPAB_GROUP_OWNER;
if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
(p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED) &&
p2p->cross_connect)
group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
}
p2p_buf_add_capability(tmp, p2p->dev_capab, group_capab);
if ((p2p->dev_capab & P2P_DEV_CAPAB_CONCURRENT_OPER) &&
(p2p->dev_capab & P2P_DEV_CAPAB_INFRA_MANAGED))
p2p_buf_add_p2p_interface(tmp, p2p);
p2p_buf_update_ie_hdr(tmp, lpos);
tmplen = wpabuf_len(tmp);
if (tmplen > len)
res = -1;
else {
os_memcpy(buf, wpabuf_head(tmp), tmplen);
res = tmplen;
}
wpabuf_free(tmp);
return res;
}
int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
size_t len, int p2p_group, struct wpabuf *p2p_ie)
{
struct wpabuf *tmp;
u8 *lpos;
struct p2p_device *peer;
size_t tmplen;
int res;
size_t extra = 0;
if (!p2p_group)
return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie);
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_assoc_req)
extra = wpabuf_len(p2p->wfd_ie_assoc_req);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]);
/*
* (Re)Association Request - P2P IE
* P2P Capability attribute (shall be present)
* Extended Listen Timing (may be present)
* P2P Device Info attribute (shall be present)
*/
tmp = wpabuf_alloc(200 + extra);
if (tmp == NULL)
return -1;
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_assoc_req)
wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ])
wpabuf_put_buf(tmp,
p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]);
peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
lpos = p2p_buf_add_ie_hdr(tmp);
p2p_buf_add_capability(tmp, p2p->dev_capab, 0);
if (p2p->ext_listen_interval)
p2p_buf_add_ext_listen_timing(tmp, p2p->ext_listen_period,
p2p->ext_listen_interval);
p2p_buf_add_device_info(tmp, p2p, peer);
p2p_buf_update_ie_hdr(tmp, lpos);
tmplen = wpabuf_len(tmp);
if (tmplen > len)
res = -1;
else {
os_memcpy(buf, wpabuf_head(tmp), tmplen);
res = tmplen;
}
wpabuf_free(tmp);
return res;
}
int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
{
struct wpabuf *p2p_ie;
int ret;
p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len, P2P_IE_VENDOR_TYPE);
if (p2p_ie == NULL)
return 0;
ret = p2p_attr_text(p2p_ie, buf, end);
wpabuf_free(p2p_ie);
return ret;
}
struct p2ps_advertisement *
p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id)
{
struct p2ps_advertisement *adv_data;
if (!p2p)
return NULL;
adv_data = p2p->p2ps_adv_list;
while (adv_data) {
if (adv_data->id == adv_id)
return adv_data;
adv_data = adv_data->next;
}
return NULL;
}
int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id)
{
struct p2ps_advertisement *adv_data;
struct p2ps_advertisement **prior;
if (!p2p)
return -1;
adv_data = p2p->p2ps_adv_list;
prior = &p2p->p2ps_adv_list;
while (adv_data) {
if (adv_data->id == adv_id) {
p2p_dbg(p2p, "Delete ASP adv_id=0x%x", adv_id);
*prior = adv_data->next;
os_free(adv_data);
return 0;
}
prior = &adv_data->next;
adv_data = adv_data->next;
}
return -1;
}
int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
const char *adv_str, u8 svc_state, u16 config_methods,
const char *svc_info, const u8 *cpt_priority)
{
struct p2ps_advertisement *adv_data, *tmp, **prev;
u8 buf[P2PS_HASH_LEN];
size_t adv_data_len, adv_len, info_len = 0;
int i;
if (!p2p || !adv_str || !adv_str[0] || !cpt_priority)
return -1;
if (!(config_methods & p2p->cfg->config_methods)) {
p2p_dbg(p2p, "Config methods not supported svc: 0x%x dev: 0x%x",
config_methods, p2p->cfg->config_methods);
return -1;
}
if (!p2ps_gen_hash(p2p, adv_str, buf))
return -1;
if (svc_info)
info_len = os_strlen(svc_info);
adv_len = os_strlen(adv_str);
adv_data_len = sizeof(struct p2ps_advertisement) + adv_len + 1 +
info_len + 1;
adv_data = os_zalloc(adv_data_len);
if (!adv_data)
return -1;
os_memcpy(adv_data->hash, buf, P2PS_HASH_LEN);
adv_data->id = adv_id;
adv_data->state = svc_state;
adv_data->config_methods = config_methods & p2p->cfg->config_methods;
adv_data->auto_accept = (u8) auto_accept;
os_memcpy(adv_data->svc_name, adv_str, adv_len);
for (i = 0; cpt_priority[i] && i < P2PS_FEATURE_CAPAB_CPT_MAX; i++) {
adv_data->cpt_priority[i] = cpt_priority[i];
adv_data->cpt_mask |= cpt_priority[i];
}
if (svc_info && info_len) {
adv_data->svc_info = &adv_data->svc_name[adv_len + 1];
os_memcpy(adv_data->svc_info, svc_info, info_len);
}
/*
* Group Advertisements by service string. They do not need to be
* sorted, but groups allow easier Probe Response instance grouping
*/
tmp = p2p->p2ps_adv_list;
prev = &p2p->p2ps_adv_list;
while (tmp) {
if (tmp->id == adv_data->id) {
if (os_strcmp(tmp->svc_name, adv_data->svc_name) != 0) {
os_free(adv_data);
return -1;
}
adv_data->next = tmp->next;
*prev = adv_data;
os_free(tmp);
goto inserted;
} else {
if (os_strcmp(tmp->svc_name, adv_data->svc_name) == 0) {
adv_data->next = tmp->next;
tmp->next = adv_data;
goto inserted;
}
}
prev = &tmp->next;
tmp = tmp->next;
}
/* No svc_name match found */
adv_data->next = p2p->p2ps_adv_list;
p2p->p2ps_adv_list = adv_data;
inserted:
p2p_dbg(p2p,
"Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s' cpt_mask=0x%x",
adv_id, adv_data->config_methods, svc_state, adv_str,
adv_data->cpt_mask);
return 0;
}
void p2p_service_flush_asp(struct p2p_data *p2p)
{
struct p2ps_advertisement *adv, *prev;
if (!p2p)
return;
adv = p2p->p2ps_adv_list;
while (adv) {
prev = adv;
adv = adv->next;
os_free(prev);
}
p2p->p2ps_adv_list = NULL;
p2ps_prov_free(p2p);
p2p_dbg(p2p, "All ASP advertisements flushed");
}
int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
{
struct p2p_message msg;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_p2p_ie(p2p_ie, &msg))
return -1;
if (msg.p2p_device_addr) {
os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
return 0;
} else if (msg.device_id) {
os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
return 0;
}
return -1;
}
int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
{
struct wpabuf *p2p_ie;
int ret;
p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
P2P_IE_VENDOR_TYPE);
if (p2p_ie == NULL)
return -1;
ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr);
wpabuf_free(p2p_ie);
return ret;
}
static void p2p_clear_go_neg(struct p2p_data *p2p)
{
p2p->go_neg_peer = NULL;
p2p_clear_timeout(p2p);
p2p_set_state(p2p, P2P_IDLE);
}
void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr)
{
if (p2p->go_neg_peer == NULL) {
p2p_dbg(p2p, "No pending Group Formation - ignore WPS registration success notification");
return; /* No pending Group Formation */
}
if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) !=
0) {
p2p_dbg(p2p, "Ignore WPS registration success notification for "
MACSTR " (GO Negotiation peer " MACSTR ")",
MAC2STR(mac_addr),
MAC2STR(p2p->go_neg_peer->intended_addr));
return; /* Ignore unexpected peer address */
}
p2p_dbg(p2p, "Group Formation completed successfully with " MACSTR,
MAC2STR(mac_addr));
p2p_clear_go_neg(p2p);
}
void p2p_group_formation_failed(struct p2p_data *p2p)
{
if (p2p->go_neg_peer == NULL) {
p2p_dbg(p2p, "No pending Group Formation - ignore group formation failure notification");
return; /* No pending Group Formation */
}
p2p_dbg(p2p, "Group Formation failed with " MACSTR,
MAC2STR(p2p->go_neg_peer->intended_addr));
p2p_clear_go_neg(p2p);
}
bool is_p2p_6ghz_disabled(struct p2p_data *p2p)
{
if (p2p)
return p2p->cfg->p2p_6ghz_disable;
return false;
}
struct p2p_data * p2p_init(const struct p2p_config *cfg)
{
struct p2p_data *p2p;
if (cfg->max_peers < 1 ||
cfg->passphrase_len < 8 || cfg->passphrase_len > 63)
return NULL;
p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
if (p2p == NULL)
return NULL;
p2p->cfg = (struct p2p_config *) (p2p + 1);
os_memcpy(p2p->cfg, cfg, sizeof(*cfg));
if (cfg->dev_name)
p2p->cfg->dev_name = os_strdup(cfg->dev_name);
if (cfg->manufacturer)
p2p->cfg->manufacturer = os_strdup(cfg->manufacturer);
if (cfg->model_name)
p2p->cfg->model_name = os_strdup(cfg->model_name);
if (cfg->model_number)
p2p->cfg->model_number = os_strdup(cfg->model_number);
if (cfg->serial_number)
p2p->cfg->serial_number = os_strdup(cfg->serial_number);
if (cfg->pref_chan) {
p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan *
sizeof(struct p2p_channel));
if (p2p->cfg->pref_chan) {
os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan,
cfg->num_pref_chan *
sizeof(struct p2p_channel));
} else
p2p->cfg->num_pref_chan = 0;
}
p2ps_gen_hash(p2p, P2PS_WILD_HASH_STR, p2p->wild_card_hash);
p2p->min_disc_int = 1;
p2p->max_disc_int = 3;
p2p->max_disc_tu = -1;
if (os_get_random(&p2p->next_tie_breaker, 1) < 0)
p2p->next_tie_breaker = 0;
p2p->next_tie_breaker &= 0x01;
if (cfg->sd_request)
p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
p2p->dev_capab |= P2P_DEV_CAPAB_INVITATION_PROCEDURE;
if (cfg->concurrent_operations)
p2p->dev_capab |= P2P_DEV_CAPAB_CONCURRENT_OPER;
p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
dl_list_init(&p2p->devices);
p2p->go_timeout = 100;
p2p->client_timeout = 20;
p2p->num_p2p_sd_queries = 0;
p2p_dbg(p2p, "initialized");
p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
return p2p;
}
void p2p_deinit(struct p2p_data *p2p)
{
#ifdef CONFIG_WIFI_DISPLAY
wpabuf_free(p2p->wfd_ie_beacon);
wpabuf_free(p2p->wfd_ie_probe_req);
wpabuf_free(p2p->wfd_ie_probe_resp);
wpabuf_free(p2p->wfd_ie_assoc_req);
wpabuf_free(p2p->wfd_ie_invitation);
wpabuf_free(p2p->wfd_ie_prov_disc_req);
wpabuf_free(p2p->wfd_ie_prov_disc_resp);
wpabuf_free(p2p->wfd_ie_go_neg);
wpabuf_free(p2p->wfd_dev_info);
wpabuf_free(p2p->wfd_assoc_bssid);
wpabuf_free(p2p->wfd_coupled_sink_info);
wpabuf_free(p2p->wfd_r2_dev_info);
#endif /* CONFIG_WIFI_DISPLAY */
eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL);
eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
p2p_flush(p2p);
p2p_free_req_dev_types(p2p);
os_free(p2p->cfg->dev_name);
os_free(p2p->cfg->manufacturer);
os_free(p2p->cfg->model_name);
os_free(p2p->cfg->model_number);
os_free(p2p->cfg->serial_number);
os_free(p2p->cfg->pref_chan);
os_free(p2p->groups);
p2ps_prov_free(p2p);
wpabuf_free(p2p->sd_resp);
p2p_remove_wps_vendor_extensions(p2p);
os_free(p2p->no_go_freq.range);
p2p_service_flush_asp(p2p);
os_free(p2p);
}
void p2p_flush(struct p2p_data *p2p)
{
struct p2p_device *dev, *prev;
p2p_ext_listen(p2p, 0, 0);
p2p_stop_find(p2p);
dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
list) {
dl_list_del(&dev->list);
p2p_device_free(p2p, dev);
}
p2p_free_sd_queries(p2p);
p2p->ssid_set = 0;
p2ps_prov_free(p2p);
p2p_reset_pending_pd(p2p);
p2p->override_pref_op_class = 0;
p2p->override_pref_channel = 0;
}
int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr)
{
struct p2p_device *dev;
dev = p2p_get_device(p2p, addr);
if (dev == NULL)
return -1;
p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr));
if (p2p->go_neg_peer == dev) {
eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
p2p->go_neg_peer = NULL;
}
dev->wps_method = WPS_NOT_READY;
dev->oob_pw_id = 0;
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
return 0;
}
int p2p_set_dev_name(struct p2p_data *p2p, const char *dev_name)
{
os_free(p2p->cfg->dev_name);
if (dev_name) {
p2p->cfg->dev_name = os_strdup(dev_name);
if (p2p->cfg->dev_name == NULL)
return -1;
} else
p2p->cfg->dev_name = NULL;
return 0;
}
int p2p_set_manufacturer(struct p2p_data *p2p, const char *manufacturer)
{
os_free(p2p->cfg->manufacturer);
p2p->cfg->manufacturer = NULL;
if (manufacturer) {
p2p->cfg->manufacturer = os_strdup(manufacturer);
if (p2p->cfg->manufacturer == NULL)
return -1;
}
return 0;
}
int p2p_set_model_name(struct p2p_data *p2p, const char *model_name)
{
os_free(p2p->cfg->model_name);
p2p->cfg->model_name = NULL;
if (model_name) {
p2p->cfg->model_name = os_strdup(model_name);
if (p2p->cfg->model_name == NULL)
return -1;
}
return 0;
}
int p2p_set_model_number(struct p2p_data *p2p, const char *model_number)
{
os_free(p2p->cfg->model_number);
p2p->cfg->model_number = NULL;
if (model_number) {
p2p->cfg->model_number = os_strdup(model_number);
if (p2p->cfg->model_number == NULL)
return -1;
}
return 0;
}
int p2p_set_serial_number(struct p2p_data *p2p, const char *serial_number)
{
os_free(p2p->cfg->serial_number);
p2p->cfg->serial_number = NULL;
if (serial_number) {
p2p->cfg->serial_number = os_strdup(serial_number);
if (p2p->cfg->serial_number == NULL)
return -1;
}
return 0;
}
void p2p_set_config_methods(struct p2p_data *p2p, u16 config_methods)
{
p2p->cfg->config_methods = config_methods;
}
void p2p_set_uuid(struct p2p_data *p2p, const u8 *uuid)
{
os_memcpy(p2p->cfg->uuid, uuid, 16);
}
int p2p_set_pri_dev_type(struct p2p_data *p2p, const u8 *pri_dev_type)
{
os_memcpy(p2p->cfg->pri_dev_type, pri_dev_type, 8);
return 0;
}
int p2p_set_sec_dev_types(struct p2p_data *p2p, const u8 dev_types[][8],
size_t num_dev_types)
{
if (num_dev_types > P2P_SEC_DEVICE_TYPES)
num_dev_types = P2P_SEC_DEVICE_TYPES;
p2p->cfg->num_sec_dev_types = num_dev_types;
os_memcpy(p2p->cfg->sec_dev_type, dev_types, num_dev_types * 8);
return 0;
}
void p2p_remove_wps_vendor_extensions(struct p2p_data *p2p)
{
int i;
for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
wpabuf_free(p2p->wps_vendor_ext[i]);
p2p->wps_vendor_ext[i] = NULL;
}
}
int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
const struct wpabuf *vendor_ext)
{
int i;
if (vendor_ext == NULL)
return -1;
for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
if (p2p->wps_vendor_ext[i] == NULL)
break;
}
if (i >= P2P_MAX_WPS_VENDOR_EXT)
return -1;
p2p->wps_vendor_ext[i] = wpabuf_dup(vendor_ext);
if (p2p->wps_vendor_ext[i] == NULL)
return -1;
return 0;
}
int p2p_set_country(struct p2p_data *p2p, const char *country)
{
os_memcpy(p2p->cfg->country, country, 3);
return 0;
}
static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev)
{
int res;
if (dev->sd_pending_bcast_queries == 0) {
/* Initialize with total number of registered broadcast
* SD queries. */
dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries;
}
res = p2p_start_sd(p2p, dev);
if (res == -2)
return -2;
if (res == 0)
return 1;
if (dev->req_config_methods &&
!(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
p2p_dbg(p2p, "Send pending Provision Discovery Request to "
MACSTR " (config methods 0x%x)",
MAC2STR(dev->info.p2p_device_addr),
dev->req_config_methods);
if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
return 1;
}
return 0;
}
void p2p_continue_find(struct p2p_data *p2p)
{
struct p2p_device *dev;
int found, res;
p2p_set_state(p2p, P2P_SEARCH);
/* Continue from the device following the last iteration */
found = 0;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
if (dev == p2p->last_p2p_find_oper) {
found = 1;
continue;
}
if (!found)
continue;
res = p2p_pre_find_operation(p2p, dev);
if (res > 0) {
p2p->last_p2p_find_oper = dev;
return;
}
if (res == -2)
goto skip_sd;
}
/*
* Wrap around to the beginning of the list and continue until the last
* iteration device.
*/
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
res = p2p_pre_find_operation(p2p, dev);
if (res > 0) {
p2p->last_p2p_find_oper = dev;
return;
}
if (res == -2)
goto skip_sd;
if (dev == p2p->last_p2p_find_oper)
break;
}
skip_sd:
os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN);
p2p_listen_in_find(p2p, 1);
}
static void p2p_sd_cb(struct p2p_data *p2p, int success)
{
p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d",
success);
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (!success) {
if (p2p->sd_peer) {
if (is_zero_ether_addr(p2p->sd_query_no_ack)) {
os_memcpy(p2p->sd_query_no_ack,
p2p->sd_peer->info.p2p_device_addr,
ETH_ALEN);
p2p_dbg(p2p,
"First SD Query no-ACK in this search iteration: "
MACSTR, MAC2STR(p2p->sd_query_no_ack));
}
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
}
p2p->sd_peer = NULL;
if (p2p->state != P2P_IDLE)
p2p_continue_find(p2p);
return;
}
if (p2p->sd_peer == NULL) {
p2p_dbg(p2p, "No SD peer entry known");
if (p2p->state != P2P_IDLE)
p2p_continue_find(p2p);
return;
}
if (p2p->sd_query && p2p->sd_query->for_all_peers) {
/* Update the pending broadcast SD query count for this device
*/
p2p->sd_peer->sd_pending_bcast_queries--;
/*
* If there are no pending broadcast queries for this device,
* mark it as done (-1).
*/
if (p2p->sd_peer->sd_pending_bcast_queries == 0)
p2p->sd_peer->sd_pending_bcast_queries = -1;
}
/* Wait for response from the peer */
p2p_set_state(p2p, P2P_SD_DURING_FIND);
p2p_set_timeout(p2p, 0, 200000);
}
/**
* p2p_retry_pd - Retry any pending provision disc requests in IDLE state
* @p2p: P2P module context from p2p_init()
*/
static void p2p_retry_pd(struct p2p_data *p2p)
{
struct p2p_device *dev;
/*
* Retry the prov disc req attempt only for the peer that the user had
* requested.
*/
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
if (os_memcmp(p2p->pending_pd_devaddr,
dev->info.p2p_device_addr, ETH_ALEN) != 0)
continue;
if (!dev->req_config_methods)
continue;
p2p_dbg(p2p, "Send pending Provision Discovery Request to "
MACSTR " (config methods 0x%x)",
MAC2STR(dev->info.p2p_device_addr),
dev->req_config_methods);
p2p_send_prov_disc_req(p2p, dev,
dev->flags & P2P_DEV_PD_FOR_JOIN,
p2p->pd_force_freq);
return;
}
}
static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
{
p2p_dbg(p2p, "Provision Discovery Request TX callback: success=%d",
success);
/*
* Postpone resetting the pending action state till after we actually
* time out. This allows us to take some action like notifying any
* interested parties about no response to the request.
*
* When the timer (below) goes off we check in IDLE, SEARCH, or
* LISTEN_ONLY state, which are the only allowed states to issue a PD
* requests in, if this was still pending and then raise notification.
*/
if (!success) {
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (p2p->user_initiated_pd &&
(p2p->state == P2P_SEARCH || p2p->state == P2P_LISTEN_ONLY))
{
/* Retry request from timeout to avoid busy loops */
p2p->pending_action_state = P2P_PENDING_PD;
p2p_set_timeout(p2p, 0, 50000);
} else if (p2p->state != P2P_IDLE)
p2p_continue_find(p2p);
else if (p2p->user_initiated_pd) {
p2p->pending_action_state = P2P_PENDING_PD;
p2p_set_timeout(p2p, 0, 300000);
}
return;
}
/*
* If after PD Request the peer doesn't expect to receive PD Response
* the PD Request ACK indicates a completion of the current PD. This
* happens only on the advertiser side sending the follow-on PD Request
* with the status different than 12 (Success: accepted by user).
*/
if (p2p->p2ps_prov && !p2p->p2ps_prov->pd_seeker &&
p2p->p2ps_prov->status != P2P_SC_SUCCESS_DEFERRED) {
p2p_dbg(p2p, "P2PS PD completion on Follow-on PD Request ACK");
if (p2p->send_action_in_progress) {
p2p->send_action_in_progress = 0;
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
}
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (p2p->cfg->p2ps_prov_complete) {
p2p->cfg->p2ps_prov_complete(
p2p->cfg->cb_ctx,
p2p->p2ps_prov->status,
p2p->p2ps_prov->adv_mac,
p2p->p2ps_prov->adv_mac,
p2p->p2ps_prov->session_mac,
NULL, p2p->p2ps_prov->adv_id,
p2p->p2ps_prov->session_id,
0, 0, NULL, 0, 0, 0,
NULL, NULL, 0, 0, NULL, 0);
}
if (p2p->user_initiated_pd)
p2p_reset_pending_pd(p2p);
p2ps_prov_free(p2p);
return;
}
/*
* This postponing, of resetting pending_action_state, needs to be
* done only for user initiated PD requests and not internal ones.
*/
if (p2p->user_initiated_pd)
p2p->pending_action_state = P2P_PENDING_PD;
else
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
/* Wait for response from the peer */
if (p2p->state == P2P_SEARCH)
p2p_set_state(p2p, P2P_PD_DURING_FIND);
p2p_set_timeout(p2p, 0, 200000);
}
static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success)
{
p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d",
success);
if (p2p->send_action_in_progress) {
p2p->send_action_in_progress = 0;
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
}
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (!success) {
if (p2p->state == P2P_SEARCH)
p2p_continue_find(p2p);
return;
}
if (!p2p->cfg->prov_disc_resp_cb ||
p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1) {
if (p2p->state == P2P_SEARCH)
p2p_continue_find(p2p);
return;
}
p2p_dbg(p2p,
"Post-Provision Discovery operations started - do not try to continue other P2P operations");
}
int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
struct os_reltime *rx_time, int level, const u8 *ies,
size_t ies_len)
{
if (os_reltime_before(rx_time, &p2p->find_start)) {
/*
* The driver may have cached (e.g., in cfg80211 BSS table) the
* scan results for relatively long time. To avoid reporting
* stale information, update P2P peers only based on results
* that have based on frames received after the last p2p_find
* operation was started.
*/
p2p_dbg(p2p, "Ignore old scan result for " MACSTR
" (rx_time=%u.%06u find_start=%u.%06u)",
MAC2STR(bssid), (unsigned int) rx_time->sec,
(unsigned int) rx_time->usec,
(unsigned int) p2p->find_start.sec,
(unsigned int) p2p->find_start.usec);
return 0;
}
p2p_add_device(p2p, bssid, freq, rx_time, level, ies, ies_len, 1);
return 0;
}
void p2p_scan_res_handled(struct p2p_data *p2p, unsigned int delay)
{
if (!p2p->p2p_scan_running) {
p2p_dbg(p2p, "p2p_scan was not running, but scan results received");
}
p2p->p2p_scan_running = 0;
/* Use this delay only when p2p_find doesn't set it */
if (!p2p->search_delay)
p2p->search_delay = delay;
eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
if (p2p_run_after_scan(p2p))
return;
if (p2p->state == P2P_SEARCH)
p2p_continue_find(p2p);
}
void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id,
unsigned int bands)
{
u8 dev_capab;
u8 *len;
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_probe_req)
wpabuf_put_buf(ies, p2p->wfd_ie_probe_req);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P])
wpabuf_put_buf(ies,
p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
len = p2p_buf_add_ie_hdr(ies);
dev_capab = p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
/* P2PS requires Probe Request frames to include SD bit */
if (p2p->p2ps_seek && p2p->p2ps_seek_count)
dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
p2p_buf_add_capability(ies, dev_capab, 0);
if (dev_id)
p2p_buf_add_device_id(ies, dev_id);
if (p2p->cfg->reg_class && p2p->cfg->channel)
p2p_buf_add_listen_channel(ies, p2p->cfg->country,
p2p->cfg->reg_class,
p2p->cfg->channel);
if (p2p->ext_listen_interval)
p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
p2p->ext_listen_interval);
if (bands & BAND_60_GHZ)
p2p_buf_add_device_info(ies, p2p, NULL);
if (p2p->p2ps_seek && p2p->p2ps_seek_count)
p2p_buf_add_service_hash(ies, p2p);
/* TODO: p2p_buf_add_operating_channel() if GO */
p2p_buf_update_ie_hdr(ies, len);
}
size_t p2p_scan_ie_buf_len(struct p2p_data *p2p)
{
size_t len = 100;
#ifdef CONFIG_WIFI_DISPLAY
if (p2p && p2p->wfd_ie_probe_req)
len += wpabuf_len(p2p->wfd_ie_probe_req);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p && p2p->vendor_elem &&
p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P])
len += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
return len;
}
int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end)
{
return p2p_attr_text(p2p_ie, buf, end);
}
static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success)
{
struct p2p_device *dev = p2p->go_neg_peer;
int timeout;
p2p_dbg(p2p, "GO Negotiation Request TX callback: success=%d", success);
if (dev == NULL) {
p2p_dbg(p2p, "No pending GO Negotiation");
return;
}
if (success) {
if (dev->flags & P2P_DEV_USER_REJECTED) {
p2p_set_state(p2p, P2P_IDLE);
return;
}
} else if (dev->go_neg_req_sent) {
/* Cancel the increment from p2p_connect_send() on failure */
dev->go_neg_req_sent--;
}
if (!success &&
(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) &&
!is_zero_ether_addr(dev->member_in_go_dev)) {
p2p_dbg(p2p, "Peer " MACSTR " did not acknowledge request - try to use device discoverability through its GO",
MAC2STR(dev->info.p2p_device_addr));
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_send_dev_disc_req(p2p, dev);
return;
}
/*
* Use P2P find, if needed, to find the other device from its listen
* channel.
*/
p2p_set_state(p2p, P2P_CONNECT);
timeout = success ? 500000 : 100000;
if (!success && p2p->go_neg_peer &&
(p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE)) {
unsigned int r;
/*
* Peer is expected to wait our response and we will skip the
* listen phase. Add some randomness to the wait time here to
* make it less likely to hit cases where we could end up in
* sync with peer not listening.
*/
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
r = 0;
timeout += r % 100000;
}
p2p_set_timeout(p2p, 0, timeout);
}
static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success)
{
p2p_dbg(p2p, "GO Negotiation Response TX callback: success=%d",
success);
if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) {
p2p_dbg(p2p, "Ignore TX callback event - GO Negotiation is not running anymore");
return;
}
p2p_set_state(p2p, P2P_CONNECT);
p2p_set_timeout(p2p, 0, 500000);
}
static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success,
const u8 *addr)
{
p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success);
if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
p2p_go_neg_failed(p2p, p2p->go_neg_peer->status);
return;
}
if (success) {
struct p2p_device *dev;
dev = p2p_get_device(p2p, addr);
if (dev &&
dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
}
if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
p2p_continue_find(p2p);
}
static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
enum p2p_send_action_result result)
{
struct p2p_device *dev;
p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result);
if (result == P2P_SEND_ACTION_FAILED) {
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_go_neg_failed(p2p, -1);
return;
}
dev = p2p->go_neg_peer;
if (result == P2P_SEND_ACTION_NO_ACK) {
/*
* Retry GO Negotiation Confirmation
* P2P_GO_NEG_CNF_MAX_RETRY_COUNT times if we did not receive
* ACK for confirmation.
*/
if (dev && dev->go_neg_conf &&
dev->go_neg_conf_sent <= P2P_GO_NEG_CNF_MAX_RETRY_COUNT) {
p2p_dbg(p2p, "GO Negotiation Confirm retry %d",
dev->go_neg_conf_sent);
p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
if (p2p_send_action(p2p, dev->go_neg_conf_freq,
dev->info.p2p_device_addr,
p2p->cfg->dev_addr,
dev->info.p2p_device_addr,
wpabuf_head(dev->go_neg_conf),
wpabuf_len(dev->go_neg_conf), 0) >=
0) {
dev->go_neg_conf_sent++;
return;
}
p2p_dbg(p2p, "Failed to re-send Action frame");
/*
* Continue with the assumption that the first attempt
* went through and just the ACK frame was lost.
*/
}
/*
* It looks like the TX status for GO Negotiation Confirm is
* often showing failure even when the peer has actually
* received the frame. Since the peer may change channels
* immediately after having received the frame, we may not see
* an Ack for retries, so just dropping a single frame may
* trigger this. To allow the group formation to succeed if the
* peer did indeed receive the frame, continue regardless of
* the TX status.
*/
p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported");
}
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
if (dev == NULL)
return;
p2p_go_complete(p2p, dev);
}
void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
enum p2p_send_action_result result)
{
enum p2p_pending_action_state state;
int success;
p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR
" src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)",
p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
MAC2STR(bssid), result, p2p_state_txt(p2p->state));
success = result == P2P_SEND_ACTION_SUCCESS;
state = p2p->pending_action_state;
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
switch (state) {
case P2P_NO_PENDING_ACTION:
if (p2p->send_action_in_progress) {
p2p->send_action_in_progress = 0;
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
}
break;
case P2P_PENDING_GO_NEG_REQUEST:
p2p_go_neg_req_cb(p2p, success);
break;
case P2P_PENDING_GO_NEG_RESPONSE:
p2p_go_neg_resp_cb(p2p, success);
break;
case P2P_PENDING_GO_NEG_RESPONSE_FAILURE:
p2p_go_neg_resp_failure_cb(p2p, success, dst);
break;
case P2P_PENDING_GO_NEG_CONFIRM:
p2p_go_neg_conf_cb(p2p, result);
break;
case P2P_PENDING_SD:
p2p_sd_cb(p2p, success);
break;
case P2P_PENDING_PD:
p2p_prov_disc_cb(p2p, success);
break;
case P2P_PENDING_PD_RESPONSE:
p2p_prov_disc_resp_cb(p2p, success);
break;
case P2P_PENDING_INVITATION_REQUEST:
p2p_invitation_req_cb(p2p, success);
break;
case P2P_PENDING_INVITATION_RESPONSE:
p2p_invitation_resp_cb(p2p, success);
break;
case P2P_PENDING_DEV_DISC_REQUEST:
p2p_dev_disc_req_cb(p2p, success);
break;
case P2P_PENDING_DEV_DISC_RESPONSE:
p2p_dev_disc_resp_cb(p2p, success);
break;
case P2P_PENDING_GO_DISC_REQ:
p2p_go_disc_req_cb(p2p, success);
break;
}
}
void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq,
unsigned int duration)
{
if (freq == p2p->pending_client_disc_freq) {
p2p_dbg(p2p, "Client discoverability remain-awake completed");
p2p->pending_client_disc_freq = 0;
return;
}
if (freq != p2p->pending_listen_freq) {
p2p_dbg(p2p, "Unexpected listen callback for freq=%u duration=%u (pending_listen_freq=%u)",
freq, duration, p2p->pending_listen_freq);
return;
}
p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback",
p2p->pending_listen_sec, p2p->pending_listen_usec,
p2p->pending_listen_freq);
p2p->in_listen = 1;
p2p->drv_in_listen = freq;
if (p2p->pending_listen_sec || p2p->pending_listen_usec) {
/*
* Add 20 msec extra wait to avoid race condition with driver
* remain-on-channel end event, i.e., give driver more time to
* complete the operation before our timeout expires.
*/
p2p_set_timeout(p2p, p2p->pending_listen_sec,
p2p->pending_listen_usec + 20000);
}
p2p->pending_listen_freq = 0;
}
int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
{
p2p_dbg(p2p, "Driver ended Listen state (freq=%u)", freq);
p2p->drv_in_listen = 0;
if (p2p->in_listen)
return 0; /* Internal timeout will trigger the next step */
if (p2p->state == P2P_WAIT_PEER_CONNECT && p2p->go_neg_peer &&
p2p->pending_listen_freq) {
/*
* Better wait a bit if the driver is unable to start
* offchannel operation for some reason to continue with
* P2P_WAIT_PEER_(IDLE/CONNECT) state transitions.
*/
p2p_dbg(p2p,
"Listen operation did not seem to start - delay idle phase to avoid busy loop");
p2p_set_timeout(p2p, 0, 100000);
return 1;
}
if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
if (p2p->go_neg_peer->connect_reqs >= 120) {
p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
p2p_go_neg_failed(p2p, -1);
return 0;
}
p2p_set_state(p2p, P2P_CONNECT);
p2p_connect_send(p2p, p2p->go_neg_peer);
return 1;
} else if (p2p->state == P2P_SEARCH) {
if (p2p->p2p_scan_running) {
/*
* Search is already in progress. This can happen if
* an Action frame RX is reported immediately after
* the end of a remain-on-channel operation and the
* response frame to that is sent using an offchannel
* operation while in p2p_find. Avoid an attempt to
* restart a scan here.
*/
p2p_dbg(p2p, "p2p_scan already in progress - do not try to start a new one");
return 1;
}
if (p2p->pending_listen_freq) {
/*
* Better wait a bit if the driver is unable to start
* offchannel operation for some reason. p2p_search()
* will be started from internal timeout.
*/
p2p_dbg(p2p, "Listen operation did not seem to start - delay search phase to avoid busy loop");
p2p_set_timeout(p2p, 0, 100000);
return 1;
}
if (p2p->search_delay) {
p2p_dbg(p2p, "Delay search operation by %u ms",
p2p->search_delay);
p2p_set_timeout(p2p, p2p->search_delay / 1000,
(p2p->search_delay % 1000) * 1000);
return 1;
}
p2p_search(p2p);
return 1;
}
return 0;
}
static void p2p_timeout_connect(struct p2p_data *p2p)
{
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
if (p2p->go_neg_peer &&
(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed");
p2p_go_neg_failed(p2p, -1);
return;
}
if (p2p->go_neg_peer &&
(p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE) &&
p2p->go_neg_peer->connect_reqs < 120) {
p2p_dbg(p2p, "Peer expected to wait our response - skip listen");
p2p_connect_send(p2p, p2p->go_neg_peer);
return;
}
if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) {
p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)");
p2p_set_state(p2p, P2P_CONNECT_LISTEN);
p2p_set_timeout(p2p, 0, 30000);
return;
}
p2p_set_state(p2p, P2P_CONNECT_LISTEN);
p2p_listen_in_find(p2p, 0);
}
static void p2p_timeout_connect_listen(struct p2p_data *p2p)
{
if (p2p->go_neg_peer) {
if (p2p->drv_in_listen) {
p2p_dbg(p2p, "Driver is still in Listen state; wait for it to complete");
return;
}
if (p2p->go_neg_peer->connect_reqs >= 120) {
p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
p2p_go_neg_failed(p2p, -1);
return;
}
p2p_set_state(p2p, P2P_CONNECT);
p2p_connect_send(p2p, p2p->go_neg_peer);
} else
p2p_set_state(p2p, P2P_IDLE);
}
static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
{
p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
if (p2p->cfg->is_concurrent_session_active &&
p2p->cfg->is_concurrent_session_active(p2p->cfg->cb_ctx))
p2p_set_timeout(p2p, 0, 500000);
else
p2p_set_timeout(p2p, 0, 200000);
}
static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
{
struct p2p_device *dev = p2p->go_neg_peer;
if (dev == NULL) {
p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait");
return;
}
p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation");
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
if (p2p->pending_listen_freq) {
p2p_dbg(p2p, "Clear pending_listen_freq for %s", __func__);
p2p->pending_listen_freq = 0;
}
p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
p2p_listen_in_find(p2p, 0);
}
static void p2p_timeout_sd_during_find(struct p2p_data *p2p)
{
p2p_dbg(p2p, "Service Discovery Query timeout");
if (p2p->sd_peer) {
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p->sd_peer = NULL;
}
p2p_continue_find(p2p);
}
static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
{
p2p_dbg(p2p, "Provision Discovery Request timeout");
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_continue_find(p2p);
}
static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
{
u32 adv_id = 0;
u8 *adv_mac = NULL;
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
/*
* For user initiated PD requests that we have not gotten any responses
* for while in IDLE state, we retry them a couple of times before
* giving up.
*/
if (!p2p->user_initiated_pd)
return;
p2p_dbg(p2p, "User initiated Provision Discovery Request timeout");
if (p2p->pd_retries) {
p2p->pd_retries--;
p2p_retry_pd(p2p);
} else {
struct p2p_device *dev;
int for_join = 0;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
if (os_memcmp(p2p->pending_pd_devaddr,
dev->info.p2p_device_addr, ETH_ALEN) != 0)
continue;
if (dev->req_config_methods &&
(dev->flags & P2P_DEV_PD_FOR_JOIN))
for_join = 1;
}
if (p2p->p2ps_prov) {
adv_id = p2p->p2ps_prov->adv_id;
adv_mac = p2p->p2ps_prov->adv_mac;
}
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
p2p->pending_pd_devaddr,
for_join ?
P2P_PROV_DISC_TIMEOUT_JOIN :
P2P_PROV_DISC_TIMEOUT,
adv_id, adv_mac, NULL);
p2p_reset_pending_pd(p2p);
}
}
static void p2p_timeout_invite(struct p2p_data *p2p)
{
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_set_state(p2p, P2P_INVITE_LISTEN);
if (p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
/*
* Better remain on operating channel instead of listen channel
* when running a group.
*/
p2p_dbg(p2p, "Inviting in active GO role - wait on operating channel");
p2p_set_timeout(p2p, 0, 100000);
return;
}
p2p_listen_in_find(p2p, 0);
}
static void p2p_timeout_invite_listen(struct p2p_data *p2p)
{
if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) {
p2p_set_state(p2p, P2P_INVITE);
p2p_invite_send(p2p, p2p->invite_peer,
p2p->invite_go_dev_addr, p2p->invite_dev_pw_id);
} else {
if (p2p->invite_peer) {
p2p_dbg(p2p, "Invitation Request retry limit reached");
if (p2p->cfg->invitation_result)
p2p->cfg->invitation_result(
p2p->cfg->cb_ctx, -1, NULL, NULL,
p2p->invite_peer->info.p2p_device_addr,
0, 0);
}
p2p_set_state(p2p, P2P_IDLE);
}
}
static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct p2p_data *p2p = eloop_ctx;
p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state));
p2p->in_listen = 0;
if (p2p->drv_in_listen) {
p2p_dbg(p2p, "Driver is still in listen state - stop it");
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
}
switch (p2p->state) {
case P2P_IDLE:
/* Check if we timed out waiting for PD req */
if (p2p->pending_action_state == P2P_PENDING_PD)
p2p_timeout_prov_disc_req(p2p);
break;
case P2P_SEARCH:
/* Check if we timed out waiting for PD req */
if (p2p->pending_action_state == P2P_PENDING_PD)
p2p_timeout_prov_disc_req(p2p);
if (p2p->search_delay && !p2p->in_search_delay) {
p2p_dbg(p2p, "Delay search operation by %u ms",
p2p->search_delay);
p2p->in_search_delay = 1;
p2p_set_timeout(p2p, p2p->search_delay / 1000,
(p2p->search_delay % 1000) * 1000);
break;
}
p2p->in_search_delay = 0;
p2p_search(p2p);
break;
case P2P_CONNECT:
p2p_timeout_connect(p2p);
break;
case P2P_CONNECT_LISTEN:
p2p_timeout_connect_listen(p2p);
break;
case P2P_GO_NEG:
break;
case P2P_LISTEN_ONLY:
/* Check if we timed out waiting for PD req */
if (p2p->pending_action_state == P2P_PENDING_PD)
p2p_timeout_prov_disc_req(p2p);
if (p2p->ext_listen_only) {
p2p_dbg(p2p, "Extended Listen Timing - Listen State completed");
p2p->ext_listen_only = 0;
p2p_set_state(p2p, P2P_IDLE);
}
break;
case P2P_WAIT_PEER_CONNECT:
p2p_timeout_wait_peer_connect(p2p);
break;
case P2P_WAIT_PEER_IDLE:
p2p_timeout_wait_peer_idle(p2p);
break;
case P2P_SD_DURING_FIND:
p2p_timeout_sd_during_find(p2p);
break;
case P2P_PROVISIONING:
break;
case P2P_PD_DURING_FIND:
p2p_timeout_prov_disc_during_find(p2p);
break;
case P2P_INVITE:
p2p_timeout_invite(p2p);
break;
case P2P_INVITE_LISTEN:
p2p_timeout_invite_listen(p2p);
break;
}
}
int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
{
struct p2p_device *dev;
dev = p2p_get_device(p2p, peer_addr);
p2p_dbg(p2p, "Local request to reject connection attempts by peer "
MACSTR, MAC2STR(peer_addr));
if (dev == NULL) {
p2p_dbg(p2p, "Peer " MACSTR " unknown", MAC2STR(peer_addr));
return -1;
}
dev->status = P2P_SC_FAIL_REJECTED_BY_USER;
dev->flags |= P2P_DEV_USER_REJECTED;
return 0;
}
const char * p2p_wps_method_text(enum p2p_wps_method method)
{
switch (method) {
case WPS_NOT_READY:
return "not-ready";
case WPS_PIN_DISPLAY:
return "Display";
case WPS_PIN_KEYPAD:
return "Keypad";
case WPS_PBC:
return "PBC";
case WPS_NFC:
return "NFC";
case WPS_P2PS:
return "P2PS";
}
return "??";
}
static const char * p2p_go_state_text(enum p2p_go_state go_state)
{
switch (go_state) {
case UNKNOWN_GO:
return "unknown";
case LOCAL_GO:
return "local";
case REMOTE_GO:
return "remote";
}
return "??";
}
const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
const u8 *addr, int next)
{
struct p2p_device *dev;
if (addr)
dev = p2p_get_device(p2p, addr);
else
dev = dl_list_first(&p2p->devices, struct p2p_device, list);
if (dev && next) {
dev = dl_list_first(&dev->list, struct p2p_device, list);
if (&dev->list == &p2p->devices)
dev = NULL;
}
if (dev == NULL)
return NULL;
return &dev->info;
}
int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
char *buf, size_t buflen)
{
struct p2p_device *dev;
int res;
char *pos, *end;
struct os_reltime now;
if (info == NULL)
return -1;
dev = (struct p2p_device *) (((u8 *) info) -
offsetof(struct p2p_device, info));
pos = buf;
end = buf + buflen;
os_get_reltime(&now);
res = os_snprintf(pos, end - pos,
"age=%d\n"
"listen_freq=%d\n"
"wps_method=%s\n"
"interface_addr=" MACSTR "\n"
"member_in_go_dev=" MACSTR "\n"
"member_in_go_iface=" MACSTR "\n"
"go_neg_req_sent=%d\n"
"go_state=%s\n"
"dialog_token=%u\n"
"intended_addr=" MACSTR "\n"
"country=%c%c\n"
"oper_freq=%d\n"
"req_config_methods=0x%x\n"
"flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
"status=%d\n"
"invitation_reqs=%u\n",
(int) (now.sec - dev->last_seen.sec),
dev->listen_freq,
p2p_wps_method_text(dev->wps_method),
MAC2STR(dev->interface_addr),
MAC2STR(dev->member_in_go_dev),
MAC2STR(dev->member_in_go_iface),
dev->go_neg_req_sent,
p2p_go_state_text(dev->go_state),
dev->dialog_token,
MAC2STR(dev->intended_addr),
dev->country[0] ? dev->country[0] : '_',
dev->country[1] ? dev->country[1] : '_',
dev->oper_freq,
dev->req_config_methods,
dev->flags & P2P_DEV_PROBE_REQ_ONLY ?
"[PROBE_REQ_ONLY]" : "",
dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "",
dev->flags & P2P_DEV_NOT_YET_READY ?
"[NOT_YET_READY]" : "",
dev->flags & P2P_DEV_PD_PEER_DISPLAY ?
"[PD_PEER_DISPLAY]" : "",
dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
"[PD_PEER_KEYPAD]" : "",
dev->flags & P2P_DEV_PD_PEER_P2PS ?
"[PD_PEER_P2PS]" : "",
dev->flags & P2P_DEV_USER_REJECTED ?
"[USER_REJECTED]" : "",
dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
"[PEER_WAITING_RESPONSE]" : "",
dev->flags & P2P_DEV_PREFER_PERSISTENT_GROUP ?
"[PREFER_PERSISTENT_GROUP]" : "",
dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE ?
"[WAIT_GO_NEG_RESPONSE]" : "",
dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM ?
"[WAIT_GO_NEG_CONFIRM]" : "",
dev->flags & P2P_DEV_GROUP_CLIENT_ONLY ?
"[GROUP_CLIENT_ONLY]" : "",
dev->flags & P2P_DEV_FORCE_FREQ ?
"[FORCE_FREQ]" : "",
dev->flags & P2P_DEV_PD_FOR_JOIN ?
"[PD_FOR_JOIN]" : "",
dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT ?
"[LAST_SEEN_AS_GROUP_CLIENT]" : "",
dev->status,
dev->invitation_reqs);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
if (dev->ext_listen_period) {
res = os_snprintf(pos, end - pos,
"ext_listen_period=%u\n"
"ext_listen_interval=%u\n",
dev->ext_listen_period,
dev->ext_listen_interval);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
if (dev->oper_ssid_len) {
res = os_snprintf(pos, end - pos,
"oper_ssid=%s\n",
wpa_ssid_txt(dev->oper_ssid,
dev->oper_ssid_len));
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
#ifdef CONFIG_WIFI_DISPLAY
if (dev->info.wfd_subelems) {
res = os_snprintf(pos, end - pos, "wfd_subelems=");
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
pos += wpa_snprintf_hex(pos, end - pos,
wpabuf_head(dev->info.wfd_subelems),
wpabuf_len(dev->info.wfd_subelems));
res = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
#endif /* CONFIG_WIFI_DISPLAY */
return pos - buf;
}
int p2p_peer_known(struct p2p_data *p2p, const u8 *addr)
{
return p2p_get_device(p2p, addr) != NULL;
}
void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
{
if (enabled) {
p2p_dbg(p2p, "Client discoverability enabled");
p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
} else {
p2p_dbg(p2p, "Client discoverability disabled");
p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
}
}
static struct wpabuf * p2p_build_presence_req(u32 duration1, u32 interval1,
u32 duration2, u32 interval2)
{
struct wpabuf *req;
struct p2p_noa_desc desc1, desc2, *ptr1 = NULL, *ptr2 = NULL;
u8 *len;
req = wpabuf_alloc(100);
if (req == NULL)
return NULL;
if (duration1 || interval1) {
os_memset(&desc1, 0, sizeof(desc1));
desc1.count_type = 1;
desc1.duration = duration1;
desc1.interval = interval1;
ptr1 = &desc1;
if (duration2 || interval2) {
os_memset(&desc2, 0, sizeof(desc2));
desc2.count_type = 2;
desc2.duration = duration2;
desc2.interval = interval2;
ptr2 = &desc2;
}
}
p2p_buf_add_action_hdr(req, P2P_PRESENCE_REQ, 1);
len = p2p_buf_add_ie_hdr(req);
p2p_buf_add_noa(req, 0, 0, 0, ptr1, ptr2);
p2p_buf_update_ie_hdr(req, len);
return req;
}
int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr,
const u8 *own_interface_addr, unsigned int freq,
u32 duration1, u32 interval1, u32 duration2,
u32 interval2)
{
struct wpabuf *req;
p2p_dbg(p2p, "Send Presence Request to GO " MACSTR
" (own interface " MACSTR ") freq=%u dur1=%u int1=%u "
"dur2=%u int2=%u",
MAC2STR(go_interface_addr), MAC2STR(own_interface_addr),
freq, duration1, interval1, duration2, interval2);
req = p2p_build_presence_req(duration1, interval1, duration2,
interval2);
if (req == NULL)
return -1;
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr,
go_interface_addr,
wpabuf_head(req), wpabuf_len(req), 200) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
}
wpabuf_free(req);
return 0;
}
static struct wpabuf * p2p_build_presence_resp(u8 status, const u8 *noa,
size_t noa_len, u8 dialog_token)
{
struct wpabuf *resp;
u8 *len;
resp = wpabuf_alloc(100 + noa_len);
if (resp == NULL)
return NULL;
p2p_buf_add_action_hdr(resp, P2P_PRESENCE_RESP, dialog_token);
len = p2p_buf_add_ie_hdr(resp);
p2p_buf_add_status(resp, status);
if (noa) {
wpabuf_put_u8(resp, P2P_ATTR_NOTICE_OF_ABSENCE);
wpabuf_put_le16(resp, noa_len);
wpabuf_put_data(resp, noa, noa_len);
} else
p2p_buf_add_noa(resp, 0, 0, 0, NULL, NULL);
p2p_buf_update_ie_hdr(resp, len);
return resp;
}
static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da,
const u8 *sa, const u8 *data, size_t len,
int rx_freq)
{
struct p2p_message msg;
u8 status;
struct wpabuf *resp;
size_t g;
struct p2p_group *group = NULL;
int parsed = 0;
u8 noa[50];
int noa_len;
p2p_dbg(p2p, "Received P2P Action - P2P Presence Request");
for (g = 0; g < p2p->num_groups; g++) {
if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]),
ETH_ALEN) == 0) {
group = p2p->groups[g];
break;
}
}
if (group == NULL) {
p2p_dbg(p2p, "Ignore P2P Presence Request for unknown group "
MACSTR, MAC2STR(da));
return;
}
if (p2p_parse(data, len, &msg) < 0) {
p2p_dbg(p2p, "Failed to parse P2P Presence Request");
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
}
parsed = 1;
if (msg.noa == NULL) {
p2p_dbg(p2p, "No NoA attribute in P2P Presence Request");
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
}
status = p2p_group_presence_req(group, sa, msg.noa, msg.noa_len);
fail:
if (p2p->cfg->get_noa)
noa_len = p2p->cfg->get_noa(p2p->cfg->cb_ctx, da, noa,
sizeof(noa));
else
noa_len = -1;
resp = p2p_build_presence_resp(status, noa_len > 0 ? noa : NULL,
noa_len > 0 ? noa_len : 0,
msg.dialog_token);
if (parsed)
p2p_parse_free(&msg);
if (resp == NULL)
return;
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (p2p_send_action(p2p, rx_freq, sa, da, da,
wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
}
wpabuf_free(resp);
}
static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
const u8 *sa, const u8 *data, size_t len)
{
struct p2p_message msg;
p2p_dbg(p2p, "Received P2P Action - P2P Presence Response");
if (p2p_parse(data, len, &msg) < 0) {
p2p_dbg(p2p, "Failed to parse P2P Presence Response");
return;
}
if (msg.status == NULL || msg.noa == NULL) {
p2p_dbg(p2p, "No Status or NoA attribute in P2P Presence Response");
p2p_parse_free(&msg);
return;
}
if (p2p->cfg->presence_resp) {
p2p->cfg->presence_resp(p2p->cfg->cb_ctx, sa, *msg.status,
msg.noa, msg.noa_len);
}
if (*msg.status) {
p2p_dbg(p2p, "P2P Presence Request was rejected: status %u",
*msg.status);
p2p_parse_free(&msg);
return;
}
p2p_dbg(p2p, "P2P Presence Request was accepted");
wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA",
msg.noa, msg.noa_len);
/* TODO: process NoA */
p2p_parse_free(&msg);
}
static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct p2p_data *p2p = eloop_ctx;
if (p2p->ext_listen_interval) {
/* Schedule next extended listen timeout */
eloop_register_timeout(p2p->ext_listen_interval_sec,
p2p->ext_listen_interval_usec,
p2p_ext_listen_timeout, p2p, NULL);
}
if ((p2p->cfg->is_p2p_in_progress &&
p2p->cfg->is_p2p_in_progress(p2p->cfg->cb_ctx)) ||
(p2p->pending_action_state == P2P_PENDING_PD &&
p2p->pd_retries > 0)) {
p2p_dbg(p2p, "Operation in progress - skip Extended Listen timeout (%s)",
p2p_state_txt(p2p->state));
return;
}
if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) {
/*
* This should not really happen, but it looks like the Listen
* command may fail is something else (e.g., a scan) was
* running at an inconvenient time. As a workaround, allow new
* Extended Listen operation to be started.
*/
p2p_dbg(p2p, "Previous Extended Listen operation had not been completed - try again");
p2p->ext_listen_only = 0;
p2p_set_state(p2p, P2P_IDLE);
}
if (p2p->state != P2P_IDLE) {
p2p_dbg(p2p, "Skip Extended Listen timeout in active state (%s)", p2p_state_txt(p2p->state));
return;
}
p2p_dbg(p2p, "Extended Listen timeout");
p2p->ext_listen_only = 1;
if (p2p_listen(p2p, p2p->ext_listen_period) < 0) {
p2p_dbg(p2p, "Failed to start Listen state for Extended Listen Timing");
p2p->ext_listen_only = 0;
}
}
int p2p_ext_listen(struct p2p_data *p2p, unsigned int period,
unsigned int interval)
{
if (period > 65535 || interval > 65535 || period > interval ||
(period == 0 && interval > 0) || (period > 0 && interval == 0)) {
p2p_dbg(p2p, "Invalid Extended Listen Timing request: period=%u interval=%u",
period, interval);
return -1;
}
eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
if (interval == 0) {
p2p_dbg(p2p, "Disabling Extended Listen Timing");
p2p->ext_listen_period = 0;
p2p->ext_listen_interval = 0;
return 0;
}
p2p_dbg(p2p, "Enabling Extended Listen Timing: period %u msec, interval %u msec",
period, interval);
p2p->ext_listen_period = period;
p2p->ext_listen_interval = interval;
p2p->ext_listen_interval_sec = interval / 1000;
p2p->ext_listen_interval_usec = (interval % 1000) * 1000;
eloop_register_timeout(p2p->ext_listen_interval_sec,
p2p->ext_listen_interval_usec,
p2p_ext_listen_timeout, p2p, NULL);
return 0;
}
void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
const u8 *ie, size_t ie_len)
{
struct p2p_message msg;
if (bssid == NULL || ie == NULL)
return;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_ies(ie, ie_len, &msg))
return;
if (msg.minor_reason_code == NULL) {
p2p_parse_free(&msg);
return;
}
p2p_dbg(p2p, "Deauthentication notification BSSID " MACSTR
" reason_code=%u minor_reason_code=%u",
MAC2STR(bssid), reason_code, *msg.minor_reason_code);
p2p_parse_free(&msg);
}
void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
const u8 *ie, size_t ie_len)
{
struct p2p_message msg;
if (bssid == NULL || ie == NULL)
return;
os_memset(&msg, 0, sizeof(msg));
if (p2p_parse_ies(ie, ie_len, &msg))
return;
if (msg.minor_reason_code == NULL) {
p2p_parse_free(&msg);
return;
}
p2p_dbg(p2p, "Disassociation notification BSSID " MACSTR
" reason_code=%u minor_reason_code=%u",
MAC2STR(bssid), reason_code, *msg.minor_reason_code);
p2p_parse_free(&msg);
}
void p2p_set_managed_oper(struct p2p_data *p2p, int enabled)
{
if (enabled) {
p2p_dbg(p2p, "Managed P2P Device operations enabled");
p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED;
} else {
p2p_dbg(p2p, "Managed P2P Device operations disabled");
p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED;
}
}
int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class,
u8 *op_channel,
struct wpa_freq_range_list *avoid_list,
struct wpa_freq_range_list *disallow_list)
{
return p2p_channel_random_social(&p2p->channels, op_class, op_channel,
avoid_list, disallow_list);
}
int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
u8 forced)
{
if (p2p_channel_to_freq(reg_class, channel) < 0)
return -1;
/*
* Listen channel was set in configuration or set by control interface;
* cannot override it.
*/
if (p2p->cfg->channel_forced && forced == 0) {
p2p_dbg(p2p,
"Listen channel was previously configured - do not override based on optimization");
return -1;
}
p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
reg_class, channel);
if (p2p->state == P2P_IDLE) {
p2p->cfg->reg_class = reg_class;
p2p->cfg->channel = channel;
p2p->cfg->channel_forced = forced;
} else {
p2p_dbg(p2p, "Defer setting listen channel");
p2p->pending_reg_class = reg_class;
p2p->pending_channel = channel;
p2p->pending_channel_forced = forced;
}
return 0;
}
u8 p2p_get_listen_channel(struct p2p_data *p2p)
{
return p2p->cfg->channel;
}
int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len)
{
p2p_dbg(p2p, "New SSID postfix: %s", wpa_ssid_txt(postfix, len));
if (postfix == NULL) {
p2p->cfg->ssid_postfix_len = 0;
return 0;
}
if (len > sizeof(p2p->cfg->ssid_postfix))
return -1;
os_memcpy(p2p->cfg->ssid_postfix, postfix, len);
p2p->cfg->ssid_postfix_len = len;
return 0;
}
int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
int cfg_op_channel)
{
if (p2p_channel_to_freq(op_reg_class, op_channel) < 0)
return -1;
p2p_dbg(p2p, "Set Operating channel: reg_class %u channel %u",
op_reg_class, op_channel);
p2p->cfg->op_reg_class = op_reg_class;
p2p->cfg->op_channel = op_channel;
p2p->cfg->cfg_op_channel = cfg_op_channel;
return 0;
}
int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
const struct p2p_channel *pref_chan)
{
struct p2p_channel *n;
if (pref_chan) {
n = os_memdup(pref_chan,
num_pref_chan * sizeof(struct p2p_channel));
if (n == NULL)
return -1;
} else
n = NULL;
os_free(p2p->cfg->pref_chan);
p2p->cfg->pref_chan = n;
p2p->cfg->num_pref_chan = num_pref_chan;
return 0;
}
int p2p_set_no_go_freq(struct p2p_data *p2p,
const struct wpa_freq_range_list *list)
{
struct wpa_freq_range *tmp;
if (list == NULL || list->num == 0) {
os_free(p2p->no_go_freq.range);
p2p->no_go_freq.range = NULL;
p2p->no_go_freq.num = 0;
return 0;
}
tmp = os_calloc(list->num, sizeof(struct wpa_freq_range));
if (tmp == NULL)
return -1;
os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range));
os_free(p2p->no_go_freq.range);
p2p->no_go_freq.range = tmp;
p2p->no_go_freq.num = list->num;
p2p_dbg(p2p, "Updated no GO chan list");
return 0;
}
int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
u8 *iface_addr)
{
struct p2p_device *dev = p2p_get_device(p2p, dev_addr);
if (dev == NULL || is_zero_ether_addr(dev->interface_addr))
return -1;
os_memcpy(iface_addr, dev->interface_addr, ETH_ALEN);
return 0;
}
int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
u8 *dev_addr)
{
struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
if (dev == NULL)
return -1;
os_memcpy(dev_addr, dev->info.p2p_device_addr, ETH_ALEN);
return 0;
}
void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr)
{
os_memcpy(p2p->peer_filter, addr, ETH_ALEN);
if (is_zero_ether_addr(p2p->peer_filter))
p2p_dbg(p2p, "Disable peer filter");
else
p2p_dbg(p2p, "Enable peer filter for " MACSTR,
MAC2STR(p2p->peer_filter));
}
void p2p_set_cross_connect(struct p2p_data *p2p, int enabled)
{
p2p_dbg(p2p, "Cross connection %s", enabled ? "enabled" : "disabled");
if (p2p->cross_connect == enabled)
return;
p2p->cross_connect = enabled;
/* TODO: may need to tear down any action group where we are GO(?) */
}
int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr)
{
struct p2p_device *dev = p2p_get_device_interface(p2p, iface_addr);
if (dev == NULL)
return -1;
if (dev->oper_freq <= 0)
return -1;
return dev->oper_freq;
}
void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled)
{
p2p_dbg(p2p, "Intra BSS distribution %s",
enabled ? "enabled" : "disabled");
p2p->cfg->p2p_intra_bss = enabled;
}
void p2p_update_channel_list(struct p2p_data *p2p,
const struct p2p_channels *chan,
const struct p2p_channels *cli_chan)
{
p2p_dbg(p2p, "Update channel list");
os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels));
p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
os_memcpy(&p2p->cfg->cli_channels, cli_chan,
sizeof(struct p2p_channels));
p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
}
int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid, const u8 *buf,
size_t len, unsigned int wait_time)
{
int res, scheduled;
res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
buf, len, wait_time, &scheduled);
if (res == 0 && scheduled && p2p->in_listen && freq > 0 &&
p2p->drv_in_listen > 0 &&
(unsigned int) p2p->drv_in_listen != freq) {
p2p_dbg(p2p,
"Stop listen on %d MHz to allow a frame to be sent immediately on %d MHz",
p2p->drv_in_listen, freq);
p2p_stop_listen_for_freq(p2p, freq);
}
return res;
}
void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5,
int freq_overall)
{
p2p_dbg(p2p, "Best channel: 2.4 GHz: %d, 5 GHz: %d, overall: %d",
freq_24, freq_5, freq_overall);
p2p->best_freq_24 = freq_24;
p2p->best_freq_5 = freq_5;
p2p->best_freq_overall = freq_overall;
}
void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq)
{
p2p_dbg(p2p, "Own frequency preference: %d MHz", freq);
p2p->own_freq_preference = freq;
}
const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p)
{
if (p2p == NULL || p2p->go_neg_peer == NULL)
return NULL;
return p2p->go_neg_peer->info.p2p_device_addr;
}
const struct p2p_peer_info *
p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next)
{
struct p2p_device *dev;
if (addr) {
dev = p2p_get_device(p2p, addr);
if (!dev)
return NULL;
if (!next) {
if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
return NULL;
return &dev->info;
} else {
do {
dev = dl_list_first(&dev->list,
struct p2p_device,
list);
if (!dev || &dev->list == &p2p->devices)
return NULL;
} while (dev->flags & P2P_DEV_PROBE_REQ_ONLY);
}
} else {
dev = dl_list_first(&p2p->devices, struct p2p_device, list);
if (!dev)
return NULL;
while (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
dev = dl_list_first(&dev->list,
struct p2p_device,
list);
if (!dev || &dev->list == &p2p->devices)
return NULL;
}
}
return &dev->info;
}
int p2p_in_progress(struct p2p_data *p2p)
{
if (p2p == NULL)
return 0;
if (p2p->state == P2P_SEARCH)
return 2;
return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
}
void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
u8 client_timeout)
{
if (p2p) {
p2p->go_timeout = go_timeout;
p2p->client_timeout = client_timeout;
}
}
#ifdef CONFIG_WIFI_DISPLAY
static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
{
size_t g;
struct p2p_group *group;
for (g = 0; g < p2p->num_groups; g++) {
group = p2p->groups[g];
p2p_group_force_beacon_update_ies(group);
}
}
int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie)
{
wpabuf_free(p2p->wfd_ie_beacon);
p2p->wfd_ie_beacon = ie;
p2p_update_wfd_ie_groups(p2p);
return 0;
}
int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie)
{
wpabuf_free(p2p->wfd_ie_probe_req);
p2p->wfd_ie_probe_req = ie;
return 0;
}
int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie)
{
wpabuf_free(p2p->wfd_ie_probe_resp);
p2p->wfd_ie_probe_resp = ie;
p2p_update_wfd_ie_groups(p2p);
return 0;
}
int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie)
{
wpabuf_free(p2p->wfd_ie_assoc_req);
p2p->wfd_ie_assoc_req = ie;
return 0;
}
int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie)
{
wpabuf_free(p2p->wfd_ie_invitation);
p2p->wfd_ie_invitation = ie;
return 0;
}
int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie)
{
wpabuf_free(p2p->wfd_ie_prov_disc_req);
p2p->wfd_ie_prov_disc_req = ie;
return 0;
}
int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie)
{
wpabuf_free(p2p->wfd_ie_prov_disc_resp);
p2p->wfd_ie_prov_disc_resp = ie;
return 0;
}
int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie)
{
wpabuf_free(p2p->wfd_ie_go_neg);
p2p->wfd_ie_go_neg = ie;
return 0;
}
int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
{
wpabuf_free(p2p->wfd_dev_info);
if (elem) {
p2p->wfd_dev_info = wpabuf_dup(elem);
if (p2p->wfd_dev_info == NULL)
return -1;
} else
p2p->wfd_dev_info = NULL;
return 0;
}
int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
{
wpabuf_free(p2p->wfd_r2_dev_info);
if (elem) {
p2p->wfd_r2_dev_info = wpabuf_dup(elem);
if (p2p->wfd_r2_dev_info == NULL)
return -1;
} else
p2p->wfd_r2_dev_info = NULL;
return 0;
}
int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem)
{
wpabuf_free(p2p->wfd_assoc_bssid);
if (elem) {
p2p->wfd_assoc_bssid = wpabuf_dup(elem);
if (p2p->wfd_assoc_bssid == NULL)
return -1;
} else
p2p->wfd_assoc_bssid = NULL;
return 0;
}
int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
const struct wpabuf *elem)
{
wpabuf_free(p2p->wfd_coupled_sink_info);
if (elem) {
p2p->wfd_coupled_sink_info = wpabuf_dup(elem);
if (p2p->wfd_coupled_sink_info == NULL)
return -1;
} else
p2p->wfd_coupled_sink_info = NULL;
return 0;
}
#endif /* CONFIG_WIFI_DISPLAY */
int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int,
int max_disc_tu)
{
if (min_disc_int > max_disc_int || min_disc_int < 0 || max_disc_int < 0)
return -1;
p2p->min_disc_int = min_disc_int;
p2p->max_disc_int = max_disc_int;
p2p->max_disc_tu = max_disc_tu;
p2p_dbg(p2p, "Set discoverable interval: min=%d max=%d max_tu=%d",
min_disc_int, max_disc_int, max_disc_tu);
return 0;
}
void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
{
va_list ap;
char buf[500];
if (!p2p->cfg->debug_print)
return;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
buf[sizeof(buf) - 1] = '\0';
va_end(ap);
p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_DEBUG, buf);
}
void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
{
va_list ap;
char buf[500];
if (!p2p->cfg->debug_print)
return;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
buf[sizeof(buf) - 1] = '\0';
va_end(ap);
p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_INFO, buf);
}
void p2p_err(struct p2p_data *p2p, const char *fmt, ...)
{
va_list ap;
char buf[500];
if (!p2p->cfg->debug_print)
return;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
buf[sizeof(buf) - 1] = '\0';
va_end(ap);
p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf);
}
void p2p_loop_on_known_peers(struct p2p_data *p2p,
void (*peer_callback)(struct p2p_peer_info *peer,
void *user_data),
void *user_data)
{
struct p2p_device *dev, *n;
dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
peer_callback(&dev->info, user_data);
}
}
#ifdef CONFIG_WPS_NFC
static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p,
int client_freq,
const u8 *go_dev_addr,
const u8 *ssid, size_t ssid_len)
{
struct wpabuf *buf;
u8 op_class, channel;
enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP;
buf = wpabuf_alloc(1000);
if (buf == NULL)
return NULL;
op_class = p2p->cfg->reg_class;
channel = p2p->cfg->channel;
p2p_buf_add_capability(buf, p2p->dev_capab &
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
p2p_buf_add_device_info(buf, p2p, NULL);
if (p2p->num_groups > 0) {
int freq = p2p_group_get_freq(p2p->groups[0]);
role = P2P_GO_IN_A_GROUP;
if (p2p_freq_to_channel(freq, &op_class, &channel) < 0) {
p2p_dbg(p2p,
"Unknown GO operating frequency %d MHz for NFC handover",
freq);
wpabuf_free(buf);
return NULL;
}
} else if (client_freq > 0) {
role = P2P_CLIENT_IN_A_GROUP;
if (p2p_freq_to_channel(client_freq, &op_class, &channel) < 0) {
p2p_dbg(p2p,
"Unknown client operating frequency %d MHz for NFC handover",
client_freq);
wpabuf_free(buf);
return NULL;
}
}
p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class,
channel, role);
if (p2p->num_groups > 0) {
/* Limit number of clients to avoid very long message */
p2p_buf_add_group_info(p2p->groups[0], buf, 5);
p2p_group_buf_add_id(p2p->groups[0], buf);
} else if (client_freq > 0 &&
go_dev_addr && !is_zero_ether_addr(go_dev_addr) &&
ssid && ssid_len > 0) {
/*
* Add the optional P2P Group ID to indicate in which group this
* device is a P2P Client.
*/
p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len);
}
return buf;
}
struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
int client_freq,
const u8 *go_dev_addr,
const u8 *ssid, size_t ssid_len)
{
return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
ssid_len);
}
struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
int client_freq,
const u8 *go_dev_addr,
const u8 *ssid, size_t ssid_len)
{
return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
ssid_len);
}
int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
struct p2p_nfc_params *params)
{
struct p2p_message msg;
struct p2p_device *dev;
const u8 *p2p_dev_addr;
int freq;
enum p2p_role_indication role;
params->next_step = NO_ACTION;
if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len,
params->p2p_attr, params->p2p_len, &msg)) {
p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC");
p2p_parse_free(&msg);
return -1;
}
if (msg.p2p_device_addr)
p2p_dev_addr = msg.p2p_device_addr;
else if (msg.device_id)
p2p_dev_addr = msg.device_id;
else {
p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id");
p2p_parse_free(&msg);
return -1;
}
if (msg.oob_dev_password) {
os_memcpy(params->oob_dev_pw, msg.oob_dev_password,
msg.oob_dev_password_len);
params->oob_dev_pw_len = msg.oob_dev_password_len;
}
dev = p2p_create_device(p2p, p2p_dev_addr);
if (dev == NULL) {
p2p_parse_free(&msg);
return -1;
}
params->peer = &dev->info;
os_get_reltime(&dev->last_seen);
dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
p2p_copy_wps_info(p2p, dev, 0, &msg);
if (!msg.oob_go_neg_channel) {
p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included");
p2p_parse_free(&msg);
return -1;
}
if (msg.oob_go_neg_channel[3] == 0 &&
msg.oob_go_neg_channel[4] == 0)
freq = 0;
else
freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3],
msg.oob_go_neg_channel[4]);
if (freq < 0) {
p2p_dbg(p2p, "Unknown peer OOB GO Neg channel");
p2p_parse_free(&msg);
return -1;
}
role = msg.oob_go_neg_channel[5];
if (role == P2P_GO_IN_A_GROUP) {
p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq);
params->go_freq = freq;
} else if (role == P2P_CLIENT_IN_A_GROUP) {
p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz",
freq);
params->go_freq = freq;
} else
p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq);
dev->oob_go_neg_freq = freq;
if (!params->sel && role != P2P_GO_IN_A_GROUP) {
freq = p2p_channel_to_freq(p2p->cfg->reg_class,
p2p->cfg->channel);
if (freq < 0) {
p2p_dbg(p2p, "Own listen channel not known");
p2p_parse_free(&msg);
return -1;
}
p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq);
dev->oob_go_neg_freq = freq;
}
if (msg.group_id) {
os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN);
params->go_ssid_len = msg.group_id_len - ETH_ALEN;
os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN,
params->go_ssid_len);
}
if (dev->flags & P2P_DEV_USER_REJECTED) {
p2p_dbg(p2p, "Do not report rejected device");
p2p_parse_free(&msg);
return 0;
}
if (!(dev->flags & P2P_DEV_REPORTED)) {
p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info,
!(dev->flags & P2P_DEV_REPORTED_ONCE));
dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
}
p2p_parse_free(&msg);
if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0)
params->next_step = BOTH_GO;
else if (role == P2P_GO_IN_A_GROUP)
params->next_step = JOIN_GROUP;
else if (role == P2P_CLIENT_IN_A_GROUP) {
dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
params->next_step = PEER_CLIENT;
} else if (p2p->num_groups > 0)
params->next_step = AUTH_JOIN;
else if (params->sel)
params->next_step = INIT_GO_NEG;
else
params->next_step = RESP_GO_NEG;
return 0;
}
void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
int go_intent,
const u8 *own_interface_addr)
{
p2p->authorized_oob_dev_pw_id = dev_pw_id;
if (dev_pw_id == 0) {
p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover");
return;
}
p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover",
dev_pw_id);
p2p->go_intent = go_intent;
os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
}
#endif /* CONFIG_WPS_NFC */
int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len)
{
if (len < 8 || len > 63)
return -1;
p2p->cfg->passphrase_len = len;
return 0;
}
void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem)
{
p2p->vendor_elem = vendor_elem;
}
void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct p2p_data *p2p = eloop_ctx;
p2p_dbg(p2p,
"Timeout on waiting peer to become ready for GO Negotiation");
p2p_go_neg_failed(p2p, -1);
}
void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
const unsigned int *pref_freq_list,
unsigned int size)
{
unsigned int i;
if (size > P2P_MAX_PREF_CHANNELS)
size = P2P_MAX_PREF_CHANNELS;
p2p->num_pref_freq = size;
for (i = 0; i < size; i++) {
p2p->pref_freq_list[i] = pref_freq_list[i];
p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz",
i, p2p->pref_freq_list[i]);
}
}
void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class,
u8 chan)
{
p2p->override_pref_op_class = op_class;
p2p->override_pref_channel = chan;
}
struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p,
unsigned int freq)
{
struct wpabuf *ies, *buf;
u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
int ret;
ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
if (!ies) {
wpa_printf(MSG_ERROR,
"CTRL: Failed to build Probe Response IEs");
return NULL;
}
buf = wpabuf_alloc(200 + wpabuf_len(ies));
if (!buf) {
wpabuf_free(ies);
return NULL;
}
ret = p2p_build_probe_resp_buf(p2p, buf, ies, addr, freq);
wpabuf_free(ies);
if (ret) {
wpabuf_free(buf);
return NULL;
}
return buf;
}
bool p2p_is_peer_6ghz_capab(struct p2p_data *p2p, const u8 *addr)
{
struct p2p_device *dev;
dev = p2p_get_device(p2p, addr);
if (!dev)
return false;
return !!(dev->info.dev_capab & P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE);
}
void p2p_set_6ghz_dev_capab(struct p2p_data *p2p, bool allow_6ghz)
{
p2p->p2p_6ghz_capable = allow_6ghz;
p2p->allow_6ghz = allow_6ghz;
p2p_dbg(p2p, "Set 6 GHz capability to %d", allow_6ghz);
if (allow_6ghz)
p2p->dev_capab |= P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE;
else
p2p->dev_capab &= ~P2P_DEV_CAPAB_6GHZ_BAND_CAPABLE;
}
bool is_p2p_6ghz_capable(struct p2p_data *p2p)
{
return p2p->p2p_6ghz_capable;
}
bool p2p_wfd_enabled(struct p2p_data *p2p)
{
#ifdef CONFIG_WIFI_DISPLAY
return p2p->wfd_ie_probe_req != NULL;
#else /* CONFIG_WIFI_DISPLAY */
return false;
#endif /* CONFIG_WIFI_DISPLAY */
}
bool p2p_peer_wfd_enabled(struct p2p_data *p2p, const u8 *peer_addr)
{
#ifdef CONFIG_WIFI_DISPLAY
struct p2p_device *dev;
dev = p2p_get_device(p2p, peer_addr);
return dev && dev->info.wfd_subelems != NULL;
#else /* CONFIG_WIFI_DISPLAY */
return false;
#endif /* CONFIG_WIFI_DISPLAY */
}
bool is_p2p_allow_6ghz(struct p2p_data *p2p)
{
return p2p->allow_6ghz;
}
void set_p2p_allow_6ghz(struct p2p_data *p2p, bool value)
{
p2p->allow_6ghz = value;
}
diff --git a/contrib/wpa/src/p2p/p2p_build.c b/contrib/wpa/src/p2p/p2p_build.c
index 63eb2e84c376..4229d9b34873 100644
--- a/contrib/wpa/src/p2p/p2p_build.c
+++ b/contrib/wpa/src/p2p/p2p_build.c
@@ -1,835 +1,835 @@
/*
* P2P - IE builder
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "common/qca-vendor.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
{
wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
wpabuf_put_u8(buf, subtype); /* OUI Subtype */
wpabuf_put_u8(buf, dialog_token);
wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
}
void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
u8 dialog_token)
{
wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
wpabuf_put_u8(buf, subtype); /* OUI Subtype */
wpabuf_put_u8(buf, dialog_token);
wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
}
u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf)
{
u8 *len;
/* P2P IE header */
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
len = wpabuf_put(buf, 1); /* IE length to be filled */
wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
return len;
}
void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len)
{
/* Update P2P IE Length */
*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
}
void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab)
{
/* P2P Capability */
wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY);
wpabuf_put_le16(buf, 2);
wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */
wpabuf_put_u8(buf, group_capab); /* Group Capabilities */
wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x",
dev_capab, group_capab);
}
void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent)
{
/* Group Owner Intent */
wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT);
wpabuf_put_le16(buf, 1);
wpabuf_put_u8(buf, go_intent);
wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u",
go_intent >> 1, go_intent & 0x01);
}
void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
u8 reg_class, u8 channel)
{
/* Listen Channel */
wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL);
wpabuf_put_le16(buf, 5);
wpabuf_put_data(buf, country, 3);
wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
wpabuf_put_u8(buf, channel); /* Channel Number */
wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u "
"Channel %u", reg_class, channel);
}
void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
u8 reg_class, u8 channel)
{
/* Operating Channel */
wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL);
wpabuf_put_le16(buf, 5);
wpabuf_put_data(buf, country, 3);
wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
wpabuf_put_u8(buf, channel); /* Channel Number */
wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u "
"Channel %u", reg_class, channel);
}
void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
- const u32 *preferred_freq_list,
+ const unsigned int *preferred_freq_list,
unsigned int size)
{
unsigned int i, count = 0;
u8 op_class, op_channel;
if (!size)
return;
/*
* First, determine the number of P2P supported channels in the
* pref_freq_list returned from driver. This is needed for calculations
* of the vendor IE size.
*/
for (i = 0; i < size; i++) {
if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
&op_channel) == 0)
count++;
}
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
wpabuf_put_u8(buf, 4 + count * sizeof(u16));
wpabuf_put_be24(buf, OUI_QCA);
wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST);
for (i = 0; i < size; i++) {
if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
&op_channel) < 0) {
wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz",
preferred_freq_list[i]);
continue;
}
wpabuf_put_u8(buf, op_class);
wpabuf_put_u8(buf, op_channel);
}
}
void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
struct p2p_channels *chan)
{
u8 *len;
size_t i;
/* Channel List */
wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST);
len = wpabuf_put(buf, 2); /* IE length to be filled */
wpabuf_put_data(buf, country, 3); /* Country String */
for (i = 0; i < chan->reg_classes; i++) {
struct p2p_reg_class *c = &chan->reg_class[i];
wpabuf_put_u8(buf, c->reg_class);
wpabuf_put_u8(buf, c->channels);
wpabuf_put_data(buf, c->channel, c->channels);
}
/* Update attribute length */
WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
wpa_hexdump(MSG_DEBUG, "P2P: * Channel List",
len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2);
}
void p2p_buf_add_status(struct wpabuf *buf, u8 status)
{
/* Status */
wpabuf_put_u8(buf, P2P_ATTR_STATUS);
wpabuf_put_le16(buf, 1);
wpabuf_put_u8(buf, status);
wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status);
}
void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
struct p2p_device *peer)
{
u8 *len;
u16 methods;
size_t nlen, i;
/* P2P Device Info */
wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO);
len = wpabuf_put(buf, 2); /* IE length to be filled */
/* P2P Device address */
wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
/* Config Methods */
methods = 0;
if (peer && peer->wps_method != WPS_NOT_READY) {
if (peer->wps_method == WPS_PBC)
methods |= WPS_CONFIG_PUSHBUTTON;
else if (peer->wps_method == WPS_P2PS)
methods |= WPS_CONFIG_P2PS;
else if (peer->wps_method == WPS_PIN_DISPLAY ||
peer->wps_method == WPS_PIN_KEYPAD)
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
} else if (p2p->cfg->config_methods) {
methods |= p2p->cfg->config_methods &
(WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS);
} else {
methods |= WPS_CONFIG_PUSHBUTTON;
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
methods |= WPS_CONFIG_P2PS;
}
wpabuf_put_be16(buf, methods);
/* Primary Device Type */
wpabuf_put_data(buf, p2p->cfg->pri_dev_type,
sizeof(p2p->cfg->pri_dev_type));
/* Number of Secondary Device Types */
wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types);
/* Secondary Device Type List */
for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i],
WPS_DEV_TYPE_LEN);
/* Device Name */
nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
wpabuf_put_be16(buf, ATTR_DEV_NAME);
wpabuf_put_be16(buf, nlen);
wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
/* Update attribute length */
WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
wpa_printf(MSG_DEBUG, "P2P: * Device Info");
}
void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr)
{
/* P2P Device ID */
wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID);
wpabuf_put_le16(buf, ETH_ALEN);
wpabuf_put_data(buf, dev_addr, ETH_ALEN);
wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr));
}
void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
u8 client_timeout)
{
/* Configuration Timeout */
wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT);
wpabuf_put_le16(buf, 2);
wpabuf_put_u8(buf, go_timeout);
wpabuf_put_u8(buf, client_timeout);
wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms) "
"client %d (*10ms)", go_timeout, client_timeout);
}
void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr)
{
/* Intended P2P Interface Address */
wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR);
wpabuf_put_le16(buf, ETH_ALEN);
wpabuf_put_data(buf, interface_addr, ETH_ALEN);
wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR,
MAC2STR(interface_addr));
}
void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid)
{
/* P2P Group BSSID */
wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID);
wpabuf_put_le16(buf, ETH_ALEN);
wpabuf_put_data(buf, bssid, ETH_ALEN);
wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR,
MAC2STR(bssid));
}
void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
const u8 *ssid, size_t ssid_len)
{
/* P2P Group ID */
wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID);
wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
wpabuf_put_data(buf, dev_addr, ETH_ALEN);
wpabuf_put_data(buf, ssid, ssid_len);
wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
MAC2STR(dev_addr));
wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len);
}
void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags)
{
/* Invitation Flags */
wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS);
wpabuf_put_le16(buf, 1);
wpabuf_put_u8(buf, flags);
wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags);
}
static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc)
{
if (desc == NULL)
return;
wpabuf_put_u8(buf, desc->count_type);
wpabuf_put_le32(buf, desc->duration);
wpabuf_put_le32(buf, desc->interval);
wpabuf_put_le32(buf, desc->start_time);
}
void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2)
{
/* Notice of Absence */
wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE);
wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0));
wpabuf_put_u8(buf, noa_index);
wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f));
p2p_buf_add_noa_desc(buf, desc1);
p2p_buf_add_noa_desc(buf, desc2);
wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
}
void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
u16 interval)
{
/* Extended Listen Timing */
wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING);
wpabuf_put_le16(buf, 4);
wpabuf_put_le16(buf, period);
wpabuf_put_le16(buf, interval);
wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec "
"interval %u msec)", period, interval);
}
void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p)
{
/* P2P Interface */
wpabuf_put_u8(buf, P2P_ATTR_INTERFACE);
wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN);
/* P2P Device address */
wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
/*
* FIX: Fetch interface address list from driver. Do not include
* the P2P Device address if it is never used as interface address.
*/
/* P2P Interface Address Count */
wpabuf_put_u8(buf, 1);
wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
}
void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
u8 oper_class, u8 channel,
enum p2p_role_indication role)
{
/* OOB Group Owner Negotiation Channel */
wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL);
wpabuf_put_le16(buf, 6);
wpabuf_put_data(buf, country, 3);
wpabuf_put_u8(buf, oper_class); /* Operating Class */
wpabuf_put_u8(buf, channel); /* Channel Number */
wpabuf_put_u8(buf, (u8) role); /* Role indication */
wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating "
"Class %u Channel %u Role %d",
oper_class, channel, role);
}
void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p)
{
if (!p2p)
return;
/* Service Hash */
wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH);
wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN);
wpabuf_put_data(buf, p2p->p2ps_seek_hash,
p2p->p2ps_seek_count * P2PS_HASH_LEN);
wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN);
}
void p2p_buf_add_session_info(struct wpabuf *buf, const char *info)
{
size_t info_len = 0;
if (info && info[0])
info_len = os_strlen(info);
/* Session Information Data Info */
wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA);
wpabuf_put_le16(buf, (u16) info_len);
if (info) {
wpabuf_put_data(buf, info, info_len);
wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info);
}
}
void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap)
{
/* Connection Capability Info */
wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY);
wpabuf_put_le16(buf, 1);
wpabuf_put_u8(buf, connection_cap);
wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
connection_cap);
}
void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac)
{
if (!buf || !mac)
return;
/* Advertisement ID Info */
wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID);
wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
wpabuf_put_le32(buf, id);
wpabuf_put_data(buf, mac, ETH_ALEN);
wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR,
id, MAC2STR(mac));
}
static int p2ps_wildcard_hash(struct p2p_data *p2p,
const u8 *hash, u8 hash_count)
{
u8 i;
const u8 *test = hash;
for (i = 0; i < hash_count; i++) {
if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
return 1;
test += P2PS_HASH_LEN;
}
return 0;
}
static int p2p_wfa_service_adv(struct p2p_data *p2p)
{
struct p2ps_advertisement *adv;
for (adv = p2p->p2ps_adv_list; adv; adv = adv->next) {
if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR,
os_strlen(P2PS_WILD_HASH_STR)) == 0)
return 1;
}
return 0;
}
static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p,
u32 adv_id, u16 config_methods,
const char *svc_name, u8 **ie_len, u8 **pos,
size_t *total_len, u8 *attr_len)
{
size_t svc_len;
size_t remaining;
size_t info_len;
p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id);
svc_len = os_strlen(svc_name);
info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) +
svc_len;
if (info_len + *total_len > MAX_SVC_ADV_LEN) {
p2p_dbg(p2p,
"Unsufficient buffer, failed to add advertised service info");
return -1;
}
if (svc_len > 255) {
p2p_dbg(p2p,
"Invalid service name length (%u bytes), failed to add advertised service info",
(unsigned int) svc_len);
return -1;
}
if (*ie_len) {
int ie_data_len = (*pos - *ie_len) - 1;
if (ie_data_len < 0 || ie_data_len > 255) {
p2p_dbg(p2p,
"Invalid IE length, failed to add advertised service info");
return -1;
}
remaining = 255 - ie_data_len;
} else {
/*
* Adding new P2P IE header takes 6 extra bytes:
* - 2 byte IE header (1 byte IE id and 1 byte length)
* - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below
*/
*ie_len = p2p_buf_add_ie_hdr(buf);
remaining = 255 - 4;
}
if (remaining < sizeof(u32) + sizeof(u16) + sizeof(u8)) {
/*
* Split adv_id, config_methods, and svc_name_len between two
* IEs.
*/
size_t front = remaining;
size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front;
u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)];
WPA_PUT_LE32(holder, adv_id);
WPA_PUT_BE16(&holder[sizeof(u32)], config_methods);
holder[sizeof(u32) + sizeof(u16)] = svc_len;
if (front)
wpabuf_put_data(buf, holder, front);
p2p_buf_update_ie_hdr(buf, *ie_len);
*ie_len = p2p_buf_add_ie_hdr(buf);
wpabuf_put_data(buf, &holder[front], back);
remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) -
back;
} else {
wpabuf_put_le32(buf, adv_id);
wpabuf_put_be16(buf, config_methods);
wpabuf_put_u8(buf, svc_len);
remaining -= sizeof(adv_id) + sizeof(config_methods) +
sizeof(u8);
}
if (remaining < svc_len) {
/* split svc_name between two or three IEs */
size_t front = remaining;
size_t back = svc_len - front;
if (front)
wpabuf_put_data(buf, svc_name, front);
p2p_buf_update_ie_hdr(buf, *ie_len);
*ie_len = p2p_buf_add_ie_hdr(buf);
/* In rare cases, we must split across 3 attributes */
if (back > 255 - 4) {
wpabuf_put_data(buf, &svc_name[front], 255 - 4);
back -= 255 - 4;
front += 255 - 4;
p2p_buf_update_ie_hdr(buf, *ie_len);
*ie_len = p2p_buf_add_ie_hdr(buf);
}
wpabuf_put_data(buf, &svc_name[front], back);
remaining = 255 - 4 - back;
} else {
wpabuf_put_data(buf, svc_name, svc_len);
remaining -= svc_len;
}
p2p_buf_update_ie_hdr(buf, *ie_len);
/* set *ie_len to NULL if a new IE has to be added on the next call */
if (!remaining)
*ie_len = NULL;
/* set *pos to point to the next byte to update */
*pos = wpabuf_put(buf, 0);
*total_len += info_len;
WPA_PUT_LE16(attr_len, (u16) *total_len);
return 0;
}
void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
u8 hash_count, const u8 *hash,
struct p2ps_advertisement *adv_list)
{
struct p2ps_advertisement *adv;
int p2ps_wildcard;
size_t total_len;
struct wpabuf *tmp_buf = NULL;
u8 *pos, *attr_len, *ie_len = NULL;
if (!adv_list || !hash || !hash_count)
return;
wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values",
hash, hash_count * P2PS_HASH_LEN);
p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) &&
p2p_wfa_service_adv(p2p);
/* Allocate temp buffer, allowing for overflow of 1 instance */
tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
if (!tmp_buf)
return;
/*
* Attribute data can be split into a number of IEs. Start with the
* first IE and the attribute headers here.
*/
ie_len = p2p_buf_add_ie_hdr(tmp_buf);
total_len = 0;
wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE);
attr_len = wpabuf_put(tmp_buf, sizeof(u16));
WPA_PUT_LE16(attr_len, (u16) total_len);
p2p_buf_update_ie_hdr(tmp_buf, ie_len);
pos = wpabuf_put(tmp_buf, 0);
if (p2ps_wildcard) {
/* org.wi-fi.wfds match found */
p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR,
&ie_len, &pos, &total_len, attr_len);
}
/* add advertised service info of matching services */
for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
adv = adv->next) {
const u8 *test = hash;
u8 i;
for (i = 0; i < hash_count; i++) {
/* exact name hash match */
if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 &&
p2p_buf_add_service_info(tmp_buf, p2p,
adv->id,
adv->config_methods,
adv->svc_name,
&ie_len, &pos,
&total_len,
attr_len))
break;
test += P2PS_HASH_LEN;
}
}
if (total_len)
wpabuf_put_buf(buf, tmp_buf);
wpabuf_free(tmp_buf);
}
void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac)
{
if (!buf || !mac)
return;
/* Session ID Info */
wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID);
wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
wpabuf_put_le32(buf, id);
wpabuf_put_data(buf, mac, ETH_ALEN);
wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR,
id, MAC2STR(mac));
}
void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask)
{
if (!buf || !len || !mask)
return;
/* Feature Capability */
wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY);
wpabuf_put_le16(buf, len);
wpabuf_put_data(buf, mask, len);
wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len);
}
void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
const u8 *ssid, size_t ssid_len)
{
/* P2P Group ID */
wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP);
wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
wpabuf_put_data(buf, dev_addr, ETH_ALEN);
wpabuf_put_data(buf, ssid, ssid_len);
wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
MAC2STR(dev_addr));
}
static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
const char *val)
{
size_t len;
len = val ? os_strlen(val) : 0;
if (wpabuf_tailroom(buf) < 4 + len)
return -1;
wpabuf_put_be16(buf, attr);
#ifndef CONFIG_WPS_STRICT
if (len == 0) {
/*
* Some deployed WPS implementations fail to parse zeor-length
* attributes. As a workaround, send a space character if the
* device attribute string is empty.
*/
if (wpabuf_tailroom(buf) < 3)
return -1;
wpabuf_put_be16(buf, 1);
wpabuf_put_u8(buf, ' ');
return 0;
}
#endif /* CONFIG_WPS_STRICT */
wpabuf_put_be16(buf, len);
if (val)
wpabuf_put_data(buf, val, len);
return 0;
}
int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
int all_attr)
{
u8 *len;
int i;
if (wpabuf_tailroom(buf) < 6)
return -1;
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
len = wpabuf_put(buf, 1);
wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
if (wps_build_version(buf) < 0)
return -1;
if (all_attr) {
if (wpabuf_tailroom(buf) < 5)
return -1;
wpabuf_put_be16(buf, ATTR_WPS_STATE);
wpabuf_put_be16(buf, 1);
wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
}
if (pw_id >= 0) {
if (wpabuf_tailroom(buf) < 6)
return -1;
/* Device Password ID */
wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
wpabuf_put_be16(buf, 2);
wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d",
pw_id);
wpabuf_put_be16(buf, pw_id);
}
if (all_attr) {
if (wpabuf_tailroom(buf) < 5)
return -1;
wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
wpabuf_put_be16(buf, 1);
wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 ||
p2p_add_wps_string(buf, ATTR_MANUFACTURER,
p2p->cfg->manufacturer) < 0 ||
p2p_add_wps_string(buf, ATTR_MODEL_NAME,
p2p->cfg->model_name) < 0 ||
p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
p2p->cfg->model_number) < 0 ||
p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
p2p->cfg->serial_number) < 0)
return -1;
if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN)
return -1;
wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE);
wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN);
wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN);
if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name)
< 0)
return -1;
if (wpabuf_tailroom(buf) < 6)
return -1;
wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
wpabuf_put_be16(buf, 2);
wpabuf_put_be16(buf, p2p->cfg->config_methods);
}
if (wps_build_wfa_ext(buf, 0, NULL, 0, 0) < 0)
return -1;
if (all_attr && p2p->cfg->num_sec_dev_types) {
if (wpabuf_tailroom(buf) <
4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types)
return -1;
wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST);
wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN *
p2p->cfg->num_sec_dev_types);
wpabuf_put_data(buf, p2p->cfg->sec_dev_type,
WPS_DEV_TYPE_LEN *
p2p->cfg->num_sec_dev_types);
}
/* Add the WPS vendor extensions */
for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
if (p2p->wps_vendor_ext[i] == NULL)
break;
if (wpabuf_tailroom(buf) <
4 + wpabuf_len(p2p->wps_vendor_ext[i]))
continue;
wpabuf_put_be16(buf, ATTR_VENDOR_EXT);
wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i]));
wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]);
}
p2p_buf_update_ie_hdr(buf, len);
return 0;
}
diff --git a/contrib/wpa/src/p2p/p2p_go_neg.c b/contrib/wpa/src/p2p/p2p_go_neg.c
index 113346141986..1d53d52f1f8e 100644
--- a/contrib/wpa/src/p2p/p2p_go_neg.c
+++ b/contrib/wpa/src/p2p/p2p_go_neg.c
@@ -1,1532 +1,1532 @@
/*
* Wi-Fi Direct - P2P Group Owner Negotiation
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "wps/wps_defs.h"
#include "p2p_i.h"
#include "p2p.h"
static int p2p_go_det(u8 own_intent, u8 peer_value)
{
u8 peer_intent = peer_value >> 1;
if (own_intent == peer_intent) {
if (own_intent == P2P_MAX_GO_INTENT)
return -1; /* both devices want to become GO */
/* Use tie breaker bit to determine GO */
return (peer_value & 0x01) ? 0 : 1;
}
return own_intent > peer_intent;
}
int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
struct p2p_device *dev,
const u8 *channel_list, size_t channel_list_len)
{
const u8 *pos, *end;
struct p2p_channels *ch;
u8 channels;
struct p2p_channels intersection;
ch = &dev->channels;
os_memset(ch, 0, sizeof(*ch));
pos = channel_list;
end = channel_list + channel_list_len;
if (end - pos < 3)
return -1;
os_memcpy(dev->country, pos, 3);
wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3);
if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) {
p2p_info(p2p, "Mismatching country (ours=%c%c peer's=%c%c)",
p2p->cfg->country[0], p2p->cfg->country[1],
pos[0], pos[1]);
return -1;
}
pos += 3;
while (end - pos > 2) {
struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes];
cl->reg_class = *pos++;
channels = *pos++;
if (channels > end - pos) {
p2p_info(p2p, "Invalid peer Channel List");
return -1;
}
cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ?
P2P_MAX_REG_CLASS_CHANNELS : channels;
os_memcpy(cl->channel, pos, cl->channels);
pos += channels;
ch->reg_classes++;
if (ch->reg_classes == P2P_MAX_REG_CLASSES)
break;
}
p2p_channels_intersect(own, &dev->channels, &intersection);
p2p_dbg(p2p, "Own reg_classes %d peer reg_classes %d intersection reg_classes %d",
(int) own->reg_classes,
(int) dev->channels.reg_classes,
(int) intersection.reg_classes);
if (intersection.reg_classes == 0) {
p2p_info(p2p, "No common channels found");
return -1;
}
return 0;
}
static int p2p_peer_channels(struct p2p_data *p2p, struct p2p_device *dev,
const u8 *channel_list, size_t channel_list_len)
{
return p2p_peer_channels_check(p2p, &p2p->channels, dev,
channel_list, channel_list_len);
}
u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
{
switch (wps_method) {
case WPS_PIN_DISPLAY:
return DEV_PW_REGISTRAR_SPECIFIED;
case WPS_PIN_KEYPAD:
return DEV_PW_USER_SPECIFIED;
case WPS_PBC:
return DEV_PW_PUSHBUTTON;
case WPS_NFC:
return DEV_PW_NFC_CONNECTION_HANDOVER;
case WPS_P2PS:
return DEV_PW_P2PS_DEFAULT;
default:
return DEV_PW_DEFAULT;
}
}
static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
{
switch (wps_method) {
case WPS_PIN_DISPLAY:
return "Display";
case WPS_PIN_KEYPAD:
return "Keypad";
case WPS_PBC:
return "PBC";
case WPS_NFC:
return "NFC";
case WPS_P2PS:
return "P2PS";
default:
return "??";
}
}
static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
struct p2p_device *peer)
{
struct wpabuf *buf;
u8 *len;
u8 group_capab;
size_t extra = 0;
u16 pw_id;
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_go_neg)
extra = wpabuf_len(p2p->wfd_ie_go_neg);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token);
len = p2p_buf_add_ie_hdr(buf);
group_capab = 0;
if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
}
if (p2p->cross_connect)
group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
if (p2p->cfg->p2p_intra_bss)
group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
p2p_buf_add_capability(buf, p2p->dev_capab &
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
group_capab);
p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker);
p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
p2p->cfg->channel);
if (p2p->ext_listen_interval)
p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
p2p->ext_listen_interval);
p2p_buf_add_intended_addr(buf, p2p->intended_addr);
p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels);
p2p_buf_add_device_info(buf, p2p, peer);
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
p2p->op_reg_class, p2p->op_channel);
p2p_buf_update_ie_hdr(buf, len);
p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
p2p->num_pref_freq);
/* WPS IE with Device Password ID attribute */
pw_id = p2p_wps_method_pw_id(peer->wps_method);
if (peer->oob_pw_id)
pw_id = peer->oob_pw_id;
if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request");
wpabuf_free(buf);
return NULL;
}
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_go_neg)
wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
return buf;
}
int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
{
struct wpabuf *req;
int freq;
if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
u16 config_method;
p2p_dbg(p2p, "Use PD-before-GO-Neg workaround for " MACSTR,
MAC2STR(dev->info.p2p_device_addr));
if (dev->wps_method == WPS_PIN_DISPLAY)
config_method = WPS_CONFIG_KEYPAD;
else if (dev->wps_method == WPS_PIN_KEYPAD)
config_method = WPS_CONFIG_DISPLAY;
else if (dev->wps_method == WPS_PBC)
config_method = WPS_CONFIG_PUSHBUTTON;
else if (dev->wps_method == WPS_P2PS)
config_method = WPS_CONFIG_P2PS;
else
return -1;
return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr,
NULL, config_method, 0, 0, 1);
}
freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
if (dev->oob_go_neg_freq > 0)
freq = dev->oob_go_neg_freq;
if (freq <= 0) {
p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
MACSTR " to send GO Negotiation Request",
MAC2STR(dev->info.p2p_device_addr));
return -1;
}
req = p2p_build_go_neg_req(p2p, dev);
if (req == NULL)
return -1;
p2p_dbg(p2p, "Sending GO Negotiation Request");
p2p_set_state(p2p, P2P_CONNECT);
p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
p2p->go_neg_peer = dev;
eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
dev->connect_reqs++;
if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
p2p->cfg->dev_addr, dev->info.p2p_device_addr,
wpabuf_head(req), wpabuf_len(req), 500) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
/* Use P2P find to recover and retry */
p2p_set_timeout(p2p, 0, 0);
} else
dev->go_neg_req_sent++;
wpabuf_free(req);
return 0;
}
static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
struct p2p_device *peer,
u8 dialog_token, u8 status,
u8 tie_breaker)
{
struct wpabuf *buf;
u8 *len;
u8 group_capab;
size_t extra = 0;
u16 pw_id;
p2p_dbg(p2p, "Building GO Negotiation Response");
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_go_neg)
extra = wpabuf_len(p2p->wfd_ie_go_neg);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_RESP, dialog_token);
len = p2p_buf_add_ie_hdr(buf);
p2p_buf_add_status(buf, status);
group_capab = 0;
if (peer && peer->go_state == LOCAL_GO) {
if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
group_capab |=
P2P_GROUP_CAPAB_PERSISTENT_RECONN;
}
if (p2p->cross_connect)
group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
if (p2p->cfg->p2p_intra_bss)
group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
}
p2p_buf_add_capability(buf, p2p->dev_capab &
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
group_capab);
p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
if (p2p->override_pref_op_class) {
p2p_dbg(p2p, "Override operating channel preference");
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
p2p->override_pref_op_class,
p2p->override_pref_channel);
} else if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
p2p_dbg(p2p, "Omit Operating Channel attribute");
} else {
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
p2p->op_reg_class,
p2p->op_channel);
}
p2p_buf_add_intended_addr(buf, p2p->intended_addr);
if (status || peer == NULL) {
p2p_buf_add_channel_list(buf, p2p->cfg->country,
&p2p->channels);
} else if (peer->go_state == REMOTE_GO) {
p2p_buf_add_channel_list(buf, p2p->cfg->country,
&p2p->channels);
} else {
struct p2p_channels res;
p2p_channels_intersect(&p2p->channels, &peer->channels,
&res);
p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
}
p2p_buf_add_device_info(buf, p2p, peer);
if (peer && peer->go_state == LOCAL_GO) {
p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
p2p->ssid_len);
}
p2p_buf_update_ie_hdr(buf, len);
/* WPS IE with Device Password ID attribute */
pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY);
if (peer && peer->oob_pw_id)
pw_id = peer->oob_pw_id;
if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response");
wpabuf_free(buf);
return NULL;
}
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_go_neg)
wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
return buf;
}
/**
* p2p_reselect_channel - Re-select operating channel based on peer information
* @p2p: P2P module context from p2p_init()
* @intersection: Support channel list intersection from local and peer
*
* This function is used to re-select the best channel after having received
* information from the peer to allow supported channel lists to be intersected.
* This can be used to improve initial channel selection done in
* p2p_prepare_channel() prior to the start of GO Negotiation. In addition, this
* can be used for Invitation case.
*/
void p2p_reselect_channel(struct p2p_data *p2p,
struct p2p_channels *intersection)
{
struct p2p_reg_class *cl;
int freq;
u8 op_reg_class, op_channel;
unsigned int i;
const int op_classes_5ghz[] = { 124, 125, 115, 0 };
const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
const int op_classes_vht[] = { 128, 129, 130, 0 };
const int op_classes_edmg[] = { 181, 182, 183, 0 };
if (p2p->own_freq_preference > 0 &&
p2p_freq_to_channel(p2p->own_freq_preference,
&op_reg_class, &op_channel) == 0 &&
p2p_channels_includes(intersection, op_reg_class, op_channel)) {
p2p_dbg(p2p, "Pick own channel preference (reg_class %u channel %u) from intersection",
op_reg_class, op_channel);
p2p->op_reg_class = op_reg_class;
p2p->op_channel = op_channel;
return;
}
if (p2p->best_freq_overall > 0 &&
p2p_freq_to_channel(p2p->best_freq_overall,
&op_reg_class, &op_channel) == 0 &&
p2p_channels_includes(intersection, op_reg_class, op_channel)) {
p2p_dbg(p2p, "Pick best overall channel (reg_class %u channel %u) from intersection",
op_reg_class, op_channel);
p2p->op_reg_class = op_reg_class;
p2p->op_channel = op_channel;
return;
}
/* First, try to pick the best channel from another band */
freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel);
if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 &&
!p2p_channels_includes(intersection, p2p->op_reg_class,
p2p->op_channel) &&
p2p_freq_to_channel(p2p->best_freq_5,
&op_reg_class, &op_channel) == 0 &&
p2p_channels_includes(intersection, op_reg_class, op_channel)) {
p2p_dbg(p2p, "Pick best 5 GHz channel (reg_class %u channel %u) from intersection",
op_reg_class, op_channel);
p2p->op_reg_class = op_reg_class;
p2p->op_channel = op_channel;
return;
}
if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 &&
!p2p_channels_includes(intersection, p2p->op_reg_class,
p2p->op_channel) &&
p2p_freq_to_channel(p2p->best_freq_24,
&op_reg_class, &op_channel) == 0 &&
p2p_channels_includes(intersection, op_reg_class, op_channel)) {
p2p_dbg(p2p, "Pick best 2.4 GHz channel (reg_class %u channel %u) from intersection",
op_reg_class, op_channel);
p2p->op_reg_class = op_reg_class;
p2p->op_channel = op_channel;
return;
}
/* Select channel with highest preference if the peer supports it */
for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
if (p2p_channels_includes(intersection,
p2p->cfg->pref_chan[i].op_class,
p2p->cfg->pref_chan[i].chan)) {
p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class;
p2p->op_channel = p2p->cfg->pref_chan[i].chan;
p2p_dbg(p2p, "Pick highest preferred channel (op_class %u channel %u) from intersection",
p2p->op_reg_class, p2p->op_channel);
return;
}
}
/* Try a channel where we might be able to use EDMG */
if (p2p_channel_select(intersection, op_classes_edmg,
&p2p->op_reg_class, &p2p->op_channel) == 0) {
p2p_dbg(p2p, "Pick possible EDMG channel (op_class %u channel %u) from intersection",
p2p->op_reg_class, p2p->op_channel);
return;
}
/* Try a channel where we might be able to use VHT */
if (p2p_channel_select(intersection, op_classes_vht,
&p2p->op_reg_class, &p2p->op_channel) == 0) {
p2p_dbg(p2p, "Pick possible VHT channel (op_class %u channel %u) from intersection",
p2p->op_reg_class, p2p->op_channel);
return;
}
/* Try a channel where we might be able to use HT40 */
if (p2p_channel_select(intersection, op_classes_ht40,
&p2p->op_reg_class, &p2p->op_channel) == 0) {
p2p_dbg(p2p, "Pick possible HT40 channel (op_class %u channel %u) from intersection",
p2p->op_reg_class, p2p->op_channel);
return;
}
/* Prefer a 5 GHz channel */
if (p2p_channel_select(intersection, op_classes_5ghz,
&p2p->op_reg_class, &p2p->op_channel) == 0) {
p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection",
p2p->op_reg_class, p2p->op_channel);
return;
}
/*
* Try to see if the original channel is in the intersection. If
* so, no need to change anything, as it already contains some
* randomness.
*/
if (p2p_channels_includes(intersection, p2p->op_reg_class,
p2p->op_channel)) {
p2p_dbg(p2p, "Using original operating class and channel (op_class %u channel %u) from intersection",
p2p->op_reg_class, p2p->op_channel);
return;
}
/*
* Fall back to whatever is included in the channel intersection since
* no better options seems to be available.
*/
cl = &intersection->reg_class[0];
p2p_dbg(p2p, "Pick another channel (reg_class %u channel %u) from intersection",
cl->reg_class, cl->channel[0]);
p2p->op_reg_class = cl->reg_class;
p2p->op_channel = cl->channel[0];
}
int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
u8 *status)
{
struct p2p_channels tmp, intersection;
p2p_channels_dump(p2p, "own channels", &p2p->channels);
p2p_channels_dump(p2p, "peer channels", &dev->channels);
p2p_channels_intersect(&p2p->channels, &dev->channels, &tmp);
p2p_channels_dump(p2p, "intersection", &tmp);
p2p_channels_remove_freqs(&tmp, &p2p->no_go_freq);
p2p_channels_dump(p2p, "intersection after no-GO removal", &tmp);
p2p_channels_intersect(&tmp, &p2p->cfg->channels, &intersection);
p2p_channels_dump(p2p, "intersection with local channel list",
&intersection);
if (intersection.reg_classes == 0 ||
intersection.reg_class[0].channels == 0) {
*status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
p2p_dbg(p2p, "No common channels found");
return -1;
}
if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
p2p->op_channel)) {
if (dev->flags & P2P_DEV_FORCE_FREQ) {
*status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
p2p_dbg(p2p, "Peer does not support the forced channel");
return -1;
}
p2p_dbg(p2p, "Selected operating channel (op_class %u channel %u) not acceptable to the peer",
p2p->op_reg_class, p2p->op_channel);
p2p_reselect_channel(p2p, &intersection);
} else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
!p2p->cfg->cfg_op_channel) {
p2p_dbg(p2p, "Try to optimize channel selection with peer information received; previously selected op_class %u channel %u",
p2p->op_reg_class, p2p->op_channel);
p2p_reselect_channel(p2p, &intersection);
}
if (!p2p->ssid_set) {
p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
p2p->ssid_set = 1;
}
return 0;
}
static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
struct p2p_device *dev,
struct p2p_message *msg,
unsigned freq_list[], unsigned int size)
{
u8 op_class, op_channel;
unsigned int oper_freq = 0, i, j;
int found = 0;
p2p_dbg(p2p,
"Peer didn't provide a preferred frequency list, see if any of our preferred channels are supported by peer device");
/*
* Search for a common channel in our preferred frequency list which is
* also supported by the peer device.
*/
for (i = 0; i < size && !found; i++) {
/* Make sure that the common frequency is supported by peer. */
oper_freq = freq_list[i];
if (p2p_freq_to_channel(oper_freq, &op_class,
&op_channel) < 0)
continue; /* cannot happen due to earlier check */
for (j = 0; j < msg->channel_list_len; j++) {
-
- if (op_channel != msg->channel_list[j])
+ if (!msg->channel_list ||
+ op_channel != msg->channel_list[j])
continue;
p2p->op_reg_class = op_class;
p2p->op_channel = op_channel;
os_memcpy(&p2p->channels, &p2p->cfg->channels,
sizeof(struct p2p_channels));
found = 1;
break;
}
}
if (found) {
p2p_dbg(p2p,
"Freq %d MHz is a preferred channel and is also supported by peer, use it as the operating channel",
oper_freq);
} else {
p2p_dbg(p2p,
"None of our preferred channels are supported by peer!");
}
}
static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
struct p2p_device *dev,
struct p2p_message *msg,
unsigned freq_list[], unsigned int size)
{
u8 op_class, op_channel;
unsigned int oper_freq = 0, i, j;
int found = 0;
/*
* Peer device supports a Preferred Frequency List.
* Search for a common channel in the preferred frequency lists
* of both peer and local devices.
*/
for (i = 0; i < size && !found; i++) {
for (j = 2; j < (msg->pref_freq_list_len / 2); j++) {
oper_freq = p2p_channel_to_freq(
msg->pref_freq_list[2 * j],
msg->pref_freq_list[2 * j + 1]);
if (freq_list[i] != oper_freq)
continue;
if (p2p_freq_to_channel(oper_freq, &op_class,
&op_channel) < 0)
continue; /* cannot happen */
p2p->op_reg_class = op_class;
p2p->op_channel = op_channel;
os_memcpy(&p2p->channels, &p2p->cfg->channels,
sizeof(struct p2p_channels));
found = 1;
break;
}
}
if (found) {
p2p_dbg(p2p,
"Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel",
oper_freq);
} else {
p2p_dbg(p2p, "No common preferred channels found!");
}
}
void p2p_check_pref_chan(struct p2p_data *p2p, int go,
struct p2p_device *dev, struct p2p_message *msg)
{
unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size;
unsigned int i;
u8 op_class, op_channel;
char txt[100], *pos, *end;
int res;
/*
* Use the preferred channel list from the driver only if there is no
* forced_freq, e.g., P2P_CONNECT freq=..., and no preferred operating
* channel hardcoded in the configuration file.
*/
if (!p2p->cfg->get_pref_freq_list || p2p->cfg->num_pref_chan ||
(dev->flags & P2P_DEV_FORCE_FREQ) || p2p->cfg->cfg_op_channel)
return;
/* Obtain our preferred frequency list from driver based on P2P role. */
size = P2P_MAX_PREF_CHANNELS;
if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size,
freq_list))
return;
/* Filter out frequencies that are not acceptable for P2P use */
i = 0;
while (i < size) {
if (p2p_freq_to_channel(freq_list[i], &op_class,
&op_channel) < 0 ||
(!p2p_channels_includes(&p2p->cfg->channels,
op_class, op_channel) &&
(go || !p2p_channels_includes(&p2p->cfg->cli_channels,
op_class, op_channel)))) {
p2p_dbg(p2p,
"Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)",
freq_list[i], go);
if (size - i - 1 > 0)
os_memmove(&freq_list[i], &freq_list[i + 1],
(size - i - 1) *
sizeof(unsigned int));
size--;
continue;
}
/* Preferred frequency is acceptable for P2P use */
i++;
}
pos = txt;
end = pos + sizeof(txt);
for (i = 0; i < size; i++) {
res = os_snprintf(pos, end - pos, " %u", freq_list[i]);
if (os_snprintf_error(end - pos, res))
break;
pos += res;
}
*pos = '\0';
p2p_dbg(p2p, "Local driver frequency preference (size=%u):%s",
size, txt);
/*
* Check if peer's preference of operating channel is in
* our preferred channel list.
*/
for (i = 0; i < size; i++) {
if (freq_list[i] == (unsigned int) dev->oper_freq)
break;
}
if (i != size &&
p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) == 0) {
/* Peer operating channel preference matches our preference */
p2p->op_reg_class = op_class;
p2p->op_channel = op_channel;
os_memcpy(&p2p->channels, &p2p->cfg->channels,
sizeof(struct p2p_channels));
return;
}
p2p_dbg(p2p,
"Peer operating channel preference: %d MHz is not in our preferred channel list",
dev->oper_freq);
/*
Check if peer's preferred channel list is
* _not_ included in the GO Negotiation Request or Invitation Request.
*/
if (msg->pref_freq_list_len == 0)
p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size);
else
p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size);
}
void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
struct p2p_device *dev = NULL;
struct wpabuf *resp;
struct p2p_message msg;
u8 status = P2P_SC_FAIL_INVALID_PARAMS;
int tie_breaker = 0;
int freq;
p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)",
MAC2STR(sa), rx_freq);
if (p2p_parse(data, len, &msg))
return;
if (!msg.capability) {
p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request");
#ifdef CONFIG_P2P_STRICT
goto fail;
#endif /* CONFIG_P2P_STRICT */
}
if (msg.go_intent)
tie_breaker = *msg.go_intent & 0x01;
else {
p2p_dbg(p2p, "Mandatory GO Intent attribute missing from GO Negotiation Request");
#ifdef CONFIG_P2P_STRICT
goto fail;
#endif /* CONFIG_P2P_STRICT */
}
if (!msg.config_timeout) {
p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Request");
#ifdef CONFIG_P2P_STRICT
goto fail;
#endif /* CONFIG_P2P_STRICT */
}
if (!msg.listen_channel) {
p2p_dbg(p2p, "No Listen Channel attribute received");
goto fail;
}
if (!msg.operating_channel) {
p2p_dbg(p2p, "No Operating Channel attribute received");
goto fail;
}
if (!msg.channel_list) {
p2p_dbg(p2p, "No Channel List attribute received");
goto fail;
}
if (!msg.intended_addr) {
p2p_dbg(p2p, "No Intended P2P Interface Address attribute received");
goto fail;
}
if (!msg.p2p_device_info) {
p2p_dbg(p2p, "No P2P Device Info attribute received");
goto fail;
}
if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) {
p2p_dbg(p2p, "Unexpected GO Negotiation Request SA=" MACSTR
" != dev_addr=" MACSTR,
MAC2STR(sa), MAC2STR(msg.p2p_device_addr));
goto fail;
}
dev = p2p_get_device(p2p, sa);
if (msg.status && *msg.status) {
p2p_dbg(p2p, "Unexpected Status attribute (%d) in GO Negotiation Request",
*msg.status);
if (dev && p2p->go_neg_peer == dev &&
*msg.status == P2P_SC_FAIL_REJECTED_BY_USER) {
/*
* This mechanism for using Status attribute in GO
* Negotiation Request is not compliant with the P2P
* specification, but some deployed devices use it to
* indicate rejection of GO Negotiation in a case where
* they have sent out GO Negotiation Response with
* status 1. The P2P specification explicitly disallows
* this. To avoid unnecessary interoperability issues
* and extra frames, mark the pending negotiation as
* failed and do not reply to this GO Negotiation
* Request frame.
*/
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_go_neg_failed(p2p, *msg.status);
p2p_parse_free(&msg);
return;
}
goto fail;
}
if (dev == NULL)
dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg);
else if ((dev->flags & P2P_DEV_PROBE_REQ_ONLY) ||
!(dev->flags & P2P_DEV_REPORTED))
p2p_add_dev_info(p2p, sa, dev, &msg);
else if (!dev->listen_freq && !dev->oper_freq) {
/*
* This may happen if the peer entry was added based on PD
* Request and no Probe Request/Response frame has been received
* from this peer (or that information has timed out).
*/
p2p_dbg(p2p, "Update peer " MACSTR
" based on GO Neg Req since listen/oper freq not known",
MAC2STR(dev->info.p2p_device_addr));
p2p_add_dev_info(p2p, sa, dev, &msg);
}
if (p2p->go_neg_peer && p2p->go_neg_peer == dev)
eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
p2p_dbg(p2p, "User has rejected this peer");
status = P2P_SC_FAIL_REJECTED_BY_USER;
} else if (dev == NULL ||
(dev->wps_method == WPS_NOT_READY &&
(p2p->authorized_oob_dev_pw_id == 0 ||
p2p->authorized_oob_dev_pw_id !=
msg.dev_password_id))) {
p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
MAC2STR(sa));
status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa,
msg.dev_password_id,
msg.go_intent ? (*msg.go_intent >> 1) :
0);
} else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) {
p2p_dbg(p2p, "Already in Group Formation with another peer");
status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
} else {
int go;
if (!p2p->go_neg_peer) {
p2p_dbg(p2p, "Starting GO Negotiation with previously authorized peer");
if (!(dev->flags & P2P_DEV_FORCE_FREQ)) {
p2p_dbg(p2p, "Use default channel settings");
p2p->op_reg_class = p2p->cfg->op_reg_class;
p2p->op_channel = p2p->cfg->op_channel;
os_memcpy(&p2p->channels, &p2p->cfg->channels,
sizeof(struct p2p_channels));
} else {
p2p_dbg(p2p, "Use previously configured forced channel settings");
}
}
dev->flags &= ~P2P_DEV_NOT_YET_READY;
if (!msg.go_intent) {
p2p_dbg(p2p, "No GO Intent attribute received");
goto fail;
}
if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
p2p_dbg(p2p, "Invalid GO Intent value (%u) received",
*msg.go_intent >> 1);
goto fail;
}
if (dev->go_neg_req_sent &&
os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) {
p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent");
p2p_parse_free(&msg);
return;
}
if (dev->go_neg_req_sent &&
(dev->flags & P2P_DEV_PEER_WAITING_RESPONSE)) {
p2p_dbg(p2p,
"Do not reply since peer is waiting for us to start a new GO Negotiation and GO Neg Request already sent");
p2p_parse_free(&msg);
return;
}
go = p2p_go_det(p2p->go_intent, *msg.go_intent);
if (go < 0) {
p2p_dbg(p2p, "Incompatible GO Intent");
status = P2P_SC_FAIL_BOTH_GO_INTENT_15;
goto fail;
}
if (p2p_peer_channels(p2p, dev, msg.channel_list,
msg.channel_list_len) < 0) {
p2p_dbg(p2p, "No common channels found");
status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
goto fail;
}
switch (msg.dev_password_id) {
case DEV_PW_REGISTRAR_SPECIFIED:
p2p_dbg(p2p, "PIN from peer Display");
if (dev->wps_method != WPS_PIN_KEYPAD) {
p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
p2p_wps_method_str(dev->wps_method));
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
break;
case DEV_PW_USER_SPECIFIED:
p2p_dbg(p2p, "Peer entered PIN on Keypad");
if (dev->wps_method != WPS_PIN_DISPLAY) {
p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
p2p_wps_method_str(dev->wps_method));
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
break;
case DEV_PW_PUSHBUTTON:
p2p_dbg(p2p, "Peer using pushbutton");
if (dev->wps_method != WPS_PBC) {
p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
p2p_wps_method_str(dev->wps_method));
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
break;
case DEV_PW_P2PS_DEFAULT:
p2p_dbg(p2p, "Peer using P2PS pin");
if (dev->wps_method != WPS_P2PS) {
p2p_dbg(p2p,
"We have wps_method=%s -> incompatible",
p2p_wps_method_str(dev->wps_method));
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
break;
default:
if (msg.dev_password_id &&
msg.dev_password_id == dev->oob_pw_id) {
p2p_dbg(p2p, "Peer using NFC");
if (dev->wps_method != WPS_NFC) {
p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
p2p_wps_method_str(
dev->wps_method));
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
break;
}
#ifdef CONFIG_WPS_NFC
if (p2p->authorized_oob_dev_pw_id &&
msg.dev_password_id ==
p2p->authorized_oob_dev_pw_id) {
p2p_dbg(p2p, "Using static handover with our device password from NFC Tag");
dev->wps_method = WPS_NFC;
dev->oob_pw_id = p2p->authorized_oob_dev_pw_id;
break;
}
#endif /* CONFIG_WPS_NFC */
p2p_dbg(p2p, "Unsupported Device Password ID %d",
msg.dev_password_id);
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
goto fail;
dev->go_state = go ? LOCAL_GO : REMOTE_GO;
dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
msg.operating_channel[4]);
p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
dev->oper_freq);
/*
* Use the driver preferred frequency list extension if
* supported.
*/
p2p_check_pref_chan(p2p, go, dev, &msg);
if (msg.config_timeout) {
dev->go_timeout = msg.config_timeout[0];
dev->client_timeout = msg.config_timeout[1];
}
p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa));
if (p2p->state != P2P_IDLE)
p2p_stop_find_for_freq(p2p, rx_freq);
p2p_set_state(p2p, P2P_GO_NEG);
p2p_clear_timeout(p2p);
dev->dialog_token = msg.dialog_token;
os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
p2p->go_neg_peer = dev;
eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
status = P2P_SC_SUCCESS;
}
fail:
if (dev)
dev->status = status;
resp = p2p_build_go_neg_resp(p2p, dev, msg.dialog_token, status,
!tie_breaker);
p2p_parse_free(&msg);
if (resp == NULL)
return;
p2p_dbg(p2p, "Sending GO Negotiation Response");
if (rx_freq > 0)
freq = rx_freq;
else
freq = p2p_channel_to_freq(p2p->cfg->reg_class,
p2p->cfg->channel);
if (freq < 0) {
p2p_dbg(p2p, "Unknown regulatory class/channel");
wpabuf_free(resp);
return;
}
if (status == P2P_SC_SUCCESS) {
p2p->pending_action_state = P2P_PENDING_GO_NEG_RESPONSE;
dev->flags |= P2P_DEV_WAIT_GO_NEG_CONFIRM;
if (os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) < 0) {
/*
* Peer has smaller address, so the GO Negotiation
* Response from us is expected to complete
* negotiation. Ignore a GO Negotiation Response from
* the peer if it happens to be received after this
* point due to a race condition in GO Negotiation
* Request transmission and processing.
*/
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
}
} else
p2p->pending_action_state =
P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
p2p->cfg->dev_addr,
wpabuf_head(resp), wpabuf_len(resp), 100) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
}
wpabuf_free(resp);
}
static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
struct p2p_device *peer,
u8 dialog_token, u8 status,
const u8 *resp_chan, int go)
{
struct wpabuf *buf;
u8 *len;
struct p2p_channels res;
u8 group_capab;
size_t extra = 0;
p2p_dbg(p2p, "Building GO Negotiation Confirm");
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_go_neg)
extra = wpabuf_len(p2p->wfd_ie_go_neg);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_CONF, dialog_token);
len = p2p_buf_add_ie_hdr(buf);
p2p_buf_add_status(buf, status);
group_capab = 0;
if (peer->go_state == LOCAL_GO) {
if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
group_capab |=
P2P_GROUP_CAPAB_PERSISTENT_RECONN;
}
if (p2p->cross_connect)
group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
if (p2p->cfg->p2p_intra_bss)
group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
}
p2p_buf_add_capability(buf, p2p->dev_capab &
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
group_capab);
if (go || resp_chan == NULL)
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
p2p->op_reg_class,
p2p->op_channel);
else
p2p_buf_add_operating_channel(buf, (const char *) resp_chan,
resp_chan[3], resp_chan[4]);
p2p_channels_intersect(&p2p->channels, &peer->channels, &res);
p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
if (go) {
p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, p2p->ssid,
p2p->ssid_len);
}
p2p_buf_update_ie_hdr(buf, len);
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_go_neg)
wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
return buf;
}
void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
struct p2p_device *dev;
int go = -1;
struct p2p_message msg;
u8 status = P2P_SC_SUCCESS;
int freq;
p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR
" (freq=%d)", MAC2STR(sa), rx_freq);
dev = p2p_get_device(p2p, sa);
if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
dev != p2p->go_neg_peer) {
p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
MAC2STR(sa));
return;
}
if (p2p_parse(data, len, &msg))
return;
if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) {
p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore");
p2p_parse_free(&msg);
return;
}
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
if (msg.dialog_token != dev->dialog_token) {
p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
msg.dialog_token, dev->dialog_token);
p2p_parse_free(&msg);
return;
}
if (!msg.status) {
p2p_dbg(p2p, "No Status attribute received");
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
}
if (*msg.status) {
p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status);
dev->go_neg_req_sent = 0;
if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation");
dev->flags |= P2P_DEV_NOT_YET_READY;
eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p,
NULL);
eloop_register_timeout(120, 0, p2p_go_neg_wait_timeout,
p2p, NULL);
if (p2p->state == P2P_CONNECT_LISTEN)
p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
else
p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
p2p_set_timeout(p2p, 0, 0);
} else {
p2p_dbg(p2p, "Stop GO Negotiation attempt");
p2p_go_neg_failed(p2p, *msg.status);
}
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_parse_free(&msg);
return;
}
if (!msg.capability) {
p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Response");
#ifdef CONFIG_P2P_STRICT
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
#endif /* CONFIG_P2P_STRICT */
}
if (!msg.p2p_device_info) {
p2p_dbg(p2p, "Mandatory P2P Device Info attribute missing from GO Negotiation Response");
#ifdef CONFIG_P2P_STRICT
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
#endif /* CONFIG_P2P_STRICT */
}
if (!msg.intended_addr) {
p2p_dbg(p2p, "No Intended P2P Interface Address attribute received");
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
}
if (!msg.go_intent) {
p2p_dbg(p2p, "No GO Intent attribute received");
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
}
if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) {
p2p_dbg(p2p, "Invalid GO Intent value (%u) received",
*msg.go_intent >> 1);
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
}
go = p2p_go_det(p2p->go_intent, *msg.go_intent);
if (go < 0) {
p2p_dbg(p2p, "Incompatible GO Intent");
status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
goto fail;
}
if (!go && msg.group_id) {
/* Store SSID for Provisioning step */
p2p->ssid_len = msg.group_id_len - ETH_ALEN;
os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
} else if (!go) {
p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Response");
p2p->ssid_len = 0;
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
}
if (!msg.config_timeout) {
p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Response");
#ifdef CONFIG_P2P_STRICT
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
#endif /* CONFIG_P2P_STRICT */
} else {
dev->go_timeout = msg.config_timeout[0];
dev->client_timeout = msg.config_timeout[1];
}
if (msg.wfd_subelems) {
wpabuf_free(dev->info.wfd_subelems);
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
}
if (!msg.operating_channel && !go) {
/*
* Note: P2P Client may omit Operating Channel attribute to
* indicate it does not have a preference.
*/
p2p_dbg(p2p, "No Operating Channel attribute received");
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
}
if (!msg.channel_list) {
p2p_dbg(p2p, "No Channel List attribute received");
status = P2P_SC_FAIL_INVALID_PARAMS;
goto fail;
}
if (p2p_peer_channels(p2p, dev, msg.channel_list,
msg.channel_list_len) < 0) {
p2p_dbg(p2p, "No common channels found");
status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
goto fail;
}
if (msg.operating_channel) {
dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
msg.operating_channel[4]);
p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
dev->oper_freq);
} else
dev->oper_freq = 0;
switch (msg.dev_password_id) {
case DEV_PW_REGISTRAR_SPECIFIED:
p2p_dbg(p2p, "PIN from peer Display");
if (dev->wps_method != WPS_PIN_KEYPAD) {
p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
p2p_wps_method_str(dev->wps_method));
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
break;
case DEV_PW_USER_SPECIFIED:
p2p_dbg(p2p, "Peer entered PIN on Keypad");
if (dev->wps_method != WPS_PIN_DISPLAY) {
p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
p2p_wps_method_str(dev->wps_method));
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
break;
case DEV_PW_PUSHBUTTON:
p2p_dbg(p2p, "Peer using pushbutton");
if (dev->wps_method != WPS_PBC) {
p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
p2p_wps_method_str(dev->wps_method));
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
break;
case DEV_PW_P2PS_DEFAULT:
p2p_dbg(p2p, "P2P: Peer using P2PS default pin");
if (dev->wps_method != WPS_P2PS) {
p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
p2p_wps_method_str(dev->wps_method));
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
break;
default:
if (msg.dev_password_id &&
msg.dev_password_id == dev->oob_pw_id) {
p2p_dbg(p2p, "Peer using NFC");
if (dev->wps_method != WPS_NFC) {
p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
p2p_wps_method_str(dev->wps_method));
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
break;
}
p2p_dbg(p2p, "Unsupported Device Password ID %d",
msg.dev_password_id);
status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
goto fail;
}
if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
goto fail;
/*
* Use the driver preferred frequency list extension if local device is
* GO.
*/
if (go)
p2p_check_pref_chan(p2p, go, dev, &msg);
p2p_set_state(p2p, P2P_GO_NEG);
p2p_clear_timeout(p2p);
p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa));
os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
fail:
/* Store GO Negotiation Confirmation to allow retransmission */
wpabuf_free(dev->go_neg_conf);
dev->go_neg_conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token,
status, msg.operating_channel,
go);
p2p_parse_free(&msg);
if (dev->go_neg_conf == NULL)
return;
p2p_dbg(p2p, "Sending GO Negotiation Confirm");
if (status == P2P_SC_SUCCESS) {
p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
dev->go_state = go ? LOCAL_GO : REMOTE_GO;
} else
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (rx_freq > 0)
freq = rx_freq;
else
freq = dev->listen_freq;
dev->go_neg_conf_freq = freq;
dev->go_neg_conf_sent = 0;
if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa,
wpabuf_head(dev->go_neg_conf),
wpabuf_len(dev->go_neg_conf), 50) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
p2p_go_neg_failed(p2p, -1);
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
} else
dev->go_neg_conf_sent++;
if (status != P2P_SC_SUCCESS) {
p2p_dbg(p2p, "GO Negotiation failed");
p2p_go_neg_failed(p2p, status);
}
}
void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len)
{
struct p2p_device *dev;
struct p2p_message msg;
p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR,
MAC2STR(sa));
dev = p2p_get_device(p2p, sa);
if (dev == NULL || dev->wps_method == WPS_NOT_READY ||
dev != p2p->go_neg_peer) {
p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
MAC2STR(sa));
return;
}
if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) {
p2p_dbg(p2p, "Stopped waiting for TX status on GO Negotiation Response since we already received Confirmation");
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
}
if (p2p_parse(data, len, &msg))
return;
if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
p2p_dbg(p2p, "Was not expecting GO Negotiation Confirm - ignore");
p2p_parse_free(&msg);
return;
}
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
if (msg.dialog_token != dev->dialog_token) {
p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
msg.dialog_token, dev->dialog_token);
p2p_parse_free(&msg);
return;
}
if (!msg.status) {
p2p_dbg(p2p, "No Status attribute received");
p2p_parse_free(&msg);
return;
}
if (*msg.status) {
p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status);
p2p_go_neg_failed(p2p, *msg.status);
p2p_parse_free(&msg);
return;
}
if (dev->go_state == REMOTE_GO && msg.group_id) {
/* Store SSID for Provisioning step */
p2p->ssid_len = msg.group_id_len - ETH_ALEN;
os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len);
} else if (dev->go_state == REMOTE_GO) {
p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation");
p2p->ssid_len = 0;
p2p_go_neg_failed(p2p, P2P_SC_FAIL_INVALID_PARAMS);
p2p_parse_free(&msg);
return;
}
if (!msg.operating_channel) {
p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation");
#ifdef CONFIG_P2P_STRICT
p2p_parse_free(&msg);
return;
#endif /* CONFIG_P2P_STRICT */
} else if (dev->go_state == REMOTE_GO) {
int oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
msg.operating_channel[4]);
if (oper_freq != dev->oper_freq) {
p2p_dbg(p2p, "Updated peer (GO) operating channel preference from %d MHz to %d MHz",
dev->oper_freq, oper_freq);
dev->oper_freq = oper_freq;
}
}
if (!msg.channel_list) {
p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation");
#ifdef CONFIG_P2P_STRICT
p2p_parse_free(&msg);
return;
#endif /* CONFIG_P2P_STRICT */
}
p2p_parse_free(&msg);
if (dev->go_state == UNKNOWN_GO) {
/*
* This should not happen since GO negotiation has already
* been completed.
*/
p2p_dbg(p2p, "Unexpected GO Neg state - do not know which end becomes GO");
return;
}
/*
* The peer could have missed our ctrl::ack frame for GO Negotiation
* Confirm and continue retransmitting the frame. To reduce the
* likelihood of the peer not getting successful TX status for the
* GO Negotiation Confirm frame, wait a short time here before starting
* the group so that we will remain on the current channel to
* acknowledge any possible retransmission from the peer.
*/
p2p_dbg(p2p, "20 ms wait on current channel before starting group");
os_sleep(0, 20000);
p2p_go_complete(p2p, dev);
}
diff --git a/contrib/wpa/src/p2p/p2p_i.h b/contrib/wpa/src/p2p/p2p_i.h
index 8220e85506a3..aa147c614fc5 100644
--- a/contrib/wpa/src/p2p/p2p_i.h
+++ b/contrib/wpa/src/p2p/p2p_i.h
@@ -1,900 +1,901 @@
/*
* P2P - Internal definitions for P2P module
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef P2P_I_H
#define P2P_I_H
#include "utils/list.h"
#include "p2p.h"
#define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1
/*
* A threshold (in seconds) to prefer a direct Probe Response frame from a P2P
* Device over the P2P Client Info received from a GO.
*/
#define P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD 1
enum p2p_role_indication;
/*
* To force Service Instances to fit within a single P2P Tag, MAX_SVC_ADV_LEN
* must equal 248 or less. Must have a minimum size of 19.
*/
#define MAX_SVC_ADV_LEN 600
#define MAX_SVC_ADV_IE_LEN (9 + MAX_SVC_ADV_LEN + (5 * (MAX_SVC_ADV_LEN / 240)))
enum p2p_go_state {
UNKNOWN_GO,
LOCAL_GO,
REMOTE_GO
};
/**
* struct p2p_device - P2P Device data (internal to P2P module)
*/
struct p2p_device {
struct dl_list list;
struct os_reltime last_seen;
int listen_freq;
int oob_go_neg_freq;
enum p2p_wps_method wps_method;
u16 oob_pw_id;
struct p2p_peer_info info;
/*
* If the peer was discovered based on an interface address (e.g., GO
* from Beacon/Probe Response), the interface address is stored here.
* p2p_device_addr must still be set in such a case to the unique
* identifier for the P2P Device.
*
* This field is also used during P2PS PD to store the intended GO
* address of the peer.
*/
u8 interface_addr[ETH_ALEN];
/*
* P2P Device Address of the GO in whose group this P2P Device is a
* client.
*/
u8 member_in_go_dev[ETH_ALEN];
/*
* P2P Interface Address of the GO in whose group this P2P Device is a
* client.
*/
u8 member_in_go_iface[ETH_ALEN];
int go_neg_req_sent;
enum p2p_go_state go_state;
u8 dialog_token;
u8 tie_breaker;
u8 intended_addr[ETH_ALEN];
char country[3];
struct p2p_channels channels;
int oper_freq;
u8 oper_ssid[SSID_MAX_LEN];
size_t oper_ssid_len;
/**
* req_config_methods - Pending provision discovery methods
*/
u16 req_config_methods;
/**
* wps_prov_info - Stored provisioning WPS config method
*
* This is used to store pending WPS config method between Provisioning
* Discovery and connection to a running group.
*/
u16 wps_prov_info;
#define P2P_DEV_PROBE_REQ_ONLY BIT(0)
#define P2P_DEV_REPORTED BIT(1)
#define P2P_DEV_NOT_YET_READY BIT(2)
#define P2P_DEV_PD_PEER_DISPLAY BIT(5)
#define P2P_DEV_PD_PEER_KEYPAD BIT(6)
#define P2P_DEV_USER_REJECTED BIT(7)
#define P2P_DEV_PEER_WAITING_RESPONSE BIT(8)
#define P2P_DEV_PREFER_PERSISTENT_GROUP BIT(9)
#define P2P_DEV_WAIT_GO_NEG_RESPONSE BIT(10)
#define P2P_DEV_WAIT_GO_NEG_CONFIRM BIT(11)
#define P2P_DEV_GROUP_CLIENT_ONLY BIT(12)
#define P2P_DEV_FORCE_FREQ BIT(13)
#define P2P_DEV_PD_FOR_JOIN BIT(14)
#define P2P_DEV_REPORTED_ONCE BIT(15)
#define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16)
#define P2P_DEV_PD_BEFORE_GO_NEG BIT(17)
#define P2P_DEV_NO_PREF_CHAN BIT(18)
#define P2P_DEV_WAIT_INV_REQ_ACK BIT(19)
#define P2P_DEV_P2PS_REPORTED BIT(20)
#define P2P_DEV_PD_PEER_P2PS BIT(21)
#define P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT BIT(22)
unsigned int flags;
int status; /* enum p2p_status_code */
unsigned int wait_count;
unsigned int connect_reqs;
unsigned int invitation_reqs;
unsigned int sd_reqs;
u16 ext_listen_period;
u16 ext_listen_interval;
u8 go_timeout;
u8 client_timeout;
/**
* go_neg_conf_sent - Number of GO Negotiation Confirmation retries
*/
u8 go_neg_conf_sent;
/**
* freq - Frquency on which the GO Negotiation Confirmation is sent
*/
int go_neg_conf_freq;
/**
* go_neg_conf - GO Negotiation Confirmation frame
*/
struct wpabuf *go_neg_conf;
int sd_pending_bcast_queries;
};
struct p2p_sd_query {
struct p2p_sd_query *next;
u8 peer[ETH_ALEN];
int for_all_peers;
int wsd; /* Wi-Fi Display Service Discovery Request */
struct wpabuf *tlvs;
};
/**
* struct p2p_data - P2P module data (internal to P2P module)
*/
struct p2p_data {
/**
* cfg - P2P module configuration
*
* This is included in the same memory allocation with the
* struct p2p_data and as such, must not be freed separately.
*/
struct p2p_config *cfg;
/**
* state - The current P2P state
*/
enum p2p_state {
/**
* P2P_IDLE - Idle
*/
P2P_IDLE,
/**
* P2P_SEARCH - Search (Device Discovery)
*/
P2P_SEARCH,
/**
* P2P_CONNECT - Trying to start GO Negotiation
*/
P2P_CONNECT,
/**
* P2P_CONNECT_LISTEN - Listen during GO Negotiation start
*/
P2P_CONNECT_LISTEN,
/**
* P2P_GO_NEG - In GO Negotiation
*/
P2P_GO_NEG,
/**
* P2P_LISTEN_ONLY - Listen only
*/
P2P_LISTEN_ONLY,
/**
* P2P_WAIT_PEER_CONNECT - Waiting peer in List for GO Neg
*/
P2P_WAIT_PEER_CONNECT,
/**
* P2P_WAIT_PEER_IDLE - Waiting peer idle for GO Neg
*/
P2P_WAIT_PEER_IDLE,
/**
* P2P_SD_DURING_FIND - Service Discovery during find
*/
P2P_SD_DURING_FIND,
/**
* P2P_PROVISIONING - Provisioning (during group formation)
*/
P2P_PROVISIONING,
/**
* P2P_PD_DURING_FIND - Provision Discovery during find
*/
P2P_PD_DURING_FIND,
/**
* P2P_INVITE - Trying to start Invite
*/
P2P_INVITE,
/**
* P2P_INVITE_LISTEN - Listen during Invite
*/
P2P_INVITE_LISTEN,
} state;
/**
* min_disc_int - minDiscoverableInterval
*/
int min_disc_int;
/**
* max_disc_int - maxDiscoverableInterval
*/
int max_disc_int;
/**
* max_disc_tu - Maximum number of TUs for discoverable interval
*/
int max_disc_tu;
/**
* devices - List of known P2P Device peers
*/
struct dl_list devices;
/**
* go_neg_peer - Pointer to GO Negotiation peer
*/
struct p2p_device *go_neg_peer;
/**
* invite_peer - Pointer to Invite peer
*/
struct p2p_device *invite_peer;
/**
* last_p2p_find_oper - Pointer to last pre-find operation peer
*/
struct p2p_device *last_p2p_find_oper;
const u8 *invite_go_dev_addr;
u8 invite_go_dev_addr_buf[ETH_ALEN];
int invite_dev_pw_id;
unsigned int retry_invite_req:1;
unsigned int retry_invite_req_sent:1;
/**
* sd_peer - Pointer to Service Discovery peer
*/
struct p2p_device *sd_peer;
/**
* sd_query - Pointer to Service Discovery query
*/
struct p2p_sd_query *sd_query;
/**
* num_p2p_sd_queries - Total number of broadcast SD queries present in
* the list
*/
int num_p2p_sd_queries;
/**
* sd_query_no_ack - The first peer (Dev Addr) that did not ACK SD Query
*
* This is used to track the first peer that did not ACK an SD Query
* within a single P2P Search iteration. All zeros address means no such
* peer was yet seen. This information is used to allow a new Listen and
* Search phases to be once every pending SD Query has been sent once to
* each peer instead of looping all pending attempts continuously until
* running out of retry maximums.
*/
u8 sd_query_no_ack[ETH_ALEN];
/* GO Negotiation data */
/**
* intended_addr - Local Intended P2P Interface Address
*
* This address is used during group owner negotiation as the Intended
* P2P Interface Address and the group interface will be created with
* address as the local address in case of successfully completed
* negotiation.
*/
u8 intended_addr[ETH_ALEN];
/**
* go_intent - Local GO Intent to be used during GO Negotiation
*/
u8 go_intent;
/**
* next_tie_breaker - Next tie-breaker value to use in GO Negotiation
*/
u8 next_tie_breaker;
/**
* ssid - Selected SSID for GO Negotiation (if local end will be GO)
*/
u8 ssid[SSID_MAX_LEN];
/**
* ssid_len - ssid length in octets
*/
size_t ssid_len;
/**
* ssid_set - Whether SSID is already set for GO Negotiation
*/
int ssid_set;
/**
* Regulatory class for own operational channel
*/
u8 op_reg_class;
/**
* op_channel - Own operational channel
*/
u8 op_channel;
/**
* channels - Own supported regulatory classes and channels
*
* List of supposerted channels per regulatory class. The regulatory
* classes are defined in IEEE Std 802.11-2007 Annex J and the
* numbering of the clases depends on the configured country code.
*/
struct p2p_channels channels;
struct wpa_freq_range_list no_go_freq;
enum p2p_pending_action_state {
P2P_NO_PENDING_ACTION,
P2P_PENDING_GO_NEG_REQUEST,
P2P_PENDING_GO_NEG_RESPONSE,
P2P_PENDING_GO_NEG_RESPONSE_FAILURE,
P2P_PENDING_GO_NEG_CONFIRM,
P2P_PENDING_SD,
P2P_PENDING_PD,
P2P_PENDING_PD_RESPONSE,
P2P_PENDING_INVITATION_REQUEST,
P2P_PENDING_INVITATION_RESPONSE,
P2P_PENDING_DEV_DISC_REQUEST,
P2P_PENDING_DEV_DISC_RESPONSE,
P2P_PENDING_GO_DISC_REQ
} pending_action_state;
unsigned int pending_listen_freq;
unsigned int pending_listen_sec;
unsigned int pending_listen_usec;
u8 dev_capab;
int in_listen;
int drv_in_listen;
/**
* sd_queries - Pending service discovery queries
*/
struct p2p_sd_query *sd_queries;
/**
* srv_update_indic - Service Update Indicator for local services
*/
u16 srv_update_indic;
struct wpabuf *sd_resp; /* Fragmented SD response */
u8 sd_resp_addr[ETH_ALEN];
u8 sd_resp_dialog_token;
size_t sd_resp_pos; /* Offset in sd_resp */
u8 sd_frag_id;
struct wpabuf *sd_rx_resp; /* Reassembled SD response */
u16 sd_rx_update_indic;
/* P2P Invitation data */
enum p2p_invite_role inv_role;
u8 inv_bssid[ETH_ALEN];
int inv_bssid_set;
u8 inv_ssid[SSID_MAX_LEN];
size_t inv_ssid_len;
u8 inv_sa[ETH_ALEN];
u8 inv_group_bssid[ETH_ALEN];
u8 *inv_group_bssid_ptr;
u8 inv_go_dev_addr[ETH_ALEN];
u8 inv_status;
int inv_op_freq;
int inv_persistent;
enum p2p_discovery_type find_type;
int find_specified_freq;
unsigned int last_p2p_find_timeout;
u8 last_prog_scan_class;
u8 last_prog_scan_chan;
unsigned int find_pending_full:1;
int p2p_scan_running;
enum p2p_after_scan {
P2P_AFTER_SCAN_NOTHING,
P2P_AFTER_SCAN_LISTEN,
P2P_AFTER_SCAN_CONNECT
} start_after_scan;
u8 after_scan_peer[ETH_ALEN];
unsigned int send_action_in_progress:1;
/* Requested device types for find/search */
unsigned int num_req_dev_types;
u8 *req_dev_types;
u8 *find_dev_id;
u8 find_dev_id_buf[ETH_ALEN];
struct os_reltime find_start; /* time of last p2p_find start */
struct p2p_group **groups;
size_t num_groups;
struct p2p_device *pending_client_disc_go;
u8 pending_client_disc_addr[ETH_ALEN];
u8 pending_dev_disc_dialog_token;
u8 pending_dev_disc_addr[ETH_ALEN];
int pending_dev_disc_freq;
unsigned int pending_client_disc_freq;
int ext_listen_only;
unsigned int ext_listen_period;
unsigned int ext_listen_interval;
unsigned int ext_listen_interval_sec;
unsigned int ext_listen_interval_usec;
u8 peer_filter[ETH_ALEN];
int cross_connect;
int best_freq_24;
int best_freq_5;
int best_freq_overall;
int own_freq_preference;
/**
* wps_vendor_ext - WPS Vendor Extensions to add
*/
struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
/*
* user_initiated_pd - Whether a PD request is user initiated or not.
*/
u8 user_initiated_pd;
/*
* Keep track of which peer a given PD request was sent to.
* Used to raise a timeout alert in case there is no response.
*/
u8 pending_pd_devaddr[ETH_ALEN];
/*
* Retry counter for provision discovery requests when issued
* in IDLE state.
*/
int pd_retries;
/**
* pd_force_freq - Forced frequency for PD retries or 0 to auto-select
*
* This is is used during PD retries for join-a-group case to use the
* correct operating frequency determined from a BSS entry for the GO.
*/
int pd_force_freq;
u8 go_timeout;
u8 client_timeout;
/* Extra delay in milliseconds between search iterations */
unsigned int search_delay;
int in_search_delay;
u8 pending_reg_class;
u8 pending_channel;
u8 pending_channel_forced;
/* ASP Support */
struct p2ps_advertisement *p2ps_adv_list;
struct p2ps_provision *p2ps_prov;
u8 wild_card_hash[P2PS_HASH_LEN];
u8 p2ps_seek;
u8 p2ps_seek_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN];
u8 p2ps_seek_count;
#ifdef CONFIG_WIFI_DISPLAY
struct wpabuf *wfd_ie_beacon;
struct wpabuf *wfd_ie_probe_req;
struct wpabuf *wfd_ie_probe_resp;
struct wpabuf *wfd_ie_assoc_req;
struct wpabuf *wfd_ie_invitation;
struct wpabuf *wfd_ie_prov_disc_req;
struct wpabuf *wfd_ie_prov_disc_resp;
struct wpabuf *wfd_ie_go_neg;
struct wpabuf *wfd_dev_info;
struct wpabuf *wfd_assoc_bssid;
struct wpabuf *wfd_coupled_sink_info;
struct wpabuf *wfd_r2_dev_info;
#endif /* CONFIG_WIFI_DISPLAY */
u16 authorized_oob_dev_pw_id;
struct wpabuf **vendor_elem;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
unsigned int num_pref_freq;
/* Override option for preferred operating channel in GO Negotiation */
u8 override_pref_op_class;
u8 override_pref_channel;
bool p2p_6ghz_capable;
bool include_6ghz;
bool allow_6ghz;
};
/**
* struct p2p_message - Parsed P2P message (or P2P IE)
*/
struct p2p_message {
struct wpabuf *p2p_attributes;
struct wpabuf *wps_attributes;
struct wpabuf *wfd_subelems;
u8 dialog_token;
const u8 *capability;
const u8 *go_intent;
const u8 *status;
const u8 *listen_channel;
const u8 *operating_channel;
const u8 *channel_list;
u8 channel_list_len;
const u8 *config_timeout;
const u8 *intended_addr;
const u8 *group_bssid;
const u8 *invitation_flags;
const u8 *group_info;
size_t group_info_len;
const u8 *group_id;
size_t group_id_len;
const u8 *device_id;
const u8 *manageability;
const u8 *noa;
size_t noa_len;
const u8 *ext_listen_timing;
const u8 *minor_reason_code;
const u8 *oob_go_neg_channel;
/* P2P Device Info */
const u8 *p2p_device_info;
size_t p2p_device_info_len;
const u8 *p2p_device_addr;
const u8 *pri_dev_type;
u8 num_sec_dev_types;
char device_name[WPS_DEV_NAME_MAX_LEN + 1];
u16 config_methods;
/* WPS IE */
u16 dev_password_id;
int dev_password_id_present;
u16 wps_config_methods;
const u8 *wps_pri_dev_type;
const u8 *wps_sec_dev_type_list;
size_t wps_sec_dev_type_list_len;
const u8 *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
size_t wps_vendor_ext_len[P2P_MAX_WPS_VENDOR_EXT];
const u8 *manufacturer;
size_t manufacturer_len;
const u8 *model_name;
size_t model_name_len;
const u8 *model_number;
size_t model_number_len;
const u8 *serial_number;
size_t serial_number_len;
const u8 *oob_dev_password;
size_t oob_dev_password_len;
/* DS Parameter Set IE */
const u8 *ds_params;
/* SSID IE */
const u8 *ssid;
/* P2PS */
u8 service_hash_count;
const u8 *service_hash;
const u8 *session_info;
size_t session_info_len;
const u8 *conn_cap;
const u8 *adv_id;
const u8 *adv_mac;
const u8 *adv_service_instance;
size_t adv_service_instance_len;
const u8 *session_id;
const u8 *session_mac;
const u8 *feature_cap;
size_t feature_cap_len;
const u8 *persistent_dev;
const u8 *persistent_ssid;
size_t persistent_ssid_len;
const u8 *pref_freq_list;
size_t pref_freq_list_len;
};
#define P2P_MAX_GROUP_ENTRIES 50
struct p2p_group_info {
unsigned int num_clients;
struct p2p_client_info {
const u8 *p2p_device_addr;
const u8 *p2p_interface_addr;
u8 dev_capab;
u16 config_methods;
const u8 *pri_dev_type;
u8 num_sec_dev_types;
const u8 *sec_dev_types;
const char *dev_name;
size_t dev_name_len;
} client[P2P_MAX_GROUP_ENTRIES];
};
/* p2p_utils.c */
int p2p_random(char *buf, size_t len);
int p2p_channel_to_freq(int op_class, int channel);
int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel);
void p2p_channels_intersect(const struct p2p_channels *a,
const struct p2p_channels *b,
struct p2p_channels *res);
void p2p_channels_union_inplace(struct p2p_channels *res,
const struct p2p_channels *b);
void p2p_channels_union(const struct p2p_channels *a,
const struct p2p_channels *b,
struct p2p_channels *res);
void p2p_channels_remove_freqs(struct p2p_channels *chan,
const struct wpa_freq_range_list *list);
int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
u8 channel);
void p2p_channels_dump(struct p2p_data *p2p, const char *title,
const struct p2p_channels *chan);
int p2p_channel_select(struct p2p_channels *chans, const int *classes,
u8 *op_class, u8 *op_channel);
int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class,
u8 *op_channel,
struct wpa_freq_range_list *avoid_list,
struct wpa_freq_range_list *disallow_list);
void p2p_copy_channels(struct p2p_channels *dst, const struct p2p_channels *src,
bool allow_6ghz);
/* p2p_parse.c */
void p2p_copy_filter_devname(char *dst, size_t dst_len,
const void *src, size_t src_len);
int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
size_t p2p_len, struct p2p_message *msg);
void p2p_parse_free(struct p2p_message *msg);
int p2p_attr_text(struct wpabuf *data, char *buf, char *end);
int p2p_group_info_parse(const u8 *gi, size_t gi_len,
struct p2p_group_info *info);
/* p2p_build.c */
struct p2p_noa_desc {
u8 count_type;
u32 duration;
u32 interval;
u32 start_time;
};
/* p2p_group.c */
const u8 * p2p_group_get_interface_addr(struct p2p_group *group);
u8 p2p_group_presence_req(struct p2p_group *group,
const u8 *client_interface_addr,
const u8 *noa, size_t noa_len);
int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
size_t group_id_len);
void p2p_group_update_ies(struct p2p_group *group);
void p2p_group_force_beacon_update_ies(struct p2p_group *group);
struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g);
void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
int max_clients);
void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf);
int p2p_group_get_freq(struct p2p_group *group);
void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
u8 dialog_token);
u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf);
void p2p_buf_add_status(struct wpabuf *buf, u8 status);
void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
struct p2p_device *peer);
void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr);
void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len);
void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab);
void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent);
void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
u8 reg_class, u8 channel);
void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
u8 reg_class, u8 channel);
void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
struct p2p_channels *chan);
void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
u8 client_timeout);
void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr);
void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid);
void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
const u8 *ssid, size_t ssid_len);
void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags);
void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2);
void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
u16 interval);
void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
u8 oper_class, u8 channel,
enum p2p_role_indication role);
void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p);
void p2p_buf_add_session_info(struct wpabuf *buf, const char *info);
void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap);
void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac);
void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
u8 count, const u8 *hash,
struct p2ps_advertisement *adv_list);
void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac);
void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len,
const u8 *mask);
void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
const u8 *ssid, size_t ssid_len);
int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
int all_attr);
void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
- const u32 *preferred_freq_list, u32 size);
+ const unsigned int *preferred_freq_list,
+ unsigned int size);
/* p2p_sd.c */
struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
struct p2p_device *dev);
void p2p_free_sd_queries(struct p2p_data *p2p);
void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev);
/* p2p_go_neg.c */
int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own,
struct p2p_device *dev,
const u8 *channel_list, size_t channel_list_len);
void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len);
int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
void p2p_reselect_channel(struct p2p_data *p2p,
struct p2p_channels *intersection);
void p2p_check_pref_chan(struct p2p_data *p2p, int go,
struct p2p_device *dev, struct p2p_message *msg);
/* p2p_pd.c */
void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len);
int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
int join, int force_freq);
void p2p_reset_pending_pd(struct p2p_data *p2p);
void p2ps_prov_free(struct p2p_data *p2p);
/* p2p_invitation.c */
void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len);
int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
const u8 *go_dev_addr, int dev_pw_id);
void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
/* p2p_dev_disc.c */
void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success);
int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev);
void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success);
void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len);
void p2p_go_disc_req_cb(struct p2p_data *p2p, int success);
void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa,
const u8 *data, size_t len, int rx_freq);
/* p2p.c */
void p2p_set_state(struct p2p_data *p2p, int new_state);
void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec,
unsigned int usec);
void p2p_clear_timeout(struct p2p_data *p2p);
void p2p_continue_find(struct p2p_data *p2p);
struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
const u8 *addr,
struct p2p_message *msg);
void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
struct p2p_device *dev, struct p2p_message *msg);
int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
struct os_reltime *rx_time, int level, const u8 *ies,
size_t ies_len, int scan_res);
struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
const u8 *addr);
void p2p_go_neg_failed(struct p2p_data *p2p, int status);
void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer);
int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps);
int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
size_t num_req_dev_type);
struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
const u8 *query_hash,
u8 query_count);
void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len);
int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid, const u8 *buf,
size_t len, unsigned int wait_time);
void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq);
int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
unsigned int force_freq, unsigned int pref_freq,
int go);
void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx);
int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
u8 *status);
void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
PRINTF_FORMAT(2, 3);
void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
PRINTF_FORMAT(2, 3);
void p2p_err(struct p2p_data *p2p, const char *fmt, ...)
PRINTF_FORMAT(2, 3);
#endif /* P2P_I_H */
diff --git a/contrib/wpa/src/p2p/p2p_pd.c b/contrib/wpa/src/p2p/p2p_pd.c
index 05fd593494ef..338b47e4e36b 100644
--- a/contrib/wpa/src/p2p/p2p_pd.c
+++ b/contrib/wpa/src/p2p/p2p_pd.c
@@ -1,1767 +1,1767 @@
/*
* Wi-Fi Direct - P2P provision discovery
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "wps/wps_defs.h"
#include "p2p_i.h"
#include "p2p.h"
/*
* Number of retries to attempt for provision discovery requests
* in case the peer is not listening.
*/
#define MAX_PROV_DISC_REQ_RETRIES 120
static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
u16 config_methods)
{
u8 *len;
wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
len = wpabuf_put(buf, 1);
wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
/* Config Methods */
wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
wpabuf_put_be16(buf, 2);
wpabuf_put_be16(buf, config_methods);
p2p_buf_update_ie_hdr(buf, len);
}
static void p2ps_add_new_group_info(struct p2p_data *p2p,
struct p2p_device *dev,
struct wpabuf *buf)
{
int found;
u8 intended_addr[ETH_ALEN];
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
int group_iface;
unsigned int force_freq;
if (!p2p->cfg->get_go_info)
return;
found = p2p->cfg->get_go_info(
p2p->cfg->cb_ctx, intended_addr, ssid,
&ssid_len, &group_iface, &force_freq);
if (found) {
if (force_freq > 0) {
p2p->p2ps_prov->force_freq = force_freq;
p2p->p2ps_prov->pref_freq = 0;
if (dev)
p2p_prepare_channel(p2p, dev, force_freq, 0, 0);
}
p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
ssid, ssid_len);
if (group_iface)
p2p_buf_add_intended_addr(buf, p2p->intended_addr);
else
p2p_buf_add_intended_addr(buf, intended_addr);
} else {
if (!p2p->ssid_set) {
p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
p2p->ssid_set = 1;
}
/* Add pre-composed P2P Group ID */
p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
p2p->ssid, p2p->ssid_len);
if (group_iface)
p2p_buf_add_intended_addr(
buf, p2p->intended_addr);
else
p2p_buf_add_intended_addr(
buf, p2p->cfg->dev_addr);
}
}
static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev,
struct wpabuf *buf, u16 config_methods)
{
struct p2ps_provision *prov = p2p->p2ps_prov;
struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 };
int shared_group = 0;
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
u8 go_dev_addr[ETH_ALEN];
u8 intended_addr[ETH_ALEN];
int follow_on_req_fail = prov->status >= 0 &&
prov->status != P2P_SC_SUCCESS_DEFERRED;
/* If we might be explicite group owner, add GO details */
if (!follow_on_req_fail &&
(prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
p2ps_add_new_group_info(p2p, dev, buf);
if (prov->status >= 0)
p2p_buf_add_status(buf, (u8) prov->status);
else
prov->method = config_methods;
if (!follow_on_req_fail) {
if (p2p->cfg->get_persistent_group) {
shared_group = p2p->cfg->get_persistent_group(
p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
NULL, 0, go_dev_addr, ssid, &ssid_len,
intended_addr);
}
if (shared_group ||
(prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW)))
p2p_buf_add_channel_list(buf, p2p->cfg->country,
&p2p->channels);
if ((shared_group && !is_zero_ether_addr(intended_addr)) ||
(prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
p2p->op_reg_class,
p2p->op_channel);
}
if (prov->status < 0 && prov->info[0])
p2p_buf_add_session_info(buf, prov->info);
if (!follow_on_req_fail)
p2p_buf_add_connection_capability(buf, prov->conncap);
p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
if (!follow_on_req_fail) {
if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
prov->conncap ==
(P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
prov->conncap ==
(P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
/* Add Config Timeout */
p2p_buf_add_config_timeout(buf, p2p->go_timeout,
p2p->client_timeout);
}
p2p_buf_add_listen_channel(buf, p2p->cfg->country,
p2p->cfg->reg_class,
p2p->cfg->channel);
}
p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap);
if (shared_group) {
p2p_buf_add_persistent_group_info(buf, go_dev_addr,
ssid, ssid_len);
/* Add intended interface address if it is not added yet */
if ((prov->conncap == P2PS_SETUP_NONE ||
prov->conncap == P2PS_SETUP_CLIENT) &&
!is_zero_ether_addr(intended_addr))
p2p_buf_add_intended_addr(buf, intended_addr);
}
}
static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
struct p2p_device *dev,
int join)
{
struct wpabuf *buf;
u8 *len;
size_t extra = 0;
u8 dialog_token = dev->dialog_token;
u16 config_methods = dev->req_config_methods;
struct p2p_device *go = join ? dev : NULL;
u8 group_capab;
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_prov_disc_req)
extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
if (p2p->p2ps_prov)
extra += os_strlen(p2p->p2ps_prov->info) + 1 +
sizeof(struct p2ps_provision);
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
len = p2p_buf_add_ie_hdr(buf);
group_capab = 0;
if (p2p->p2ps_prov) {
group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
if (p2p->cross_connect)
group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
if (p2p->cfg->p2p_intra_bss)
group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
}
p2p_buf_add_capability(buf, p2p->dev_capab &
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
group_capab);
p2p_buf_add_device_info(buf, p2p, NULL);
if (p2p->p2ps_prov) {
p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods);
} else if (go) {
p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
go->oper_ssid, go->oper_ssid_len);
}
p2p_buf_update_ie_hdr(buf, len);
/* WPS IE with Config Methods attribute */
p2p_build_wps_ie_config_methods(buf, config_methods);
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_prov_disc_req)
wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
return buf;
}
static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
struct p2p_device *dev,
u8 dialog_token,
enum p2p_status_code status,
u16 config_methods,
u32 adv_id,
const u8 *group_id,
size_t group_id_len,
const u8 *persist_ssid,
size_t persist_ssid_len,
const u8 *fcap,
u16 fcap_len)
{
struct wpabuf *buf;
size_t extra = 0;
int persist = 0;
#ifdef CONFIG_WIFI_DISPLAY
struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
if (wfd_ie && group_id) {
size_t i;
for (i = 0; i < p2p->num_groups; i++) {
struct p2p_group *g = p2p->groups[i];
struct wpabuf *ie;
if (!p2p_group_is_group_id_match(g, group_id,
group_id_len))
continue;
ie = p2p_group_get_wfd_ie(g);
if (ie) {
wfd_ie = ie;
break;
}
}
}
if (wfd_ie)
extra = wpabuf_len(wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
/* Add P2P IE for P2PS */
if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
u8 *len = p2p_buf_add_ie_hdr(buf);
struct p2ps_provision *prov = p2p->p2ps_prov;
u8 group_capab;
u8 conncap = 0;
if (status == P2P_SC_SUCCESS ||
status == P2P_SC_SUCCESS_DEFERRED)
conncap = prov->conncap;
if (!status && prov->status != -1)
status = prov->status;
p2p_buf_add_status(buf, status);
group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP |
P2P_GROUP_CAPAB_PERSISTENT_RECONN;
if (p2p->cross_connect)
group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
if (p2p->cfg->p2p_intra_bss)
group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
p2p_buf_add_capability(buf, p2p->dev_capab &
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
group_capab);
p2p_buf_add_device_info(buf, p2p, NULL);
if (persist_ssid && p2p->cfg->get_persistent_group && dev &&
(status == P2P_SC_SUCCESS ||
status == P2P_SC_SUCCESS_DEFERRED)) {
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
u8 go_dev_addr[ETH_ALEN];
u8 intended_addr[ETH_ALEN];
persist = p2p->cfg->get_persistent_group(
p2p->cfg->cb_ctx,
dev->info.p2p_device_addr,
persist_ssid, persist_ssid_len, go_dev_addr,
ssid, &ssid_len, intended_addr);
if (persist) {
p2p_buf_add_persistent_group_info(
buf, go_dev_addr, ssid, ssid_len);
if (!is_zero_ether_addr(intended_addr))
p2p_buf_add_intended_addr(
buf, intended_addr);
}
}
if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER))
p2ps_add_new_group_info(p2p, dev, buf);
/* Add Operating Channel if conncap indicates GO */
if (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) {
if (p2p->op_reg_class && p2p->op_channel)
p2p_buf_add_operating_channel(
buf, p2p->cfg->country,
p2p->op_reg_class,
p2p->op_channel);
else
p2p_buf_add_operating_channel(
buf, p2p->cfg->country,
p2p->cfg->op_reg_class,
p2p->cfg->op_channel);
}
if (persist ||
(conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
p2p_buf_add_channel_list(buf, p2p->cfg->country,
&p2p->channels);
if (!persist && conncap)
p2p_buf_add_connection_capability(buf, conncap);
p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
if (persist ||
(conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
p2p_buf_add_config_timeout(buf, p2p->go_timeout,
p2p->client_timeout);
p2p_buf_add_session_id(buf, prov->session_id,
prov->session_mac);
p2p_buf_add_feature_capability(buf, fcap_len, fcap);
p2p_buf_update_ie_hdr(buf, len);
} else if (status != P2P_SC_SUCCESS || adv_id) {
u8 *len = p2p_buf_add_ie_hdr(buf);
p2p_buf_add_status(buf, status);
if (p2p->p2ps_prov)
p2p_buf_add_advertisement_id(buf, adv_id,
p2p->p2ps_prov->adv_mac);
p2p_buf_update_ie_hdr(buf, len);
}
/* WPS IE with Config Methods attribute */
p2p_build_wps_ie_config_methods(buf, config_methods);
#ifdef CONFIG_WIFI_DISPLAY
if (wfd_ie)
wpabuf_put_buf(buf, wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
return buf;
}
static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id,
u32 session_id, u16 method,
const u8 *session_mac, const u8 *adv_mac)
{
struct p2ps_provision *tmp;
if (!p2p->p2ps_prov) {
p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1);
if (!p2p->p2ps_prov)
return -1;
} else {
os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1);
}
tmp = p2p->p2ps_prov;
tmp->adv_id = adv_id;
tmp->session_id = session_id;
tmp->method = method;
os_memcpy(tmp->session_mac, session_mac, ETH_ALEN);
os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN);
tmp->info[0] = '\0';
return 0;
}
static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask)
{
int i;
for (i = 0; cpt_priority[i]; i++)
if (req_cpt_mask & cpt_priority[i])
return cpt_priority[i];
return 0;
}
/* Check if the message contains a valid P2PS PD Request */
static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg,
const u8 *addr)
{
u8 group_id = 0;
u8 intended_addr = 0;
u8 operating_channel = 0;
u8 channel_list = 0;
u8 config_timeout = 0;
u8 listen_channel = 0;
#define P2PS_PD_REQ_CHECK(_val, _attr) \
do { \
if ((_val) && !msg->_attr) { \
p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \
return -1; \
} \
} while (0)
P2PS_PD_REQ_CHECK(1, adv_id);
P2PS_PD_REQ_CHECK(1, session_id);
P2PS_PD_REQ_CHECK(1, session_mac);
P2PS_PD_REQ_CHECK(1, adv_mac);
P2PS_PD_REQ_CHECK(1, capability);
P2PS_PD_REQ_CHECK(1, p2p_device_info);
P2PS_PD_REQ_CHECK(1, feature_cap);
/*
* We don't need to check Connection Capability, Persistent Group,
* and related attributes for follow-on PD Request with a status
* other than SUCCESS_DEFERRED.
*/
if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED)
return 0;
P2PS_PD_REQ_CHECK(1, conn_cap);
/*
* Note 1: A feature capability attribute structure can be changed
* in the future. The assumption is that such modifications are
* backward compatible, therefore we allow processing of msg.feature_cap
* exceeding the size of the p2ps_feature_capab structure.
* Note 2: Verification of msg.feature_cap_len below has to be changed
* to allow 2 byte feature capability processing if
* struct p2ps_feature_capab is extended to include additional fields
* and it affects the structure size.
*/
if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) {
p2p_dbg(p2p, "P2PS: Invalid feature capability len");
return -1;
}
switch (*msg->conn_cap) {
case P2PS_SETUP_NEW:
group_id = 1;
intended_addr = 1;
operating_channel = 1;
channel_list = 1;
config_timeout = 1;
listen_channel = 1;
break;
case P2PS_SETUP_CLIENT:
channel_list = 1;
listen_channel = 1;
break;
case P2PS_SETUP_GROUP_OWNER:
group_id = 1;
intended_addr = 1;
operating_channel = 1;
break;
case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER:
group_id = 1;
operating_channel = 1;
intended_addr = 1;
channel_list = 1;
config_timeout = 1;
break;
case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER:
group_id = 1;
intended_addr = 1;
operating_channel = 1;
channel_list = 1;
config_timeout = 1;
break;
default:
p2p_dbg(p2p, "Invalid P2PS PD connection capability");
return -1;
}
if (msg->persistent_dev) {
channel_list = 1;
config_timeout = 1;
if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) {
intended_addr = 1;
operating_channel = 1;
}
}
P2PS_PD_REQ_CHECK(group_id, group_id);
P2PS_PD_REQ_CHECK(intended_addr, intended_addr);
P2PS_PD_REQ_CHECK(operating_channel, operating_channel);
P2PS_PD_REQ_CHECK(channel_list, channel_list);
P2PS_PD_REQ_CHECK(config_timeout, config_timeout);
P2PS_PD_REQ_CHECK(listen_channel, listen_channel);
#undef P2PS_PD_REQ_CHECK
return 0;
}
void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
struct p2p_message msg;
struct p2p_device *dev;
int freq;
enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
struct wpabuf *resp;
u32 adv_id = 0;
struct p2ps_advertisement *p2ps_adv = NULL;
u8 conncap = P2PS_SETUP_NEW;
u8 auto_accept = 0;
u32 session_id = 0;
u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
const u8 *group_mac;
int passwd_id = DEV_PW_DEFAULT;
u16 config_methods;
u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
struct p2ps_feature_capab resp_fcap = { 0, 0 };
struct p2ps_feature_capab *req_fcap = NULL;
u8 remote_conncap;
u16 method;
if (p2p_parse(data, len, &msg))
return;
p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
" with config methods 0x%x (freq=%d)",
MAC2STR(sa), msg.wps_config_methods, rx_freq);
group_mac = msg.intended_addr;
dev = p2p_get_device(p2p, sa);
if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
p2p_dbg(p2p, "Provision Discovery Request from unknown peer "
MACSTR, MAC2STR(sa));
if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
0)) {
p2p_dbg(p2p, "Provision Discovery Request add device failed "
MACSTR, MAC2STR(sa));
goto out;
}
dev = p2p_get_device(p2p, sa);
if (!dev) {
p2p_dbg(p2p,
"Provision Discovery device not found "
MACSTR, MAC2STR(sa));
goto out;
}
} else if (msg.wfd_subelems) {
wpabuf_free(dev->info.wfd_subelems);
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
}
if (!msg.adv_id) {
allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
if (!(msg.wps_config_methods & allowed_config_methods)) {
p2p_dbg(p2p,
"Unsupported Config Methods in Provision Discovery Request");
goto out;
}
/* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
if (msg.group_id) {
size_t i;
for (i = 0; i < p2p->num_groups; i++) {
if (p2p_group_is_group_id_match(
p2p->groups[i],
msg.group_id, msg.group_id_len))
break;
}
if (i == p2p->num_groups) {
p2p_dbg(p2p,
"PD request for unknown P2P Group ID - reject");
goto out;
}
}
} else {
allowed_config_methods |= WPS_CONFIG_P2PS;
/*
* Set adv_id here, so in case of an error, a P2PS PD Response
* will be sent.
*/
adv_id = WPA_GET_LE32(msg.adv_id);
if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) {
reject = P2P_SC_FAIL_INVALID_PARAMS;
goto out;
}
req_fcap = (struct p2ps_feature_capab *) msg.feature_cap;
os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
session_id = WPA_GET_LE32(msg.session_id);
if (msg.conn_cap)
conncap = *msg.conn_cap;
/*
* We need to verify a P2PS config methog in an initial PD
* request or in a follow-on PD request with the status
* SUCCESS_DEFERRED.
*/
if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) &&
!(msg.wps_config_methods & allowed_config_methods)) {
p2p_dbg(p2p,
"Unsupported Config Methods in Provision Discovery Request");
goto out;
}
/*
* TODO: since we don't support multiple PD, reject PD request
* if we are in the middle of P2PS PD with some other peer
*/
}
dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
P2P_DEV_PD_PEER_KEYPAD |
P2P_DEV_PD_PEER_P2PS);
if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
p2p_dbg(p2p, "Peer " MACSTR
" requested us to show a PIN on display", MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
passwd_id = DEV_PW_USER_SPECIFIED;
} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
p2p_dbg(p2p, "Peer " MACSTR
" requested us to write its PIN using keypad",
MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_P2PS;
passwd_id = DEV_PW_P2PS_DEFAULT;
}
/* Remove stale persistent groups */
if (p2p->cfg->remove_stale_groups) {
p2p->cfg->remove_stale_groups(
p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
msg.persistent_dev,
msg.persistent_ssid, msg.persistent_ssid_len);
}
reject = P2P_SC_SUCCESS;
/*
* End of a legacy P2P PD Request processing, from this point continue
* with P2PS one.
*/
if (!msg.adv_id)
goto out;
remote_conncap = conncap;
if (!msg.status) {
unsigned int forced_freq, pref_freq;
if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) {
p2p_dbg(p2p,
"P2PS PD adv mac does not match the local one");
reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
goto out;
}
p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
if (!p2ps_adv) {
p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id);
reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
goto out;
}
p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv);
auto_accept = p2ps_adv->auto_accept;
conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
conncap, auto_accept,
&forced_freq,
&pref_freq);
p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
auto_accept, remote_conncap, conncap);
p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0);
resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority,
req_fcap->cpt);
p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x",
p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
if (!resp_fcap.cpt) {
p2p_dbg(p2p,
"Incompatible P2PS feature capability CPT bitmask");
reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
} else if (p2ps_adv->config_methods &&
!(msg.wps_config_methods &
p2ps_adv->config_methods)) {
p2p_dbg(p2p,
"Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
p2ps_adv->config_methods,
msg.wps_config_methods);
reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
} else if (!p2ps_adv->state) {
p2p_dbg(p2p, "P2PS state unavailable");
reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
} else if (!conncap) {
p2p_dbg(p2p, "Conncap resolution failed");
reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
}
if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
p2p_dbg(p2p, "Keypad - always defer");
auto_accept = 0;
}
if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
msg.channel_list && msg.channel_list_len &&
p2p_peer_channels_check(p2p, &p2p->channels, dev,
msg.channel_list,
msg.channel_list_len) < 0) {
p2p_dbg(p2p,
"No common channels - force deferred flow");
auto_accept = 0;
}
if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) ||
msg.persistent_dev) && msg.operating_channel) {
struct p2p_channels intersect;
/*
* There are cases where only the operating channel is
* provided. This requires saving the channel as the
* supported channel list, and verifying that it is
* supported.
*/
if (dev->channels.reg_classes == 0 ||
!p2p_channels_includes(&dev->channels,
msg.operating_channel[3],
msg.operating_channel[4])) {
struct p2p_channels *ch = &dev->channels;
os_memset(ch, 0, sizeof(*ch));
ch->reg_class[0].reg_class =
msg.operating_channel[3];
ch->reg_class[0].channel[0] =
msg.operating_channel[4];
ch->reg_class[0].channels = 1;
ch->reg_classes = 1;
}
p2p_channels_intersect(&p2p->channels, &dev->channels,
&intersect);
if (intersect.reg_classes == 0) {
p2p_dbg(p2p,
"No common channels - force deferred flow");
auto_accept = 0;
}
}
if (auto_accept || reject != P2P_SC_SUCCESS) {
struct p2ps_provision *tmp;
if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
msg.wps_config_methods,
session_mac, adv_mac) < 0) {
reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
goto out;
}
tmp = p2p->p2ps_prov;
tmp->force_freq = forced_freq;
tmp->pref_freq = pref_freq;
if (conncap) {
tmp->conncap = conncap;
tmp->status = P2P_SC_SUCCESS;
} else {
tmp->conncap = auto_accept;
tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
}
if (reject != P2P_SC_SUCCESS)
goto out;
}
}
if (!msg.status && !auto_accept &&
(!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
struct p2ps_provision *tmp;
if (!conncap) {
reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
goto out;
}
if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
msg.wps_config_methods,
session_mac, adv_mac) < 0) {
reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
goto out;
}
tmp = p2p->p2ps_prov;
reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
tmp->status = reject;
}
/* Not a P2PS Follow-on PD */
if (!msg.status)
goto out;
if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) {
reject = *msg.status;
goto out;
}
if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov)
goto out;
if (p2p->p2ps_prov->adv_id != adv_id ||
os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) {
p2p_dbg(p2p,
"P2PS Follow-on PD with mismatch Advertisement ID/MAC");
goto out;
}
if (p2p->p2ps_prov->session_id != session_id ||
os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) {
p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC");
goto out;
}
method = p2p->p2ps_prov->method;
conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
remote_conncap,
p2p->p2ps_prov->conncap,
&p2p->p2ps_prov->force_freq,
&p2p->p2ps_prov->pref_freq);
resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority,
req_fcap->cpt);
p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x",
p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
p2p->p2ps_prov->pref_freq, 0);
/*
* Ensure that if we asked for PIN originally, our method is consistent
* with original request.
*/
if (method & WPS_CONFIG_DISPLAY)
method = WPS_CONFIG_KEYPAD;
else if (method & WPS_CONFIG_KEYPAD)
method = WPS_CONFIG_DISPLAY;
if (!conncap || !(msg.wps_config_methods & method)) {
/*
* Reject this "Deferred Accept*
* if incompatible conncap or method
*/
reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
} else if (!resp_fcap.cpt) {
p2p_dbg(p2p,
"Incompatible P2PS feature capability CPT bitmask");
reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
} else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
msg.channel_list && msg.channel_list_len &&
p2p_peer_channels_check(p2p, &p2p->channels, dev,
msg.channel_list,
msg.channel_list_len) < 0) {
p2p_dbg(p2p,
"No common channels in Follow-On Provision Discovery Request");
reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
} else {
reject = P2P_SC_SUCCESS;
}
dev->oper_freq = 0;
if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) {
u8 tmp;
if (msg.operating_channel)
dev->oper_freq =
p2p_channel_to_freq(msg.operating_channel[3],
msg.operating_channel[4]);
if ((conncap & P2PS_SETUP_GROUP_OWNER) &&
p2p_go_select_channel(p2p, dev, &tmp) < 0)
reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
}
p2p->p2ps_prov->status = reject;
p2p->p2ps_prov->conncap = conncap;
out:
if (reject == P2P_SC_SUCCESS ||
reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
config_methods = msg.wps_config_methods;
else
config_methods = 0;
/*
* Send PD Response for an initial PD Request or for follow-on
* PD Request with P2P_SC_SUCCESS_DEFERRED status.
*/
if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) {
resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token,
reject, config_methods, adv_id,
msg.group_id, msg.group_id_len,
msg.persistent_ssid,
msg.persistent_ssid_len,
(const u8 *) &resp_fcap,
sizeof(resp_fcap));
if (!resp) {
p2p_parse_free(&msg);
return;
}
p2p_dbg(p2p, "Sending Provision Discovery Response");
if (rx_freq > 0)
freq = rx_freq;
else
freq = p2p_channel_to_freq(p2p->cfg->reg_class,
p2p->cfg->channel);
if (freq < 0) {
p2p_dbg(p2p, "Unknown regulatory class/channel");
wpabuf_free(resp);
p2p_parse_free(&msg);
return;
}
p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
p2p->cfg->dev_addr,
wpabuf_head(resp), wpabuf_len(resp),
50) < 0)
p2p_dbg(p2p, "Failed to send Action frame");
else
p2p->send_action_in_progress = 1;
wpabuf_free(resp);
}
if (!dev) {
p2p_parse_free(&msg);
return;
}
freq = 0;
if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) {
freq = p2p_channel_to_freq(p2p->op_reg_class,
p2p->op_channel);
if (freq < 0)
freq = 0;
}
if (!p2p->cfg->p2ps_prov_complete) {
/* Don't emit anything */
} else if (msg.status && *msg.status != P2P_SC_SUCCESS &&
*msg.status != P2P_SC_SUCCESS_DEFERRED) {
reject = *msg.status;
p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
sa, adv_mac, session_mac,
NULL, adv_id, session_id,
0, 0, msg.persistent_ssid,
msg.persistent_ssid_len,
0, 0, NULL, NULL, 0, freq,
NULL, 0);
} else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
p2p->p2ps_prov) {
p2p->p2ps_prov->status = reject;
p2p->p2ps_prov->conncap = conncap;
if (reject != P2P_SC_SUCCESS)
p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
sa, adv_mac, session_mac,
NULL, adv_id,
session_id, conncap, 0,
msg.persistent_ssid,
msg.persistent_ssid_len, 0,
0, NULL, NULL, 0, freq,
NULL, 0);
else
p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
*msg.status,
sa, adv_mac, session_mac,
group_mac, adv_id,
session_id, conncap,
passwd_id,
msg.persistent_ssid,
msg.persistent_ssid_len, 0,
0, NULL,
(const u8 *) &resp_fcap,
sizeof(resp_fcap), freq,
NULL, 0);
} else if (msg.status && p2p->p2ps_prov) {
p2p->p2ps_prov->status = P2P_SC_SUCCESS;
p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
adv_mac, session_mac, group_mac,
adv_id, session_id, conncap,
passwd_id,
msg.persistent_ssid,
msg.persistent_ssid_len,
0, 0, NULL,
(const u8 *) &resp_fcap,
sizeof(resp_fcap), freq, NULL, 0);
} else if (msg.status) {
} else if (auto_accept && reject == P2P_SC_SUCCESS) {
p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
sa, adv_mac, session_mac,
group_mac, adv_id, session_id,
conncap, passwd_id,
msg.persistent_ssid,
msg.persistent_ssid_len,
0, 0, NULL,
(const u8 *) &resp_fcap,
sizeof(resp_fcap), freq,
msg.group_id ?
msg.group_id + ETH_ALEN : NULL,
msg.group_id ?
msg.group_id_len - ETH_ALEN : 0);
} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
(!msg.session_info || !msg.session_info_len)) {
p2p->p2ps_prov->method = msg.wps_config_methods;
p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
sa, adv_mac, session_mac,
group_mac, adv_id, session_id,
conncap, passwd_id,
msg.persistent_ssid,
msg.persistent_ssid_len,
0, 1, NULL,
(const u8 *) &resp_fcap,
sizeof(resp_fcap), freq, NULL, 0);
} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
size_t buf_len = msg.session_info_len;
char *buf = os_malloc(2 * buf_len + 1);
if (buf) {
p2p->p2ps_prov->method = msg.wps_config_methods;
utf8_escape((char *) msg.session_info, buf_len,
buf, 2 * buf_len + 1);
p2p->cfg->p2ps_prov_complete(
p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa,
adv_mac, session_mac, group_mac, adv_id,
session_id, conncap, passwd_id,
msg.persistent_ssid, msg.persistent_ssid_len,
0, 1, buf,
(const u8 *) &resp_fcap, sizeof(resp_fcap),
freq, NULL, 0);
os_free(buf);
}
}
/*
* prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN,
* P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
* Call it either on legacy P2P PD or on P2PS PD only if we need to
* enter/show PIN.
*
* The callback is called in the following cases:
* 1. Legacy P2P PD request, response status SUCCESS
* 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE,
* response status: SUCCESS
* 3. P2PS advertiser, method DISPLAY, autoaccept: FALSE,
* response status: INFO_CURRENTLY_UNAVAILABLE
* 4. P2PS advertiser, method: KEYPAD, autoaccept==any,
* response status: INFO_CURRENTLY_UNAVAILABLE
* 5. P2PS follow-on with SUCCESS_DEFERRED,
* advertiser role: DISPLAY, autoaccept: FALSE,
* seeker: KEYPAD, response status: SUCCESS
*/
if (p2p->cfg->prov_disc_req &&
((reject == P2P_SC_SUCCESS && !msg.adv_id) ||
(!msg.status &&
(reject == P2P_SC_SUCCESS ||
reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) &&
passwd_id == DEV_PW_USER_SPECIFIED) ||
(!msg.status &&
reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
(reject == P2P_SC_SUCCESS &&
msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) {
const u8 *dev_addr = sa;
if (msg.p2p_device_addr)
dev_addr = msg.p2p_device_addr;
p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
msg.wps_config_methods,
dev_addr, msg.pri_dev_type,
msg.device_name, msg.config_methods,
msg.capability ? msg.capability[0] : 0,
msg.capability ? msg.capability[1] :
0,
msg.group_id, msg.group_id_len);
}
if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
p2ps_prov_free(p2p);
if (reject == P2P_SC_SUCCESS) {
switch (config_methods) {
case WPS_CONFIG_DISPLAY:
dev->wps_prov_info = WPS_CONFIG_KEYPAD;
break;
case WPS_CONFIG_KEYPAD:
dev->wps_prov_info = WPS_CONFIG_DISPLAY;
break;
case WPS_CONFIG_PUSHBUTTON:
dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON;
break;
case WPS_CONFIG_P2PS:
dev->wps_prov_info = WPS_CONFIG_P2PS;
break;
default:
dev->wps_prov_info = 0;
break;
}
if (msg.intended_addr)
os_memcpy(dev->interface_addr, msg.intended_addr,
ETH_ALEN);
}
p2p_parse_free(&msg);
}
static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p,
struct p2p_message *msg)
{
u8 conn_cap_go = 0;
u8 conn_cap_cli = 0;
u32 session_id;
u32 adv_id;
#define P2PS_PD_RESP_CHECK(_val, _attr) \
do { \
if ((_val) && !msg->_attr) { \
p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \
return -1; \
} \
} while (0)
P2PS_PD_RESP_CHECK(1, status);
P2PS_PD_RESP_CHECK(1, adv_id);
P2PS_PD_RESP_CHECK(1, adv_mac);
P2PS_PD_RESP_CHECK(1, capability);
P2PS_PD_RESP_CHECK(1, p2p_device_info);
P2PS_PD_RESP_CHECK(1, session_id);
P2PS_PD_RESP_CHECK(1, session_mac);
P2PS_PD_RESP_CHECK(1, feature_cap);
session_id = WPA_GET_LE32(msg->session_id);
adv_id = WPA_GET_LE32(msg->adv_id);
if (p2p->p2ps_prov->session_id != session_id) {
p2p_dbg(p2p,
"Ignore PD Response with unexpected Session ID");
return -1;
}
if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac,
ETH_ALEN)) {
p2p_dbg(p2p,
"Ignore PD Response with unexpected Session MAC");
return -1;
}
if (p2p->p2ps_prov->adv_id != adv_id) {
p2p_dbg(p2p,
"Ignore PD Response with unexpected Advertisement ID");
return -1;
}
if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) {
p2p_dbg(p2p,
"Ignore PD Response with unexpected Advertisement MAC");
return -1;
}
if (msg->listen_channel) {
p2p_dbg(p2p,
"Ignore malformed PD Response - unexpected Listen Channel");
return -1;
}
if (*msg->status == P2P_SC_SUCCESS &&
!(!!msg->conn_cap ^ !!msg->persistent_dev)) {
p2p_dbg(p2p,
"Ignore malformed PD Response - either conn_cap or persistent group should be present");
return -1;
}
if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) {
p2p_dbg(p2p,
"Ignore malformed PD Response - persistent group is present, but the status isn't success");
return -1;
}
if (msg->conn_cap) {
conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER;
conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT;
}
P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
channel_list);
P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
config_timeout);
P2PS_PD_RESP_CHECK(conn_cap_go, group_id);
P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr);
P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel);
/*
* TODO: Also validate that operating channel is present if the device
* is a GO in a persistent group. We can't do it here since we don't
* know what is the role of the peer. It should be probably done in
* p2ps_prov_complete callback, but currently operating channel isn't
* passed to it.
*/
#undef P2PS_PD_RESP_CHECK
return 0;
}
void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len)
{
struct p2p_message msg;
struct p2p_device *dev;
u16 report_config_methods = 0, req_config_methods;
u8 status = P2P_SC_SUCCESS;
u32 adv_id = 0;
u8 conncap = P2PS_SETUP_NEW;
u8 adv_mac[ETH_ALEN];
const u8 *group_mac;
int passwd_id = DEV_PW_DEFAULT;
int p2ps_seeker;
if (p2p_parse(data, len, &msg))
return;
if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) {
p2p_parse_free(&msg);
return;
}
/* Parse the P2PS members present */
if (msg.status)
status = *msg.status;
group_mac = msg.intended_addr;
if (msg.adv_mac)
os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
else
os_memset(adv_mac, 0, ETH_ALEN);
if (msg.adv_id)
adv_id = WPA_GET_LE32(msg.adv_id);
if (msg.conn_cap) {
conncap = *msg.conn_cap;
/* Switch bits to local relative */
switch (conncap) {
case P2PS_SETUP_GROUP_OWNER:
conncap = P2PS_SETUP_CLIENT;
break;
case P2PS_SETUP_CLIENT:
conncap = P2PS_SETUP_GROUP_OWNER;
break;
}
}
p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
" with config methods 0x%x",
MAC2STR(sa), msg.wps_config_methods);
dev = p2p_get_device(p2p, sa);
if (dev == NULL || !dev->req_config_methods) {
p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR
" with no pending request", MAC2STR(sa));
p2p_parse_free(&msg);
return;
} else if (msg.wfd_subelems) {
wpabuf_free(dev->info.wfd_subelems);
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
}
if (dev->dialog_token != msg.dialog_token) {
p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
msg.dialog_token, dev->dialog_token);
p2p_parse_free(&msg);
return;
}
if (p2p->pending_action_state == P2P_PENDING_PD) {
os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
}
p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker;
/*
* Use a local copy of the requested config methods since
* p2p_reset_pending_pd() can clear this in the peer entry.
*/
req_config_methods = dev->req_config_methods;
/*
* If the response is from the peer to whom a user initiated request
* was sent earlier, we reset that state info here.
*/
if (p2p->user_initiated_pd &&
os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
p2p_reset_pending_pd(p2p);
if (msg.wps_config_methods != req_config_methods) {
p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x",
msg.wps_config_methods, req_config_methods);
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
P2P_PROV_DISC_REJECTED,
adv_id, adv_mac, NULL);
p2p_parse_free(&msg);
p2ps_prov_free(p2p);
goto out;
}
report_config_methods = req_config_methods;
dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
P2P_DEV_PD_PEER_KEYPAD |
P2P_DEV_PD_PEER_P2PS);
if (req_config_methods & WPS_CONFIG_DISPLAY) {
p2p_dbg(p2p, "Peer " MACSTR
" accepted to show a PIN on display", MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
p2p_dbg(p2p, "Peer " MACSTR
" accepted to write our PIN using keypad",
MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
passwd_id = DEV_PW_USER_SPECIFIED;
} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN",
MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_P2PS;
passwd_id = DEV_PW_P2PS_DEFAULT;
}
if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
p2p->p2ps_prov) {
dev->oper_freq = 0;
/*
* Save the reported channel list and operating frequency.
* Note that the specification mandates that the responder
* should include in the channel list only channels reported by
- * the initiator, so this is only a sanity check, and if this
+ * the initiator, so this is only a validity check, and if this
* fails the flow would continue, although it would probably
* fail. Same is true for the operating channel.
*/
if (msg.channel_list && msg.channel_list_len &&
p2p_peer_channels_check(p2p, &p2p->channels, dev,
msg.channel_list,
msg.channel_list_len) < 0)
p2p_dbg(p2p, "P2PS PD Response - no common channels");
if (msg.operating_channel) {
if (p2p_channels_includes(&p2p->channels,
msg.operating_channel[3],
msg.operating_channel[4]) &&
p2p_channels_includes(&dev->channels,
msg.operating_channel[3],
msg.operating_channel[4])) {
dev->oper_freq =
p2p_channel_to_freq(
msg.operating_channel[3],
msg.operating_channel[4]);
} else {
p2p_dbg(p2p,
"P2PS PD Response - invalid operating channel");
}
}
if (p2p->cfg->p2ps_prov_complete) {
int freq = 0;
if (conncap == P2PS_SETUP_GROUP_OWNER) {
u8 tmp;
/*
* Re-select the operating channel as it is
* possible that original channel is no longer
* valid. This should not really fail.
*/
if (p2p_go_select_channel(p2p, dev, &tmp) < 0)
p2p_dbg(p2p,
"P2PS PD channel selection failed");
freq = p2p_channel_to_freq(p2p->op_reg_class,
p2p->op_channel);
if (freq < 0)
freq = 0;
}
p2p->cfg->p2ps_prov_complete(
p2p->cfg->cb_ctx, status, sa, adv_mac,
p2p->p2ps_prov->session_mac,
group_mac, adv_id, p2p->p2ps_prov->session_id,
conncap, passwd_id, msg.persistent_ssid,
msg.persistent_ssid_len, 1, 0, NULL,
msg.feature_cap, msg.feature_cap_len, freq,
msg.group_id ? msg.group_id + ETH_ALEN : NULL,
msg.group_id ? msg.group_id_len - ETH_ALEN : 0);
}
p2ps_prov_free(p2p);
} else if (status != P2P_SC_SUCCESS &&
status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
if (p2p->cfg->p2ps_prov_complete)
p2p->cfg->p2ps_prov_complete(
p2p->cfg->cb_ctx, status, sa, adv_mac,
p2p->p2ps_prov->session_mac,
group_mac, adv_id, p2p->p2ps_prov->session_id,
0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0, NULL, 0);
p2ps_prov_free(p2p);
}
if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
if (p2p->cfg->remove_stale_groups) {
p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx,
dev->info.p2p_device_addr,
NULL, NULL, 0);
}
if (msg.session_info && msg.session_info_len) {
size_t info_len = msg.session_info_len;
char *deferred_sess_resp = os_malloc(2 * info_len + 1);
if (!deferred_sess_resp) {
p2p_parse_free(&msg);
p2ps_prov_free(p2p);
goto out;
}
utf8_escape((char *) msg.session_info, info_len,
deferred_sess_resp, 2 * info_len + 1);
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(
p2p->cfg->cb_ctx, sa,
P2P_PROV_DISC_INFO_UNAVAILABLE,
adv_id, adv_mac,
deferred_sess_resp);
os_free(deferred_sess_resp);
} else
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(
p2p->cfg->cb_ctx, sa,
P2P_PROV_DISC_INFO_UNAVAILABLE,
adv_id, adv_mac, NULL);
} else if (status != P2P_SC_SUCCESS) {
p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
P2P_PROV_DISC_REJECTED,
adv_id, adv_mac, NULL);
p2p_parse_free(&msg);
p2ps_prov_free(p2p);
goto out;
}
/* Store the provisioning info */
dev->wps_prov_info = msg.wps_config_methods;
if (msg.intended_addr)
os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN);
p2p_parse_free(&msg);
out:
dev->req_config_methods = 0;
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with "
MACSTR, MAC2STR(dev->info.p2p_device_addr));
dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
p2p_connect_send(p2p, dev);
return;
}
/*
* prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN,
* P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
* Call it only for a legacy P2P PD or for P2PS PD scenarios where
* show/enter PIN events are needed.
*
* The callback is called in the following cases:
* 1. Legacy P2P PD response with a status SUCCESS
* 2. P2PS, advertiser method: DISPLAY, autoaccept: true,
* response status: SUCCESS, local method KEYPAD
* 3. P2PS, advertiser method: KEYPAD,Seeker side,
* response status: INFO_CURRENTLY_UNAVAILABLE,
* local method: DISPLAY
*/
if (p2p->cfg->prov_disc_resp &&
((status == P2P_SC_SUCCESS && !adv_id) ||
(p2ps_seeker && status == P2P_SC_SUCCESS &&
passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
(p2ps_seeker &&
status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
passwd_id == DEV_PW_USER_SPECIFIED)))
p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
report_config_methods);
if (p2p->state == P2P_PD_DURING_FIND) {
p2p_stop_listen_for_freq(p2p, 0);
p2p_continue_find(p2p);
}
}
int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
int join, int force_freq)
{
struct wpabuf *req;
int freq;
if (force_freq > 0)
freq = force_freq;
else
freq = dev->listen_freq > 0 ? dev->listen_freq :
dev->oper_freq;
if (freq <= 0) {
p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
MACSTR " to send Provision Discovery Request",
MAC2STR(dev->info.p2p_device_addr));
return -1;
}
if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
if (!(dev->info.dev_capab &
P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR
" that is in a group and is not discoverable",
MAC2STR(dev->info.p2p_device_addr));
return -1;
}
/* TODO: use device discoverability request through GO */
}
if (p2p->p2ps_prov) {
if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) {
if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY)
dev->req_config_methods = WPS_CONFIG_KEYPAD;
else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD)
dev->req_config_methods = WPS_CONFIG_DISPLAY;
else
dev->req_config_methods = WPS_CONFIG_P2PS;
} else {
/* Order of preference, based on peer's capabilities */
if (p2p->p2ps_prov->method)
dev->req_config_methods =
p2p->p2ps_prov->method;
else if (dev->info.config_methods & WPS_CONFIG_P2PS)
dev->req_config_methods = WPS_CONFIG_P2PS;
else if (dev->info.config_methods & WPS_CONFIG_DISPLAY)
dev->req_config_methods = WPS_CONFIG_DISPLAY;
else
dev->req_config_methods = WPS_CONFIG_KEYPAD;
}
p2p_dbg(p2p,
"Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
p2p->p2ps_prov->method, p2p->p2ps_prov->status,
dev->req_config_methods);
if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
p2p->p2ps_prov->pref_freq, 1) < 0)
return -1;
}
req = p2p_build_prov_disc_req(p2p, dev, join);
if (req == NULL)
return -1;
if (p2p->state != P2P_IDLE)
p2p_stop_listen_for_freq(p2p, freq);
p2p->pending_action_state = P2P_PENDING_PD;
if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
p2p->cfg->dev_addr, dev->info.p2p_device_addr,
wpabuf_head(req), wpabuf_len(req), 200) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
wpabuf_free(req);
return -1;
}
os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
wpabuf_free(req);
return 0;
}
int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
struct p2ps_provision *p2ps_prov,
u16 config_methods, int join, int force_freq,
int user_initiated_pd)
{
struct p2p_device *dev;
dev = p2p_get_device(p2p, peer_addr);
if (dev == NULL)
dev = p2p_get_device_interface(p2p, peer_addr);
if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
" not yet known", MAC2STR(peer_addr));
os_free(p2ps_prov);
return -1;
}
p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
" (config methods 0x%x)",
MAC2STR(peer_addr), config_methods);
if (config_methods == 0 && !p2ps_prov) {
os_free(p2ps_prov);
return -1;
}
if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED &&
p2p->p2ps_prov) {
/* Use cached method from deferred provisioning */
p2ps_prov->method = p2p->p2ps_prov->method;
}
/* Reset provisioning info */
dev->wps_prov_info = 0;
p2ps_prov_free(p2p);
p2p->p2ps_prov = p2ps_prov;
dev->req_config_methods = config_methods;
if (join)
dev->flags |= P2P_DEV_PD_FOR_JOIN;
else
dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
p2p->state != P2P_LISTEN_ONLY) {
p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with "
MACSTR " (config methods 0x%x)",
MAC2STR(peer_addr), config_methods);
return 0;
}
p2p->user_initiated_pd = user_initiated_pd;
p2p->pd_force_freq = force_freq;
if (p2p->user_initiated_pd)
p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
/*
* Assign dialog token here to use the same value in each retry within
* the same PD exchange.
*/
dev->dialog_token++;
if (dev->dialog_token == 0)
dev->dialog_token = 1;
return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
}
void p2p_reset_pending_pd(struct p2p_data *p2p)
{
struct p2p_device *dev;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
if (os_memcmp(p2p->pending_pd_devaddr,
dev->info.p2p_device_addr, ETH_ALEN))
continue;
if (!dev->req_config_methods)
continue;
if (dev->flags & P2P_DEV_PD_FOR_JOIN)
continue;
/* Reset the config methods of the device */
dev->req_config_methods = 0;
}
p2p->user_initiated_pd = 0;
os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
p2p->pd_retries = 0;
p2p->pd_force_freq = 0;
}
void p2ps_prov_free(struct p2p_data *p2p)
{
os_free(p2p->p2ps_prov);
p2p->p2ps_prov = NULL;
}
diff --git a/contrib/wpa/src/pae/ieee802_1x_kay.c b/contrib/wpa/src/pae/ieee802_1x_kay.c
index 2fe88ac0c5f2..657de93ae748 100644
--- a/contrib/wpa/src/pae/ieee802_1x_kay.c
+++ b/contrib/wpa/src/pae/ieee802_1x_kay.c
@@ -1,4143 +1,4143 @@
/*
* IEEE 802.1X-2010 Key Agreement Protocol of PAE state machine
* Copyright (c) 2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include <time.h>
#include "includes.h"
#include "common.h"
#include "list.h"
#include "eloop.h"
#include "wpabuf.h"
#include "state_machine.h"
#include "l2_packet/l2_packet.h"
#include "common/eapol_common.h"
#include "crypto/aes_wrap.h"
#include "ieee802_1x_cp.h"
#include "ieee802_1x_key.h"
#include "ieee802_1x_kay.h"
#include "ieee802_1x_kay_i.h"
#include "ieee802_1x_secy_ops.h"
#define DEFAULT_SA_KEY_LEN 16
#define DEFAULT_ICV_LEN 16
#define MAX_ICV_LEN 32 /* 32 bytes, 256 bits */
#define MAX_MISSING_SAK_USE 10 /* Accept up to 10 inbound MKPDUs without
* SAK-USE before dropping */
#define PENDING_PN_EXHAUSTION 0xC0000000
#define MKA_ALIGN_LENGTH(len) (((len) + 0x3) & ~0x3)
/* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */
#define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 }
static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009;
/* IEEE802.1AE-2006 Table 14-1 MACsec Cipher Suites */
static struct macsec_ciphersuite cipher_suite_tbl[] = {
/* GCM-AES-128 */
{
.id = CS_ID_GCM_AES_128,
.name = CS_NAME_GCM_AES_128,
.capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50,
.sak_len = DEFAULT_SA_KEY_LEN,
},
/* GCM-AES-256 */
{
.id = CS_ID_GCM_AES_256,
.name = CS_NAME_GCM_AES_256,
.capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50,
.sak_len = 32,
},
};
#define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl))
#define DEFAULT_CS_INDEX 0
static struct mka_alg mka_alg_tbl[] = {
{
.parameter = MKA_ALGO_AGILITY_2009,
.icv_len = DEFAULT_ICV_LEN,
.cak_trfm = ieee802_1x_cak_aes_cmac,
.ckn_trfm = ieee802_1x_ckn_aes_cmac,
.kek_trfm = ieee802_1x_kek_aes_cmac,
.ick_trfm = ieee802_1x_ick_aes_cmac,
.icv_hash = ieee802_1x_icv_aes_cmac,
},
};
#define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl))
static int is_ki_equal(struct ieee802_1x_mka_ki *ki1,
struct ieee802_1x_mka_ki *ki2)
{
return os_memcmp(ki1->mi, ki2->mi, MI_LEN) == 0 &&
ki1->kn == ki2->kn;
}
static void set_mka_param_body_len(void *body, unsigned int len)
{
struct ieee802_1x_mka_hdr *hdr = body;
hdr->length = (len >> 8) & 0x0f;
hdr->length1 = len & 0xff;
}
static unsigned int get_mka_param_body_len(const void *body)
{
const struct ieee802_1x_mka_hdr *hdr = body;
return (hdr->length << 8) | hdr->length1;
}
static u8 get_mka_param_body_type(const void *body)
{
const struct ieee802_1x_mka_hdr *hdr = body;
return hdr->type;
}
static const char * mi_txt(const u8 *mi)
{
static char txt[MI_LEN * 2 + 1];
wpa_snprintf_hex(txt, sizeof(txt), mi, MI_LEN);
return txt;
}
static const char * sci_txt(const struct ieee802_1x_mka_sci *sci)
{
static char txt[ETH_ALEN * 3 + 1 + 5 + 1];
os_snprintf(txt, sizeof(txt), MACSTR "@%u",
MAC2STR(sci->addr), be_to_host16(sci->port));
return txt;
}
static const char * algo_agility_txt(const u8 *algo_agility)
{
static char txt[4 * 2 + 1];
wpa_snprintf_hex(txt, sizeof(txt), algo_agility, 4);
return txt;
}
/**
* ieee802_1x_mka_dump_basic_body -
*/
static void
ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body)
{
size_t body_len;
if (!body)
return;
/* IEEE Std 802.1X-2010, Figure 11-8 */
body_len = get_mka_param_body_len(body);
wpa_printf(MSG_DEBUG, "MKA Basic Parameter Set");
wpa_printf(MSG_DEBUG, "\tMKA Version Identifier: %d", body->version);
wpa_printf(MSG_DEBUG, "\tKey Server Priority: %d", body->priority);
wpa_printf(MSG_DEBUG, "\tKey Server: %d", body->key_server);
wpa_printf(MSG_DEBUG, "\tMACsec Desired: %d", body->macsec_desired);
wpa_printf(MSG_DEBUG, "\tMACsec Capability: %d",
body->macsec_capability);
wpa_printf(MSG_DEBUG, "\tParameter set body length: %zu", body_len);
wpa_printf(MSG_DEBUG, "\tSCI: %s", sci_txt(&body->actor_sci));
wpa_printf(MSG_DEBUG, "\tActor's Member Identifier: %s",
mi_txt(body->actor_mi));
wpa_printf(MSG_DEBUG, "\tActor's Message Number: %d",
be_to_host32(body->actor_mn));
wpa_printf(MSG_DEBUG, "\tAlgorithm Agility: %s",
algo_agility_txt(body->algo_agility));
wpa_hexdump(MSG_DEBUG, "\tCAK Name", body->ckn,
body_len + MKA_HDR_LEN - sizeof(*body));
}
/**
* ieee802_1x_mka_dump_peer_body -
*/
static void
ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body)
{
size_t body_len;
size_t i;
u8 *mi;
be32 mn;
if (body == NULL)
return;
/* IEEE Std 802.1X-2010, Figure 11-9 */
body_len = get_mka_param_body_len(body);
if (body->type == MKA_LIVE_PEER_LIST) {
wpa_printf(MSG_DEBUG, "Live Peer List parameter set");
wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len);
} else if (body->type == MKA_POTENTIAL_PEER_LIST) {
wpa_printf(MSG_DEBUG, "Potential Peer List parameter set");
wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len);
}
for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) {
mi = body->peer + i;
os_memcpy(&mn, mi + MI_LEN, sizeof(mn));
wpa_printf(MSG_DEBUG, "\tMember Id: %s Message Number: %d",
mi_txt(mi), be_to_host32(mn));
}
}
/**
* ieee802_1x_mka_dump_dist_sak_body -
*/
static void
ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body)
{
size_t body_len;
if (body == NULL)
return;
/* IEEE Std 802.1X-2010, Figure 11-11 and 11-12 */
body_len = get_mka_param_body_len(body);
wpa_printf(MSG_DEBUG, "Distributed SAK parameter set");
wpa_printf(MSG_DEBUG, "\tDistributed AN........: %d", body->dan);
wpa_printf(MSG_DEBUG, "\tConfidentiality Offset: %d",
body->confid_offset);
wpa_printf(MSG_DEBUG, "\tBody Length...........: %zu", body_len);
if (!body_len)
return;
wpa_printf(MSG_DEBUG, "\tKey Number............: %d",
be_to_host32(body->kn));
/* TODO: Other than GCM-AES-128 case: MACsec Cipher Suite */
wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:", body->sak, 24);
}
static const char * yes_no(int val)
{
return val ? "Yes" : "No";
}
/**
* ieee802_1x_mka_dump_sak_use_body -
*/
static void
ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body)
{
int body_len;
if (body == NULL)
return;
/* IEEE Std 802.1X-2010, Figure 11-10 */
body_len = get_mka_param_body_len(body);
wpa_printf(MSG_DEBUG, "MACsec SAK Use parameter set");
wpa_printf(MSG_DEBUG, "\tLatest Key AN....: %d", body->lan);
wpa_printf(MSG_DEBUG, "\tLatest Key Tx....: %s", yes_no(body->ltx));
wpa_printf(MSG_DEBUG, "\tLatest Key Rx....: %s", yes_no(body->lrx));
wpa_printf(MSG_DEBUG, "\tOld Key AN.......: %d", body->oan);
wpa_printf(MSG_DEBUG, "\tOld Key Tx.......: %s", yes_no(body->otx));
wpa_printf(MSG_DEBUG, "\tOld Key Rx.......: %s", yes_no(body->orx));
wpa_printf(MSG_DEBUG, "\tPlain Tx.........: %s", yes_no(body->ptx));
wpa_printf(MSG_DEBUG, "\tPlain Rx.........: %s", yes_no(body->prx));
wpa_printf(MSG_DEBUG, "\tDelay Protect....: %s",
yes_no(body->delay_protect));
wpa_printf(MSG_DEBUG, "\tBody Length......: %d", body_len);
if (!body_len)
return;
wpa_printf(MSG_DEBUG, "\tKey Server MI....: %s", mi_txt(body->lsrv_mi));
wpa_printf(MSG_DEBUG, "\tKey Number.......: %u",
be_to_host32(body->lkn));
wpa_printf(MSG_DEBUG, "\tLowest PN........: %u",
be_to_host32(body->llpn));
wpa_printf(MSG_DEBUG, "\tOld Key Server MI: %s", mi_txt(body->osrv_mi));
wpa_printf(MSG_DEBUG, "\tOld Key Number...: %u",
be_to_host32(body->okn));
wpa_printf(MSG_DEBUG, "\tOld Lowest PN....: %u",
be_to_host32(body->olpn));
}
/**
* ieee802_1x_kay_get_participant -
*/
static struct ieee802_1x_mka_participant *
ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn,
size_t len)
{
struct ieee802_1x_mka_participant *participant;
dl_list_for_each(participant, &kay->participant_list,
struct ieee802_1x_mka_participant, list) {
if (participant->ckn.len == len &&
os_memcmp(participant->ckn.name, ckn,
participant->ckn.len) == 0)
return participant;
}
wpa_printf(MSG_DEBUG, "KaY: participant is not found");
return NULL;
}
/**
* ieee802_1x_kay_get_principal_participant -
*/
static struct ieee802_1x_mka_participant *
ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay *kay)
{
struct ieee802_1x_mka_participant *participant;
dl_list_for_each(participant, &kay->participant_list,
struct ieee802_1x_mka_participant, list) {
if (participant->principal)
return participant;
}
wpa_printf(MSG_DEBUG, "KaY: principal participant is not found");
return NULL;
}
static struct ieee802_1x_kay_peer * get_peer_mi(struct dl_list *peers,
const u8 *mi)
{
struct ieee802_1x_kay_peer *peer;
dl_list_for_each(peer, peers, struct ieee802_1x_kay_peer, list) {
if (os_memcmp(peer->mi, mi, MI_LEN) == 0)
return peer;
}
return NULL;
}
/**
* ieee802_1x_kay_get_potential_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_potential_peer(
struct ieee802_1x_mka_participant *participant, const u8 *mi)
{
return get_peer_mi(&participant->potential_peers, mi);
}
/**
* ieee802_1x_kay_get_live_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant,
const u8 *mi)
{
return get_peer_mi(&participant->live_peers, mi);
}
/**
* ieee802_1x_kay_is_in_potential_peer
*/
static bool
ieee802_1x_kay_is_in_potential_peer(
struct ieee802_1x_mka_participant *participant, const u8 *mi)
{
return ieee802_1x_kay_get_potential_peer(participant, mi) != NULL;
}
/**
* ieee802_1x_kay_is_in_live_peer
*/
static bool
ieee802_1x_kay_is_in_live_peer(
struct ieee802_1x_mka_participant *participant, const u8 *mi)
{
return ieee802_1x_kay_get_live_peer(participant, mi) != NULL;
}
/**
* ieee802_1x_kay_get_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant,
const u8 *mi)
{
struct ieee802_1x_kay_peer *peer;
peer = ieee802_1x_kay_get_live_peer(participant, mi);
if (peer)
return peer;
return ieee802_1x_kay_get_potential_peer(participant, mi);
}
/**
* ieee802_1x_kay_get_cipher_suite
*/
static struct macsec_ciphersuite *
ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
const u8 *cs_id, unsigned int *idx)
{
unsigned int i;
u64 cs;
be64 _cs;
os_memcpy(&_cs, cs_id, CS_ID_LEN);
cs = be_to_host64(_cs);
for (i = 0; i < CS_TABLE_SIZE; i++) {
if (cipher_suite_tbl[i].id == cs) {
*idx = i;
return &cipher_suite_tbl[i];
}
}
return NULL;
}
u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci)
{
struct ieee802_1x_mka_sci tmp;
os_memcpy(tmp.addr, sci->addr, ETH_ALEN);
tmp.port = sci->port;
return *((u64 *) &tmp);
}
static bool sci_equal(const struct ieee802_1x_mka_sci *a,
const struct ieee802_1x_mka_sci *b)
{
return os_memcmp(a, b, sizeof(struct ieee802_1x_mka_sci)) == 0;
}
/**
* ieee802_1x_kay_get_peer_sci
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant,
const struct ieee802_1x_mka_sci *sci)
{
struct ieee802_1x_kay_peer *peer;
dl_list_for_each(peer, &participant->live_peers,
struct ieee802_1x_kay_peer, list) {
if (sci_equal(&peer->sci, sci))
return peer;
}
dl_list_for_each(peer, &participant->potential_peers,
struct ieee802_1x_kay_peer, list) {
if (sci_equal(&peer->sci, sci))
return peer;
}
return NULL;
}
static void ieee802_1x_kay_use_data_key(struct data_key *pkey);
/**
* ieee802_1x_kay_init_receive_sa -
*/
static struct receive_sa *
ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn,
struct data_key *key)
{
struct receive_sa *psa;
if (!psc || !key)
return NULL;
psa = os_zalloc(sizeof(*psa));
if (!psa) {
wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
return NULL;
}
ieee802_1x_kay_use_data_key(key);
psa->pkey = key;
psa->lowest_pn = lowest_pn;
psa->next_pn = lowest_pn;
psa->an = an;
psa->sc = psc;
os_get_time(&psa->created_time);
psa->in_use = false;
dl_list_add(&psc->sa_list, &psa->list);
wpa_printf(MSG_DEBUG,
"KaY: Create receive SA(an: %hhu lowest_pn: %u) of SC",
an, lowest_pn);
return psa;
}
static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey);
/**
* ieee802_1x_kay_deinit_receive_sa -
*/
static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa)
{
ieee802_1x_kay_deinit_data_key(psa->pkey);
psa->pkey = NULL;
wpa_printf(MSG_DEBUG,
"KaY: Delete receive SA(an: %hhu) of SC",
psa->an);
dl_list_del(&psa->list);
os_free(psa);
}
/**
* ieee802_1x_kay_init_receive_sc -
*/
static struct receive_sc *
ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci)
{
struct receive_sc *psc;
if (!psci)
return NULL;
psc = os_zalloc(sizeof(*psc));
if (!psc) {
wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
return NULL;
}
os_memcpy(&psc->sci, psci, sizeof(psc->sci));
os_get_time(&psc->created_time);
psc->receiving = false;
dl_list_init(&psc->sa_list);
wpa_printf(MSG_DEBUG, "KaY: Create receive SC: SCI %s",
sci_txt(&psc->sci));
return psc;
}
static void ieee802_1x_delete_receive_sa(struct ieee802_1x_kay *kay,
struct receive_sa *sa)
{
secy_disable_receive_sa(kay, sa);
secy_delete_receive_sa(kay, sa);
ieee802_1x_kay_deinit_receive_sa(sa);
}
/**
* ieee802_1x_kay_deinit_receive_sc -
**/
static void
ieee802_1x_kay_deinit_receive_sc(
struct ieee802_1x_mka_participant *participant, struct receive_sc *psc)
{
struct receive_sa *psa, *pre_sa;
wpa_printf(MSG_DEBUG, "KaY: Delete receive SC");
dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa,
list)
ieee802_1x_delete_receive_sa(participant->kay, psa);
dl_list_del(&psc->list);
secy_delete_receive_sc(participant->kay, psc);
os_free(psc);
}
static void ieee802_1x_kay_dump_peer(struct ieee802_1x_kay_peer *peer)
{
wpa_printf(MSG_DEBUG, "\tMI: %s MN: %d SCI: %s",
mi_txt(peer->mi), peer->mn, sci_txt(&peer->sci));
}
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_create_peer(const u8 *mi, u32 mn)
{
struct ieee802_1x_kay_peer *peer;
peer = os_zalloc(sizeof(*peer));
if (!peer) {
wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
return NULL;
}
os_memcpy(peer->mi, mi, MI_LEN);
peer->mn = mn;
peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
peer->sak_used = false;
peer->missing_sak_use_count = 0;
return peer;
}
/**
* ieee802_1x_kay_create_live_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
const u8 *mi, u32 mn)
{
struct ieee802_1x_kay_peer *peer;
struct receive_sc *rxsc;
peer = ieee802_1x_kay_create_peer(mi, mn);
if (!peer)
return NULL;
os_memcpy(&peer->sci, &participant->current_peer_sci,
sizeof(peer->sci));
rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci);
if (!rxsc) {
os_free(peer);
return NULL;
}
if (secy_create_receive_sc(participant->kay, rxsc)) {
os_free(rxsc);
os_free(peer);
return NULL;
}
dl_list_add(&participant->live_peers, &peer->list);
dl_list_add(&participant->rxsc_list, &rxsc->list);
wpa_printf(MSG_DEBUG, "KaY: Live peer created");
ieee802_1x_kay_dump_peer(peer);
return peer;
}
/**
* ieee802_1x_kay_create_potential_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_create_potential_peer(
struct ieee802_1x_mka_participant *participant, const u8 *mi, u32 mn)
{
struct ieee802_1x_kay_peer *peer;
peer = ieee802_1x_kay_create_peer(mi, mn);
if (!peer)
return NULL;
dl_list_add(&participant->potential_peers, &peer->list);
wpa_printf(MSG_DEBUG, "KaY: Potential peer created");
ieee802_1x_kay_dump_peer(peer);
return peer;
}
/**
* ieee802_1x_kay_move_live_peer
*/
static struct ieee802_1x_kay_peer *
ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant,
u8 *mi, u32 mn)
{
struct ieee802_1x_kay_peer *peer;
struct receive_sc *rxsc;
peer = ieee802_1x_kay_get_potential_peer(participant, mi);
if (!peer)
return NULL;
rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci);
if (!rxsc)
return NULL;
os_memcpy(&peer->sci, &participant->current_peer_sci,
sizeof(peer->sci));
peer->mn = mn;
peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
wpa_printf(MSG_DEBUG, "KaY: Move potential peer to live peer");
ieee802_1x_kay_dump_peer(peer);
dl_list_del(&peer->list);
if (secy_create_receive_sc(participant->kay, rxsc)) {
wpa_printf(MSG_ERROR, "KaY: Can't create SC, discard peer");
os_free(rxsc);
os_free(peer);
return NULL;
}
dl_list_add_tail(&participant->live_peers, &peer->list);
dl_list_add(&participant->rxsc_list, &rxsc->list);
return peer;
}
/**
* ieee802_1x_mka_basic_body_present -
*/
static bool
ieee802_1x_mka_basic_body_present(
struct ieee802_1x_mka_participant *participant)
{
return true;
}
/**
* ieee802_1x_mka_basic_body_length -
*/
static int
ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant *participant)
{
int length;
length = sizeof(struct ieee802_1x_mka_basic_body);
length += participant->ckn.len;
return MKA_ALIGN_LENGTH(length);
}
/**
* ieee802_1x_mka_encode_basic_body
*/
static int
ieee802_1x_mka_encode_basic_body(
struct ieee802_1x_mka_participant *participant,
struct wpabuf *buf)
{
struct ieee802_1x_mka_basic_body *body;
struct ieee802_1x_kay *kay = participant->kay;
unsigned int length = sizeof(struct ieee802_1x_mka_basic_body);
length += participant->ckn.len;
body = wpabuf_put(buf, MKA_ALIGN_LENGTH(length));
body->version = kay->mka_version;
body->priority = kay->actor_priority;
/* The Key Server flag is set if and only if the participant has not
* decided that another participant is or will be the Key Server. */
if (participant->is_elected)
body->key_server = participant->is_key_server;
else
body->key_server = participant->can_be_key_server;
body->macsec_desired = kay->macsec_desired;
body->macsec_capability = kay->macsec_capable;
set_mka_param_body_len(body, length - MKA_HDR_LEN);
os_memcpy(body->actor_sci.addr, kay->actor_sci.addr,
sizeof(kay->actor_sci.addr));
body->actor_sci.port = kay->actor_sci.port;
os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi));
participant->mn = participant->mn + 1;
body->actor_mn = host_to_be32(participant->mn);
os_memcpy(body->algo_agility, kay->algo_agility,
sizeof(body->algo_agility));
os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len);
ieee802_1x_mka_dump_basic_body(body);
return 0;
}
static bool
reset_participant_mi(struct ieee802_1x_mka_participant *participant)
{
if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
return false;
participant->mn = 0;
return true;
}
/**
* ieee802_1x_mka_decode_basic_body -
*/
static struct ieee802_1x_mka_participant *
ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
size_t msg_len)
{
struct ieee802_1x_mka_participant *participant;
const struct ieee802_1x_mka_basic_body *body;
struct ieee802_1x_kay_peer *peer;
size_t ckn_len;
size_t body_len;
body = (const struct ieee802_1x_mka_basic_body *) mka_msg;
if (body->version > MKA_VERSION_ID) {
wpa_printf(MSG_DEBUG,
"KaY: Peer's version(%d) greater than MKA current version(%d)",
body->version, MKA_VERSION_ID);
}
if (kay->is_obliged_key_server && body->key_server) {
wpa_printf(MSG_DEBUG, "KaY: I must be key server - ignore MKPDU claiming to be from a key server");
return NULL;
}
body_len = get_mka_param_body_len(body);
if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
body_len);
return NULL;
}
ckn_len = body_len -
(sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
if (!participant) {
wpa_printf(MSG_DEBUG,
"KaY: Peer is not included in my CA - ignore MKPDU");
return NULL;
}
/* If the peer's MI is my MI, I will choose new MI */
if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) {
if (!reset_participant_mi(participant))
return NULL;
wpa_printf(MSG_DEBUG,
"KaY: Peer using my MI - selected a new random MI: %s",
mi_txt(participant->mi));
}
os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN);
participant->current_peer_id.mn = body->actor_mn;
os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr,
sizeof(participant->current_peer_sci.addr));
participant->current_peer_sci.port = body->actor_sci.port;
/* handler peer */
peer = ieee802_1x_kay_get_peer(participant, body->actor_mi);
if (!peer) {
/* Check duplicated SCI
*
* A duplicated SCI indicates either an active attacker or
* a valid peer whose MI is being changed. The latter scenario
* is more likely because to have gotten this far the received
* MKPDU must have had a valid ICV, indicating the peer holds
* the same CAK as our participant.
*
* Before creating a new peer object for the new MI we must
* clean up the resources (SCs and SAs) associated with the
* old peer. An easy way to do this is to ignore MKPDUs with
* the new MI's for now and just wait for the old peer to
* time out and clean itself up (within MKA_LIFE_TIME).
*
* This method is preferable to deleting the old peer here
* and now and continuing on with processing because if this
* MKPDU is from an attacker it's better to ignore the MKPDU
* than to process it (and delete a valid peer as well).
*/
peer = ieee802_1x_kay_get_peer_sci(participant,
&body->actor_sci);
if (peer) {
time_t new_expire;
wpa_printf(MSG_WARNING,
"KaY: duplicated SCI detected - maybe active attacker or peer selected new MI - ignore MKPDU");
/* Reduce timeout to speed up this process but left the
* chance for old one to prove aliveness. */
new_expire = time(NULL) + MKA_HELLO_TIME * 1.5 / 1000;
if (peer->expire > new_expire)
peer->expire = new_expire;
return NULL;
}
peer = ieee802_1x_kay_create_potential_peer(
participant, body->actor_mi,
be_to_host32(body->actor_mn));
if (!peer) {
wpa_printf(MSG_DEBUG,
"KaY: No potential peer entry found - ignore MKPDU");
return NULL;
}
peer->macsec_desired = body->macsec_desired;
peer->macsec_capability = body->macsec_capability;
peer->is_key_server = body->key_server;
peer->key_server_priority = body->priority;
} else if (peer->mn < be_to_host32(body->actor_mn)) {
peer->mn = be_to_host32(body->actor_mn);
peer->macsec_desired = body->macsec_desired;
peer->macsec_capability = body->macsec_capability;
peer->is_key_server = body->key_server;
peer->key_server_priority = body->priority;
} else {
wpa_printf(MSG_WARNING,
"KaY: The peer MN did not increase - ignore MKPDU");
return NULL;
}
return participant;
}
/**
* ieee802_1x_mka_live_peer_body_present
*/
static bool
ieee802_1x_mka_live_peer_body_present(
struct ieee802_1x_mka_participant *participant)
{
return !dl_list_empty(&participant->live_peers);
}
/**
* ieee802_1x_kay_get_live_peer_length
*/
static int
ieee802_1x_mka_get_live_peer_length(
struct ieee802_1x_mka_participant *participant)
{
int len = MKA_HDR_LEN;
struct ieee802_1x_kay_peer *peer;
dl_list_for_each(peer, &participant->live_peers,
struct ieee802_1x_kay_peer, list)
len += sizeof(struct ieee802_1x_mka_peer_id);
return MKA_ALIGN_LENGTH(len);
}
/**
* ieee802_1x_mka_encode_live_peer_body -
*/
static int
ieee802_1x_mka_encode_live_peer_body(
struct ieee802_1x_mka_participant *participant,
struct wpabuf *buf)
{
struct ieee802_1x_mka_peer_body *body;
struct ieee802_1x_kay_peer *peer;
unsigned int length;
struct ieee802_1x_mka_peer_id *body_peer;
length = ieee802_1x_mka_get_live_peer_length(participant);
body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
body->type = MKA_LIVE_PEER_LIST;
set_mka_param_body_len(body, length - MKA_HDR_LEN);
dl_list_for_each(peer, &participant->live_peers,
struct ieee802_1x_kay_peer, list) {
body_peer = wpabuf_put(buf,
sizeof(struct ieee802_1x_mka_peer_id));
os_memcpy(body_peer->mi, peer->mi, MI_LEN);
body_peer->mn = host_to_be32(peer->mn);
}
ieee802_1x_mka_dump_peer_body(body);
return 0;
}
/**
* ieee802_1x_mka_potential_peer_body_present
*/
static bool
ieee802_1x_mka_potential_peer_body_present(
struct ieee802_1x_mka_participant *participant)
{
return !dl_list_empty(&participant->potential_peers);
}
/**
* ieee802_1x_kay_get_potential_peer_length
*/
static int
ieee802_1x_mka_get_potential_peer_length(
struct ieee802_1x_mka_participant *participant)
{
int len = MKA_HDR_LEN;
struct ieee802_1x_kay_peer *peer;
dl_list_for_each(peer, &participant->potential_peers,
struct ieee802_1x_kay_peer, list)
len += sizeof(struct ieee802_1x_mka_peer_id);
return MKA_ALIGN_LENGTH(len);
}
/**
* ieee802_1x_mka_encode_potential_peer_body -
*/
static int
ieee802_1x_mka_encode_potential_peer_body(
struct ieee802_1x_mka_participant *participant,
struct wpabuf *buf)
{
struct ieee802_1x_mka_peer_body *body;
struct ieee802_1x_kay_peer *peer;
unsigned int length;
struct ieee802_1x_mka_peer_id *body_peer;
length = ieee802_1x_mka_get_potential_peer_length(participant);
body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
body->type = MKA_POTENTIAL_PEER_LIST;
set_mka_param_body_len(body, length - MKA_HDR_LEN);
dl_list_for_each(peer, &participant->potential_peers,
struct ieee802_1x_kay_peer, list) {
body_peer = wpabuf_put(buf,
sizeof(struct ieee802_1x_mka_peer_id));
os_memcpy(body_peer->mi, peer->mi, MI_LEN);
body_peer->mn = host_to_be32(peer->mn);
}
ieee802_1x_mka_dump_peer_body(body);
return 0;
}
/**
* ieee802_1x_mka_i_in_peerlist -
*/
static bool
ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
const u8 *mka_msg, size_t msg_len)
{
struct ieee802_1x_mka_hdr *hdr;
size_t body_len;
size_t left_len;
u8 body_type;
const u8 *pos;
size_t i;
for (pos = mka_msg, left_len = msg_len;
left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN;
left_len -= MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN,
pos += MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN) {
hdr = (struct ieee802_1x_mka_hdr *) pos;
body_len = get_mka_param_body_len(hdr);
body_type = get_mka_param_body_type(hdr);
if (left_len < (MKA_HDR_LEN + MKA_ALIGN_LENGTH(body_len) + DEFAULT_ICV_LEN)) {
wpa_printf(MSG_ERROR,
"KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
left_len, MKA_HDR_LEN,
MKA_ALIGN_LENGTH(body_len),
DEFAULT_ICV_LEN);
return false;
}
if (body_type != MKA_LIVE_PEER_LIST &&
body_type != MKA_POTENTIAL_PEER_LIST)
continue;
if ((body_len % 16) != 0) {
wpa_printf(MSG_ERROR,
"KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
body_len);
continue;
}
ieee802_1x_mka_dump_peer_body(
(struct ieee802_1x_mka_peer_body *)pos);
for (i = 0; i < body_len;
i += sizeof(struct ieee802_1x_mka_peer_id)) {
const struct ieee802_1x_mka_peer_id *peer_mi;
peer_mi = (const struct ieee802_1x_mka_peer_id *)
(pos + MKA_HDR_LEN + i);
if (os_memcmp(peer_mi->mi, participant->mi,
MI_LEN) == 0) {
u32 mn = be_to_host32(peer_mi->mn);
wpa_printf(MSG_DEBUG,
"KaY: My MI - received MN %u, most recently transmitted MN %u",
mn, participant->mn);
/* IEEE Std 802.1X-2010 is not exactly clear
* which values of MN should be accepted here.
* It uses "acceptably recent MN" language
* without defining what would be acceptable
* recent. For now, allow the last two used MN
* values (i.e., peer having copied my MI,MN
* from either of the last two MKPDUs that I
* have sent). */
if (mn == participant->mn ||
(participant->mn > 1 &&
mn == participant->mn - 1))
return true;
}
}
}
return false;
}
/**
* ieee802_1x_mka_decode_live_peer_body -
*/
static int ieee802_1x_mka_decode_live_peer_body(
struct ieee802_1x_mka_participant *participant,
const u8 *peer_msg, size_t msg_len)
{
const struct ieee802_1x_mka_hdr *hdr;
struct ieee802_1x_kay_peer *peer;
size_t body_len;
size_t i;
bool is_included;
is_included = ieee802_1x_kay_is_in_live_peer(
participant, participant->current_peer_id.mi);
hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
body_len = get_mka_param_body_len(hdr);
if (body_len % 16 != 0) {
wpa_printf(MSG_ERROR,
"KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
body_len);
return -1;
}
for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) {
const struct ieee802_1x_mka_peer_id *peer_mi;
u32 peer_mn;
peer_mi = (const struct ieee802_1x_mka_peer_id *)
(peer_msg + MKA_HDR_LEN + i);
peer_mn = be_to_host32(peer_mi->mn);
/* it is myself */
if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
/* My message id is used by other participant */
if (peer_mn > participant->mn &&
!reset_participant_mi(participant))
wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
continue;
}
if (!is_included)
continue;
peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi);
if (peer) {
peer->mn = peer_mn;
} else if (!ieee802_1x_kay_create_potential_peer(
participant, peer_mi->mi, peer_mn)) {
return -1;
}
}
return 0;
}
/**
* ieee802_1x_mka_decode_potential_peer_body -
*/
static int
ieee802_1x_mka_decode_potential_peer_body(
struct ieee802_1x_mka_participant *participant,
const u8 *peer_msg, size_t msg_len)
{
const struct ieee802_1x_mka_hdr *hdr;
size_t body_len;
size_t i;
hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
body_len = get_mka_param_body_len(hdr);
if (body_len % 16 != 0) {
wpa_printf(MSG_ERROR,
"KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
body_len);
return -1;
}
for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) {
const struct ieee802_1x_mka_peer_id *peer_mi;
u32 peer_mn;
peer_mi = (struct ieee802_1x_mka_peer_id *)
(peer_msg + MKA_HDR_LEN + i);
peer_mn = be_to_host32(peer_mi->mn);
/* it is myself */
if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
/* My message id is used by other participant */
if (peer_mn > participant->mn &&
!reset_participant_mi(participant))
wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
continue;
}
}
return 0;
}
/**
* ieee802_1x_mka_sak_use_body_present
*/
static bool
ieee802_1x_mka_sak_use_body_present(
struct ieee802_1x_mka_participant *participant)
{
return participant->to_use_sak;
}
/**
* ieee802_1x_mka_get_sak_use_length
*/
static int
ieee802_1x_mka_get_sak_use_length(
struct ieee802_1x_mka_participant *participant)
{
int length = MKA_HDR_LEN;
if (participant->kay->macsec_desired && participant->advised_desired)
length = sizeof(struct ieee802_1x_mka_sak_use_body);
return MKA_ALIGN_LENGTH(length);
}
/**
* ieee802_1x_mka_get_lpn
*/
static u32
ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal,
struct ieee802_1x_mka_ki *ki)
{
struct transmit_sa *txsa;
u32 lpn = 0;
dl_list_for_each(txsa, &principal->txsc->sa_list,
struct transmit_sa, list) {
if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
/* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses
* MKA to communicate the lowest PN used for
* transmission with the SAK within the last two
* seconds". Achieve this 2 second delay by setting the
* lpn using the transmit next PN (i.e., txsa->next_pn)
* that was read last time here (i.e., mka_hello_time
* 2 seconds ago).
*
* The lowest acceptable PN is the same as the last
* transmitted PN, which is one less than the next
* transmit PN.
*
* NOTE: This method only works if mka_hello_time is 2s.
*/
lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0;
/* Now read the current transmit next PN for use next
* time through. */
secy_get_transmit_next_pn(principal->kay, txsa);
break;
}
}
if (lpn == 0)
lpn = 1;
return lpn;
}
/**
* ieee802_1x_mka_encode_sak_use_body -
*/
static int
ieee802_1x_mka_encode_sak_use_body(
struct ieee802_1x_mka_participant *participant,
struct wpabuf *buf)
{
struct ieee802_1x_mka_sak_use_body *body;
struct ieee802_1x_kay *kay = participant->kay;
unsigned int length;
u32 olpn, llpn;
length = ieee802_1x_mka_get_sak_use_length(participant);
body = wpabuf_put(buf, length);
body->type = MKA_SAK_USE;
set_mka_param_body_len(body, length - MKA_HDR_LEN);
if (length == MKA_HDR_LEN) {
body->ptx = true;
body->prx = true;
body->lan = 0;
body->lrx = false;
body->ltx = false;
body->delay_protect = false;
return 0;
}
/* data delay protect */
body->delay_protect = kay->mka_hello_time <= MKA_BOUNDED_HELLO_TIME;
/* lowest accept packet numbers */
olpn = ieee802_1x_mka_get_lpn(participant, &participant->oki);
body->olpn = host_to_be32(olpn);
llpn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
body->llpn = host_to_be32(llpn);
if (participant->is_key_server) {
/* The CP will spend most of it's time in RETIRE where only
* the old key is populated. Therefore we should be checking
* the OLPN most of the time.
*/
if (participant->lrx) {
if (llpn > kay->pn_exhaustion) {
wpa_printf(MSG_WARNING,
"KaY: My LLPN exhaustion");
participant->new_sak = true;
}
} else {
if (olpn > kay->pn_exhaustion) {
wpa_printf(MSG_WARNING,
"KaY: My OLPN exhaustion");
participant->new_sak = true;
}
}
}
/* plain tx, plain rx */
body->ptx = !kay->macsec_protect;
body->prx = kay->macsec_validate != Strict;
/* latest key: rx, tx, key server member identifier key number */
body->lan = participant->lan;
os_memcpy(body->lsrv_mi, participant->lki.mi, sizeof(body->lsrv_mi));
body->lkn = host_to_be32(participant->lki.kn);
body->lrx = participant->lrx;
body->ltx = participant->ltx;
/* old key: rx, tx, key server member identifier key number */
body->oan = participant->oan;
if (participant->oki.kn != participant->lki.kn &&
participant->oki.kn != 0) {
body->otx = true;
body->orx = true;
os_memcpy(body->osrv_mi, participant->oki.mi,
sizeof(body->osrv_mi));
body->okn = host_to_be32(participant->oki.kn);
} else {
body->otx = false;
body->orx = false;
}
/* set CP's variable */
if (body->ltx) {
kay->tx_enable = true;
kay->port_enable = true;
}
if (body->lrx)
kay->rx_enable = true;
ieee802_1x_mka_dump_sak_use_body(body);
return 0;
}
/**
* ieee802_1x_mka_decode_sak_use_body -
*/
static int
ieee802_1x_mka_decode_sak_use_body(
struct ieee802_1x_mka_participant *participant,
const u8 *mka_msg, size_t msg_len)
{
struct ieee802_1x_mka_hdr *hdr;
struct ieee802_1x_mka_sak_use_body *body;
struct ieee802_1x_kay_peer *peer;
struct data_key *sa_key = NULL;
size_t body_len;
struct ieee802_1x_mka_ki ki;
u32 lpn;
struct ieee802_1x_kay *kay = participant->kay;
u32 olpn, llpn;
if (!participant->principal) {
wpa_printf(MSG_WARNING, "KaY: Participant is not principal");
return -1;
}
peer = ieee802_1x_kay_get_live_peer(participant,
participant->current_peer_id.mi);
if (!peer) {
wpa_printf(MSG_WARNING,
"KaY: The peer (%s) is not my live peer - ignore MACsec SAK Use parameter set",
mi_txt(participant->current_peer_id.mi));
return -1;
}
hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
body_len = get_mka_param_body_len(hdr);
body = (struct ieee802_1x_mka_sak_use_body *) mka_msg;
ieee802_1x_mka_dump_sak_use_body(body);
if ((body_len != 0) && (body_len < 40)) {
wpa_printf(MSG_ERROR,
"KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 40, or more octets",
body_len);
return -1;
}
/* TODO: what action should I take when peer does not support MACsec */
if (body_len == 0) {
wpa_printf(MSG_WARNING, "KaY: Peer does not support MACsec");
return 0;
}
/* TODO: when the plain tx or rx of peer is true, should I change
* the attribute of controlled port
*/
if (body->prx)
wpa_printf(MSG_WARNING, "KaY: peer's plain rx are TRUE");
if (body->ptx)
wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE");
/* TODO: how to set the MACsec hardware when delay_protect is true */
if (body->delay_protect &&
(!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) {
wpa_printf(MSG_WARNING,
"KaY: Lowest packet number should be greater than 0 when delay_protect is TRUE");
return -1;
}
olpn = be_to_host32(body->olpn);
llpn = be_to_host32(body->llpn);
/* Our most recent distributed key should be the first in the list.
* If it doesn't exist then we can't really do anything.
* Be lenient and don't return error here as there are legitimate cases
* where this can happen such as when a new participant joins the CA and
* the first frame it receives can have a SAKuse but not distSAK.
*/
sa_key = dl_list_first(&participant->sak_list, struct data_key, list);
if (!sa_key) {
wpa_printf(MSG_INFO,
"KaY: We don't have a latest distributed key - ignore SAK use");
return 0;
}
/* The peer's most recent key will be the "latest key" if it is present
* otherwise it will be the "old key" if in the RETIRE state.
*/
if (body->lrx) {
os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi));
ki.kn = be_to_host32(body->lkn);
lpn = llpn;
} else {
os_memcpy(ki.mi, body->osrv_mi, sizeof(ki.mi));
ki.kn = be_to_host32(body->okn);
lpn = olpn;
}
/* If the most recent distributed keys don't agree then someone is out
* of sync. Perhaps non key server hasn't processed the most recent
* distSAK yet and the key server is processing an old packet after it
* has done distSAK. Be lenient and don't return error in this
* particular case; otherwise, the key server will reset its MI and
* cause a traffic disruption which is really undesired for a simple
* timing issue.
*/
if (!is_ki_equal(&sa_key->key_identifier, &ki)) {
wpa_printf(MSG_INFO,
"KaY: Distributed keys don't match - ignore SAK use");
return 0;
}
sa_key->next_pn = lpn;
/* The key server must check that all peers are using the most recent
* distributed key. Non key servers must check if the key server is
* transmitting.
*/
if (participant->is_key_server) {
struct ieee802_1x_kay_peer *peer_iter;
bool all_receiving = true;
/* Distributed keys are equal from above comparison. */
peer->sak_used = true;
dl_list_for_each(peer_iter, &participant->live_peers,
struct ieee802_1x_kay_peer, list) {
if (!peer_iter->sak_used) {
all_receiving = false;
break;
}
}
if (all_receiving) {
participant->to_dist_sak = false;
ieee802_1x_cp_set_allreceiving(kay->cp, true);
ieee802_1x_cp_sm_step(kay->cp);
}
} else if (peer->is_key_server) {
if (body->ltx) {
ieee802_1x_cp_set_servertransmitting(kay->cp, true);
ieee802_1x_cp_sm_step(kay->cp);
}
}
/* If I'm key server, and detects peer member PN exhaustion, rekey.
* We only need to check the PN of the most recent distributed key. This
* could be the peer's "latest" or "old" key depending on its current
* state. If both "old" and "latest" keys are present then the "old" key
* has already been exhausted.
*/
if (participant->is_key_server && lpn > kay->pn_exhaustion) {
participant->new_sak = true;
wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion");
}
/* Get the associated RX SAs of the keys for delay protection since both
* can be in use. Delay protect window (communicated via MKA) is tighter
* than SecY's current replay protect window, so tell SecY the new (and
* higher) lpn.
*/
if (body->delay_protect) {
struct receive_sc *rxsc;
struct receive_sa *rxsa;
bool found = false;
dl_list_for_each(rxsc, &participant->rxsc_list,
struct receive_sc, list) {
dl_list_for_each(rxsa, &rxsc->sa_list,
struct receive_sa, list) {
if (sa_key && rxsa->pkey == sa_key) {
found = true;
break;
}
}
if (found)
break;
}
if (found) {
secy_get_receive_lowest_pn(participant->kay, rxsa);
if (lpn > rxsa->lowest_pn) {
rxsa->lowest_pn = lpn;
secy_set_receive_lowest_pn(participant->kay,
rxsa);
wpa_printf(MSG_DEBUG,
"KaY: update dist LPN=0x%x", lpn);
}
}
/* FIX: Delay protection for the SA being replaced is not
* implemented. Note that this key will be active for at least
* MKA_SAK_RETIRE_TIME (3 seconds) but could be longer depending
* on how long it takes to get from RECEIVE to TRANSMITTING or
* if going via ABANDON. Delay protection does allow PNs within
* a 2 second window, so getting PN would be a lot of work for
* just 1 second's worth of protection.
*/
}
return 0;
}
/**
* ieee802_1x_mka_dist_sak_body_present
*/
static bool
ieee802_1x_mka_dist_sak_body_present(
struct ieee802_1x_mka_participant *participant)
{
return participant->is_key_server && participant->to_dist_sak &&
participant->new_key;
}
/**
* ieee802_1x_kay_get_dist_sak_length
*/
static int
ieee802_1x_mka_get_dist_sak_length(
struct ieee802_1x_mka_participant *participant)
{
int length = MKA_HDR_LEN;
unsigned int cs_index = participant->kay->macsec_csindex;
if (participant->advised_desired && cs_index < CS_TABLE_SIZE) {
length = sizeof(struct ieee802_1x_mka_dist_sak_body);
if (cs_index != DEFAULT_CS_INDEX)
length += CS_ID_LEN;
length += cipher_suite_tbl[cs_index].sak_len + 8;
}
return MKA_ALIGN_LENGTH(length);
}
/**
* ieee802_1x_mka_encode_dist_sak_body -
*/
static int
ieee802_1x_mka_encode_dist_sak_body(
struct ieee802_1x_mka_participant *participant,
struct wpabuf *buf)
{
struct ieee802_1x_mka_dist_sak_body *body;
struct data_key *sak;
unsigned int length;
unsigned int cs_index;
int sak_pos;
length = ieee802_1x_mka_get_dist_sak_length(participant);
body = wpabuf_put(buf, length);
body->type = MKA_DISTRIBUTED_SAK;
set_mka_param_body_len(body, length - MKA_HDR_LEN);
if (length == MKA_HDR_LEN) {
body->confid_offset = 0;
body->dan = 0;
return 0;
}
sak = participant->new_key;
if (!sak) {
wpa_printf(MSG_DEBUG,
"KaY: No SAK available to build Distributed SAK parameter set");
return -1;
}
body->confid_offset = sak->confidentiality_offset;
body->dan = sak->an;
body->kn = host_to_be32(sak->key_identifier.kn);
cs_index = participant->kay->macsec_csindex;
sak_pos = 0;
if (cs_index >= CS_TABLE_SIZE)
return -1;
if (cs_index != DEFAULT_CS_INDEX) {
be64 cs;
cs = host_to_be64(cipher_suite_tbl[cs_index].id);
os_memcpy(body->sak, &cs, CS_ID_LEN);
sak_pos = CS_ID_LEN;
}
if (aes_wrap(participant->kek.key, participant->kek.len,
cipher_suite_tbl[cs_index].sak_len / 8,
sak->key, body->sak + sak_pos)) {
wpa_printf(MSG_ERROR, "KaY: AES wrap failed");
return -1;
}
ieee802_1x_mka_dump_dist_sak_body(body);
return 0;
}
/**
* ieee802_1x_kay_init_data_key -
*/
static void ieee802_1x_kay_init_data_key(struct data_key *pkey)
{
pkey->transmits = true;
pkey->receives = true;
os_get_time(&pkey->created_time);
pkey->next_pn = 1;
pkey->user = 1;
}
/**
* ieee802_1x_kay_decode_dist_sak_body -
*/
static int
ieee802_1x_mka_decode_dist_sak_body(
struct ieee802_1x_mka_participant *participant,
const u8 *mka_msg, size_t msg_len)
{
struct ieee802_1x_mka_hdr *hdr;
struct ieee802_1x_mka_dist_sak_body *body;
struct ieee802_1x_kay_peer *peer;
struct macsec_ciphersuite *cs;
size_t body_len;
struct data_key *sa_key = NULL;
int sak_len;
u8 *wrap_sak;
u8 *unwrap_sak;
struct ieee802_1x_kay *kay = participant->kay;
hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
body_len = get_mka_param_body_len(hdr);
if ((body_len != 0) && (body_len != 28) && (body_len < 36)) {
wpa_printf(MSG_ERROR,
"KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 28, 36, or more octets",
body_len);
return -1;
}
if (!participant->principal) {
wpa_printf(MSG_ERROR,
"KaY: I can't accept the distributed SAK as I am not principal");
return -1;
}
if (participant->is_key_server) {
wpa_printf(MSG_ERROR,
"KaY: Reject distributed SAK since I'm a key server");
return -1;
}
if (!kay->macsec_desired ||
kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
wpa_printf(MSG_ERROR,
"KaY: I am not MACsec-desired or without MACsec capable");
return -1;
}
peer = ieee802_1x_kay_get_live_peer(participant,
participant->current_peer_id.mi);
if (!peer) {
wpa_printf(MSG_ERROR,
"KaY: The key server is not in my live peers list");
return -1;
}
if (!sci_equal(&kay->key_server_sci, &peer->sci)) {
wpa_printf(MSG_ERROR, "KaY: The key server is not elected");
return -1;
}
if (body_len == 0) {
kay->authenticated = true;
kay->secured = false;
kay->failed = false;
participant->advised_desired = false;
ieee802_1x_cp_connect_authenticated(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
wpa_printf(MSG_WARNING, "KaY: The Key server advise no MACsec");
participant->to_use_sak = false;
return 0;
}
participant->advised_desired = true;
kay->authenticated = false;
kay->secured = true;
kay->failed = false;
ieee802_1x_cp_connect_secure(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg;
ieee802_1x_mka_dump_dist_sak_body(body);
dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list)
{
if (os_memcmp(sa_key->key_identifier.mi,
participant->current_peer_id.mi, MI_LEN) == 0 &&
sa_key->key_identifier.kn == be_to_host32(body->kn)) {
wpa_printf(MSG_DEBUG,
"KaY: SAK has already been installed - do not set it again");
return 0;
}
}
if (body_len == 28) {
sak_len = DEFAULT_SA_KEY_LEN;
wrap_sak = body->sak;
kay->macsec_csindex = DEFAULT_CS_INDEX;
cs = &cipher_suite_tbl[kay->macsec_csindex];
} else {
unsigned int idx;
cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak,
&idx);
if (!cs) {
wpa_printf(MSG_ERROR,
"KaY: I can't support the Cipher Suite advised by key server");
return -1;
}
sak_len = cs->sak_len;
wrap_sak = body->sak + CS_ID_LEN;
kay->macsec_csindex = idx;
}
unwrap_sak = os_zalloc(sak_len);
if (!unwrap_sak) {
wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
return -1;
}
if (aes_unwrap(participant->kek.key, participant->kek.len,
sak_len >> 3, wrap_sak, unwrap_sak)) {
wpa_printf(MSG_ERROR, "KaY: AES unwrap failed");
os_free(unwrap_sak);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK.:",
unwrap_sak, sak_len);
sa_key = os_zalloc(sizeof(*sa_key));
if (!sa_key) {
os_free(unwrap_sak);
return -1;
}
os_memcpy(&sa_key->key_identifier.mi, &participant->current_peer_id.mi,
MI_LEN);
sa_key->key_identifier.kn = be_to_host32(body->kn);
sa_key->key = unwrap_sak;
sa_key->key_len = sak_len;
sa_key->confidentiality_offset = body->confid_offset;
sa_key->an = body->dan;
ieee802_1x_kay_init_data_key(sa_key);
ieee802_1x_kay_use_data_key(sa_key);
dl_list_add(&participant->sak_list, &sa_key->list);
ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
ieee802_1x_cp_sm_step(kay->cp);
ieee802_1x_cp_set_offset(kay->cp, body->confid_offset);
ieee802_1x_cp_sm_step(kay->cp);
ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier);
ieee802_1x_cp_set_distributedan(kay->cp, body->dan);
ieee802_1x_cp_signal_newsak(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
kay->rcvd_keys++;
participant->to_use_sak = true;
return 0;
}
/**
* ieee802_1x_mka_icv_body_present
*/
static bool
ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant *participant)
{
return true;
}
/**
* ieee802_1x_kay_get_icv_length
*/
static int
ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant)
{
int length;
/* Determine if we need space for the ICV Indicator */
if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
DEFAULT_ICV_LEN)
length = sizeof(struct ieee802_1x_mka_icv_body);
else
length = 0;
length += mka_alg_tbl[participant->kay->mka_algindex].icv_len;
return MKA_ALIGN_LENGTH(length);
}
/**
* ieee802_1x_mka_encode_icv_body -
*/
static int
ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant,
struct wpabuf *buf)
{
struct ieee802_1x_mka_icv_body *body;
unsigned int length;
u8 cmac[MAX_ICV_LEN];
length = ieee802_1x_mka_get_icv_length(participant);
if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
DEFAULT_ICV_LEN) {
wpa_printf(MSG_DEBUG, "KaY: ICV Indicator");
body = wpabuf_put(buf, MKA_HDR_LEN);
body->type = MKA_ICV_INDICATOR;
length -= MKA_HDR_LEN;
set_mka_param_body_len(body, length);
}
if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash(
participant->ick.key, participant->ick.len,
wpabuf_head(buf), wpabuf_len(buf), cmac)) {
wpa_printf(MSG_ERROR, "KaY: failed to calculate ICV");
return -1;
}
wpa_hexdump(MSG_DEBUG, "KaY: ICV", cmac, length);
os_memcpy(wpabuf_put(buf, length), cmac, length);
return 0;
}
/**
* ieee802_1x_mka_decode_icv_body -
*/
static const u8 *
ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant,
const u8 *mka_msg, size_t msg_len)
{
const struct ieee802_1x_mka_hdr *hdr;
const struct ieee802_1x_mka_icv_body *body;
size_t body_len;
size_t left_len;
u8 body_type;
const u8 *pos;
pos = mka_msg;
left_len = msg_len;
while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) {
hdr = (const struct ieee802_1x_mka_hdr *) pos;
body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
body_type = get_mka_param_body_type(hdr);
if (left_len < body_len + MKA_HDR_LEN)
break;
if (body_type != MKA_ICV_INDICATOR) {
left_len -= MKA_HDR_LEN + body_len;
pos += MKA_HDR_LEN + body_len;
continue;
}
body = (const struct ieee802_1x_mka_icv_body *) pos;
if (body_len
< mka_alg_tbl[participant->kay->mka_algindex].icv_len)
return NULL;
return body->icv;
}
return mka_msg + msg_len - DEFAULT_ICV_LEN;
}
/**
* ieee802_1x_mka_decode_dist_cak_body-
*/
static int
ieee802_1x_mka_decode_dist_cak_body(
struct ieee802_1x_mka_participant *participant,
const u8 *mka_msg, size_t msg_len)
{
struct ieee802_1x_mka_hdr *hdr;
size_t body_len;
hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
body_len = get_mka_param_body_len(hdr);
if (body_len < 28) {
wpa_printf(MSG_ERROR,
"KaY: MKA Use CAK Packet Body Length (%zu bytes) should be 28 or more octets",
body_len);
return -1;
}
return 0;
}
/**
* ieee802_1x_mka_decode_kmd_body -
*/
static int
ieee802_1x_mka_decode_kmd_body(
struct ieee802_1x_mka_participant *participant,
const u8 *mka_msg, size_t msg_len)
{
struct ieee802_1x_mka_hdr *hdr;
size_t body_len;
hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
body_len = get_mka_param_body_len(hdr);
if (body_len < 5) {
wpa_printf(MSG_ERROR,
"KaY: MKA Use KMD Packet Body Length (%zu bytes) should be 5 or more octets",
body_len);
return -1;
}
return 0;
}
/**
* ieee802_1x_mka_decode_announce_body -
*/
static int ieee802_1x_mka_decode_announce_body(
struct ieee802_1x_mka_participant *participant,
const u8 *mka_msg, size_t msg_len)
{
return 0;
}
struct mka_param_body_handler {
int (*body_tx)(struct ieee802_1x_mka_participant *participant,
struct wpabuf *buf);
int (*body_rx)(struct ieee802_1x_mka_participant *participant,
const u8 *mka_msg, size_t msg_len);
int (*body_length)(struct ieee802_1x_mka_participant *participant);
bool (*body_present)(struct ieee802_1x_mka_participant *participant);
};
static struct mka_param_body_handler mka_body_handler[] = {
/* Basic parameter set */
{
.body_tx = ieee802_1x_mka_encode_basic_body,
.body_rx = NULL,
.body_length = ieee802_1x_mka_basic_body_length,
.body_present = ieee802_1x_mka_basic_body_present
},
/* Live Peer List parameter set */
{
.body_tx = ieee802_1x_mka_encode_live_peer_body,
.body_rx = ieee802_1x_mka_decode_live_peer_body,
.body_length = ieee802_1x_mka_get_live_peer_length,
.body_present = ieee802_1x_mka_live_peer_body_present
},
/* Potential Peer List parameter set */
{
.body_tx = ieee802_1x_mka_encode_potential_peer_body,
.body_rx = ieee802_1x_mka_decode_potential_peer_body,
.body_length = ieee802_1x_mka_get_potential_peer_length,
.body_present = ieee802_1x_mka_potential_peer_body_present
},
/* MACsec SAK Use parameter set */
{
.body_tx = ieee802_1x_mka_encode_sak_use_body,
.body_rx = ieee802_1x_mka_decode_sak_use_body,
.body_length = ieee802_1x_mka_get_sak_use_length,
.body_present = ieee802_1x_mka_sak_use_body_present
},
/* Distributed SAK parameter set */
{
.body_tx = ieee802_1x_mka_encode_dist_sak_body,
.body_rx = ieee802_1x_mka_decode_dist_sak_body,
.body_length = ieee802_1x_mka_get_dist_sak_length,
.body_present = ieee802_1x_mka_dist_sak_body_present
},
/* Distribute CAK parameter set */
{
.body_tx = NULL,
.body_rx = ieee802_1x_mka_decode_dist_cak_body,
.body_length = NULL,
.body_present = NULL
},
/* KMD parameter set */
{
.body_tx = NULL,
.body_rx = ieee802_1x_mka_decode_kmd_body,
.body_length = NULL,
.body_present = NULL
},
/* Announcement parameter set */
{
.body_tx = NULL,
.body_rx = ieee802_1x_mka_decode_announce_body,
.body_length = NULL,
.body_present = NULL
},
/* ICV Indicator parameter set */
{
.body_tx = ieee802_1x_mka_encode_icv_body,
.body_rx = NULL,
.body_length = ieee802_1x_mka_get_icv_length,
.body_present = ieee802_1x_mka_icv_body_present
},
};
/**
* ieee802_1x_kay_use_data_key - Take reference on a key
*/
static void ieee802_1x_kay_use_data_key(struct data_key *pkey)
{
pkey->user++;
}
/**
* ieee802_1x_kay_deinit_data_key - Release reference on a key and
* free if there are no remaining users
*/
static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
{
if (!pkey)
return;
pkey->user--;
if (pkey->user > 1)
return;
os_free(pkey->key);
os_free(pkey);
}
/**
* ieee802_1x_kay_generate_new_sak -
*/
static int
ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
{
struct data_key *sa_key = NULL;
struct ieee802_1x_kay_peer *peer;
struct ieee802_1x_kay *kay = participant->kay;
int ctx_len, ctx_offset;
u8 *context;
unsigned int key_len;
u8 *key;
struct macsec_ciphersuite *cs;
/* check condition for generating a fresh SAK:
* must have one live peer
* and MKA life time elapse since last distribution
* or potential peer is empty
*/
if (dl_list_empty(&participant->live_peers)) {
wpa_printf(MSG_ERROR,
"KaY: Live peers list must not be empty when generating fresh SAK");
return -1;
}
/* FIXME: A fresh SAK not generated until
* the live peer list contains at least one peer and
* MKA life time has elapsed since the prior SAK was first distributed,
* or the Key server's potential peer is empty
* but I can't understand the second item, so
* here only check first item and ingore
* && (!dl_list_empty(&participant->potential_peers))) {
*/
if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) {
wpa_printf(MSG_ERROR,
"KaY: Life time has not elapsed since prior SAK distributed");
return -1;
}
cs = &cipher_suite_tbl[kay->macsec_csindex];
key_len = cs->sak_len;
key = os_zalloc(key_len);
if (!key) {
wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
return -1;
}
ctx_len = key_len + sizeof(kay->dist_kn);
dl_list_for_each(peer, &participant->live_peers,
struct ieee802_1x_kay_peer, list)
ctx_len += sizeof(peer->mi);
ctx_len += sizeof(participant->mi);
context = os_zalloc(ctx_len);
if (!context)
goto fail;
ctx_offset = 0;
if (os_get_random(context + ctx_offset, key_len) < 0)
goto fail;
ctx_offset += key_len;
dl_list_for_each(peer, &participant->live_peers,
struct ieee802_1x_kay_peer, list) {
os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi));
ctx_offset += sizeof(peer->mi);
}
os_memcpy(context + ctx_offset, participant->mi,
sizeof(participant->mi));
ctx_offset += sizeof(participant->mi);
os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn));
if (key_len == 16 || key_len == 32) {
if (ieee802_1x_sak_aes_cmac(participant->cak.key,
participant->cak.len,
context, ctx_len,
key, key_len)) {
wpa_printf(MSG_ERROR, "KaY: Failed to generate SAK");
goto fail;
}
} else {
wpa_printf(MSG_ERROR, "KaY: SAK Length(%u) not supported",
key_len);
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len);
os_free(context);
context = NULL;
sa_key = os_zalloc(sizeof(*sa_key));
if (!sa_key) {
wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
goto fail;
}
sa_key->key = key;
sa_key->key_len = key_len;
os_memcpy(sa_key->key_identifier.mi, participant->mi, MI_LEN);
sa_key->key_identifier.kn = kay->dist_kn;
sa_key->confidentiality_offset = kay->macsec_confidentiality;
sa_key->an = kay->dist_an;
ieee802_1x_kay_init_data_key(sa_key);
participant->new_key = sa_key;
ieee802_1x_kay_use_data_key(sa_key);
dl_list_add(&participant->sak_list, &sa_key->list);
ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
ieee802_1x_cp_sm_step(kay->cp);
ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality);
ieee802_1x_cp_sm_step(kay->cp);
ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier);
ieee802_1x_cp_set_distributedan(kay->cp, sa_key->an);
ieee802_1x_cp_signal_newsak(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
dl_list_for_each(peer, &participant->live_peers,
struct ieee802_1x_kay_peer, list)
peer->sak_used = false;
kay->dist_kn++;
kay->dist_an++;
if (kay->dist_an > 3)
kay->dist_an = 0;
kay->dist_time = time(NULL);
return 0;
fail:
os_free(key);
os_free(context);
return -1;
}
static int compare_priorities(const struct ieee802_1x_kay_peer *peer,
const struct ieee802_1x_kay_peer *other)
{
if (peer->key_server_priority < other->key_server_priority)
return -1;
if (other->key_server_priority < peer->key_server_priority)
return 1;
return os_memcmp(peer->sci.addr, other->sci.addr, ETH_ALEN);
}
/**
* ieee802_1x_kay_elect_key_server - elect the key server
* when to elect: whenever the live peers list changes
*/
static int
ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
{
struct ieee802_1x_kay_peer *peer;
struct ieee802_1x_kay_peer *key_server = NULL;
struct ieee802_1x_kay *kay = participant->kay;
bool i_is_key_server;
int priority_comparison;
if (participant->is_obliged_key_server) {
participant->new_sak = true;
participant->to_dist_sak = false;
ieee802_1x_cp_set_electedself(kay->cp, true);
return 0;
}
/* elect the key server among the peers */
dl_list_for_each(peer, &participant->live_peers,
struct ieee802_1x_kay_peer, list) {
if (!peer->is_key_server)
continue;
if (!key_server) {
key_server = peer;
continue;
}
if (compare_priorities(peer, key_server) < 0)
key_server = peer;
}
/* elect the key server between me and the above elected peer */
i_is_key_server = false;
if (key_server && participant->can_be_key_server) {
struct ieee802_1x_kay_peer tmp;
tmp.key_server_priority = kay->actor_priority;
os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci));
priority_comparison = compare_priorities(&tmp, key_server);
if (priority_comparison < 0) {
i_is_key_server = true;
} else if (priority_comparison == 0) {
wpa_printf(MSG_WARNING,
"KaY: Cannot elect key server between me and peer, duplicate MAC detected");
key_server = NULL;
}
} else if (participant->can_be_key_server) {
i_is_key_server = true;
}
if (i_is_key_server) {
ieee802_1x_cp_set_electedself(kay->cp, true);
if (!sci_equal(&kay->key_server_sci, &kay->actor_sci)) {
ieee802_1x_cp_signal_chgdserver(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
}
participant->is_key_server = true;
participant->principal = true;
participant->new_sak = true;
wpa_printf(MSG_DEBUG, "KaY: I am elected as key server");
participant->to_dist_sak = false;
participant->is_elected = true;
os_memcpy(&kay->key_server_sci, &kay->actor_sci,
sizeof(kay->key_server_sci));
kay->key_server_priority = kay->actor_priority;
} else if (key_server) {
wpa_printf(MSG_DEBUG,
"KaY: Peer %s was elected as the key server",
mi_txt(key_server->mi));
ieee802_1x_cp_set_electedself(kay->cp, false);
if (!sci_equal(&kay->key_server_sci, &key_server->sci)) {
ieee802_1x_cp_signal_chgdserver(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
}
participant->is_key_server = false;
participant->principal = true;
participant->is_elected = true;
os_memcpy(&kay->key_server_sci, &key_server->sci,
sizeof(kay->key_server_sci));
kay->key_server_priority = key_server->key_server_priority;
} else {
participant->principal = false;
participant->is_key_server = false;
participant->is_elected = false;
}
return 0;
}
/**
* ieee802_1x_kay_decide_macsec_use - the key server determinate
* how to use MACsec: whether use MACsec and its capability
* protectFrames will be advised if the key server and one of its live peers are
* MACsec capable and one of those request MACsec protection
*/
static int
ieee802_1x_kay_decide_macsec_use(
struct ieee802_1x_mka_participant *participant)
{
struct ieee802_1x_kay *kay = participant->kay;
struct ieee802_1x_kay_peer *peer;
enum macsec_cap less_capability;
bool has_peer;
if (!participant->is_key_server)
return -1;
/* key server self is MACsec-desired and requesting MACsec */
if (!kay->macsec_desired) {
participant->advised_desired = false;
return -1;
}
if (kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
participant->advised_desired = false;
return -1;
}
less_capability = kay->macsec_capable;
/* at least one of peers is MACsec-desired and requesting MACsec */
has_peer = false;
dl_list_for_each(peer, &participant->live_peers,
struct ieee802_1x_kay_peer, list) {
if (!peer->macsec_desired)
continue;
if (peer->macsec_capability == MACSEC_CAP_NOT_IMPLEMENTED)
continue;
less_capability = (less_capability < peer->macsec_capability) ?
less_capability : peer->macsec_capability;
has_peer = true;
}
if (has_peer) {
participant->advised_desired = true;
participant->advised_capability = less_capability;
kay->authenticated = false;
kay->secured = true;
kay->failed = false;
ieee802_1x_cp_connect_secure(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
} else {
participant->advised_desired = false;
participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED;
participant->to_use_sak = false;
kay->authenticated = true;
kay->secured = false;
kay->failed = false;
kay->ltx_kn = 0;
kay->ltx_an = 0;
kay->lrx_kn = 0;
kay->lrx_an = 0;
kay->otx_kn = 0;
kay->otx_an = 0;
kay->orx_kn = 0;
kay->orx_an = 0;
ieee802_1x_cp_connect_authenticated(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
}
return 0;
}
static const u8 pae_group_addr[ETH_ALEN] = {
0x01, 0x80, 0xc2, 0x00, 0x00, 0x03
};
/**
* ieee802_1x_kay_encode_mkpdu -
*/
static int
ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant,
struct wpabuf *pbuf)
{
unsigned int i;
struct ieee8023_hdr *ether_hdr;
struct ieee802_1x_hdr *eapol_hdr;
ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr));
os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest));
os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr,
sizeof(ether_hdr->dest));
ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL);
wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR
" Ethertype=0x%x",
MAC2STR(ether_hdr->dest), MAC2STR(ether_hdr->src),
be_to_host16(ether_hdr->ethertype));
eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr));
eapol_hdr->version = EAPOL_VERSION;
eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA;
eapol_hdr->length = host_to_be16(wpabuf_tailroom(pbuf));
wpa_printf(MSG_DEBUG,
"KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u",
eapol_hdr->version, eapol_hdr->type,
be_to_host16(eapol_hdr->length));
for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
if (mka_body_handler[i].body_present &&
mka_body_handler[i].body_present(participant)) {
if (mka_body_handler[i].body_tx(participant, pbuf))
return -1;
}
}
return 0;
}
/**
* ieee802_1x_participant_send_mkpdu -
*/
static int
ieee802_1x_participant_send_mkpdu(
struct ieee802_1x_mka_participant *participant)
{
struct wpabuf *buf;
struct ieee802_1x_kay *kay = participant->kay;
size_t length = 0;
unsigned int i;
wpa_printf(MSG_DEBUG, "KaY: Encode and send an MKPDU (ifname=%s)",
kay->if_name);
length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr);
for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
if (mka_body_handler[i].body_present &&
mka_body_handler[i].body_present(participant))
length += mka_body_handler[i].body_length(participant);
}
buf = wpabuf_alloc(length);
if (!buf) {
wpa_printf(MSG_ERROR, "KaY: out of memory");
return -1;
}
if (ieee802_1x_kay_encode_mkpdu(participant, buf)) {
wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail");
return -1;
}
wpa_hexdump_buf(MSG_MSGDUMP, "KaY: Outgoing MKPDU", buf);
l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf));
wpabuf_free(buf);
kay->active = true;
participant->active = true;
return 0;
}
static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa);
static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay,
struct transmit_sa *sa)
{
secy_disable_transmit_sa(kay, sa);
secy_delete_transmit_sa(kay, sa);
ieee802_1x_kay_deinit_transmit_sa(sa);
}
/**
* ieee802_1x_participant_timer -
*/
static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
{
struct ieee802_1x_mka_participant *participant;
struct ieee802_1x_kay *kay;
struct ieee802_1x_kay_peer *peer, *pre_peer;
time_t now = time(NULL);
bool lp_changed;
struct receive_sc *rxsc, *pre_rxsc;
struct transmit_sa *txsa, *pre_txsa;
participant = (struct ieee802_1x_mka_participant *)eloop_ctx;
kay = participant->kay;
wpa_printf(MSG_DEBUG, "KaY: Participant timer (ifname=%s)",
kay->if_name);
if (participant->cak_life) {
if (now > participant->cak_life)
goto delete_mka;
}
/* should delete MKA instance if there are not live peers
* when the MKA life elapsed since its creating */
if (participant->mka_life) {
if (dl_list_empty(&participant->live_peers)) {
if (now > participant->mka_life)
goto delete_mka;
} else {
participant->mka_life = 0;
}
}
lp_changed = false;
dl_list_for_each_safe(peer, pre_peer, &participant->live_peers,
struct ieee802_1x_kay_peer, list) {
if (now > peer->expire) {
wpa_printf(MSG_DEBUG, "KaY: Live peer removed");
wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
sizeof(peer->mi));
wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
dl_list_for_each_safe(rxsc, pre_rxsc,
&participant->rxsc_list,
struct receive_sc, list) {
if (sci_equal(&rxsc->sci, &peer->sci)) {
ieee802_1x_kay_deinit_receive_sc(
participant, rxsc);
}
}
dl_list_del(&peer->list);
os_free(peer);
lp_changed = true;
}
}
if (lp_changed) {
if (dl_list_empty(&participant->live_peers)) {
participant->advised_desired = false;
participant->advised_capability =
MACSEC_CAP_NOT_IMPLEMENTED;
participant->to_use_sak = false;
participant->ltx = false;
participant->lrx = false;
participant->otx = false;
participant->orx = false;
participant->is_key_server = false;
participant->is_elected = false;
kay->authenticated = false;
kay->secured = false;
kay->failed = false;
kay->ltx_kn = 0;
kay->ltx_an = 0;
kay->lrx_kn = 0;
kay->lrx_an = 0;
kay->otx_kn = 0;
kay->otx_an = 0;
kay->orx_kn = 0;
kay->orx_an = 0;
dl_list_for_each_safe(txsa, pre_txsa,
&participant->txsc->sa_list,
struct transmit_sa, list) {
ieee802_1x_delete_transmit_sa(kay, txsa);
}
ieee802_1x_cp_connect_pending(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
} else {
ieee802_1x_kay_elect_key_server(participant);
ieee802_1x_kay_decide_macsec_use(participant);
}
}
dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers,
struct ieee802_1x_kay_peer, list) {
if (now > peer->expire) {
wpa_printf(MSG_DEBUG, "KaY: Potential peer removed");
wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
sizeof(peer->mi));
wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
dl_list_del(&peer->list);
os_free(peer);
}
}
if (participant->new_sak && participant->is_key_server) {
if (!ieee802_1x_kay_generate_new_sak(participant))
participant->to_dist_sak = true;
participant->new_sak = false;
}
if (participant->retry_count < MAX_RETRY_CNT ||
participant->mode == PSK) {
ieee802_1x_participant_send_mkpdu(participant);
participant->retry_count++;
}
eloop_register_timeout(kay->mka_hello_time / 1000, 0,
ieee802_1x_participant_timer,
participant, NULL);
return;
delete_mka:
kay->authenticated = false;
kay->secured = false;
kay->failed = true;
ieee802_1x_kay_delete_mka(kay, &participant->ckn);
}
/**
* ieee802_1x_kay_init_transmit_sa -
*/
static struct transmit_sa *
ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
struct data_key *key)
{
struct transmit_sa *psa;
key->tx_latest = true;
key->rx_latest = true;
psa = os_zalloc(sizeof(*psa));
if (!psa) {
wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
return NULL;
}
if (key->confidentiality_offset >= CONFIDENTIALITY_OFFSET_0 &&
key->confidentiality_offset <= CONFIDENTIALITY_OFFSET_50)
psa->confidentiality = true;
else
psa->confidentiality = false;
psa->an = an;
ieee802_1x_kay_use_data_key(key);
psa->pkey = key;
psa->next_pn = next_PN;
psa->sc = psc;
os_get_time(&psa->created_time);
psa->in_use = false;
dl_list_add(&psc->sa_list, &psa->list);
wpa_printf(MSG_DEBUG,
"KaY: Create transmit SA(an: %hhu, next_pn: %u) of SC",
an, next_PN);
return psa;
}
/**
* ieee802_1x_kay_deinit_transmit_sa -
*/
static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
{
ieee802_1x_kay_deinit_data_key(psa->pkey);
psa->pkey = NULL;
wpa_printf(MSG_DEBUG,
"KaY: Delete transmit SA(an: %hhu) of SC",
psa->an);
dl_list_del(&psa->list);
os_free(psa);
}
/**
* init_transmit_sc -
*/
static struct transmit_sc *
ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci)
{
struct transmit_sc *psc;
psc = os_zalloc(sizeof(*psc));
if (!psc) {
wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
return NULL;
}
os_memcpy(&psc->sci, sci, sizeof(psc->sci));
os_get_time(&psc->created_time);
psc->transmitting = false;
psc->encoding_sa = false;
psc->enciphering_sa = false;
dl_list_init(&psc->sa_list);
wpa_printf(MSG_DEBUG, "KaY: Create transmit SC - SCI: %s",
sci_txt(&psc->sci));
return psc;
}
/**
* ieee802_1x_kay_deinit_transmit_sc -
*/
static void
ieee802_1x_kay_deinit_transmit_sc(
struct ieee802_1x_mka_participant *participant, struct transmit_sc *psc)
{
struct transmit_sa *psa, *tmp;
wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC");
dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list)
ieee802_1x_delete_transmit_sa(participant->kay, psa);
secy_delete_transmit_sc(participant->kay, psc);
os_free(psc);
}
/****************** Interface between CP and KAY *********************/
/**
* ieee802_1x_kay_set_latest_sa_attr -
*/
int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *lki, u8 lan,
bool ltx, bool lrx)
{
struct ieee802_1x_mka_participant *principal;
principal = ieee802_1x_kay_get_principal_participant(kay);
if (!principal)
return -1;
if (!lki)
os_memset(&principal->lki, 0, sizeof(principal->lki));
else
os_memcpy(&principal->lki, lki, sizeof(principal->lki));
principal->lan = lan;
principal->ltx = ltx;
principal->lrx = lrx;
if (!lki) {
kay->ltx_kn = 0;
kay->lrx_kn = 0;
} else {
kay->ltx_kn = lki->kn;
kay->lrx_kn = lki->kn;
}
kay->ltx_an = lan;
kay->lrx_an = lan;
return 0;
}
/**
* ieee802_1x_kay_set_old_sa_attr -
*/
int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *oki,
u8 oan, bool otx, bool orx)
{
struct ieee802_1x_mka_participant *principal;
principal = ieee802_1x_kay_get_principal_participant(kay);
if (!principal)
return -1;
if (!oki)
os_memset(&principal->oki, 0, sizeof(principal->oki));
else
os_memcpy(&principal->oki, oki, sizeof(principal->oki));
principal->oan = oan;
principal->otx = otx;
principal->orx = orx;
if (!oki) {
kay->otx_kn = 0;
kay->orx_kn = 0;
} else {
kay->otx_kn = oki->kn;
kay->orx_kn = oki->kn;
}
kay->otx_an = oan;
kay->orx_an = oan;
return 0;
}
static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an)
{
struct transmit_sa *txsa;
dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) {
if (txsa->an == an)
return txsa;
}
return NULL;
}
static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an)
{
struct receive_sa *rxsa;
dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) {
if (rxsa->an == an)
return rxsa;
}
return NULL;
}
/**
* ieee802_1x_kay_create_sas -
*/
int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *lki)
{
struct data_key *sa_key, *latest_sak;
struct ieee802_1x_mka_participant *principal;
struct receive_sc *rxsc;
struct receive_sa *rxsa;
struct transmit_sa *txsa;
principal = ieee802_1x_kay_get_principal_participant(kay);
if (!principal)
return -1;
latest_sak = NULL;
dl_list_for_each(sa_key, &principal->sak_list, struct data_key, list) {
if (is_ki_equal(&sa_key->key_identifier, lki)) {
sa_key->rx_latest = true;
sa_key->tx_latest = true;
latest_sak = sa_key;
principal->to_use_sak = true;
} else {
sa_key->rx_latest = false;
sa_key->tx_latest = false;
}
}
if (!latest_sak) {
wpa_printf(MSG_ERROR, "KaY: lki related sak not found");
return -1;
}
dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL)
ieee802_1x_delete_receive_sa(kay, rxsa);
rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1,
latest_sak);
if (!rxsa)
return -1;
secy_create_receive_sa(kay, rxsa);
}
while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) !=
NULL)
ieee802_1x_delete_transmit_sa(kay, txsa);
txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
latest_sak->next_pn ?
latest_sak->next_pn : 1,
latest_sak);
if (!txsa)
return -1;
secy_create_transmit_sa(kay, txsa);
return 0;
}
/**
* ieee802_1x_kay_delete_sas -
*/
int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *ki)
{
struct data_key *sa_key, *pre_key;
struct transmit_sa *txsa, *pre_txsa;
struct receive_sa *rxsa, *pre_rxsa;
struct receive_sc *rxsc;
struct ieee802_1x_mka_participant *principal;
wpa_printf(MSG_DEBUG, "KaY: Entry into %s", __func__);
principal = ieee802_1x_kay_get_principal_participant(kay);
if (!principal)
return -1;
/* remove the transmit sa */
dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list,
struct transmit_sa, list) {
if (is_ki_equal(&txsa->pkey->key_identifier, ki))
ieee802_1x_delete_transmit_sa(kay, txsa);
}
/* remove the receive sa */
dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list,
struct receive_sa, list) {
if (is_ki_equal(&rxsa->pkey->key_identifier, ki))
ieee802_1x_delete_receive_sa(kay, rxsa);
}
}
/* remove the sak */
dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
struct data_key, list) {
if (is_ki_equal(&sa_key->key_identifier, ki)) {
if (principal->new_key == sa_key)
principal->new_key = NULL;
dl_list_del(&sa_key->list);
ieee802_1x_kay_deinit_data_key(sa_key);
break;
}
}
return 0;
}
/**
* ieee802_1x_kay_enable_tx_sas -
*/
int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *lki)
{
struct ieee802_1x_mka_participant *principal;
struct transmit_sa *txsa;
principal = ieee802_1x_kay_get_principal_participant(kay);
if (!principal)
return -1;
dl_list_for_each(txsa, &principal->txsc->sa_list, struct transmit_sa,
list) {
if (is_ki_equal(&txsa->pkey->key_identifier, lki)) {
txsa->in_use = true;
secy_enable_transmit_sa(kay, txsa);
ieee802_1x_cp_set_usingtransmitas(
principal->kay->cp, true);
ieee802_1x_cp_sm_step(principal->kay->cp);
}
}
return 0;
}
/**
* ieee802_1x_kay_enable_rx_sas -
*/
int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_ki *lki)
{
struct ieee802_1x_mka_participant *principal;
struct receive_sa *rxsa;
struct receive_sc *rxsc;
principal = ieee802_1x_kay_get_principal_participant(kay);
if (!principal)
return -1;
dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
{
if (is_ki_equal(&rxsa->pkey->key_identifier, lki)) {
rxsa->in_use = true;
secy_enable_receive_sa(kay, rxsa);
ieee802_1x_cp_set_usingreceivesas(
principal->kay->cp, true);
ieee802_1x_cp_sm_step(principal->kay->cp);
}
}
}
return 0;
}
/**
* ieee802_1x_kay_enable_new_info -
*/
int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay)
{
struct ieee802_1x_mka_participant *principal;
principal = ieee802_1x_kay_get_principal_participant(kay);
if (!principal)
return -1;
if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) {
ieee802_1x_participant_send_mkpdu(principal);
principal->retry_count++;
}
return 0;
}
/**
- * ieee802_1x_kay_mkpdu_sanity_check -
- * Sanity checks specified in IEEE Std 802.1X-2010, 11.11.2 (Validation of
+ * ieee802_1x_kay_mkpdu_validity_check -
+ * Validity checks specified in IEEE Std 802.1X-2010, 11.11.2 (Validation of
* MKPDUs)
*/
-static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
- const u8 *buf, size_t len)
+static int ieee802_1x_kay_mkpdu_validity_check(struct ieee802_1x_kay *kay,
+ const u8 *buf, size_t len)
{
struct ieee8023_hdr *eth_hdr;
struct ieee802_1x_hdr *eapol_hdr;
struct ieee802_1x_mka_hdr *mka_hdr;
struct ieee802_1x_mka_basic_body *body;
size_t mka_msg_len;
struct ieee802_1x_mka_participant *participant;
size_t body_len;
size_t ckn_len;
u8 icv[MAX_ICV_LEN];
const u8 *msg_icv;
/* len > eth+eapol header already verified in kay_l2_receive();
* likewise, eapol_hdr->length validated there */
eth_hdr = (struct ieee8023_hdr *) buf;
eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1);
wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR
" Ethertype=0x%x",
MAC2STR(eth_hdr->dest), MAC2STR(eth_hdr->src),
be_to_host16(eth_hdr->ethertype));
/* the destination address shall not be an individual address */
if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG,
"KaY: ethernet destination address is not PAE group address");
return -1;
}
wpa_printf(MSG_DEBUG,
"KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u",
eapol_hdr->version, eapol_hdr->type,
be_to_host16(eapol_hdr->length));
/* MKPDU shall not be less than 32 octets */
mka_msg_len = be_to_host16(eapol_hdr->length);
if (mka_msg_len < 32) {
wpa_printf(MSG_DEBUG, "KaY: MKPDU is less than 32 octets");
return -1;
}
/* MKPDU shall be a multiple of 4 octets */
if ((mka_msg_len % 4) != 0) {
wpa_printf(MSG_DEBUG,
"KaY: MKPDU is not multiple of 4 octets");
return -1;
}
wpa_hexdump(MSG_MSGDUMP, "KaY: EAPOL-MKA Packet Body (MKPDU)",
mka_hdr, mka_msg_len);
/* Room for body_len already verified in kay_l2_receive() */
body = (struct ieee802_1x_mka_basic_body *) mka_hdr;
body_len = get_mka_param_body_len(body);
/* EAPOL-MKA body should comprise basic parameter set and ICV */
if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) {
wpa_printf(MSG_ERROR,
"KaY: Received EAPOL-MKA Packet Body Length (%zu bytes) is less than the Basic Parameter Set Header Length (%zu bytes) + the Basic Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
mka_msg_len, MKA_HDR_LEN,
body_len, DEFAULT_ICV_LEN);
return -1;
}
if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) {
wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu",
body_len);
return -1;
}
ckn_len = body_len -
(sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) {
wpa_printf(MSG_WARNING,
"KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)",
ckn_len, MAX_CKN_LEN);
return -1;
}
ieee802_1x_mka_dump_basic_body(body);
/* CKN should be owned by I */
participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
if (!participant) {
wpa_printf(MSG_DEBUG, "KaY: CKN is not included in my CA");
return -1;
}
/* algorithm agility check */
if (os_memcmp(body->algo_agility, mka_algo_agility,
sizeof(body->algo_agility)) != 0) {
wpa_printf(MSG_INFO,
"KaY: Peer's algorithm agility (%s) not supported",
algo_agility_txt(body->algo_agility));
return -1;
}
/* ICV check */
/*
* The ICV will comprise the final octets of the packet body, whatever
* its size, not the fixed length 16 octets, indicated by the EAPOL
* packet body length.
*/
if (len < mka_alg_tbl[kay->mka_algindex].icv_len ||
mka_alg_tbl[kay->mka_algindex].icv_hash(
participant->ick.key, participant->ick.len,
buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) {
wpa_printf(MSG_ERROR, "KaY: Failed to calculate ICV");
return -1;
}
msg_icv = ieee802_1x_mka_decode_icv_body(participant,
(const u8 *) mka_hdr,
mka_msg_len);
if (!msg_icv) {
wpa_printf(MSG_WARNING, "KaY: No ICV in MKPDU - ignore it");
return -1;
}
wpa_hexdump(MSG_DEBUG, "KaY: Received ICV",
msg_icv, mka_alg_tbl[kay->mka_algindex].icv_len);
if (os_memcmp_const(msg_icv, icv,
mka_alg_tbl[kay->mka_algindex].icv_len) != 0) {
wpa_printf(MSG_WARNING,
"KaY: Computed ICV is not equal to Received ICV");
wpa_hexdump(MSG_DEBUG, "KaY: Calculated ICV",
icv, mka_alg_tbl[kay->mka_algindex].icv_len);
return -1;
}
return 0;
}
/**
* ieee802_1x_kay_decode_mkpdu -
*/
static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
const u8 *buf, size_t len)
{
struct ieee802_1x_mka_participant *participant;
struct ieee802_1x_mka_hdr *hdr;
struct ieee802_1x_kay_peer *peer;
size_t body_len;
size_t left_len;
u8 body_type;
int i;
const u8 *pos;
bool handled[256];
bool bad_sak_use = false; /* Error detected while processing SAK Use
* parameter set */
bool i_in_peerlist, is_in_live_peer, is_in_potential_peer;
wpa_printf(MSG_DEBUG, "KaY: Decode received MKPDU (ifname=%s)",
kay->if_name);
- if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len))
+ if (ieee802_1x_kay_mkpdu_validity_check(kay, buf, len))
return -1;
/* handle basic parameter set */
pos = buf + sizeof(struct ieee8023_hdr) + sizeof(struct ieee802_1x_hdr);
left_len = len - sizeof(struct ieee8023_hdr) -
sizeof(struct ieee802_1x_hdr);
participant = ieee802_1x_mka_decode_basic_body(kay, pos, left_len);
if (!participant)
return -1;
/* to skip basic parameter set */
hdr = (struct ieee802_1x_mka_hdr *) pos;
body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
if (left_len < body_len + MKA_HDR_LEN)
return -1;
pos += body_len + MKA_HDR_LEN;
left_len -= body_len + MKA_HDR_LEN;
/* check i am in the peer's peer list */
i_in_peerlist = ieee802_1x_mka_i_in_peerlist(participant, pos,
left_len);
is_in_live_peer = ieee802_1x_kay_is_in_live_peer(
participant, participant->current_peer_id.mi);
wpa_printf(MSG_DEBUG, "KaY: i_in_peerlist=%s is_in_live_peer=%s",
yes_no(i_in_peerlist), yes_no(is_in_live_peer));
if (i_in_peerlist && !is_in_live_peer) {
/* accept the peer as live peer */
is_in_potential_peer = ieee802_1x_kay_is_in_potential_peer(
participant, participant->current_peer_id.mi);
if (is_in_potential_peer) {
if (!ieee802_1x_kay_move_live_peer(
participant,
participant->current_peer_id.mi,
be_to_host32(participant->
current_peer_id.mn)))
return -1;
} else if (!ieee802_1x_kay_create_live_peer(
participant, participant->current_peer_id.mi,
be_to_host32(participant->
current_peer_id.mn))) {
return -1;
}
ieee802_1x_kay_elect_key_server(participant);
ieee802_1x_kay_decide_macsec_use(participant);
}
/*
* Handle other parameter set than basic parameter set.
* Each parameter set should be present only once.
*/
for (i = 0; i < 256; i++)
handled[i] = false;
handled[0] = true;
for (; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN;
pos += body_len + MKA_HDR_LEN,
left_len -= body_len + MKA_HDR_LEN) {
hdr = (struct ieee802_1x_mka_hdr *) pos;
body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
body_type = get_mka_param_body_type(hdr);
if (body_type == MKA_ICV_INDICATOR)
return 0;
if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
wpa_printf(MSG_ERROR,
"KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
left_len, MKA_HDR_LEN,
body_len, DEFAULT_ICV_LEN);
return -1;
}
if (handled[body_type]) {
wpa_printf(MSG_DEBUG,
"KaY: Ignore duplicated body type %u",
body_type);
continue;
}
handled[body_type] = true;
if (body_type < ARRAY_SIZE(mka_body_handler) &&
mka_body_handler[body_type].body_rx) {
if (mka_body_handler[body_type].body_rx
(participant, pos, left_len) != 0) {
/* Handle parameter set failure */
if (body_type != MKA_SAK_USE) {
wpa_printf(MSG_INFO,
"KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
body_type);
return -1;
}
/* Ideally DIST-SAK should be processed before
* SAK-USE. Unfortunately IEEE Std 802.1X-2010,
* 11.11.3 (Encoding MKPDUs) states SAK-USE(3)
* must always be encoded before DIST-SAK(4).
* Rather than redesigning mka_body_handler so
* that it somehow processes DIST-SAK before
* SAK-USE, just ignore SAK-USE failures if
* DIST-SAK is also present in this MKPDU. */
bad_sak_use = true;
}
} else {
wpa_printf(MSG_ERROR,
"KaY: The body type %d is not supported in this MKA version %d",
body_type, MKA_VERSION_ID);
}
}
if (bad_sak_use && !handled[MKA_DISTRIBUTED_SAK]) {
wpa_printf(MSG_INFO,
"KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
MKA_SAK_USE);
if (!reset_participant_mi(participant))
wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
else
wpa_printf(MSG_DEBUG,
"KaY: Selected a new random MI: %s",
mi_txt(participant->mi));
return -1;
}
/* Detect missing parameter sets */
peer = ieee802_1x_kay_get_live_peer(participant,
participant->current_peer_id.mi);
if (peer) {
/* MKPDU is from live peer */
if (!handled[MKA_SAK_USE]) {
/* Once a live peer starts sending SAK-USE, it should be
* sent every time. */
if (peer->sak_used) {
wpa_printf(MSG_INFO,
"KaY: Discarding Rx MKPDU: Live Peer stopped sending SAK-USE");
return -1;
}
/* Live peer is probably hung if it hasn't sent SAK-USE
* after a reasonable number of MKPDUs. Drop the MKPDU,
* which will eventually force an timeout. */
if (++peer->missing_sak_use_count >
MAX_MISSING_SAK_USE) {
wpa_printf(MSG_INFO,
"KaY: Discarding Rx MKPDU: Live Peer not sending SAK-USE");
return -1;
}
} else {
peer->missing_sak_use_count = 0;
/* Only update live peer watchdog after successful
* decode of all parameter sets */
peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
}
} else {
/* MKPDU is from new or potential peer */
peer = ieee802_1x_kay_get_peer(participant,
participant->current_peer_id.mi);
if (!peer) {
wpa_printf(MSG_DEBUG, "KaY: No peer entry found");
return -1;
}
/* Do not update potential peer watchdog. Per IEEE Std
* 802.1X-2010, 9.4.3, potential peers need to show liveness by
* including our MI/MN in their transmitted MKPDU (within
* potential or live parameter sets). Whena potential peer does
* include our MI/MN in an MKPDU, we respond by moving the peer
* from 'potential_peers' to 'live_peers'. */
}
kay->active = true;
participant->retry_count = 0;
participant->active = true;
return 0;
}
static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len)
{
struct ieee802_1x_kay *kay = ctx;
struct ieee8023_hdr *eth_hdr;
struct ieee802_1x_hdr *eapol_hdr;
size_t calc_len;
/* IEEE Std 802.1X-2010, 11.4 (Validation of received EAPOL PDUs) */
/* must contain at least ieee8023_hdr + ieee802_1x_hdr */
if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) {
wpa_printf(MSG_MSGDUMP, "KaY: EAPOL frame too short (%lu)",
(unsigned long) len);
return;
}
eth_hdr = (struct ieee8023_hdr *) buf;
eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
calc_len = sizeof(*eth_hdr) + sizeof(*eapol_hdr) +
be_to_host16(eapol_hdr->length);
if (len < calc_len) {
wpa_printf(MSG_MSGDUMP, "KaY: EAPOL MPDU is invalid: (received len %lu, calculated len %lu, EAPOL length %u)",
(unsigned long) len,
(unsigned long) calc_len,
be_to_host16(eapol_hdr->length));
return;
}
if (len > calc_len) {
wpa_hexdump(MSG_DEBUG,
"KaY: Ignore extra octets following the Packey Body field",
&buf[calc_len], len - calc_len);
len = calc_len;
}
if (eapol_hdr->version < EAPOL_VERSION) {
wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA",
eapol_hdr->version);
return;
}
if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE ||
eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA)
return; /* ignore other EAPOL types silently here */
wpa_hexdump(MSG_DEBUG, "KaY: RX EAPOL-MKA", buf, len);
if (dl_list_empty(&kay->participant_list)) {
wpa_printf(MSG_ERROR,
"KaY: No MKA participant instance - ignore EAPOL-MKA");
return;
}
ieee802_1x_kay_decode_mkpdu(kay, buf, len);
}
/**
* ieee802_1x_kay_init -
*/
struct ieee802_1x_kay *
ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
bool macsec_replay_protect, u32 macsec_replay_window,
u16 port, u8 priority, const char *ifname, const u8 *addr)
{
struct ieee802_1x_kay *kay;
wpa_printf(MSG_DEBUG, "KaY: Initialize - ifname=%s addr=" MACSTR
" port=%u priority=%u",
ifname, MAC2STR(addr), port, priority);
kay = os_zalloc(sizeof(*kay));
if (!kay) {
wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
os_free(ctx);
return NULL;
}
kay->ctx = ctx;
kay->enable = true;
kay->active = false;
kay->authenticated = false;
kay->secured = false;
kay->failed = false;
kay->policy = policy;
os_strlcpy(kay->if_name, ifname, IFNAMSIZ);
os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN);
kay->actor_sci.port = host_to_be16(port ? port : 0x0001);
wpa_printf(MSG_DEBUG, "KaY: Generated SCI: %s",
sci_txt(&kay->actor_sci));
kay->actor_priority = priority;
/* While actor acts as a key server, shall distribute sakey */
kay->dist_kn = 1;
kay->dist_an = 0;
kay->dist_time = 0;
kay->pn_exhaustion = PENDING_PN_EXHAUSTION;
kay->macsec_csindex = DEFAULT_CS_INDEX;
kay->mka_algindex = DEFAULT_MKA_ALG_INDEX;
kay->mka_version = MKA_VERSION_ID;
os_memcpy(kay->algo_agility, mka_algo_agility,
sizeof(kay->algo_agility));
dl_list_init(&kay->participant_list);
if (policy != DO_NOT_SECURE &&
secy_get_capability(kay, &kay->macsec_capable) < 0)
goto error;
if (policy == DO_NOT_SECURE ||
kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
kay->macsec_desired = false;
kay->macsec_protect = false;
kay->macsec_encrypt = false;
kay->macsec_validate = Disabled;
kay->macsec_replay_protect = false;
kay->macsec_replay_window = 0;
kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
kay->mka_hello_time = MKA_HELLO_TIME;
} else {
kay->macsec_desired = true;
kay->macsec_protect = true;
if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF &&
policy == SHOULD_ENCRYPT) {
kay->macsec_encrypt = true;
kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
} else { /* SHOULD_SECURE */
kay->macsec_encrypt = false;
kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
}
kay->macsec_validate = Strict;
kay->macsec_replay_protect = macsec_replay_protect;
kay->macsec_replay_window = macsec_replay_window;
kay->mka_hello_time = MKA_HELLO_TIME;
}
wpa_printf(MSG_DEBUG, "KaY: state machine created");
/* Initialize the SecY must be prio to CP, as CP will control SecY */
if (secy_init_macsec(kay) < 0) {
wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec");
goto error;
}
wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
/* init CP */
kay->cp = ieee802_1x_cp_sm_init(kay);
if (kay->cp == NULL)
goto error;
if (policy == DO_NOT_SECURE) {
ieee802_1x_cp_connect_authenticated(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
} else {
kay->l2_mka = l2_packet_init(kay->if_name, NULL, ETH_P_PAE,
kay_l2_receive, kay, 1);
if (kay->l2_mka == NULL) {
wpa_printf(MSG_WARNING,
"KaY: Failed to initialize L2 packet processing for MKA packet");
goto error;
}
}
return kay;
error:
ieee802_1x_kay_deinit(kay);
return NULL;
}
/**
* ieee802_1x_kay_deinit -
*/
void
ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay)
{
struct ieee802_1x_mka_participant *participant;
if (!kay)
return;
wpa_printf(MSG_DEBUG, "KaY: state machine removed");
while (!dl_list_empty(&kay->participant_list)) {
participant = dl_list_entry(kay->participant_list.next,
struct ieee802_1x_mka_participant,
list);
ieee802_1x_kay_delete_mka(kay, &participant->ckn);
}
ieee802_1x_cp_sm_deinit(kay->cp);
secy_deinit_macsec(kay);
if (kay->l2_mka) {
l2_packet_deinit(kay->l2_mka);
kay->l2_mka = NULL;
}
os_free(kay->ctx);
os_free(kay);
}
static const char * mode_txt(enum mka_created_mode mode)
{
switch (mode) {
case PSK:
return "PSK";
case EAP_EXCHANGE:
return "EAP";
}
return "?";
}
/**
* ieee802_1x_kay_create_mka -
*/
struct ieee802_1x_mka_participant *
ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
const struct mka_key_name *ckn,
const struct mka_key *cak, u32 life,
enum mka_created_mode mode, bool is_authenticator)
{
struct ieee802_1x_mka_participant *participant;
unsigned int usecs;
wpa_printf(MSG_DEBUG,
"KaY: Create MKA (ifname=%s mode=%s authenticator=%s)",
kay->if_name, mode_txt(mode), yes_no(is_authenticator));
if (!kay || !ckn || !cak) {
wpa_printf(MSG_ERROR, "KaY: ckn or cak is null");
return NULL;
}
if (cak->len != 16 && cak->len != 32) {
wpa_printf(MSG_ERROR, "KaY: Unexpected CAK length %u",
(unsigned int) cak->len);
return NULL;
}
if (ckn->len > MAX_CKN_LEN) {
wpa_printf(MSG_ERROR, "KaY: CKN is out of range (>32 bytes)");
return NULL;
}
if (!kay->enable) {
wpa_printf(MSG_ERROR, "KaY: Now is at disable state");
return NULL;
}
participant = os_zalloc(sizeof(*participant));
if (!participant) {
wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
return NULL;
}
participant->ckn.len = ckn->len;
os_memcpy(participant->ckn.name, ckn->name, ckn->len);
wpa_hexdump(MSG_DEBUG, "KaY: CKN", participant->ckn.name,
participant->ckn.len);
participant->cak.len = cak->len;
os_memcpy(participant->cak.key, cak->key, cak->len);
wpa_hexdump_key(MSG_DEBUG, "KaY: CAK", participant->cak.key,
participant->cak.len);
if (life)
participant->cak_life = life + time(NULL);
switch (mode) {
case EAP_EXCHANGE:
if (is_authenticator) {
participant->is_obliged_key_server = true;
participant->can_be_key_server = true;
participant->is_key_server = true;
participant->principal = true;
os_memcpy(&kay->key_server_sci, &kay->actor_sci,
sizeof(kay->key_server_sci));
kay->key_server_priority = kay->actor_priority;
participant->is_elected = true;
} else {
participant->is_obliged_key_server = false;
participant->can_be_key_server = false;
participant->is_key_server = false;
participant->is_elected = true;
}
break;
default:
participant->is_obliged_key_server = false;
participant->can_be_key_server = true;
participant->is_key_server = true;
participant->is_elected = false;
break;
}
participant->cached = false;
participant->active = false;
participant->participant = false;
participant->retain = false;
participant->activate = DEFAULT;
if (participant->is_key_server)
participant->principal = true;
dl_list_init(&participant->live_peers);
dl_list_init(&participant->potential_peers);
participant->retry_count = 0;
participant->kay = kay;
if (!reset_participant_mi(participant))
goto fail;
wpa_printf(MSG_DEBUG, "KaY: Selected random MI: %s",
mi_txt(participant->mi));
participant->lrx = false;
participant->ltx = false;
participant->orx = false;
participant->otx = false;
participant->to_dist_sak = false;
participant->to_use_sak = false;
participant->new_sak = false;
dl_list_init(&participant->sak_list);
participant->new_key = NULL;
dl_list_init(&participant->rxsc_list);
participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci);
secy_cp_control_protect_frames(kay, kay->macsec_protect);
secy_cp_control_replay(kay, kay->macsec_replay_protect,
kay->macsec_replay_window);
if (secy_create_transmit_sc(kay, participant->txsc))
goto fail;
/* to derive KEK from CAK and CKN */
participant->kek.len = participant->cak.len;
if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key,
participant->cak.len,
participant->ckn.name,
participant->ckn.len,
participant->kek.key,
participant->kek.len)) {
wpa_printf(MSG_ERROR, "KaY: KEK derivation failed");
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK",
participant->kek.key, participant->kek.len);
/* to derive ICK from CAK and CKN */
participant->ick.len = participant->cak.len;
if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key,
participant->cak.len,
participant->ckn.name,
participant->ckn.len,
participant->ick.key,
participant->ick.len)) {
wpa_printf(MSG_ERROR, "KaY: ICK derivation failed");
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK",
participant->ick.key, participant->ick.len);
dl_list_add(&kay->participant_list, &participant->list);
usecs = os_random() % (kay->mka_hello_time * 1000);
eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
participant, NULL);
/* Disable MKA lifetime for PSK mode.
* The peer(s) can take a long time to come up, because we
* create a "standby" MKA, and we need it to remain live until
* some peer appears.
*/
if (mode != PSK) {
participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
usecs / 1000000;
}
participant->mode = mode;
return participant;
fail:
os_free(participant->txsc);
os_free(participant);
return NULL;
}
/**
* ieee802_1x_kay_delete_mka -
*/
void
ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn)
{
struct ieee802_1x_mka_participant *participant;
struct ieee802_1x_kay_peer *peer;
struct data_key *sak;
struct receive_sc *rxsc;
if (!kay || !ckn)
return;
wpa_printf(MSG_DEBUG, "KaY: participant removed");
/* get the participant */
participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
if (!participant) {
wpa_hexdump(MSG_DEBUG, "KaY: participant is not found",
ckn->name, ckn->len);
return;
}
eloop_cancel_timeout(ieee802_1x_participant_timer, participant, NULL);
dl_list_del(&participant->list);
/* remove live peer */
while (!dl_list_empty(&participant->live_peers)) {
peer = dl_list_entry(participant->live_peers.next,
struct ieee802_1x_kay_peer, list);
dl_list_del(&peer->list);
os_free(peer);
}
/* remove potential peer */
while (!dl_list_empty(&participant->potential_peers)) {
peer = dl_list_entry(participant->potential_peers.next,
struct ieee802_1x_kay_peer, list);
dl_list_del(&peer->list);
os_free(peer);
}
/* remove sak */
while (!dl_list_empty(&participant->sak_list)) {
sak = dl_list_entry(participant->sak_list.next,
struct data_key, list);
dl_list_del(&sak->list);
ieee802_1x_kay_deinit_data_key(sak);
}
while (!dl_list_empty(&participant->rxsc_list)) {
rxsc = dl_list_entry(participant->rxsc_list.next,
struct receive_sc, list);
ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
}
ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
os_memset(&participant->cak, 0, sizeof(participant->cak));
os_memset(&participant->kek, 0, sizeof(participant->kek));
os_memset(&participant->ick, 0, sizeof(participant->ick));
os_free(participant);
}
/**
* ieee802_1x_kay_mka_participate -
*/
void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
struct mka_key_name *ckn, bool status)
{
struct ieee802_1x_mka_participant *participant;
if (!kay || !ckn)
return;
participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len);
if (!participant)
return;
participant->active = status;
}
/**
* ieee802_1x_kay_new_sak -
*/
int
ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay)
{
struct ieee802_1x_mka_participant *participant;
if (!kay)
return -1;
participant = ieee802_1x_kay_get_principal_participant(kay);
if (!participant)
return -1;
participant->new_sak = true;
wpa_printf(MSG_DEBUG, "KaY: new SAK signal");
return 0;
}
/**
* ieee802_1x_kay_change_cipher_suite -
*/
int
ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
unsigned int cs_index)
{
struct ieee802_1x_mka_participant *participant;
enum macsec_cap secy_cap;
if (!kay)
return -1;
if (cs_index >= CS_TABLE_SIZE) {
wpa_printf(MSG_ERROR,
"KaY: Configured cipher suite index is out of range");
return -1;
}
if (kay->macsec_csindex == cs_index)
return -2;
if (cs_index == 0)
kay->macsec_desired = false;
kay->macsec_csindex = cs_index;
kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable;
if (secy_get_capability(kay, &secy_cap) < 0)
return -3;
if (kay->macsec_capable > secy_cap)
kay->macsec_capable = secy_cap;
participant = ieee802_1x_kay_get_principal_participant(kay);
if (participant) {
wpa_printf(MSG_INFO, "KaY: Cipher Suite changed");
participant->new_sak = true;
}
return 0;
}
#ifdef CONFIG_CTRL_IFACE
/**
* ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details
* @sm: Pointer to KaY allocated with ieee802_1x_kay_init()
* @buf: Buffer for status information
* @buflen: Maximum buffer length
* @verbose: Whether to include verbose status information
* Returns: Number of bytes written to buf.
*
* Query KaY status information. This function fills in a text area with current
* status information. If the buffer (buf) is not large enough, status
* information will be truncated to fit the buffer.
*/
int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
size_t buflen)
{
char *pos, *end;
int res, count;
struct ieee802_1x_mka_participant *p;
if (!kay)
return 0;
pos = buf;
end = buf + buflen;
res = os_snprintf(pos, end - pos,
"PAE KaY status=%s\n"
"Authenticated=%s\n"
"Secured=%s\n"
"Failed=%s\n"
"Actor Priority=%u\n"
"Key Server Priority=%u\n"
"Is Key Server=%s\n"
"Number of Keys Distributed=%u\n"
"Number of Keys Received=%u\n"
"MKA Hello Time=%u\n",
kay->active ? "Active" : "Not-Active",
kay->authenticated ? "Yes" : "No",
kay->secured ? "Yes" : "No",
kay->failed ? "Yes" : "No",
kay->actor_priority,
kay->key_server_priority,
kay->is_key_server ? "Yes" : "No",
kay->dist_kn - 1,
kay->rcvd_keys,
kay->mka_hello_time);
if (os_snprintf_error(buflen, res))
return 0;
pos += res;
res = os_snprintf(pos, end - pos,
"actor_sci=%s\n", sci_txt(&kay->actor_sci));
if (os_snprintf_error(buflen, res))
return end - pos;
pos += res;
res = os_snprintf(pos, end - pos,
"key_server_sci=%s\n", sci_txt(&kay->key_server_sci));
if (os_snprintf_error(buflen, res))
return end - pos;
pos += res;
count = 0;
dl_list_for_each(p, &kay->participant_list,
struct ieee802_1x_mka_participant, list) {
char *pos2 = pos;
res = os_snprintf(pos2, end - pos2, "participant_idx=%d\nckn=",
count);
if (os_snprintf_error(buflen, res))
return end - pos;
pos2 += res;
count++;
pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name,
p->ckn.len);
res = os_snprintf(pos2, end - pos2,
"\nmi=%s\n"
"mn=%u\n"
"active=%s\n"
"participant=%s\n"
"retain=%s\n"
"live_peers=%u\n"
"potential_peers=%u\n"
"is_key_server=%s\n"
"is_elected=%s\n",
mi_txt(p->mi), p->mn,
yes_no(p->active),
yes_no(p->participant),
yes_no(p->retain),
dl_list_len(&p->live_peers),
dl_list_len(&p->potential_peers),
yes_no(p->is_key_server),
yes_no(p->is_elected));
if (os_snprintf_error(buflen, res))
return end - pos;
pos2 += res;
pos = pos2;
}
return pos - buf;
}
static const char * true_false(bool val)
{
return val ? "true" : "false";
}
static const char * activate_control_txt(enum activate_ctrl activate)
{
switch (activate) {
case DEFAULT:
return "default";
case DISABLED:
return "disabled";
case ON_OPER_UP:
return "onOperUp";
case ALWAYS:
return "always";
}
return "?";
}
static char * mka_mib_peer(struct dl_list *peers, bool live, char *buf,
char *end)
{
char *pos = buf;
struct ieee802_1x_kay_peer *p;
int res;
dl_list_for_each(p, peers, struct ieee802_1x_kay_peer, list) {
res = os_snprintf(pos, end - pos,
"ieee8021XKayMkaPeerListMI=%s\n"
"ieee8021XKayMkaPeerListMN=%u\n"
"ieee8021XKayMkaPeerListType=%u\n"
"ieee8021XKayMkaPeerListSCI=%s\n",
mi_txt(p->mi),
p->mn,
live ? 1 : 2,
sci_txt(&p->sci));
if (os_snprintf_error(end - pos, res))
return pos;
pos += res;
}
return pos;
}
int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf,
size_t buflen)
{
char *pos, *end;
int res;
struct ieee802_1x_mka_participant *p;
if (!kay)
return 0;
pos = buf;
end = buf + buflen;
dl_list_for_each(p, &kay->participant_list,
struct ieee802_1x_mka_participant, list) {
char *pos2 = pos;
res = os_snprintf(pos2, end - pos2, "ieee8021XKayMkaPartCKN=");
if (os_snprintf_error(buflen, res))
return end - pos;
pos2 += res;
pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name,
p->ckn.len);
res = os_snprintf(pos2, end - pos2,
"\nieee8021XKayMkaPartCached=%s\n"
"ieee8021XKayMkaPartActive=%s\n"
"ieee8021XKayMkaPartRetain=%s\n"
"ieee8021XKayMkaPartActivateControl=%s\n"
"ieee8021XKayMkaPartPrincipal=%s\n",
true_false(p->cached),
true_false(p->active),
true_false(p->retain),
activate_control_txt(p->activate),
true_false(p->principal));
if (os_snprintf_error(buflen, res))
return end - pos;
pos2 += res;
pos = pos2;
pos = mka_mib_peer(&p->live_peers, true, pos, end);
pos = mka_mib_peer(&p->potential_peers, false, pos, end);
}
return pos - buf;
}
#endif /* CONFIG_CTRL_IFACE */
diff --git a/contrib/wpa/src/radius/radius_client.c b/contrib/wpa/src/radius/radius_client.c
index 15902f0c9e29..ee9e46d2aa01 100644
--- a/contrib/wpa/src/radius/radius_client.c
+++ b/contrib/wpa/src/radius/radius_client.c
@@ -1,1731 +1,1728 @@
/*
* RADIUS client
* Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <net/if.h>
#include "common.h"
#include "radius.h"
#include "radius_client.h"
#include "eloop.h"
/* Defaults for RADIUS retransmit values (exponential backoff) */
/**
* RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
*/
#define RADIUS_CLIENT_FIRST_WAIT 3
/**
* RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
*/
#define RADIUS_CLIENT_MAX_WAIT 120
/**
* RADIUS_CLIENT_MAX_FAILOVER - RADIUS client maximum retries
*
* Maximum number of server failovers before the entry is removed from
* retransmit list.
*/
#define RADIUS_CLIENT_MAX_FAILOVER 3
/**
* RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
*
* Maximum number of entries in retransmit list (oldest entries will be
* removed, if this limit is exceeded).
*/
#define RADIUS_CLIENT_MAX_ENTRIES 30
/**
* RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
*
* The number of failed retry attempts after which the RADIUS server will be
* changed (if one of more backup servers are configured).
*/
#define RADIUS_CLIENT_NUM_FAILOVER 4
/**
* struct radius_rx_handler - RADIUS client RX handler
*
* This data structure is used internally inside the RADIUS client module to
* store registered RX handlers. These handlers are registered by calls to
* radius_client_register() and unregistered when the RADIUS client is
* deinitialized with a call to radius_client_deinit().
*/
struct radius_rx_handler {
/**
* handler - Received RADIUS message handler
*/
RadiusRxResult (*handler)(struct radius_msg *msg,
struct radius_msg *req,
const u8 *shared_secret,
size_t shared_secret_len,
void *data);
/**
* data - Context data for the handler
*/
void *data;
};
/**
* struct radius_msg_list - RADIUS client message retransmit list
*
* This data structure is used internally inside the RADIUS client module to
* store pending RADIUS requests that may still need to be retransmitted.
*/
struct radius_msg_list {
/**
* addr - STA/client address
*
* This is used to find RADIUS messages for the same STA.
*/
u8 addr[ETH_ALEN];
/**
* msg - RADIUS message
*/
struct radius_msg *msg;
/**
* msg_type - Message type
*/
RadiusType msg_type;
/**
* first_try - Time of the first transmission attempt
*/
os_time_t first_try;
/**
* next_try - Time for the next transmission attempt
*/
os_time_t next_try;
/**
* attempts - Number of transmission attempts for one server
*/
int attempts;
/**
* accu_attempts - Number of accumulated attempts
*/
int accu_attempts;
/**
* next_wait - Next retransmission wait time in seconds
*/
int next_wait;
/**
* last_attempt - Time of the last transmission attempt
*/
struct os_reltime last_attempt;
/**
* shared_secret - Shared secret with the target RADIUS server
*/
const u8 *shared_secret;
/**
* shared_secret_len - shared_secret length in octets
*/
size_t shared_secret_len;
/* TODO: server config with failover to backup server(s) */
/**
* next - Next message in the list
*/
struct radius_msg_list *next;
};
/**
* struct radius_client_data - Internal RADIUS client data
*
* This data structure is used internally inside the RADIUS client module.
* External users allocate this by calling radius_client_init() and free it by
* calling radius_client_deinit(). The pointer to this opaque data is used in
* calls to other functions as an identifier for the RADIUS client instance.
*/
struct radius_client_data {
/**
* ctx - Context pointer for hostapd_logger() callbacks
*/
void *ctx;
/**
* conf - RADIUS client configuration (list of RADIUS servers to use)
*/
struct hostapd_radius_servers *conf;
/**
* auth_serv_sock - IPv4 socket for RADIUS authentication messages
*/
int auth_serv_sock;
/**
* acct_serv_sock - IPv4 socket for RADIUS accounting messages
*/
int acct_serv_sock;
/**
* auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
*/
int auth_serv_sock6;
/**
* acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
*/
int acct_serv_sock6;
/**
* auth_sock - Currently used socket for RADIUS authentication server
*/
int auth_sock;
/**
* acct_sock - Currently used socket for RADIUS accounting server
*/
int acct_sock;
/**
* auth_handlers - Authentication message handlers
*/
struct radius_rx_handler *auth_handlers;
/**
* num_auth_handlers - Number of handlers in auth_handlers
*/
size_t num_auth_handlers;
/**
* acct_handlers - Accounting message handlers
*/
struct radius_rx_handler *acct_handlers;
/**
* num_acct_handlers - Number of handlers in acct_handlers
*/
size_t num_acct_handlers;
/**
* msgs - Pending outgoing RADIUS messages
*/
struct radius_msg_list *msgs;
/**
* num_msgs - Number of pending messages in the msgs list
*/
size_t num_msgs;
/**
* next_radius_identifier - Next RADIUS message identifier to use
*/
u8 next_radius_identifier;
/**
* interim_error_cb - Interim accounting error callback
*/
void (*interim_error_cb)(const u8 *addr, void *ctx);
/**
* interim_error_cb_ctx - interim_error_cb() context data
*/
void *interim_error_cb_ctx;
};
static int
radius_change_server(struct radius_client_data *radius,
struct hostapd_radius_server *nserv,
struct hostapd_radius_server *oserv,
int sock, int sock6, int auth);
static int radius_client_init_acct(struct radius_client_data *radius);
static int radius_client_init_auth(struct radius_client_data *radius);
static void radius_client_auth_failover(struct radius_client_data *radius);
static void radius_client_acct_failover(struct radius_client_data *radius);
static void radius_client_msg_free(struct radius_msg_list *req)
{
radius_msg_free(req->msg);
os_free(req);
}
/**
* radius_client_register - Register a RADIUS client RX handler
* @radius: RADIUS client context from radius_client_init()
* @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
* @handler: Handler for received RADIUS messages
* @data: Context pointer for handler callbacks
* Returns: 0 on success, -1 on failure
*
* This function is used to register a handler for processing received RADIUS
* authentication and accounting messages. The handler() callback function will
* be called whenever a RADIUS message is received from the active server.
*
* There can be multiple registered RADIUS message handlers. The handlers will
* be called in order until one of them indicates that it has processed or
* queued the message.
*/
int radius_client_register(struct radius_client_data *radius,
RadiusType msg_type,
RadiusRxResult (*handler)(struct radius_msg *msg,
struct radius_msg *req,
const u8 *shared_secret,
size_t shared_secret_len,
void *data),
void *data)
{
struct radius_rx_handler **handlers, *newh;
size_t *num;
if (msg_type == RADIUS_ACCT) {
handlers = &radius->acct_handlers;
num = &radius->num_acct_handlers;
} else {
handlers = &radius->auth_handlers;
num = &radius->num_auth_handlers;
}
newh = os_realloc_array(*handlers, *num + 1,
sizeof(struct radius_rx_handler));
if (newh == NULL)
return -1;
newh[*num].handler = handler;
newh[*num].data = data;
(*num)++;
*handlers = newh;
return 0;
}
/**
* radius_client_set_interim_erro_cb - Register an interim acct error callback
* @radius: RADIUS client context from radius_client_init()
* @addr: Station address from the failed message
* @cb: Handler for interim accounting errors
* @ctx: Context pointer for handler callbacks
*
* This function is used to register a handler for processing failed
* transmission attempts of interim accounting update messages.
*/
void radius_client_set_interim_error_cb(struct radius_client_data *radius,
void (*cb)(const u8 *addr, void *ctx),
void *ctx)
{
radius->interim_error_cb = cb;
radius->interim_error_cb_ctx = ctx;
}
/*
* Returns >0 if message queue was flushed (i.e., the message that triggered
* the error is not available anymore)
*/
static int radius_client_handle_send_error(struct radius_client_data *radius,
int s, RadiusType msg_type)
{
#ifndef CONFIG_NATIVE_WINDOWS
int _errno = errno;
wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
_errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) {
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
"Send failed - maybe interface status changed -"
" try to connect again");
if (msg_type == RADIUS_ACCT ||
msg_type == RADIUS_ACCT_INTERIM) {
radius_client_init_acct(radius);
return 0;
} else {
radius_client_init_auth(radius);
return 1;
}
}
#endif /* CONFIG_NATIVE_WINDOWS */
return 0;
}
static int radius_client_retransmit(struct radius_client_data *radius,
struct radius_msg_list *entry,
os_time_t now)
{
struct hostapd_radius_servers *conf = radius->conf;
int s;
struct wpabuf *buf;
size_t prev_num_msgs;
u8 *acct_delay_time;
size_t acct_delay_time_len;
int num_servers;
if (entry->msg_type == RADIUS_ACCT ||
entry->msg_type == RADIUS_ACCT_INTERIM) {
num_servers = conf->num_acct_servers;
if (radius->acct_sock < 0)
radius_client_init_acct(radius);
if (radius->acct_sock < 0 && conf->num_acct_servers > 1) {
prev_num_msgs = radius->num_msgs;
radius_client_acct_failover(radius);
if (prev_num_msgs != radius->num_msgs)
return 0;
}
s = radius->acct_sock;
if (entry->attempts == 0)
conf->acct_server->requests++;
else {
conf->acct_server->timeouts++;
conf->acct_server->retransmissions++;
}
} else {
num_servers = conf->num_auth_servers;
if (radius->auth_sock < 0)
radius_client_init_auth(radius);
if (radius->auth_sock < 0 && conf->num_auth_servers > 1) {
prev_num_msgs = radius->num_msgs;
radius_client_auth_failover(radius);
if (prev_num_msgs != radius->num_msgs)
return 0;
}
s = radius->auth_sock;
if (entry->attempts == 0)
conf->auth_server->requests++;
else {
conf->auth_server->timeouts++;
conf->auth_server->retransmissions++;
}
}
if (entry->msg_type == RADIUS_ACCT_INTERIM) {
wpa_printf(MSG_DEBUG,
"RADIUS: Failed to transmit interim accounting update to "
MACSTR " - drop message and request a new update",
MAC2STR(entry->addr));
if (radius->interim_error_cb)
radius->interim_error_cb(entry->addr,
radius->interim_error_cb_ctx);
return 1;
}
if (s < 0) {
wpa_printf(MSG_INFO,
"RADIUS: No valid socket for retransmission");
return 1;
}
if (entry->msg_type == RADIUS_ACCT &&
radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME,
&acct_delay_time, &acct_delay_time_len,
NULL) == 0 &&
acct_delay_time_len == 4) {
struct radius_hdr *hdr;
u32 delay_time;
/*
* Need to assign a new identifier since attribute contents
* changes.
*/
hdr = radius_msg_get_hdr(entry->msg);
hdr->identifier = radius_client_get_id(radius);
/* Update Acct-Delay-Time to show wait time in queue */
delay_time = now - entry->first_try;
WPA_PUT_BE32(acct_delay_time, delay_time);
wpa_printf(MSG_DEBUG,
"RADIUS: Updated Acct-Delay-Time to %u for retransmission",
delay_time);
radius_msg_finish_acct(entry->msg, entry->shared_secret,
entry->shared_secret_len);
if (radius->conf->msg_dumps)
radius_msg_dump(entry->msg);
}
/* retransmit; remove entry if too many attempts */
if (entry->accu_attempts >= RADIUS_CLIENT_MAX_FAILOVER *
RADIUS_CLIENT_NUM_FAILOVER * num_servers) {
wpa_printf(MSG_INFO,
"RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
return 1;
}
entry->attempts++;
entry->accu_attempts++;
hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
radius_msg_get_hdr(entry->msg)->identifier);
os_get_reltime(&entry->last_attempt);
buf = radius_msg_get_buf(entry->msg);
if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
if (radius_client_handle_send_error(radius, s, entry->msg_type)
> 0)
return 0;
}
entry->next_try = now + entry->next_wait;
entry->next_wait *= 2;
if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
return 0;
}
static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
{
struct radius_client_data *radius = eloop_ctx;
struct os_reltime now;
os_time_t first;
struct radius_msg_list *entry, *prev, *tmp;
int auth_failover = 0, acct_failover = 0;
size_t prev_num_msgs;
int s;
entry = radius->msgs;
if (!entry)
return;
os_get_reltime(&now);
while (entry) {
if (now.sec >= entry->next_try) {
s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
radius->acct_sock;
if (entry->attempts >= RADIUS_CLIENT_NUM_FAILOVER ||
(s < 0 && entry->attempts > 0)) {
if (entry->msg_type == RADIUS_ACCT ||
entry->msg_type == RADIUS_ACCT_INTERIM)
acct_failover++;
else
auth_failover++;
}
}
entry = entry->next;
}
if (auth_failover)
radius_client_auth_failover(radius);
if (acct_failover)
radius_client_acct_failover(radius);
entry = radius->msgs;
first = 0;
prev = NULL;
while (entry) {
prev_num_msgs = radius->num_msgs;
if (now.sec >= entry->next_try &&
radius_client_retransmit(radius, entry, now.sec)) {
if (prev)
prev->next = entry->next;
else
radius->msgs = entry->next;
tmp = entry;
entry = entry->next;
radius_client_msg_free(tmp);
radius->num_msgs--;
continue;
}
if (prev_num_msgs != radius->num_msgs) {
wpa_printf(MSG_DEBUG,
"RADIUS: Message removed from queue - restart from beginning");
entry = radius->msgs;
prev = NULL;
continue;
}
if (first == 0 || entry->next_try < first)
first = entry->next_try;
prev = entry;
entry = entry->next;
}
if (radius->msgs) {
if (first < now.sec)
first = now.sec;
eloop_cancel_timeout(radius_client_timer, radius, NULL);
eloop_register_timeout(first - now.sec, 0,
radius_client_timer, radius, NULL);
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
"retransmit in %ld seconds",
(long int) (first - now.sec));
}
}
static void radius_client_auth_failover(struct radius_client_data *radius)
{
struct hostapd_radius_servers *conf = radius->conf;
struct hostapd_radius_server *next, *old;
struct radius_msg_list *entry;
char abuf[50];
old = conf->auth_server;
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_NOTICE,
"No response from Authentication server %s:%d - failover",
hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
old->port);
for (entry = radius->msgs; entry; entry = entry->next) {
if (entry->msg_type == RADIUS_AUTH)
old->timeouts++;
}
next = old + 1;
if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
next = conf->auth_servers;
conf->auth_server = next;
radius_change_server(radius, next, old,
radius->auth_serv_sock,
radius->auth_serv_sock6, 1);
}
static void radius_client_acct_failover(struct radius_client_data *radius)
{
struct hostapd_radius_servers *conf = radius->conf;
struct hostapd_radius_server *next, *old;
struct radius_msg_list *entry;
char abuf[50];
old = conf->acct_server;
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_NOTICE,
"No response from Accounting server %s:%d - failover",
hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
old->port);
for (entry = radius->msgs; entry; entry = entry->next) {
if (entry->msg_type == RADIUS_ACCT ||
entry->msg_type == RADIUS_ACCT_INTERIM)
old->timeouts++;
}
next = old + 1;
if (next > &conf->acct_servers[conf->num_acct_servers - 1])
next = conf->acct_servers;
conf->acct_server = next;
radius_change_server(radius, next, old,
radius->acct_serv_sock,
radius->acct_serv_sock6, 0);
}
static void radius_client_update_timeout(struct radius_client_data *radius)
{
struct os_reltime now;
os_time_t first;
struct radius_msg_list *entry;
eloop_cancel_timeout(radius_client_timer, radius, NULL);
if (radius->msgs == NULL) {
return;
}
first = 0;
for (entry = radius->msgs; entry; entry = entry->next) {
if (first == 0 || entry->next_try < first)
first = entry->next_try;
}
os_get_reltime(&now);
if (first < now.sec)
first = now.sec;
eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
NULL);
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
" %ld seconds", (long int) (first - now.sec));
}
static void radius_client_list_add(struct radius_client_data *radius,
struct radius_msg *msg,
RadiusType msg_type,
const u8 *shared_secret,
size_t shared_secret_len, const u8 *addr)
{
struct radius_msg_list *entry, *prev;
if (eloop_terminated()) {
/* No point in adding entries to retransmit queue since event
* loop has already been terminated. */
radius_msg_free(msg);
return;
}
entry = os_zalloc(sizeof(*entry));
if (entry == NULL) {
wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list");
radius_msg_free(msg);
return;
}
if (addr)
os_memcpy(entry->addr, addr, ETH_ALEN);
entry->msg = msg;
entry->msg_type = msg_type;
entry->shared_secret = shared_secret;
entry->shared_secret_len = shared_secret_len;
os_get_reltime(&entry->last_attempt);
entry->first_try = entry->last_attempt.sec;
entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
entry->attempts = 1;
entry->accu_attempts = 1;
entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
entry->next = radius->msgs;
radius->msgs = entry;
radius_client_update_timeout(radius);
if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits");
prev = NULL;
while (entry->next) {
prev = entry;
entry = entry->next;
}
if (prev) {
prev->next = NULL;
radius_client_msg_free(entry);
}
} else
radius->num_msgs++;
}
/**
* radius_client_send - Send a RADIUS request
* @radius: RADIUS client context from radius_client_init()
* @msg: RADIUS message to be sent
* @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
* @addr: MAC address of the device related to this message or %NULL
* Returns: 0 on success, -1 on failure
*
* This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
* accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
* between accounting and interim accounting messages is that the interim
* message will not be retransmitted. Instead, a callback is used to indicate
* that the transmission failed for the specific station @addr so that a new
* interim accounting update message can be generated with up-to-date session
* data instead of trying to resend old information.
*
* The message is added on the retransmission queue and will be retransmitted
* automatically until a response is received or maximum number of retries
* (RADIUS_CLIENT_MAX_FAILOVER * RADIUS_CLIENT_NUM_FAILOVER) is reached. No
* such retries are used with RADIUS_ACCT_INTERIM, i.e., such a pending message
* is removed from the queue automatically on transmission failure.
*
* The related device MAC address can be used to identify pending messages that
* can be removed with radius_client_flush_auth().
*/
int radius_client_send(struct radius_client_data *radius,
struct radius_msg *msg, RadiusType msg_type,
const u8 *addr)
{
struct hostapd_radius_servers *conf = radius->conf;
const u8 *shared_secret;
size_t shared_secret_len;
char *name;
int s, res;
struct wpabuf *buf;
if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
if (conf->acct_server && radius->acct_sock < 0)
radius_client_init_acct(radius);
if (conf->acct_server == NULL || radius->acct_sock < 0 ||
conf->acct_server->shared_secret == NULL) {
hostapd_logger(radius->ctx, NULL,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
"No accounting server configured");
return -1;
}
shared_secret = conf->acct_server->shared_secret;
shared_secret_len = conf->acct_server->shared_secret_len;
radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
name = "accounting";
s = radius->acct_sock;
conf->acct_server->requests++;
} else {
if (conf->auth_server && radius->auth_sock < 0)
radius_client_init_auth(radius);
if (conf->auth_server == NULL || radius->auth_sock < 0 ||
conf->auth_server->shared_secret == NULL) {
hostapd_logger(radius->ctx, NULL,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
"No authentication server configured");
return -1;
}
shared_secret = conf->auth_server->shared_secret;
shared_secret_len = conf->auth_server->shared_secret_len;
radius_msg_finish(msg, shared_secret, shared_secret_len);
name = "authentication";
s = radius->auth_sock;
conf->auth_server->requests++;
}
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
"server", name);
if (conf->msg_dumps)
radius_msg_dump(msg);
buf = radius_msg_get_buf(msg);
res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
if (res < 0)
radius_client_handle_send_error(radius, s, msg_type);
radius_client_list_add(radius, msg, msg_type, shared_secret,
shared_secret_len, addr);
return 0;
}
static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
struct radius_client_data *radius = eloop_ctx;
struct hostapd_radius_servers *conf = radius->conf;
-#if defined(__clang_major__) && __clang_major__ >= 11
-#pragma GCC diagnostic ignored "-Wvoid-pointer-to-enum-cast"
-#endif
- RadiusType msg_type = (RadiusType) sock_ctx;
+ RadiusType msg_type = (uintptr_t) sock_ctx;
int len, roundtrip;
unsigned char buf[RADIUS_MAX_MSG_LEN];
struct msghdr msghdr = {0};
struct iovec iov;
struct radius_msg *msg;
struct radius_hdr *hdr;
struct radius_rx_handler *handlers;
size_t num_handlers, i;
struct radius_msg_list *req, *prev_req;
struct os_reltime now;
struct hostapd_radius_server *rconf;
int invalid_authenticator = 0;
if (msg_type == RADIUS_ACCT) {
handlers = radius->acct_handlers;
num_handlers = radius->num_acct_handlers;
rconf = conf->acct_server;
} else {
handlers = radius->auth_handlers;
num_handlers = radius->num_auth_handlers;
rconf = conf->auth_server;
}
iov.iov_base = buf;
iov.iov_len = RADIUS_MAX_MSG_LEN;
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
msghdr.msg_flags = 0;
len = recvmsg(sock, &msghdr, MSG_DONTWAIT);
if (len < 0) {
wpa_printf(MSG_INFO, "recvmsg[RADIUS]: %s", strerror(errno));
return;
}
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
"server", len);
if (msghdr.msg_flags & MSG_TRUNC) {
wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it");
return;
}
msg = radius_msg_parse(buf, len);
if (msg == NULL) {
wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
rconf->malformed_responses++;
return;
}
hdr = radius_msg_get_hdr(msg);
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
if (conf->msg_dumps)
radius_msg_dump(msg);
switch (hdr->code) {
case RADIUS_CODE_ACCESS_ACCEPT:
rconf->access_accepts++;
break;
case RADIUS_CODE_ACCESS_REJECT:
rconf->access_rejects++;
break;
case RADIUS_CODE_ACCESS_CHALLENGE:
rconf->access_challenges++;
break;
case RADIUS_CODE_ACCOUNTING_RESPONSE:
rconf->responses++;
break;
}
prev_req = NULL;
req = radius->msgs;
while (req) {
/* TODO: also match by src addr:port of the packet when using
* alternative RADIUS servers (?) */
if ((req->msg_type == msg_type ||
(req->msg_type == RADIUS_ACCT_INTERIM &&
msg_type == RADIUS_ACCT)) &&
radius_msg_get_hdr(req->msg)->identifier ==
hdr->identifier)
break;
prev_req = req;
req = req->next;
}
if (req == NULL) {
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG,
"No matching RADIUS request found (type=%d "
"id=%d) - dropping packet",
msg_type, hdr->identifier);
goto fail;
}
os_get_reltime(&now);
roundtrip = (now.sec - req->last_attempt.sec) * 100 +
(now.usec - req->last_attempt.usec) / 10000;
hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG,
"Received RADIUS packet matched with a pending "
"request, round trip time %d.%02d sec",
roundtrip / 100, roundtrip % 100);
rconf->round_trip_time = roundtrip;
/* Remove ACKed RADIUS packet from retransmit list */
if (prev_req)
prev_req->next = req->next;
else
radius->msgs = req->next;
radius->num_msgs--;
for (i = 0; i < num_handlers; i++) {
RadiusRxResult res;
res = handlers[i].handler(msg, req->msg, req->shared_secret,
req->shared_secret_len,
handlers[i].data);
switch (res) {
case RADIUS_RX_PROCESSED:
radius_msg_free(msg);
/* fall through */
case RADIUS_RX_QUEUED:
radius_client_msg_free(req);
return;
case RADIUS_RX_INVALID_AUTHENTICATOR:
invalid_authenticator++;
/* fall through */
case RADIUS_RX_UNKNOWN:
/* continue with next handler */
break;
}
}
if (invalid_authenticator)
rconf->bad_authenticators++;
else
rconf->unknown_types++;
hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
"(type=%d code=%d id=%d)%s - dropping packet",
msg_type, hdr->code, hdr->identifier,
invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
"");
radius_client_msg_free(req);
fail:
radius_msg_free(msg);
}
/**
* radius_client_get_id - Get an identifier for a new RADIUS message
* @radius: RADIUS client context from radius_client_init()
* Returns: Allocated identifier
*
* This function is used to fetch a unique (among pending requests) identifier
* for a new RADIUS message.
*/
u8 radius_client_get_id(struct radius_client_data *radius)
{
struct radius_msg_list *entry, *prev, *_remove;
u8 id = radius->next_radius_identifier++;
/* remove entries with matching id from retransmit list to avoid
* using new reply from the RADIUS server with an old request */
entry = radius->msgs;
prev = NULL;
while (entry) {
if (radius_msg_get_hdr(entry->msg)->identifier == id) {
hostapd_logger(radius->ctx, entry->addr,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG,
"Removing pending RADIUS message, "
"since its id (%d) is reused", id);
if (prev)
prev->next = entry->next;
else
radius->msgs = entry->next;
_remove = entry;
} else {
_remove = NULL;
prev = entry;
}
entry = entry->next;
if (_remove)
radius_client_msg_free(_remove);
}
return id;
}
/**
* radius_client_flush - Flush all pending RADIUS client messages
* @radius: RADIUS client context from radius_client_init()
* @only_auth: Whether only authentication messages are removed
*/
void radius_client_flush(struct radius_client_data *radius, int only_auth)
{
struct radius_msg_list *entry, *prev, *tmp;
if (!radius)
return;
prev = NULL;
entry = radius->msgs;
while (entry) {
if (!only_auth || entry->msg_type == RADIUS_AUTH) {
if (prev)
prev->next = entry->next;
else
radius->msgs = entry->next;
tmp = entry;
entry = entry->next;
radius_client_msg_free(tmp);
radius->num_msgs--;
} else {
prev = entry;
entry = entry->next;
}
}
if (radius->msgs == NULL)
eloop_cancel_timeout(radius_client_timer, radius, NULL);
}
static void radius_client_update_acct_msgs(struct radius_client_data *radius,
const u8 *shared_secret,
size_t shared_secret_len)
{
struct radius_msg_list *entry;
if (!radius)
return;
for (entry = radius->msgs; entry; entry = entry->next) {
if (entry->msg_type == RADIUS_ACCT) {
entry->shared_secret = shared_secret;
entry->shared_secret_len = shared_secret_len;
radius_msg_finish_acct(entry->msg, shared_secret,
shared_secret_len);
}
}
}
static int
radius_change_server(struct radius_client_data *radius,
struct hostapd_radius_server *nserv,
struct hostapd_radius_server *oserv,
int sock, int sock6, int auth)
{
struct sockaddr_in serv, claddr;
#ifdef CONFIG_IPV6
struct sockaddr_in6 serv6, claddr6;
#endif /* CONFIG_IPV6 */
struct sockaddr *addr, *cl_addr;
socklen_t addrlen, claddrlen;
char abuf[50];
int sel_sock;
struct radius_msg_list *entry;
struct hostapd_radius_servers *conf = radius->conf;
struct sockaddr_in disconnect_addr = {
.sin_family = AF_UNSPEC,
};
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
"%s server %s:%d",
auth ? "Authentication" : "Accounting",
hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
nserv->port);
if (oserv && oserv == nserv) {
/* Reconnect to same server, flush */
if (auth)
radius_client_flush(radius, 1);
}
if (oserv && oserv != nserv &&
(nserv->shared_secret_len != oserv->shared_secret_len ||
os_memcmp(nserv->shared_secret, oserv->shared_secret,
nserv->shared_secret_len) != 0)) {
/* Pending RADIUS packets used different shared secret, so
* they need to be modified. Update accounting message
* authenticators here. Authentication messages are removed
* since they would require more changes and the new RADIUS
* server may not be prepared to receive them anyway due to
* missing state information. Client will likely retry
* authentication, so this should not be an issue. */
if (auth)
radius_client_flush(radius, 1);
else {
radius_client_update_acct_msgs(
radius, nserv->shared_secret,
nserv->shared_secret_len);
}
}
/* Reset retry counters */
for (entry = radius->msgs; oserv && entry; entry = entry->next) {
if ((auth && entry->msg_type != RADIUS_AUTH) ||
(!auth && entry->msg_type != RADIUS_ACCT))
continue;
entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
entry->attempts = 0;
entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
}
if (radius->msgs) {
eloop_cancel_timeout(radius_client_timer, radius, NULL);
eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
radius_client_timer, radius, NULL);
}
switch (nserv->addr.af) {
case AF_INET:
os_memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
serv.sin_port = htons(nserv->port);
addr = (struct sockaddr *) &serv;
addrlen = sizeof(serv);
sel_sock = sock;
break;
#ifdef CONFIG_IPV6
case AF_INET6:
os_memset(&serv6, 0, sizeof(serv6));
serv6.sin6_family = AF_INET6;
os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
sizeof(struct in6_addr));
serv6.sin6_port = htons(nserv->port);
addr = (struct sockaddr *) &serv6;
addrlen = sizeof(serv6);
sel_sock = sock6;
break;
#endif /* CONFIG_IPV6 */
default:
return -1;
}
if (sel_sock < 0) {
wpa_printf(MSG_INFO,
"RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d",
nserv->addr.af, sock, sock6, auth);
return -1;
}
/* Force a reconnect by disconnecting the socket first */
if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
sizeof(disconnect_addr)) < 0)
wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
#ifdef __linux__
if (conf->force_client_dev && conf->force_client_dev[0]) {
if (setsockopt(sel_sock, SOL_SOCKET, SO_BINDTODEVICE,
conf->force_client_dev,
os_strlen(conf->force_client_dev)) < 0) {
wpa_printf(MSG_ERROR,
"RADIUS: setsockopt[SO_BINDTODEVICE]: %s",
strerror(errno));
/* Probably not a critical error; continue on and hope
* for the best. */
} else {
wpa_printf(MSG_DEBUG,
"RADIUS: Bound client socket to device: %s",
conf->force_client_dev);
}
}
#endif /* __linux__ */
if (conf->force_client_addr) {
switch (conf->client_addr.af) {
case AF_INET:
os_memset(&claddr, 0, sizeof(claddr));
claddr.sin_family = AF_INET;
claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
claddr.sin_port = htons(0);
cl_addr = (struct sockaddr *) &claddr;
claddrlen = sizeof(claddr);
break;
#ifdef CONFIG_IPV6
case AF_INET6:
os_memset(&claddr6, 0, sizeof(claddr6));
claddr6.sin6_family = AF_INET6;
os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
sizeof(struct in6_addr));
claddr6.sin6_port = htons(0);
cl_addr = (struct sockaddr *) &claddr6;
claddrlen = sizeof(claddr6);
break;
#endif /* CONFIG_IPV6 */
default:
return -1;
}
if (bind(sel_sock, cl_addr, claddrlen) < 0) {
wpa_printf(MSG_INFO, "bind[radius]: %s",
strerror(errno));
return -1;
}
}
if (connect(sel_sock, addr, addrlen) < 0) {
wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
return -1;
}
#ifndef CONFIG_NATIVE_WINDOWS
switch (nserv->addr.af) {
case AF_INET:
claddrlen = sizeof(claddr);
if (getsockname(sel_sock, (struct sockaddr *) &claddr,
&claddrlen) == 0) {
wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
inet_ntoa(claddr.sin_addr),
ntohs(claddr.sin_port));
}
break;
#ifdef CONFIG_IPV6
case AF_INET6: {
claddrlen = sizeof(claddr6);
if (getsockname(sel_sock, (struct sockaddr *) &claddr6,
&claddrlen) == 0) {
wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
inet_ntop(AF_INET6, &claddr6.sin6_addr,
abuf, sizeof(abuf)),
ntohs(claddr6.sin6_port));
}
break;
}
#endif /* CONFIG_IPV6 */
}
#endif /* CONFIG_NATIVE_WINDOWS */
if (auth)
radius->auth_sock = sel_sock;
else
radius->acct_sock = sel_sock;
return 0;
}
static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
{
struct radius_client_data *radius = eloop_ctx;
struct hostapd_radius_servers *conf = radius->conf;
struct hostapd_radius_server *oserv;
if (radius->auth_sock >= 0 && conf->auth_servers &&
conf->auth_server != conf->auth_servers) {
oserv = conf->auth_server;
conf->auth_server = conf->auth_servers;
if (radius_change_server(radius, conf->auth_server, oserv,
radius->auth_serv_sock,
radius->auth_serv_sock6, 1) < 0) {
conf->auth_server = oserv;
radius_change_server(radius, oserv, conf->auth_server,
radius->auth_serv_sock,
radius->auth_serv_sock6, 1);
}
}
if (radius->acct_sock >= 0 && conf->acct_servers &&
conf->acct_server != conf->acct_servers) {
oserv = conf->acct_server;
conf->acct_server = conf->acct_servers;
if (radius_change_server(radius, conf->acct_server, oserv,
radius->acct_serv_sock,
radius->acct_serv_sock6, 0) < 0) {
conf->acct_server = oserv;
radius_change_server(radius, oserv, conf->acct_server,
radius->acct_serv_sock,
radius->acct_serv_sock6, 0);
}
}
if (conf->retry_primary_interval)
eloop_register_timeout(conf->retry_primary_interval, 0,
radius_retry_primary_timer, radius,
NULL);
}
static int radius_client_disable_pmtu_discovery(int s)
{
int r = -1;
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
/* Turn off Path MTU discovery on IPv4/UDP sockets. */
int action = IP_PMTUDISC_DONT;
r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
sizeof(action));
if (r == -1)
wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
strerror(errno));
#endif
return r;
}
static void radius_close_auth_sockets(struct radius_client_data *radius)
{
radius->auth_sock = -1;
if (radius->auth_serv_sock >= 0) {
eloop_unregister_read_sock(radius->auth_serv_sock);
close(radius->auth_serv_sock);
radius->auth_serv_sock = -1;
}
#ifdef CONFIG_IPV6
if (radius->auth_serv_sock6 >= 0) {
eloop_unregister_read_sock(radius->auth_serv_sock6);
close(radius->auth_serv_sock6);
radius->auth_serv_sock6 = -1;
}
#endif /* CONFIG_IPV6 */
}
static void radius_close_acct_sockets(struct radius_client_data *radius)
{
radius->acct_sock = -1;
if (radius->acct_serv_sock >= 0) {
eloop_unregister_read_sock(radius->acct_serv_sock);
close(radius->acct_serv_sock);
radius->acct_serv_sock = -1;
}
#ifdef CONFIG_IPV6
if (radius->acct_serv_sock6 >= 0) {
eloop_unregister_read_sock(radius->acct_serv_sock6);
close(radius->acct_serv_sock6);
radius->acct_serv_sock6 = -1;
}
#endif /* CONFIG_IPV6 */
}
static int radius_client_init_auth(struct radius_client_data *radius)
{
struct hostapd_radius_servers *conf = radius->conf;
int ok = 0;
radius_close_auth_sockets(radius);
radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (radius->auth_serv_sock < 0)
wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
strerror(errno));
else {
radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
ok++;
}
#ifdef CONFIG_IPV6
radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
if (radius->auth_serv_sock6 < 0)
wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
strerror(errno));
else
ok++;
#endif /* CONFIG_IPV6 */
if (ok == 0)
return -1;
radius_change_server(radius, conf->auth_server, NULL,
radius->auth_serv_sock, radius->auth_serv_sock6,
1);
if (radius->auth_serv_sock >= 0 &&
eloop_register_read_sock(radius->auth_serv_sock,
radius_client_receive, radius,
(void *) RADIUS_AUTH)) {
wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
radius_close_auth_sockets(radius);
return -1;
}
#ifdef CONFIG_IPV6
if (radius->auth_serv_sock6 >= 0 &&
eloop_register_read_sock(radius->auth_serv_sock6,
radius_client_receive, radius,
(void *) RADIUS_AUTH)) {
wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
radius_close_auth_sockets(radius);
return -1;
}
#endif /* CONFIG_IPV6 */
return 0;
}
static int radius_client_init_acct(struct radius_client_data *radius)
{
struct hostapd_radius_servers *conf = radius->conf;
int ok = 0;
radius_close_acct_sockets(radius);
radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (radius->acct_serv_sock < 0)
wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
strerror(errno));
else {
radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
ok++;
}
#ifdef CONFIG_IPV6
radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
if (radius->acct_serv_sock6 < 0)
wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
strerror(errno));
else
ok++;
#endif /* CONFIG_IPV6 */
if (ok == 0)
return -1;
radius_change_server(radius, conf->acct_server, NULL,
radius->acct_serv_sock, radius->acct_serv_sock6,
0);
if (radius->acct_serv_sock >= 0 &&
eloop_register_read_sock(radius->acct_serv_sock,
radius_client_receive, radius,
(void *) RADIUS_ACCT)) {
wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
radius_close_acct_sockets(radius);
return -1;
}
#ifdef CONFIG_IPV6
if (radius->acct_serv_sock6 >= 0 &&
eloop_register_read_sock(radius->acct_serv_sock6,
radius_client_receive, radius,
(void *) RADIUS_ACCT)) {
wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
radius_close_acct_sockets(radius);
return -1;
}
#endif /* CONFIG_IPV6 */
return 0;
}
/**
* radius_client_init - Initialize RADIUS client
* @ctx: Callback context to be used in hostapd_logger() calls
* @conf: RADIUS client configuration (RADIUS servers)
* Returns: Pointer to private RADIUS client context or %NULL on failure
*
* The caller is responsible for keeping the configuration data available for
* the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
* called for the returned context pointer.
*/
struct radius_client_data *
radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
{
struct radius_client_data *radius;
radius = os_zalloc(sizeof(struct radius_client_data));
if (radius == NULL)
return NULL;
radius->ctx = ctx;
radius->conf = conf;
radius->auth_serv_sock = radius->acct_serv_sock =
radius->auth_serv_sock6 = radius->acct_serv_sock6 =
radius->auth_sock = radius->acct_sock = -1;
if (conf->auth_server && radius_client_init_auth(radius)) {
radius_client_deinit(radius);
return NULL;
}
if (conf->acct_server && radius_client_init_acct(radius)) {
radius_client_deinit(radius);
return NULL;
}
if (conf->retry_primary_interval)
eloop_register_timeout(conf->retry_primary_interval, 0,
radius_retry_primary_timer, radius,
NULL);
return radius;
}
/**
* radius_client_deinit - Deinitialize RADIUS client
* @radius: RADIUS client context from radius_client_init()
*/
void radius_client_deinit(struct radius_client_data *radius)
{
if (!radius)
return;
radius_close_auth_sockets(radius);
radius_close_acct_sockets(radius);
eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
radius_client_flush(radius, 0);
os_free(radius->auth_handlers);
os_free(radius->acct_handlers);
os_free(radius);
}
/**
* radius_client_flush_auth - Flush pending RADIUS messages for an address
* @radius: RADIUS client context from radius_client_init()
* @addr: MAC address of the related device
*
* This function can be used to remove pending RADIUS authentication messages
* that are related to a specific device. The addr parameter is matched with
* the one used in radius_client_send() call that was used to transmit the
* authentication request.
*/
void radius_client_flush_auth(struct radius_client_data *radius,
const u8 *addr)
{
struct radius_msg_list *entry, *prev, *tmp;
prev = NULL;
entry = radius->msgs;
while (entry) {
if (entry->msg_type == RADIUS_AUTH &&
os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
hostapd_logger(radius->ctx, addr,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG,
"Removing pending RADIUS authentication"
" message for removed client");
if (prev)
prev->next = entry->next;
else
radius->msgs = entry->next;
tmp = entry;
entry = entry->next;
radius_client_msg_free(tmp);
radius->num_msgs--;
continue;
}
prev = entry;
entry = entry->next;
}
}
static int radius_client_dump_auth_server(char *buf, size_t buflen,
struct hostapd_radius_server *serv,
struct radius_client_data *cli)
{
int pending = 0;
struct radius_msg_list *msg;
char abuf[50];
if (cli) {
for (msg = cli->msgs; msg; msg = msg->next) {
if (msg->msg_type == RADIUS_AUTH)
pending++;
}
}
return os_snprintf(buf, buflen,
"radiusAuthServerIndex=%d\n"
"radiusAuthServerAddress=%s\n"
"radiusAuthClientServerPortNumber=%d\n"
"radiusAuthClientRoundTripTime=%d\n"
"radiusAuthClientAccessRequests=%u\n"
"radiusAuthClientAccessRetransmissions=%u\n"
"radiusAuthClientAccessAccepts=%u\n"
"radiusAuthClientAccessRejects=%u\n"
"radiusAuthClientAccessChallenges=%u\n"
"radiusAuthClientMalformedAccessResponses=%u\n"
"radiusAuthClientBadAuthenticators=%u\n"
"radiusAuthClientPendingRequests=%u\n"
"radiusAuthClientTimeouts=%u\n"
"radiusAuthClientUnknownTypes=%u\n"
"radiusAuthClientPacketsDropped=%u\n",
serv->index,
hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
serv->port,
serv->round_trip_time,
serv->requests,
serv->retransmissions,
serv->access_accepts,
serv->access_rejects,
serv->access_challenges,
serv->malformed_responses,
serv->bad_authenticators,
pending,
serv->timeouts,
serv->unknown_types,
serv->packets_dropped);
}
static int radius_client_dump_acct_server(char *buf, size_t buflen,
struct hostapd_radius_server *serv,
struct radius_client_data *cli)
{
int pending = 0;
struct radius_msg_list *msg;
char abuf[50];
if (cli) {
for (msg = cli->msgs; msg; msg = msg->next) {
if (msg->msg_type == RADIUS_ACCT ||
msg->msg_type == RADIUS_ACCT_INTERIM)
pending++;
}
}
return os_snprintf(buf, buflen,
"radiusAccServerIndex=%d\n"
"radiusAccServerAddress=%s\n"
"radiusAccClientServerPortNumber=%d\n"
"radiusAccClientRoundTripTime=%d\n"
"radiusAccClientRequests=%u\n"
"radiusAccClientRetransmissions=%u\n"
"radiusAccClientResponses=%u\n"
"radiusAccClientMalformedResponses=%u\n"
"radiusAccClientBadAuthenticators=%u\n"
"radiusAccClientPendingRequests=%u\n"
"radiusAccClientTimeouts=%u\n"
"radiusAccClientUnknownTypes=%u\n"
"radiusAccClientPacketsDropped=%u\n",
serv->index,
hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
serv->port,
serv->round_trip_time,
serv->requests,
serv->retransmissions,
serv->responses,
serv->malformed_responses,
serv->bad_authenticators,
pending,
serv->timeouts,
serv->unknown_types,
serv->packets_dropped);
}
/**
* radius_client_get_mib - Get RADIUS client MIB information
* @radius: RADIUS client context from radius_client_init()
* @buf: Buffer for returning MIB data in text format
* @buflen: Maximum buf length in octets
* Returns: Number of octets written into the buffer
*/
int radius_client_get_mib(struct radius_client_data *radius, char *buf,
size_t buflen)
{
struct hostapd_radius_servers *conf;
int i;
struct hostapd_radius_server *serv;
int count = 0;
if (!radius)
return 0;
conf = radius->conf;
if (conf->auth_servers) {
for (i = 0; i < conf->num_auth_servers; i++) {
serv = &conf->auth_servers[i];
count += radius_client_dump_auth_server(
buf + count, buflen - count, serv,
serv == conf->auth_server ?
radius : NULL);
}
}
if (conf->acct_servers) {
for (i = 0; i < conf->num_acct_servers; i++) {
serv = &conf->acct_servers[i];
count += radius_client_dump_acct_server(
buf + count, buflen - count, serv,
serv == conf->acct_server ?
radius : NULL);
}
}
return count;
}
void radius_client_reconfig(struct radius_client_data *radius,
struct hostapd_radius_servers *conf)
{
if (radius)
radius->conf = conf;
}
diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.c b/contrib/wpa/src/rsn_supp/pmksa_cache.c
index 97a01a2f81e8..0cd5159821bb 100644
--- a/contrib/wpa/src/rsn_supp/pmksa_cache.c
+++ b/contrib/wpa/src/rsn_supp/pmksa_cache.c
@@ -1,668 +1,748 @@
/*
* WPA Supplicant - RSN PMKSA cache
* Copyright (c) 2004-2009, 2011-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "eloop.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "wpa.h"
#include "wpa_i.h"
#include "pmksa_cache.h"
#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
static const int pmksa_cache_max_entries = 32;
struct rsn_pmksa_cache {
struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
int pmksa_count; /* number of entries in PMKSA cache */
struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
enum pmksa_free_reason reason);
+ bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx);
void *ctx;
};
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
{
bin_clear_free(entry, sizeof(*entry));
}
static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry,
enum pmksa_free_reason reason)
{
wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
entry->pmkid,
entry->fils_cache_id_set ? entry->fils_cache_id :
NULL);
pmksa->pmksa_count--;
pmksa->free_cb(entry, pmksa->ctx, reason);
_pmksa_cache_free_entry(entry);
}
static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
struct os_reltime now;
+ struct rsn_pmksa_cache_entry *prev = NULL, *tmp;
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
os_get_reltime(&now);
- while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
- struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
- pmksa->pmksa = entry->next;
+ while (entry && entry->expiration <= now.sec) {
+ if (wpa_key_mgmt_sae(entry->akmp) &&
+ pmksa->is_current_cb(entry, pmksa->ctx)) {
+ /* Do not expire the currently used PMKSA entry for SAE
+ * since there is no convenient mechanism for
+ * reauthenticating during an association with SAE. The
+ * expired entry will be removed after this association
+ * has been lost. */
+ wpa_printf(MSG_DEBUG,
+ "RSN: postpone PMKSA cache entry expiration for SAE with "
+ MACSTR, MAC2STR(entry->aa));
+ prev = entry;
+ entry = entry->next;
+ continue;
+ }
+
wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
MACSTR, MAC2STR(entry->aa));
- pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE);
+ if (prev)
+ prev->next = entry->next;
+ else
+ pmksa->pmksa = entry->next;
+ tmp = entry;
+ entry = entry->next;
+ pmksa_cache_free_entry(pmksa, tmp, PMKSA_EXPIRE);
}
pmksa_cache_set_expiration(pmksa);
}
static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
pmksa->sm->cur_pmksa = NULL;
eapol_sm_request_reauth(pmksa->sm->eapol);
}
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
{
int sec;
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
if (pmksa->pmksa == NULL)
return;
os_get_reltime(&now);
sec = pmksa->pmksa->expiration - now.sec;
- if (sec < 0)
+ if (sec < 0) {
sec = 0;
+ if (wpa_key_mgmt_sae(pmksa->pmksa->akmp) &&
+ pmksa->is_current_cb(pmksa->pmksa, pmksa->ctx)) {
+ /* Do not continue polling for the current PMKSA entry
+ * from SAE to expire every second. Use the expiration
+ * time to the following entry, if any, and wait at
+ * maximum 10 minutes to check again.
+ */
+ entry = pmksa->pmksa->next;
+ if (entry) {
+ sec = entry->expiration - now.sec;
+ if (sec < 0)
+ sec = 0;
+ else if (sec > 600)
+ sec = 600;
+ } else {
+ sec = 600;
+ }
+ }
+ }
eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0);
- if (entry) {
+ if (entry && !wpa_key_mgmt_sae(entry->akmp)) {
sec = pmksa->pmksa->reauth_time - now.sec;
if (sec < 0)
sec = 0;
eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
NULL);
}
}
/**
* pmksa_cache_add - Add a PMKSA cache entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
* @pmk: The new pairwise master key
* @pmk_len: PMK length in bytes, usually PMK_LEN (32)
* @pmkid: Calculated PMKID
* @kck: Key confirmation key or %NULL if not yet derived
* @kck_len: KCK length in bytes
* @aa: Authenticator address
* @spa: Supplicant address
* @network_ctx: Network configuration context for this PMK
* @akmp: WPA_KEY_MGMT_* used in key derivation
* @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised
* Returns: Pointer to the added PMKSA cache entry or %NULL on error
*
* This function create a PMKSA entry for a new PMK and adds it to the PMKSA
* cache. If an old entry is already in the cache for the same Authenticator,
* this entry will be replaced with the new entry. PMKID will be calculated
* based on the PMK and the driver interface is notified of the new PMKID.
*/
struct rsn_pmksa_cache_entry *
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *kck, size_t kck_len,
const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
const u8 *cache_id)
{
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
if (pmk_len > PMK_LEN_MAX)
return NULL;
if (wpa_key_mgmt_suite_b(akmp) && !kck)
return NULL;
entry = os_zalloc(sizeof(*entry));
if (entry == NULL)
return NULL;
os_memcpy(entry->pmk, pmk, pmk_len);
entry->pmk_len = pmk_len;
if (pmkid)
os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
else if (wpa_key_mgmt_suite_b(akmp))
rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
else
rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
os_get_reltime(&now);
entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
entry->akmp = akmp;
if (cache_id) {
entry->fils_cache_id_set = 1;
os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN);
}
os_memcpy(entry->aa, aa, ETH_ALEN);
entry->network_ctx = network_ctx;
return pmksa_cache_add_entry(pmksa, entry);
}
struct rsn_pmksa_cache_entry *
pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry)
{
struct rsn_pmksa_cache_entry *pos, *prev;
/* Replace an old entry for the same Authenticator (if found) with the
* new entry */
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) {
if (pos->pmk_len == entry->pmk_len &&
os_memcmp_const(pos->pmk, entry->pmk,
entry->pmk_len) == 0 &&
os_memcmp_const(pos->pmkid, entry->pmkid,
PMKID_LEN) == 0) {
wpa_printf(MSG_DEBUG, "WPA: reusing previous "
"PMKSA entry");
os_free(entry);
return pos;
}
if (prev == NULL)
pmksa->pmksa = pos->next;
else
prev->next = pos->next;
/*
* If OKC is used, there may be other PMKSA cache
* entries based on the same PMK. These needs to be
* flushed so that a new entry can be created based on
* the new PMK. Only clear other entries if they have a
* matching PMK and this PMK has been used successfully
* with the current AP, i.e., if opportunistic flag has
* been cleared in wpa_supplicant_key_neg_complete().
*/
wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
"the current AP and any PMKSA cache entry "
"that was based on the old PMK");
if (!pos->opportunistic)
pmksa_cache_flush(pmksa, entry->network_ctx,
pos->pmk, pos->pmk_len,
false);
pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
break;
}
prev = pos;
pos = pos->next;
}
if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
/* Remove the oldest entry to make room for the new entry */
pos = pmksa->pmksa;
if (pos == pmksa->sm->cur_pmksa) {
/*
* Never remove the current PMKSA cache entry, since
* it's in use, and removing it triggers a needless
* deauthentication.
*/
pos = pos->next;
pmksa->pmksa->next = pos ? pos->next : NULL;
} else
pmksa->pmksa = pos->next;
if (pos) {
wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle "
"PMKSA cache entry (for " MACSTR ") to "
"make room for new one",
MAC2STR(pos->aa));
pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE);
}
}
/* Add the new entry; order by expiration time */
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
if (pos->expiration > entry->expiration)
break;
prev = pos;
pos = pos->next;
}
if (prev == NULL) {
entry->next = pmksa->pmksa;
pmksa->pmksa = entry;
pmksa_cache_set_expiration(pmksa);
} else {
entry->next = prev->next;
prev->next = entry;
}
pmksa->pmksa_count++;
wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
" network_ctx=%p akmp=0x%x", MAC2STR(entry->aa),
entry->network_ctx, entry->akmp);
wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid,
entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
entry->pmk, entry->pmk_len,
pmksa->sm->dot11RSNAConfigPMKLifetime,
pmksa->sm->dot11RSNAConfigPMKReauthThreshold,
entry->akmp);
return entry;
}
/**
* pmksa_cache_flush - Flush PMKSA cache entries for a specific network
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
* @network_ctx: Network configuration context or %NULL to flush all entries
* @pmk: PMK to match for or %NULL to match all PMKs
* @pmk_len: PMK length
* @external_only: Flush only PMKSA cache entries configured by external
* applications
*/
void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
const u8 *pmk, size_t pmk_len, bool external_only)
{
struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
int removed = 0;
entry = pmksa->pmksa;
while (entry) {
if ((entry->network_ctx == network_ctx ||
network_ctx == NULL) &&
(pmk == NULL ||
(pmk_len == entry->pmk_len &&
os_memcmp(pmk, entry->pmk, pmk_len) == 0)) &&
(!external_only || entry->external)) {
wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
"for " MACSTR, MAC2STR(entry->aa));
if (prev)
prev->next = entry->next;
else
pmksa->pmksa = entry->next;
tmp = entry;
entry = entry->next;
pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE);
removed++;
} else {
prev = entry;
entry = entry->next;
}
}
if (removed)
pmksa_cache_set_expiration(pmksa);
}
/**
* pmksa_cache_deinit - Free all entries in PMKSA cache
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
*/
void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
{
struct rsn_pmksa_cache_entry *entry, *prev;
if (pmksa == NULL)
return;
entry = pmksa->pmksa;
pmksa->pmksa = NULL;
while (entry) {
prev = entry;
entry = entry->next;
os_free(prev);
}
pmksa_cache_set_expiration(pmksa);
os_free(pmksa);
}
/**
* pmksa_cache_get - Fetch a PMKSA cache entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
* @aa: Authenticator address or %NULL to match any
* @pmkid: PMKID or %NULL to match any
* @network_ctx: Network context or %NULL to match any
* @akmp: Specific AKMP to search for or 0 for any
* Returns: Pointer to PMKSA cache entry or %NULL if no match was found
*/
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *pmkid,
const void *network_ctx,
int akmp)
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
while (entry) {
if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
(pmkid == NULL ||
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
(!akmp || akmp == entry->akmp) &&
(network_ctx == NULL || network_ctx == entry->network_ctx))
return entry;
entry = entry->next;
}
return NULL;
}
static struct rsn_pmksa_cache_entry *
pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
const struct rsn_pmksa_cache_entry *old_entry,
const u8 *aa)
{
struct rsn_pmksa_cache_entry *new_entry;
os_time_t old_expiration = old_entry->expiration;
+ os_time_t old_reauth_time = old_entry->reauth_time;
const u8 *pmkid = NULL;
if (wpa_key_mgmt_sae(old_entry->akmp) ||
wpa_key_mgmt_fils(old_entry->akmp))
pmkid = old_entry->pmkid;
new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
pmkid, NULL, 0,
aa, pmksa->sm->own_addr,
old_entry->network_ctx, old_entry->akmp,
old_entry->fils_cache_id_set ?
old_entry->fils_cache_id : NULL);
if (new_entry == NULL)
return NULL;
/* TODO: reorder entries based on expiration time? */
new_entry->expiration = old_expiration;
+ new_entry->reauth_time = old_reauth_time;
new_entry->opportunistic = 1;
return new_entry;
}
/**
* pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
* @network_ctx: Network configuration context
* @aa: Authenticator address for the new AP
* @akmp: Specific AKMP to search for or 0 for any
* Returns: Pointer to a new PMKSA cache entry or %NULL if not available
*
* Try to create a new PMKSA cache entry opportunistically by guessing that the
* new AP is sharing the same PMK as another AP that has the same SSID and has
* already an entry in PMKSA cache.
*/
struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
const u8 *aa, int akmp)
{
struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa));
if (network_ctx == NULL)
return NULL;
while (entry) {
if (entry->network_ctx == network_ctx &&
(!akmp || entry->akmp == akmp)) {
struct os_reltime now;
if (wpa_key_mgmt_sae(entry->akmp) &&
os_get_reltime(&now) == 0 &&
entry->reauth_time < now.sec) {
wpa_printf(MSG_DEBUG,
"RSN: Do not clone PMKSA cache entry for "
MACSTR
" since its reauth threshold has passed",
MAC2STR(entry->aa));
entry = entry->next;
continue;
}
entry = pmksa_cache_clone_entry(pmksa, entry, aa);
if (entry) {
wpa_printf(MSG_DEBUG, "RSN: added "
"opportunistic PMKSA cache entry "
"for " MACSTR, MAC2STR(aa));
}
return entry;
}
entry = entry->next;
}
return NULL;
}
static struct rsn_pmksa_cache_entry *
pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache *pmksa,
const void *network_ctx, const u8 *cache_id)
{
struct rsn_pmksa_cache_entry *entry;
for (entry = pmksa->pmksa; entry; entry = entry->next) {
if (network_ctx == entry->network_ctx &&
entry->fils_cache_id_set &&
os_memcmp(cache_id, entry->fils_cache_id,
FILS_CACHE_ID_LEN) == 0)
return entry;
}
return NULL;
}
/**
* pmksa_cache_get_current - Get the current used PMKSA entry
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* Returns: Pointer to the current PMKSA cache entry or %NULL if not available
*/
struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
{
if (sm == NULL)
return NULL;
return sm->cur_pmksa;
}
/**
* pmksa_cache_clear_current - Clear the current PMKSA entry selection
* @sm: Pointer to WPA state machine data from wpa_sm_init()
*/
void pmksa_cache_clear_current(struct wpa_sm *sm)
{
if (sm == NULL)
return;
if (sm->cur_pmksa)
wpa_printf(MSG_DEBUG,
"RSN: Clear current PMKSA entry selection");
sm->cur_pmksa = NULL;
}
/**
* pmksa_cache_set_current - Set the current PMKSA entry selection
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @pmkid: PMKID for selecting PMKSA or %NULL if not used
* @bssid: BSSID for PMKSA or %NULL if not used
* @network_ctx: Network configuration context
* @try_opportunistic: Whether to allow opportunistic PMKSA caching
* @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used
* Returns: 0 if PMKSA was found or -1 if no matching entry was found
*/
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
const u8 *bssid, void *network_ctx,
int try_opportunistic, const u8 *fils_cache_id,
int akmp)
{
struct rsn_pmksa_cache *pmksa = sm->pmksa;
wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
"try_opportunistic=%d akmp=0x%x",
network_ctx, try_opportunistic, akmp);
if (pmkid)
wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID",
pmkid, PMKID_LEN);
if (bssid)
wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR,
MAC2STR(bssid));
if (fils_cache_id)
wpa_printf(MSG_DEBUG,
"RSN: Search for FILS Cache Identifier %02x%02x",
fils_cache_id[0], fils_cache_id[1]);
sm->cur_pmksa = NULL;
if (pmkid)
sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
network_ctx, akmp);
if (sm->cur_pmksa == NULL && bssid)
sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
network_ctx, akmp);
if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
network_ctx,
bssid, akmp);
if (sm->cur_pmksa == NULL && fils_cache_id)
sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa,
network_ctx,
fils_cache_id);
if (sm->cur_pmksa) {
struct os_reltime now;
if (wpa_key_mgmt_sae(sm->cur_pmksa->akmp) &&
os_get_reltime(&now) == 0 &&
sm->cur_pmksa->reauth_time < now.sec) {
wpa_printf(MSG_DEBUG,
"RSN: Do not allow PMKSA cache entry for "
MACSTR
" to be used for SAE since its reauth threshold has passed",
MAC2STR(sm->cur_pmksa->aa));
sm->cur_pmksa = NULL;
return -1;
}
wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
sm->cur_pmksa->pmkid, PMKID_LEN);
return 0;
}
wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found");
return -1;
}
/**
* pmksa_cache_list - Dump text list of entries in PMKSA cache
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
* @buf: Buffer for the list
* @len: Length of the buffer
* Returns: number of bytes written to buffer
*
* This function is used to generate a text format representation of the
* current PMKSA cache contents for the ctrl_iface PMKSA command.
*/
int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
{
int i, ret;
char *pos = buf;
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
int cache_id_used = 0;
for (entry = pmksa->pmksa; entry; entry = entry->next) {
if (entry->fils_cache_id_set) {
cache_id_used = 1;
break;
}
}
os_get_reltime(&now);
ret = os_snprintf(pos, buf + len - pos,
"Index / AA / PMKID / expiration (in seconds) / "
"opportunistic%s\n",
cache_id_used ? " / FILS Cache Identifier" : "");
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
i = 0;
entry = pmksa->pmksa;
while (entry) {
i++;
ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
i, MAC2STR(entry->aa));
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
PMKID_LEN);
ret = os_snprintf(pos, buf + len - pos, " %d %d",
(int) (entry->expiration - now.sec),
entry->opportunistic);
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
if (entry->fils_cache_id_set) {
ret = os_snprintf(pos, buf + len - pos, " %02x%02x",
entry->fils_cache_id[0],
entry->fils_cache_id[1]);
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
}
ret = os_snprintf(pos, buf + len - pos, "\n");
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
entry = entry->next;
}
return pos - buf;
}
struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
{
return pmksa->pmksa;
}
/**
* pmksa_cache_init - Initialize PMKSA cache
* @free_cb: Callback function to be called when a PMKSA cache entry is freed
* @ctx: Context pointer for free_cb function
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* Returns: Pointer to PMKSA cache data or %NULL on failure
*/
struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx, enum pmksa_free_reason reason),
+ bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
void *ctx, struct wpa_sm *sm)
{
struct rsn_pmksa_cache *pmksa;
pmksa = os_zalloc(sizeof(*pmksa));
if (pmksa) {
pmksa->free_cb = free_cb;
+ pmksa->is_current_cb = is_current_cb;
pmksa->ctx = ctx;
pmksa->sm = sm;
}
return pmksa;
}
+
+void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
+{
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_reltime now;
+
+ if (!pmksa || !pmksa->pmksa)
+ return;
+
+ os_get_reltime(&now);
+ for (entry = pmksa->pmksa; entry; entry = entry->next) {
+ u32 life_time;
+ u8 reauth_threshold;
+
+ if (entry->expiration - now.sec < 1 ||
+ entry->reauth_time - now.sec < 1)
+ continue;
+
+ life_time = entry->expiration - now.sec;
+ reauth_threshold = (entry->reauth_time - now.sec) * 100 /
+ life_time;
+ if (!reauth_threshold)
+ continue;
+
+ wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
+ entry->pmkid,
+ entry->fils_cache_id_set ?
+ entry->fils_cache_id : NULL,
+ entry->pmk, entry->pmk_len, life_time,
+ reauth_threshold, entry->akmp);
+ }
+}
+
#endif /* IEEE8021X_EAPOL */
diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.h b/contrib/wpa/src/rsn_supp/pmksa_cache.h
index ae7bc13fa118..b801268599a9 100644
--- a/contrib/wpa/src/rsn_supp/pmksa_cache.h
+++ b/contrib/wpa/src/rsn_supp/pmksa_cache.h
@@ -1,168 +1,177 @@
/*
* wpa_supplicant - WPA2/RSN PMKSA cache functions
* Copyright (c) 2003-2009, 2011-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef PMKSA_CACHE_H
#define PMKSA_CACHE_H
/**
* struct rsn_pmksa_cache_entry - PMKSA cache entry
*/
struct rsn_pmksa_cache_entry {
struct rsn_pmksa_cache_entry *next;
u8 pmkid[PMKID_LEN];
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */
u8 aa[ETH_ALEN];
/*
* If FILS Cache Identifier is included (fils_cache_id_set), this PMKSA
* cache entry is applicable to all BSSs (any BSSID/aa[]) that
* advertise the same FILS Cache Identifier within the same ESS.
*/
u8 fils_cache_id[2];
unsigned int fils_cache_id_set:1;
unsigned int dpp_pfs:1;
os_time_t reauth_time;
/**
* network_ctx - Network configuration context
*
* This field is only used to match PMKSA cache entries to a specific
* network configuration (e.g., a specific SSID and security policy).
* This can be a pointer to the configuration entry, but PMKSA caching
* code does not dereference the value and this could be any kind of
* identifier.
*/
void *network_ctx;
int opportunistic;
bool external;
};
struct rsn_pmksa_cache;
enum pmksa_free_reason {
PMKSA_FREE,
PMKSA_REPLACE,
PMKSA_EXPIRE,
};
#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx, enum pmksa_free_reason reason),
+ bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
void *ctx, struct wpa_sm *sm);
void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
const u8 *aa, const u8 *pmkid,
const void *network_ctx,
int akmp);
int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry *
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *kck, size_t kck_len,
const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
const u8 *cache_id);
struct rsn_pmksa_cache_entry *
pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry);
struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
void pmksa_cache_clear_current(struct wpa_sm *sm);
int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
const u8 *bssid, void *network_ctx,
int try_opportunistic, const u8 *fils_cache_id,
int akmp);
struct rsn_pmksa_cache_entry *
pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
void *network_ctx, const u8 *aa, int akmp);
void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
const u8 *pmk, size_t pmk_len, bool external_only);
+void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa);
#else /* IEEE8021X_EAPOL */
static inline struct rsn_pmksa_cache *
pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx, enum pmksa_free_reason reason),
+ bool (*is_current_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx),
void *ctx, struct wpa_sm *sm)
{
return (void *) -1;
}
static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
{
}
static inline struct rsn_pmksa_cache_entry *
pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid,
const void *network_ctx, int akmp)
{
return NULL;
}
static inline struct rsn_pmksa_cache_entry *
pmksa_cache_get_current(struct wpa_sm *sm)
{
return NULL;
}
static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf,
size_t len)
{
return -1;
}
static inline struct rsn_pmksa_cache_entry *
pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
{
return NULL;
}
static inline struct rsn_pmksa_cache_entry *
pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry)
{
return NULL;
}
static inline struct rsn_pmksa_cache_entry *
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *kck, size_t kck_len,
const u8 *aa, const u8 *spa, void *network_ctx, int akmp,
const u8 *cache_id)
{
return NULL;
}
static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
{
}
static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
const u8 *bssid,
void *network_ctx,
int try_opportunistic,
const u8 *fils_cache_id,
int akmp)
{
return -1;
}
static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa,
void *network_ctx,
const u8 *pmk, size_t pmk_len,
bool external_only)
{
}
+static inline void pmksa_cache_reconfig(struct rsn_pmksa_cache *pmksa)
+{
+}
+
#endif /* IEEE8021X_EAPOL */
#endif /* PMKSA_CACHE_H */
diff --git a/contrib/wpa/src/rsn_supp/tdls.c b/contrib/wpa/src/rsn_supp/tdls.c
index 411cbf46a40d..c26a63d822af 100644
--- a/contrib/wpa/src/rsn_supp/tdls.c
+++ b/contrib/wpa/src/rsn_supp/tdls.c
@@ -1,3110 +1,3143 @@
/*
* wpa_supplicant - TDLS
* Copyright (c) 2010-2011, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/os.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "crypto/sha256.h"
#include "crypto/crypto.h"
#include "crypto/aes_wrap.h"
#include "rsn_supp/wpa.h"
#include "rsn_supp/wpa_ie.h"
#include "rsn_supp/wpa_i.h"
#include "drivers/driver.h"
#include "l2_packet/l2_packet.h"
#ifdef CONFIG_TDLS_TESTING
#define TDLS_TESTING_LONG_FRAME BIT(0)
#define TDLS_TESTING_ALT_RSN_IE BIT(1)
#define TDLS_TESTING_DIFF_BSSID BIT(2)
#define TDLS_TESTING_SHORT_LIFETIME BIT(3)
#define TDLS_TESTING_WRONG_LIFETIME_RESP BIT(4)
#define TDLS_TESTING_WRONG_LIFETIME_CONF BIT(5)
#define TDLS_TESTING_LONG_LIFETIME BIT(6)
#define TDLS_TESTING_CONCURRENT_INIT BIT(7)
#define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
#define TDLS_TESTING_DECLINE_RESP BIT(9)
#define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
#define TDLS_TESTING_WRONG_MIC BIT(11)
#define TDLS_TESTING_DOUBLE_TPK_M2 BIT(12)
unsigned int tdls_testing = 0;
#endif /* CONFIG_TDLS_TESTING */
#define TPK_LIFETIME 43200 /* 12 hours */
#define TPK_M1_RETRY_COUNT 3
#define TPK_M1_TIMEOUT 5000 /* in milliseconds */
#define TPK_M2_RETRY_COUNT 10
#define TPK_M2_TIMEOUT 500 /* in milliseconds */
#define TDLS_MIC_LEN 16
#define TDLS_TIMEOUT_LEN 4
struct wpa_tdls_ftie {
u8 ie_type; /* FTIE */
u8 ie_len;
u8 mic_ctrl[2];
u8 mic[TDLS_MIC_LEN];
u8 Anonce[WPA_NONCE_LEN]; /* Responder Nonce in TDLS */
u8 Snonce[WPA_NONCE_LEN]; /* Initiator Nonce in TDLS */
/* followed by optional elements */
} STRUCT_PACKED;
struct wpa_tdls_timeoutie {
u8 ie_type; /* Timeout IE */
u8 ie_len;
u8 interval_type;
u8 value[TDLS_TIMEOUT_LEN];
} STRUCT_PACKED;
struct wpa_tdls_lnkid {
u8 ie_type; /* Link Identifier IE */
u8 ie_len;
u8 bssid[ETH_ALEN];
u8 init_sta[ETH_ALEN];
u8 resp_sta[ETH_ALEN];
} STRUCT_PACKED;
/* TDLS frame headers as per IEEE Std 802.11z-2010 */
struct wpa_tdls_frame {
u8 payloadtype; /* IEEE80211_TDLS_RFTYPE */
u8 category; /* Category */
u8 action; /* Action (enum tdls_frame_type) */
} STRUCT_PACKED;
static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs);
static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx);
static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer);
static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
struct wpa_tdls_peer *peer);
static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
u16 reason_code);
#define TDLS_MAX_IE_LEN 80
#define IEEE80211_MAX_SUPP_RATES 32
struct wpa_tdls_peer {
struct wpa_tdls_peer *next;
unsigned int reconfig_key:1;
int initiator; /* whether this end was initiator for TDLS setup */
u8 addr[ETH_ALEN]; /* other end MAC address */
u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
u8 rnonce[WPA_NONCE_LEN]; /* Responder Nonce */
u8 rsnie_i[TDLS_MAX_IE_LEN]; /* Initiator RSN IE */
size_t rsnie_i_len;
u8 rsnie_p[TDLS_MAX_IE_LEN]; /* Peer RSN IE */
size_t rsnie_p_len;
u32 lifetime;
int cipher; /* Selected cipher (WPA_CIPHER_*) */
u8 dtoken;
struct tpk {
u8 kck[16]; /* TPK-KCK */
u8 tk[16]; /* TPK-TK; assuming only CCMP will be used */
} tpk;
int tpk_set;
int tk_set; /* TPK-TK configured to the driver */
int tpk_success;
int tpk_in_progress;
struct tpk_timer {
u8 dest[ETH_ALEN];
int count; /* Retry Count */
int timer; /* Timeout in milliseconds */
u8 action_code; /* TDLS frame type */
u8 dialog_token;
u16 status_code;
u32 peer_capab;
int buf_len; /* length of TPK message for retransmission */
u8 *buf; /* buffer for TPK message */
} sm_tmr;
u16 capability;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
size_t supp_rates_len;
struct ieee80211_ht_capabilities *ht_capabilities;
struct ieee80211_vht_capabilities *vht_capabilities;
struct ieee80211_he_capabilities *he_capabilities;
size_t he_capab_len;
+ struct ieee80211_he_6ghz_band_cap *he_6ghz_band_capabilities;
u8 qos_info;
u16 aid;
u8 *ext_capab;
size_t ext_capab_len;
u8 *supp_channels;
size_t supp_channels_len;
u8 *supp_oper_classes;
size_t supp_oper_classes_len;
u8 wmm_capable;
/* channel switch currently enabled */
int chan_switch_enabled;
};
static int wpa_tdls_get_privacy(struct wpa_sm *sm)
{
/*
* Get info needed from supplicant to check if the current BSS supports
* security. Other than OPEN mode, rest are considered secured
* WEP/WPA/WPA2 hence TDLS frames are processed for TPK handshake.
*/
return sm->pairwise_cipher != WPA_CIPHER_NONE;
}
static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
{
os_memcpy(pos, ie, ie_len);
return pos + ie_len;
}
static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
{
if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
0, 0, NULL, 0, NULL, 0, KEY_FLAG_PAIRWISE) < 0) {
wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
"the driver");
return -1;
}
return 0;
}
static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
{
u8 key_len;
u8 rsc[6];
enum wpa_alg alg;
if (peer->tk_set) {
/*
* This same TPK-TK has already been configured to the driver
* and this new configuration attempt (likely due to an
* unexpected retransmitted frame) would result in clearing
* the TX/RX sequence number which can break security, so must
* not allow that to happen.
*/
wpa_printf(MSG_INFO, "TDLS: TPK-TK for the peer " MACSTR
" has already been configured to the driver - do not reconfigure",
MAC2STR(peer->addr));
return -1;
}
os_memset(rsc, 0, 6);
switch (peer->cipher) {
case WPA_CIPHER_CCMP:
alg = WPA_ALG_CCMP;
key_len = 16;
break;
case WPA_CIPHER_NONE:
wpa_printf(MSG_DEBUG, "TDLS: Pairwise Cipher Suite: "
"NONE - do not use pairwise keys");
return -1;
default:
wpa_printf(MSG_WARNING, "TDLS: Unsupported pairwise cipher %d",
sm->pairwise_cipher);
return -1;
}
wpa_printf(MSG_DEBUG, "TDLS: Configure pairwise key for peer " MACSTR,
MAC2STR(peer->addr));
if (wpa_sm_set_key(sm, alg, peer->addr, 0, 1, rsc, sizeof(rsc),
peer->tpk.tk, key_len,
KEY_FLAG_PAIRWISE_RX_TX) < 0) {
wpa_printf(MSG_WARNING, "TDLS: Failed to set TPK to the "
"driver");
return -1;
}
peer->tk_set = 1;
return 0;
}
static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capab,
int initiator, const u8 *buf, size_t len)
{
return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
status_code, peer_capab, initiator, buf,
len);
}
static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
u8 dialog_token, u16 status_code, u32 peer_capab,
int initiator, const u8 *msg, size_t msg_len)
{
struct wpa_tdls_peer *peer;
wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
"dialog_token=%u status_code=%u peer_capab=%u initiator=%d "
"msg_len=%u",
MAC2STR(dest), action_code, dialog_token, status_code,
peer_capab, initiator, (unsigned int) msg_len);
if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
status_code, peer_capab, initiator, msg,
msg_len)) {
wpa_printf(MSG_INFO, "TDLS: Failed to send message "
"(action_code=%u)", action_code);
return -1;
}
if (action_code == WLAN_TDLS_SETUP_CONFIRM ||
action_code == WLAN_TDLS_TEARDOWN ||
action_code == WLAN_TDLS_DISCOVERY_REQUEST ||
action_code == WLAN_TDLS_DISCOVERY_RESPONSE)
return 0; /* No retries */
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, dest, ETH_ALEN) == 0)
break;
}
if (peer == NULL) {
wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
"retry " MACSTR, MAC2STR(dest));
return 0;
}
eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
peer->sm_tmr.count = TPK_M2_RETRY_COUNT;
peer->sm_tmr.timer = TPK_M2_TIMEOUT;
} else {
peer->sm_tmr.count = TPK_M1_RETRY_COUNT;
peer->sm_tmr.timer = TPK_M1_TIMEOUT;
}
/* Copy message to resend on timeout */
os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN);
peer->sm_tmr.action_code = action_code;
peer->sm_tmr.dialog_token = dialog_token;
peer->sm_tmr.status_code = status_code;
peer->sm_tmr.peer_capab = peer_capab;
peer->sm_tmr.buf_len = msg_len;
os_free(peer->sm_tmr.buf);
peer->sm_tmr.buf = os_memdup(msg, msg_len);
if (peer->sm_tmr.buf == NULL)
return -1;
wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered "
"(action_code=%u)", action_code);
eloop_register_timeout(peer->sm_tmr.timer / 1000,
(peer->sm_tmr.timer % 1000) * 1000,
wpa_tdls_tpk_retry_timeout, sm, peer);
return 0;
}
static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
u16 reason_code)
{
int ret;
ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
/* disable the link after teardown was sent */
wpa_tdls_disable_peer_link(sm, peer);
return ret;
}
static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_sm *sm = eloop_ctx;
struct wpa_tdls_peer *peer = timeout_ctx;
if (peer->sm_tmr.count) {
peer->sm_tmr.count--;
wpa_printf(MSG_INFO, "TDLS: Retrying sending of message "
"(action_code=%u)",
peer->sm_tmr.action_code);
if (peer->sm_tmr.buf == NULL) {
wpa_printf(MSG_INFO, "TDLS: No retry buffer available "
"for action_code=%u",
peer->sm_tmr.action_code);
eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm,
peer);
return;
}
/* resend TPK Handshake Message to Peer */
if (wpa_tdls_send_tpk_msg(sm, peer->sm_tmr.dest,
peer->sm_tmr.action_code,
peer->sm_tmr.dialog_token,
peer->sm_tmr.status_code,
peer->sm_tmr.peer_capab,
peer->initiator,
peer->sm_tmr.buf,
peer->sm_tmr.buf_len)) {
wpa_printf(MSG_INFO, "TDLS: Failed to retry "
"transmission");
}
eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
eloop_register_timeout(peer->sm_tmr.timer / 1000,
(peer->sm_tmr.timer % 1000) * 1000,
wpa_tdls_tpk_retry_timeout, sm, peer);
} else {
eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
wpa_tdls_do_teardown(sm, peer,
WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
}
}
static void wpa_tdls_tpk_retry_timeout_cancel(struct wpa_sm *sm,
struct wpa_tdls_peer *peer,
u8 action_code)
{
if (action_code == peer->sm_tmr.action_code) {
wpa_printf(MSG_DEBUG, "TDLS: Retry timeout cancelled for "
"action_code=%u", action_code);
/* Cancel Timeout registered */
eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
/* free all resources meant for retry */
os_free(peer->sm_tmr.buf);
peer->sm_tmr.buf = NULL;
peer->sm_tmr.count = 0;
peer->sm_tmr.timer = 0;
peer->sm_tmr.buf_len = 0;
peer->sm_tmr.action_code = 0xff;
} else {
wpa_printf(MSG_INFO, "TDLS: Error in cancelling retry timeout "
"(Unknown action_code=%u)", action_code);
}
}
static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer,
const u8 *own_addr, const u8 *bssid)
{
u8 key_input[SHA256_MAC_LEN];
const u8 *nonce[2];
size_t len[2];
u8 data[3 * ETH_ALEN];
/* IEEE Std 802.11-2016 12.7.9.2:
* TPK-Key-Input = Hash(min(SNonce, ANonce) || max(SNonce, ANonce))
* Hash = SHA-256 for TDLS
*/
len[0] = WPA_NONCE_LEN;
len[1] = WPA_NONCE_LEN;
if (os_memcmp(peer->inonce, peer->rnonce, WPA_NONCE_LEN) < 0) {
nonce[0] = peer->inonce;
nonce[1] = peer->rnonce;
} else {
nonce[0] = peer->rnonce;
nonce[1] = peer->inonce;
}
wpa_hexdump(MSG_DEBUG, "TDLS: min(Nonce)", nonce[0], WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "TDLS: max(Nonce)", nonce[1], WPA_NONCE_LEN);
sha256_vector(2, nonce, len, key_input);
wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
key_input, SHA256_MAC_LEN);
/*
* TPK = KDF-Hash-Length(TPK-Key-Input, "TDLS PMK",
* min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID)
*/
if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) {
os_memcpy(data, own_addr, ETH_ALEN);
os_memcpy(data + ETH_ALEN, peer->addr, ETH_ALEN);
} else {
os_memcpy(data, peer->addr, ETH_ALEN);
os_memcpy(data + ETH_ALEN, own_addr, ETH_ALEN);
}
os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
(u8 *) &peer->tpk, sizeof(peer->tpk));
wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
peer->tpk.kck, sizeof(peer->tpk.kck));
wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
peer->tpk.tk, sizeof(peer->tpk.tk));
peer->tpk_set = 1;
}
/**
* wpa_tdls_ftie_mic - Calculate TDLS FTIE MIC
* @kck: TPK-KCK
* @lnkid: Pointer to the beginning of Link Identifier IE
* @rsnie: Pointer to the beginning of RSN IE used for handshake
* @timeoutie: Pointer to the beginning of Timeout IE used for handshake
* @ftie: Pointer to the beginning of FT IE
* @mic: Pointer for writing MIC
*
* Calculate MIC for TDLS frame.
*/
static int wpa_tdls_ftie_mic(const u8 *kck, u8 trans_seq, const u8 *lnkid,
const u8 *rsnie, const u8 *timeoutie,
const u8 *ftie, u8 *mic)
{
u8 *buf, *pos;
struct wpa_tdls_ftie *_ftie;
const struct wpa_tdls_lnkid *_lnkid;
int ret;
int len = 2 * ETH_ALEN + 1 + 2 + lnkid[1] + 2 + rsnie[1] +
2 + timeoutie[1] + 2 + ftie[1];
buf = os_zalloc(len);
if (!buf) {
wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
return -1;
}
pos = buf;
_lnkid = (const struct wpa_tdls_lnkid *) lnkid;
/* 1) TDLS initiator STA MAC address */
os_memcpy(pos, _lnkid->init_sta, ETH_ALEN);
pos += ETH_ALEN;
/* 2) TDLS responder STA MAC address */
os_memcpy(pos, _lnkid->resp_sta, ETH_ALEN);
pos += ETH_ALEN;
/* 3) Transaction Sequence number */
*pos++ = trans_seq;
/* 4) Link Identifier IE */
os_memcpy(pos, lnkid, 2 + lnkid[1]);
pos += 2 + lnkid[1];
/* 5) RSN IE */
os_memcpy(pos, rsnie, 2 + rsnie[1]);
pos += 2 + rsnie[1];
/* 6) Timeout Interval IE */
os_memcpy(pos, timeoutie, 2 + timeoutie[1]);
pos += 2 + timeoutie[1];
/* 7) FTIE, with the MIC field of the FTIE set to 0 */
os_memcpy(pos, ftie, 2 + ftie[1]);
_ftie = (struct wpa_tdls_ftie *) pos;
os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
pos += 2 + ftie[1];
wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
ret = omac1_aes_128(kck, buf, pos - buf, mic);
os_free(buf);
wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
return ret;
}
/**
* wpa_tdls_key_mic_teardown - Calculate TDLS FTIE MIC for Teardown frame
* @kck: TPK-KCK
* @trans_seq: Transaction Sequence Number (4 - Teardown)
* @rcode: Reason code for Teardown
* @dtoken: Dialog Token used for that particular link
* @lnkid: Pointer to the beginning of Link Identifier IE
* @ftie: Pointer to the beginning of FT IE
* @mic: Pointer for writing MIC
*
* Calculate MIC for TDLS frame.
*/
static int wpa_tdls_key_mic_teardown(const u8 *kck, u8 trans_seq, u16 rcode,
u8 dtoken, const u8 *lnkid,
const u8 *ftie, u8 *mic)
{
u8 *buf, *pos;
struct wpa_tdls_ftie *_ftie;
int ret;
int len;
if (lnkid == NULL)
return -1;
len = 2 + lnkid[1] + sizeof(rcode) + sizeof(dtoken) +
sizeof(trans_seq) + 2 + ftie[1];
buf = os_zalloc(len);
if (!buf) {
wpa_printf(MSG_WARNING, "TDLS: No memory for MIC calculation");
return -1;
}
pos = buf;
/* 1) Link Identifier IE */
os_memcpy(pos, lnkid, 2 + lnkid[1]);
pos += 2 + lnkid[1];
/* 2) Reason Code */
WPA_PUT_LE16(pos, rcode);
pos += sizeof(rcode);
/* 3) Dialog token */
*pos++ = dtoken;
/* 4) Transaction Sequence number */
*pos++ = trans_seq;
/* 7) FTIE, with the MIC field of the FTIE set to 0 */
os_memcpy(pos, ftie, 2 + ftie[1]);
_ftie = (struct wpa_tdls_ftie *) pos;
os_memset(_ftie->mic, 0, TDLS_MIC_LEN);
pos += 2 + ftie[1];
wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", kck, 16);
ret = omac1_aes_128(kck, buf, pos - buf, mic);
os_free(buf);
wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
return ret;
}
static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
struct wpa_tdls_peer *peer,
const u8 *lnkid, const u8 *timeoutie,
const struct wpa_tdls_ftie *ftie)
{
u8 mic[16];
if (peer->tpk_set) {
wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
peer->rsnie_p, timeoutie, (u8 *) ftie,
mic);
if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
"dropping packet");
wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
ftie->mic, 16);
wpa_hexdump(MSG_DEBUG, "TDLS: Calculated MIC",
mic, 16);
return -1;
}
} else {
wpa_printf(MSG_WARNING, "TDLS: Could not verify TDLS MIC, "
"TPK not set - dropping packet");
return -1;
}
return 0;
}
static int wpa_supplicant_verify_tdls_mic_teardown(
u8 trans_seq, u16 rcode, u8 dtoken, struct wpa_tdls_peer *peer,
const u8 *lnkid, const struct wpa_tdls_ftie *ftie)
{
u8 mic[16];
if (peer->tpk_set) {
wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
dtoken, lnkid, (u8 *) ftie, mic);
if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
"dropping packet");
return -1;
}
} else {
wpa_printf(MSG_INFO, "TDLS: Could not verify TDLS Teardown "
"MIC, TPK not set - dropping packet");
return -1;
}
return 0;
}
static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_sm *sm = eloop_ctx;
struct wpa_tdls_peer *peer = timeout_ctx;
/*
* On TPK lifetime expiration, we have an option of either tearing down
* the direct link or trying to re-initiate it. The selection of what
* to do is not strictly speaking controlled by our role in the expired
* link, but for now, use that to select whether to renew or tear down
* the link.
*/
if (peer->initiator) {
u8 addr[ETH_ALEN];
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
" - try to renew", MAC2STR(peer->addr));
/* cache the peer address before do_teardown */
os_memcpy(addr, peer->addr, ETH_ALEN);
wpa_tdls_do_teardown(sm, peer,
WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
wpa_tdls_start(sm, addr);
} else {
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
" - tear down", MAC2STR(peer->addr));
wpa_tdls_do_teardown(sm, peer,
WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
}
}
static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
struct wpa_tdls_peer *peer)
{
struct wpa_tdls_peer *cur, *prev;
cur = sm->tdls;
prev = NULL;
while (cur && cur != peer) {
prev = cur;
cur = cur->next;
}
if (cur != peer) {
wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
" to remove it from the list",
MAC2STR(peer->addr));
return;
}
if (prev)
prev->next = peer->next;
else
sm->tdls = peer->next;
}
static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
{
wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
MAC2STR(peer->addr));
eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
peer->reconfig_key = 0;
peer->initiator = 0;
peer->tpk_in_progress = 0;
os_free(peer->sm_tmr.buf);
peer->sm_tmr.buf = NULL;
os_free(peer->ht_capabilities);
peer->ht_capabilities = NULL;
os_free(peer->vht_capabilities);
peer->vht_capabilities = NULL;
os_free(peer->he_capabilities);
peer->he_capabilities = NULL;
+ os_free(peer->he_6ghz_band_capabilities);
+ peer->he_6ghz_band_capabilities = NULL;
os_free(peer->ext_capab);
peer->ext_capab = NULL;
os_free(peer->supp_channels);
peer->supp_channels = NULL;
os_free(peer->supp_oper_classes);
peer->supp_oper_classes = NULL;
peer->rsnie_i_len = peer->rsnie_p_len = 0;
peer->cipher = 0;
peer->qos_info = 0;
peer->wmm_capable = 0;
peer->tk_set = peer->tpk_set = peer->tpk_success = 0;
peer->chan_switch_enabled = 0;
os_memset(&peer->tpk, 0, sizeof(peer->tpk));
os_memset(peer->inonce, 0, WPA_NONCE_LEN);
os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
}
static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
{
wpa_tdls_peer_clear(sm, peer);
wpa_tdls_peer_remove_from_list(sm, peer);
os_free(peer);
}
static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
struct wpa_tdls_lnkid *lnkid)
{
lnkid->ie_type = WLAN_EID_LINK_ID;
lnkid->ie_len = 3 * ETH_ALEN;
os_memcpy(lnkid->bssid, sm->bssid, ETH_ALEN);
if (peer->initiator) {
os_memcpy(lnkid->init_sta, sm->own_addr, ETH_ALEN);
os_memcpy(lnkid->resp_sta, peer->addr, ETH_ALEN);
} else {
os_memcpy(lnkid->init_sta, peer->addr, ETH_ALEN);
os_memcpy(lnkid->resp_sta, sm->own_addr, ETH_ALEN);
}
}
static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
u16 reason_code)
{
struct wpa_tdls_peer *peer;
struct wpa_tdls_ftie *ftie;
struct wpa_tdls_lnkid lnkid;
u8 dialog_token;
u8 *rbuf, *pos;
int ielen;
if (sm->tdls_disabled || !sm->tdls_supported)
return -1;
/* Find the node and free from the list */
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
break;
}
if (peer == NULL) {
wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
"Teardown " MACSTR, MAC2STR(addr));
return 0;
}
/* Cancel active channel switch before teardown */
if (peer->chan_switch_enabled) {
wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR
" to base channel", MAC2STR(addr));
wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
}
dialog_token = peer->dtoken;
wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
MAC2STR(addr));
ielen = 0;
if (wpa_tdls_get_privacy(sm) && peer->tpk_set && peer->tpk_success) {
/* To add FTIE for Teardown request and compute MIC */
ielen += sizeof(*ftie);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_LONG_FRAME)
ielen += 170;
#endif /* CONFIG_TDLS_TESTING */
}
rbuf = os_zalloc(ielen + 1);
if (rbuf == NULL)
return -1;
pos = rbuf;
if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
goto skip_ies;
ftie = (struct wpa_tdls_ftie *) pos;
ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
/* Using the recent nonce which should be for CONFIRM frame */
os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
pos = (u8 *) (ftie + 1);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
"FTIE");
ftie->ie_len += 170;
*pos++ = 255; /* FTIE subelem */
*pos++ = 168; /* FTIE subelem length */
pos += 168;
}
#endif /* CONFIG_TDLS_TESTING */
wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TDLS Teardown handshake",
(u8 *) ftie, pos - (u8 *) ftie);
/* compute MIC before sending */
wpa_tdls_linkid(sm, peer, &lnkid);
wpa_tdls_key_mic_teardown(peer->tpk.kck, 4, reason_code,
dialog_token, (u8 *) &lnkid, (u8 *) ftie,
ftie->mic);
skip_ies:
/* TODO: register for a Timeout handler, if Teardown is not received at
* the other end, then try again another time */
/* request driver to send Teardown using this FTIE */
wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
reason_code, 0, peer->initiator, rbuf, pos - rbuf);
os_free(rbuf);
return 0;
}
int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
{
struct wpa_tdls_peer *peer;
if (sm->tdls_disabled || !sm->tdls_supported)
return -1;
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
break;
}
if (peer == NULL) {
wpa_printf(MSG_DEBUG, "TDLS: Could not find peer " MACSTR
" for link Teardown", MAC2STR(addr));
return -1;
}
if (!peer->tpk_success) {
wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
" not connected - cannot Teardown link", MAC2STR(addr));
return -1;
}
return wpa_tdls_do_teardown(sm, peer, reason_code);
}
static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
struct wpa_tdls_peer *peer)
{
wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
wpa_tdls_peer_free(sm, peer);
}
void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
{
struct wpa_tdls_peer *peer;
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
break;
}
if (!peer || !peer->tpk_success) {
wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
" not connected - cannot teardown unreachable link",
MAC2STR(addr));
return;
}
if (wpa_tdls_is_external_setup(sm)) {
/*
* Get us on the base channel, disable the link, send a
* teardown packet through the AP, and then reset link data.
*/
if (peer->chan_switch_enabled)
wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
wpa_tdls_send_teardown(sm, addr,
WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
wpa_tdls_peer_free(sm, peer);
} else {
wpa_tdls_disable_peer_link(sm, peer);
}
}
const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr)
{
struct wpa_tdls_peer *peer;
if (sm->tdls_disabled || !sm->tdls_supported)
return "disabled";
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
break;
}
if (peer == NULL)
return "peer does not exist";
if (!peer->tpk_success)
return "peer not connected";
return "connected";
}
static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len)
{
struct wpa_tdls_peer *peer = NULL;
struct wpa_tdls_ftie *ftie;
struct wpa_tdls_lnkid *lnkid;
struct wpa_eapol_ie_parse kde;
u16 reason_code;
const u8 *pos;
int ielen;
/* Find the node and free from the list */
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
break;
}
if (peer == NULL) {
wpa_printf(MSG_INFO, "TDLS: No matching entry found for "
"Teardown " MACSTR, MAC2STR(src_addr));
return 0;
}
pos = buf;
pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
reason_code = WPA_GET_LE16(pos);
pos += 2;
wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown Request from " MACSTR
" (reason code %u)", MAC2STR(src_addr), reason_code);
ielen = len - (pos - buf); /* start of IE in buf */
/*
* Don't reject the message if failing to parse IEs. The IEs we need are
* explicitly checked below. Some APs may add arbitrary padding to the
* end of short TDLS frames and that would look like invalid IEs.
*/
if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0)
wpa_printf(MSG_DEBUG,
"TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround");
if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
"Teardown");
return -1;
}
lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
if (!wpa_tdls_get_privacy(sm) || !peer->tpk_set || !peer->tpk_success)
goto skip_ftie;
if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_INFO, "TDLS: No FTIE in TDLS Teardown");
return -1;
}
ftie = (struct wpa_tdls_ftie *) kde.ftie;
/* Process MIC check to see if TDLS Teardown is right */
if (wpa_supplicant_verify_tdls_mic_teardown(4, reason_code,
peer->dtoken, peer,
(u8 *) lnkid, ftie) < 0) {
wpa_printf(MSG_DEBUG, "TDLS: MIC failure for TDLS "
"Teardown Request from " MACSTR, MAC2STR(src_addr));
return -1;
}
skip_ftie:
/*
* Request the driver to disable the direct link and clear associated
* keys.
*/
wpa_tdls_disable_peer_link(sm, peer);
return 0;
}
/**
* wpa_tdls_send_error - To send suitable TDLS status response with
* appropriate status code mentioning reason for error/failure.
* @dst - MAC addr of Peer station
* @tdls_action - TDLS frame type for which error code is sent
* @initiator - was this end the initiator of the connection
* @status - status code mentioning reason
*/
static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
u8 tdls_action, u8 dialog_token, int initiator,
u16 status)
{
wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
" (action=%u status=%u)",
MAC2STR(dst), tdls_action, status);
return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
0, initiator, NULL, 0);
}
static struct wpa_tdls_peer *
wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing)
{
struct wpa_tdls_peer *peer;
if (existing)
*existing = 0;
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) {
if (existing)
*existing = 1;
return peer; /* re-use existing entry */
}
}
wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR,
MAC2STR(addr));
peer = os_zalloc(sizeof(*peer));
if (peer == NULL)
return NULL;
os_memcpy(peer->addr, addr, ETH_ALEN);
peer->next = sm->tdls;
sm->tdls = peer;
return peer;
}
static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
struct wpa_tdls_peer *peer)
{
size_t buf_len;
struct wpa_tdls_timeoutie timeoutie;
u16 rsn_capab;
struct wpa_tdls_ftie *ftie;
u8 *rbuf, *pos, *count_pos;
u16 count;
struct rsn_ie_hdr *hdr;
int status;
if (!wpa_tdls_get_privacy(sm)) {
wpa_printf(MSG_DEBUG, "TDLS: No security used on the link");
peer->rsnie_i_len = 0;
goto skip_rsnie;
}
/*
* TPK Handshake Message 1:
* FTIE: ANonce=0, SNonce=initiator nonce MIC=0, DataKDs=(RSNIE_I,
* Timeout Interval IE))
*/
/* Filling RSN IE */
hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
hdr->elem_id = WLAN_EID_RSN;
WPA_PUT_LE16(hdr->version, RSN_VERSION);
pos = (u8 *) (hdr + 1);
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
pos += RSN_SELECTOR_LEN;
count_pos = pos;
pos += 2;
count = 0;
/*
* AES-CCMP is the default Encryption preferred for TDLS, so
* RSN IE is filled only with CCMP CIPHER
* Note: TKIP is not used to encrypt TDLS link.
*
* Regardless of the cipher used on the AP connection, select CCMP
* here.
*/
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
pos += RSN_SELECTOR_LEN;
count++;
WPA_PUT_LE16(count_pos, count);
WPA_PUT_LE16(pos, 1);
pos += 2;
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
pos += RSN_SELECTOR_LEN;
rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
wpa_printf(MSG_DEBUG, "TDLS: Use alternative RSN IE for "
"testing");
rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
}
#endif /* CONFIG_TDLS_TESTING */
WPA_PUT_LE16(pos, rsn_capab);
pos += 2;
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_ALT_RSN_IE) {
/* Number of PMKIDs */
*pos++ = 0x00;
*pos++ = 0x00;
}
#endif /* CONFIG_TDLS_TESTING */
hdr->len = (pos - peer->rsnie_i) - 2;
peer->rsnie_i_len = pos - peer->rsnie_i;
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
peer->rsnie_i, peer->rsnie_i_len);
skip_rsnie:
buf_len = 0;
if (wpa_tdls_get_privacy(sm))
buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
sizeof(struct wpa_tdls_timeoutie);
#ifdef CONFIG_TDLS_TESTING
if (wpa_tdls_get_privacy(sm) &&
(tdls_testing & TDLS_TESTING_LONG_FRAME))
buf_len += 170;
if (tdls_testing & TDLS_TESTING_DIFF_BSSID)
buf_len += sizeof(struct wpa_tdls_lnkid);
#endif /* CONFIG_TDLS_TESTING */
rbuf = os_zalloc(buf_len + 1);
if (rbuf == NULL) {
wpa_tdls_peer_free(sm, peer);
return -2;
}
pos = rbuf;
if (!wpa_tdls_get_privacy(sm))
goto skip_ies;
/* Initiator RSN IE */
pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
ftie = (struct wpa_tdls_ftie *) pos;
ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
if (os_get_random(peer->inonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"TDLS: Failed to get random data for initiator Nonce");
os_free(rbuf);
wpa_tdls_peer_free(sm, peer);
return -2;
}
peer->tk_set = 0; /* A new nonce results in a new TK */
wpa_hexdump(MSG_DEBUG, "TDLS: Initiator Nonce for TPK handshake",
peer->inonce, WPA_NONCE_LEN);
os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK Handshake M1",
(u8 *) ftie, sizeof(struct wpa_tdls_ftie));
pos = (u8 *) (ftie + 1);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
"FTIE");
ftie->ie_len += 170;
*pos++ = 255; /* FTIE subelem */
*pos++ = 168; /* FTIE subelem length */
pos += 168;
}
#endif /* CONFIG_TDLS_TESTING */
/* Lifetime */
peer->lifetime = TPK_LIFETIME;
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_SHORT_LIFETIME) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - use short TPK "
"lifetime");
peer->lifetime = 301;
}
if (tdls_testing & TDLS_TESTING_LONG_LIFETIME) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - use long TPK "
"lifetime");
peer->lifetime = 0xffffffff;
}
#endif /* CONFIG_TDLS_TESTING */
pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
sizeof(timeoutie), peer->lifetime);
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
skip_ies:
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
"Link Identifier");
wpa_tdls_linkid(sm, peer, l);
l->bssid[5] ^= 0x01;
pos += sizeof(*l);
}
#endif /* CONFIG_TDLS_TESTING */
wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Request / TPK "
"Handshake Message 1 (peer " MACSTR ")",
MAC2STR(peer->addr));
status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
1, 0, 0, peer->initiator, rbuf, pos - rbuf);
os_free(rbuf);
return status;
}
static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
const unsigned char *src_addr, u8 dtoken,
struct wpa_tdls_lnkid *lnkid,
const struct wpa_tdls_peer *peer)
{
u8 *rbuf, *pos;
size_t buf_len;
u32 lifetime;
struct wpa_tdls_timeoutie timeoutie;
struct wpa_tdls_ftie *ftie;
int status;
buf_len = 0;
if (wpa_tdls_get_privacy(sm)) {
/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
* Lifetime */
buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
sizeof(struct wpa_tdls_timeoutie);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_LONG_FRAME)
buf_len += 170;
#endif /* CONFIG_TDLS_TESTING */
}
rbuf = os_zalloc(buf_len + 1);
if (rbuf == NULL)
return -1;
pos = rbuf;
if (!wpa_tdls_get_privacy(sm))
goto skip_ies;
/* Peer RSN IE */
pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
ftie = (struct wpa_tdls_ftie *) pos;
ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
/* TODO: ftie->mic_control to set 2-RESPONSE */
os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
wpa_hexdump(MSG_DEBUG, "TDLS: FTIE for TPK M2",
(u8 *) ftie, sizeof(*ftie));
pos = (u8 *) (ftie + 1);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
"FTIE");
ftie->ie_len += 170;
*pos++ = 255; /* FTIE subelem */
*pos++ = 168; /* FTIE subelem length */
pos += 168;
}
#endif /* CONFIG_TDLS_TESTING */
/* Lifetime */
lifetime = peer->lifetime;
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_RESP) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
"lifetime in response");
lifetime++;
}
#endif /* CONFIG_TDLS_TESTING */
pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
sizeof(timeoutie), lifetime);
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds from initiator",
lifetime);
/* compute MIC before sending */
wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
(u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
ftie->mic[0] ^= 0x01;
}
#endif /* CONFIG_TDLS_TESTING */
skip_ies:
status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
dtoken, 0, 0, peer->initiator, rbuf,
pos - rbuf);
os_free(rbuf);
return status;
}
static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
const unsigned char *src_addr, u8 dtoken,
struct wpa_tdls_lnkid *lnkid,
const struct wpa_tdls_peer *peer)
{
u8 *rbuf, *pos;
size_t buf_len;
struct wpa_tdls_ftie *ftie;
struct wpa_tdls_timeoutie timeoutie;
u32 lifetime;
int status;
u32 peer_capab = 0;
buf_len = 0;
if (wpa_tdls_get_privacy(sm)) {
/* Peer RSN IE, FTIE(Initiator Nonce, Responder Nonce),
* Lifetime */
buf_len += peer->rsnie_i_len + sizeof(struct wpa_tdls_ftie) +
sizeof(struct wpa_tdls_timeoutie);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_LONG_FRAME)
buf_len += 170;
#endif /* CONFIG_TDLS_TESTING */
}
rbuf = os_zalloc(buf_len + 1);
if (rbuf == NULL)
return -1;
pos = rbuf;
if (!wpa_tdls_get_privacy(sm))
goto skip_ies;
/* Peer RSN IE */
pos = wpa_add_ie(pos, peer->rsnie_p, peer->rsnie_p_len);
ftie = (struct wpa_tdls_ftie *) pos;
ftie->ie_type = WLAN_EID_FAST_BSS_TRANSITION;
/*TODO: ftie->mic_control to set 3-CONFIRM */
os_memcpy(ftie->Anonce, peer->rnonce, WPA_NONCE_LEN);
os_memcpy(ftie->Snonce, peer->inonce, WPA_NONCE_LEN);
ftie->ie_len = sizeof(struct wpa_tdls_ftie) - 2;
pos = (u8 *) (ftie + 1);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_LONG_FRAME) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - add extra subelem to "
"FTIE");
ftie->ie_len += 170;
*pos++ = 255; /* FTIE subelem */
*pos++ = 168; /* FTIE subelem length */
pos += 168;
}
#endif /* CONFIG_TDLS_TESTING */
/* Lifetime */
lifetime = peer->lifetime;
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_WRONG_LIFETIME_CONF) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong TPK "
"lifetime in confirm");
lifetime++;
}
#endif /* CONFIG_TDLS_TESTING */
pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
sizeof(timeoutie), lifetime);
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds",
lifetime);
/* compute MIC before sending */
wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
(u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
ftie->mic[0] ^= 0x01;
}
#endif /* CONFIG_TDLS_TESTING */
skip_ies:
if (peer->he_capabilities)
peer_capab |= TDLS_PEER_HE;
if (peer->vht_capabilities)
peer_capab |= TDLS_PEER_VHT;
if (peer->ht_capabilities)
peer_capab |= TDLS_PEER_HT;
if (peer->wmm_capable)
peer_capab |= TDLS_PEER_WMM;
status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
dtoken, 0, peer_capab, peer->initiator,
rbuf, pos - rbuf);
os_free(rbuf);
return status;
}
static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
struct wpa_tdls_peer *peer,
u8 dialog_token)
{
size_t buf_len = 0;
struct wpa_tdls_timeoutie timeoutie;
u16 rsn_capab;
u8 *rbuf, *pos, *count_pos;
u16 count;
struct rsn_ie_hdr *hdr;
int status;
wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
"(peer " MACSTR ")", MAC2STR(peer->addr));
if (!wpa_tdls_get_privacy(sm))
goto skip_rsn_ies;
/* Filling RSN IE */
hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
hdr->elem_id = WLAN_EID_RSN;
WPA_PUT_LE16(hdr->version, RSN_VERSION);
pos = (u8 *) (hdr + 1);
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
pos += RSN_SELECTOR_LEN;
count_pos = pos;
pos += 2;
count = 0;
/*
* AES-CCMP is the default encryption preferred for TDLS, so
* RSN IE is filled only with CCMP cipher suite.
* Note: TKIP is not used to encrypt TDLS link.
*
* Regardless of the cipher used on the AP connection, select CCMP
* here.
*/
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
pos += RSN_SELECTOR_LEN;
count++;
WPA_PUT_LE16(count_pos, count);
WPA_PUT_LE16(pos, 1);
pos += 2;
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
pos += RSN_SELECTOR_LEN;
rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
WPA_PUT_LE16(pos, rsn_capab);
pos += 2;
hdr->len = (pos - (u8 *) hdr) - 2;
peer->rsnie_i_len = pos - peer->rsnie_i;
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response",
(u8 *) hdr, hdr->len + 2);
skip_rsn_ies:
buf_len = 0;
if (wpa_tdls_get_privacy(sm)) {
/* Peer RSN IE, Lifetime */
buf_len += peer->rsnie_i_len +
sizeof(struct wpa_tdls_timeoutie);
}
rbuf = os_zalloc(buf_len + 1);
if (rbuf == NULL) {
wpa_tdls_peer_free(sm, peer);
return -1;
}
pos = rbuf;
if (!wpa_tdls_get_privacy(sm))
goto skip_ies;
/* Initiator RSN IE */
pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
/* Lifetime */
peer->lifetime = TPK_LIFETIME;
pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
sizeof(timeoutie), peer->lifetime);
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
skip_ies:
status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
dialog_token, 0, 0, 0, rbuf, pos - rbuf);
os_free(rbuf);
return status;
}
static int
wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
const u8 *buf, size_t len)
{
struct wpa_eapol_ie_parse kde;
const struct wpa_tdls_lnkid *lnkid;
struct wpa_tdls_peer *peer;
size_t min_req_len = sizeof(struct wpa_tdls_frame) +
1 /* dialog token */ + sizeof(struct wpa_tdls_lnkid);
u8 dialog_token;
wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from " MACSTR,
MAC2STR(addr));
if (len < min_req_len) {
wpa_printf(MSG_DEBUG, "TDLS Discovery Request is too short: "
"%d", (int) len);
return -1;
}
dialog_token = buf[sizeof(struct wpa_tdls_frame)];
/*
* Some APs will tack on a weird IE to the end of a TDLS
* discovery request packet. This needn't fail the response,
* since the required IE are verified separately.
*/
if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
len - (sizeof(struct wpa_tdls_frame) + 1),
&kde) < 0) {
wpa_printf(MSG_DEBUG,
"TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
}
if (!kde.lnkid) {
wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
"Request");
return -1;
}
lnkid = (const struct wpa_tdls_lnkid *) kde.lnkid;
if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "TDLS: Discovery Request from different "
" BSS " MACSTR, MAC2STR(lnkid->bssid));
return -1;
}
peer = wpa_tdls_add_peer(sm, addr, NULL);
if (peer == NULL)
return -1;
return wpa_tdls_send_discovery_response(sm, peer, dialog_token);
}
int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
{
if (sm->tdls_disabled || !sm->tdls_supported)
return -1;
wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
MACSTR, MAC2STR(addr));
return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
1, 0, 0, 1, NULL, 0);
}
static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
struct wpa_tdls_peer *peer)
{
if (!kde->supp_rates) {
wpa_printf(MSG_DEBUG, "TDLS: No supported rates received");
return -1;
}
peer->supp_rates_len = merge_byte_arrays(
peer->supp_rates, sizeof(peer->supp_rates),
kde->supp_rates + 2, kde->supp_rates_len - 2,
kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL,
kde->ext_supp_rates ? kde->ext_supp_rates_len - 2 : 0);
return 0;
}
static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
struct wpa_tdls_peer *peer)
{
if (!kde->ht_capabilities) {
wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
"received");
return 0;
}
if (!peer->ht_capabilities) {
peer->ht_capabilities =
os_zalloc(sizeof(struct ieee80211_ht_capabilities));
if (peer->ht_capabilities == NULL)
return -1;
}
os_memcpy(peer->ht_capabilities, kde->ht_capabilities,
sizeof(struct ieee80211_ht_capabilities));
wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities",
(u8 *) peer->ht_capabilities,
sizeof(struct ieee80211_ht_capabilities));
return 0;
}
static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde,
struct wpa_tdls_peer *peer)
{
if (!kde->vht_capabilities) {
wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities "
"received");
return 0;
}
if (!peer->vht_capabilities) {
peer->vht_capabilities =
os_zalloc(sizeof(struct ieee80211_vht_capabilities));
if (peer->vht_capabilities == NULL)
return -1;
}
os_memcpy(peer->vht_capabilities, kde->vht_capabilities,
sizeof(struct ieee80211_vht_capabilities));
wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities",
(u8 *) peer->vht_capabilities,
sizeof(struct ieee80211_vht_capabilities));
return 0;
}
static int copy_peer_he_capab(const struct wpa_eapol_ie_parse *kde,
struct wpa_tdls_peer *peer)
{
if (!kde->he_capabilities) {
wpa_printf(MSG_DEBUG, "TDLS: No HE capabilities received");
return 0;
}
os_free(peer->he_capabilities);
peer->he_capab_len = 0;
peer->he_capabilities = os_memdup(kde->he_capabilities,
kde->he_capab_len);
if (!peer->he_capabilities)
return -1;
peer->he_capab_len = kde->he_capab_len;
wpa_hexdump(MSG_DEBUG, "TDLS: Peer HE capabilities",
peer->he_capabilities, peer->he_capab_len);
return 0;
}
+static int copy_peer_he_6ghz_band_capab(const struct wpa_eapol_ie_parse *kde,
+ struct wpa_tdls_peer *peer)
+{
+ if (!kde->he_6ghz_capabilities) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: No HE 6 GHz band capabilities received");
+ return 0;
+ }
+
+ if (!peer->he_6ghz_band_capabilities) {
+ peer->he_6ghz_band_capabilities =
+ os_zalloc(sizeof(struct ieee80211_he_6ghz_band_cap));
+ if (peer->he_6ghz_band_capabilities == NULL)
+ return -1;
+ }
+
+ os_memcpy(peer->he_6ghz_band_capabilities, kde->he_6ghz_capabilities,
+ sizeof(struct ieee80211_he_6ghz_band_cap));
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: Peer 6 GHz band HE capabilities",
+ peer->he_6ghz_band_capabilities,
+ sizeof(struct ieee80211_he_6ghz_band_cap));
+
+ return 0;
+}
+
+
static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
struct wpa_tdls_peer *peer)
{
if (!kde->ext_capab) {
wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities "
"received");
return 0;
}
if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) {
/* Need to allocate buffer to fit the new information */
os_free(peer->ext_capab);
peer->ext_capab = os_zalloc(kde->ext_capab_len - 2);
if (peer->ext_capab == NULL)
return -1;
}
peer->ext_capab_len = kde->ext_capab_len - 2;
os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len);
return 0;
}
static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde,
struct wpa_tdls_peer *peer)
{
struct wmm_information_element *wmm;
if (!kde->wmm) {
wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received");
return 0;
}
if (kde->wmm_len < sizeof(struct wmm_information_element)) {
wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received");
return -1;
}
wmm = (struct wmm_information_element *) kde->wmm;
peer->qos_info = wmm->qos_info;
peer->wmm_capable = 1;
wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info);
return 0;
}
static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde,
struct wpa_tdls_peer *peer)
{
if (!kde->supp_channels) {
wpa_printf(MSG_DEBUG, "TDLS: No supported channels received");
return 0;
}
if (!peer->supp_channels ||
peer->supp_channels_len < kde->supp_channels_len) {
os_free(peer->supp_channels);
peer->supp_channels = os_zalloc(kde->supp_channels_len);
if (peer->supp_channels == NULL)
return -1;
}
peer->supp_channels_len = kde->supp_channels_len;
os_memcpy(peer->supp_channels, kde->supp_channels,
peer->supp_channels_len);
wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels",
(u8 *) peer->supp_channels, peer->supp_channels_len);
return 0;
}
static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde,
struct wpa_tdls_peer *peer)
{
if (!kde->supp_oper_classes) {
wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received");
return 0;
}
if (!peer->supp_oper_classes ||
peer->supp_oper_classes_len < kde->supp_oper_classes_len) {
os_free(peer->supp_oper_classes);
peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len);
if (peer->supp_oper_classes == NULL)
return -1;
}
peer->supp_oper_classes_len = kde->supp_oper_classes_len;
os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes,
peer->supp_oper_classes_len);
wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes",
(u8 *) peer->supp_oper_classes,
peer->supp_oper_classes_len);
return 0;
}
static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
int add)
{
return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid,
peer->capability,
peer->supp_rates, peer->supp_rates_len,
peer->ht_capabilities,
peer->vht_capabilities,
peer->he_capabilities,
peer->he_capab_len,
+ peer->he_6ghz_band_capabilities,
peer->qos_info, peer->wmm_capable,
peer->ext_capab, peer->ext_capab_len,
peer->supp_channels,
peer->supp_channels_len,
peer->supp_oper_classes,
peer->supp_oper_classes_len);
}
static int tdls_nonce_set(const u8 *nonce)
{
int i;
for (i = 0; i < WPA_NONCE_LEN; i++) {
if (nonce[i])
return 1;
}
return 0;
}
static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len)
{
struct wpa_tdls_peer *peer;
struct wpa_eapol_ie_parse kde;
struct wpa_ie_data ie;
int cipher;
const u8 *cpos;
struct wpa_tdls_ftie *ftie = NULL;
struct wpa_tdls_timeoutie *timeoutie;
struct wpa_tdls_lnkid *lnkid;
u32 lifetime = 0;
#if 0
struct rsn_ie_hdr *hdr;
u8 *pos;
u16 rsn_capab;
u16 rsn_ver;
#endif
u8 dtoken;
u16 ielen;
u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
int tdls_prohibited = sm->tdls_prohibited;
int existing_peer = 0;
if (len < 3 + 3)
return -1;
cpos = buf;
cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
/* driver had already verified the frame format */
dtoken = *cpos++; /* dialog token */
wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken);
peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer);
if (peer == NULL)
goto error;
/* If found, use existing entry instead of adding a new one;
* how to handle the case where both ends initiate at the
* same time? */
if (existing_peer) {
if (peer->tpk_success) {
wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
"direct link is enabled - tear down the "
"old link first");
wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
wpa_tdls_peer_clear(sm, peer);
} else if (peer->initiator) {
/*
* An entry is already present, so check if we already
* sent a TDLS Setup Request. If so, compare MAC
* addresses and let the STA with the lower MAC address
* continue as the initiator. The other negotiation is
* terminated.
*/
if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
wpa_printf(MSG_DEBUG, "TDLS: Discard request "
"from peer with higher address "
MACSTR, MAC2STR(src_addr));
return -1;
} else {
wpa_printf(MSG_DEBUG, "TDLS: Accept request "
"from peer with lower address "
MACSTR " (terminate previously "
"initiated negotiation",
MAC2STR(src_addr));
wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
peer->addr);
wpa_tdls_peer_clear(sm, peer);
}
}
}
/* capability information */
peer->capability = WPA_GET_LE16(cpos);
cpos += 2;
ielen = len - (cpos - buf); /* start of IE in buf */
/*
* Don't reject the message if failing to parse IEs. The IEs we need are
* explicitly checked below. Some APs may add arbitrary padding to the
* end of short TDLS frames and that would look like invalid IEs.
*/
if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0)
wpa_printf(MSG_DEBUG,
"TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround");
if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
"TPK M1");
goto error;
}
wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
kde.lnkid, kde.lnkid_len);
lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
status = WLAN_STATUS_REQUEST_DECLINED;
goto error;
}
wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
MAC2STR(src_addr));
if (copy_supp_rates(&kde, peer) < 0)
goto error;
if (copy_peer_ht_capab(&kde, peer) < 0)
goto error;
if (copy_peer_vht_capab(&kde, peer) < 0 ||
- copy_peer_he_capab(&kde, peer) < 0)
+ copy_peer_he_capab(&kde, peer) < 0 ||
+ copy_peer_he_6ghz_band_capab(&kde, peer) < 0)
goto error;
if (copy_peer_ext_capab(&kde, peer) < 0)
goto error;
if (copy_peer_supp_channels(&kde, peer) < 0)
goto error;
if (copy_peer_supp_oper_classes(&kde, peer) < 0)
goto error;
peer->qos_info = kde.qosinfo;
/* Overwrite with the qos_info obtained in WMM IE */
if (copy_peer_wmm_capab(&kde, peer) < 0)
goto error;
peer->aid = kde.aid;
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
peer = wpa_tdls_add_peer(sm, src_addr, NULL);
if (peer == NULL)
goto error;
wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
"TDLS setup - send own request");
peer->initiator = 1;
wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
- NULL, NULL, 0, 0, 0, NULL, 0, NULL, 0,
- NULL, 0);
+ NULL, NULL, 0, NULL, 0, 0, NULL, 0,
+ NULL, 0, NULL, 0);
if (wpa_tdls_send_tpk_m1(sm, peer) == -2) {
peer = NULL;
goto error;
}
}
if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
tdls_prohibited) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
"on TDLS");
tdls_prohibited = 0;
}
#endif /* CONFIG_TDLS_TESTING */
if (tdls_prohibited) {
wpa_printf(MSG_INFO, "TDLS: TDLS prohibited in this BSS");
status = WLAN_STATUS_REQUEST_DECLINED;
goto error;
}
if (!wpa_tdls_get_privacy(sm)) {
if (kde.rsn_ie) {
wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
"security is disabled");
status = WLAN_STATUS_SECURITY_DISABLED;
goto error;
}
goto skip_rsn;
}
if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
kde.rsn_ie == NULL) {
wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
status = WLAN_STATUS_INVALID_PARAMETERS;
goto error;
}
if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
"TPK M1");
status = WLAN_STATUS_INVALID_RSNIE;
goto error;
}
if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
status = WLAN_STATUS_INVALID_RSNIE;
goto error;
}
cipher = ie.pairwise_cipher;
if (cipher & WPA_CIPHER_CCMP) {
wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
cipher = WPA_CIPHER_CCMP;
} else {
wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
goto error;
}
if ((ie.capabilities &
(WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
WPA_CAPABILITY_PEERKEY_ENABLED) {
wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
"TPK M1");
status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
goto error;
}
/* Lifetime */
if (kde.key_lifetime == NULL) {
wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
goto error;
}
timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
lifetime = WPA_GET_LE32(timeoutie->value);
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
if (lifetime < 300) {
wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
goto error;
}
skip_rsn:
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) {
if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) {
/*
* The request frame from us is going to win, so do not
* replace information based on this request frame from
* the peer.
*/
goto skip_rsn_check;
}
}
#endif /* CONFIG_TDLS_TESTING */
peer->initiator = 0; /* Need to check */
peer->dtoken = dtoken;
if (!wpa_tdls_get_privacy(sm)) {
peer->rsnie_i_len = 0;
peer->rsnie_p_len = 0;
peer->cipher = WPA_CIPHER_NONE;
goto skip_rsn_check;
}
ftie = (struct wpa_tdls_ftie *) kde.ftie;
os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
peer->rsnie_i_len = kde.rsn_ie_len;
peer->cipher = cipher;
if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0 ||
!tdls_nonce_set(peer->inonce)) {
/*
* There is no point in updating the RNonce for every obtained
* TPK M1 frame (e.g., retransmission due to timeout) with the
* same INonce (SNonce in FTIE). However, if the TPK M1 is
* retransmitted with a different INonce, update the RNonce
* since this is for a new TDLS session.
*/
wpa_printf(MSG_DEBUG,
"TDLS: New TPK M1 INonce - generate new RNonce");
os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->ctx, MSG_WARNING,
"TDLS: Failed to get random data for responder nonce");
goto error;
}
peer->tk_set = 0; /* A new nonce results in a new TK */
}
#if 0
/* get version info from RSNIE received from Peer */
hdr = (struct rsn_ie_hdr *) kde.rsn_ie;
rsn_ver = WPA_GET_LE16(hdr->version);
/* use min(peer's version, out version) */
if (rsn_ver > RSN_VERSION)
rsn_ver = RSN_VERSION;
hdr = (struct rsn_ie_hdr *) peer->rsnie_p;
hdr->elem_id = WLAN_EID_RSN;
WPA_PUT_LE16(hdr->version, rsn_ver);
pos = (u8 *) (hdr + 1);
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
pos += RSN_SELECTOR_LEN;
/* Include only the selected cipher in pairwise cipher suite */
WPA_PUT_LE16(pos, 1);
pos += 2;
if (cipher == WPA_CIPHER_CCMP)
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
pos += RSN_SELECTOR_LEN;
WPA_PUT_LE16(pos, 1);
pos += 2;
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
pos += RSN_SELECTOR_LEN;
rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
WPA_PUT_LE16(pos, rsn_capab);
pos += 2;
hdr->len = (pos - peer->rsnie_p) - 2;
peer->rsnie_p_len = pos - peer->rsnie_p;
#endif
/* temp fix: validation of RSNIE later */
os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
peer->rsnie_p_len = peer->rsnie_i_len;
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
peer->rsnie_p, peer->rsnie_p_len);
peer->lifetime = lifetime;
wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
skip_rsn_check:
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT)
goto skip_add_peer;
#endif /* CONFIG_TDLS_TESTING */
/* add supported rates, capabilities, and qos_info to the TDLS peer */
if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
goto error;
#ifdef CONFIG_TDLS_TESTING
skip_add_peer:
#endif /* CONFIG_TDLS_TESTING */
peer->tpk_in_progress = 1;
wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
goto error;
}
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_DOUBLE_TPK_M2) {
wpa_printf(MSG_INFO, "TDLS: Testing - Send another TPK M2");
wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer);
}
#endif /* CONFIG_TDLS_TESTING */
return 0;
error:
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
status);
if (peer)
wpa_tdls_peer_free(sm, peer);
return -1;
}
static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
{
peer->tpk_success = 1;
peer->tpk_in_progress = 0;
eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
if (wpa_tdls_get_privacy(sm)) {
u32 lifetime = peer->lifetime;
/*
* Start the initiator process a bit earlier to avoid race
* condition with the responder sending teardown request.
*/
if (lifetime > 3 && peer->initiator)
lifetime -= 3;
eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout,
sm, peer);
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) {
wpa_printf(MSG_DEBUG,
"TDLS: Testing - disable TPK expiration");
eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
}
#endif /* CONFIG_TDLS_TESTING */
}
if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
"driver");
return -1;
}
peer->reconfig_key = 0;
return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr);
}
static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len)
{
struct wpa_tdls_peer *peer;
struct wpa_eapol_ie_parse kde;
struct wpa_ie_data ie;
int cipher;
struct wpa_tdls_ftie *ftie;
struct wpa_tdls_timeoutie *timeoutie;
struct wpa_tdls_lnkid *lnkid;
u32 lifetime;
u8 dtoken;
int ielen;
u16 status;
const u8 *pos;
int ret = 0;
wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
"(Peer " MACSTR ")", MAC2STR(src_addr));
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
break;
}
if (peer == NULL) {
wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
"TPK M2: " MACSTR, MAC2STR(src_addr));
return -1;
}
if (!peer->initiator) {
/*
* This may happen if both devices try to initiate TDLS at the
* same time and we accept the TPK M1 from the peer in
* wpa_tdls_process_tpk_m1() and clear our previous state.
*/
wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so "
"ignore TPK M2 from " MACSTR, MAC2STR(src_addr));
return -1;
}
if (peer->tpk_success) {
wpa_printf(MSG_INFO, "TDLS: Ignore incoming TPK M2 retry, from "
MACSTR " as TPK M3 was already sent",
MAC2STR(src_addr));
return 0;
}
wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
if (len < 3 + 2 + 1) {
wpa_tdls_disable_peer_link(sm, peer);
return -1;
}
pos = buf;
pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
status = WPA_GET_LE16(pos);
pos += 2 /* status code */;
if (status != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
status);
wpa_tdls_disable_peer_link(sm, peer);
return -1;
}
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
/* TODO: need to verify dialog token matches here or in kernel */
dtoken = *pos++; /* dialog token */
wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
if (len < 3 + 2 + 1 + 2) {
wpa_tdls_disable_peer_link(sm, peer);
return -1;
}
/* capability information */
peer->capability = WPA_GET_LE16(pos);
pos += 2;
ielen = len - (pos - buf); /* start of IE in buf */
/*
* Don't reject the message if failing to parse IEs. The IEs we need are
* explicitly checked below. Some APs may add arbitrary padding to the
* end of short TDLS frames and that would look like invalid IEs.
*/
if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0)
wpa_printf(MSG_DEBUG,
"TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround");
#ifdef CONFIG_TDLS_TESTING
if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - decline response");
status = WLAN_STATUS_REQUEST_DECLINED;
goto error;
}
#endif /* CONFIG_TDLS_TESTING */
if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
"TPK M2");
goto error;
}
wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
kde.lnkid, kde.lnkid_len);
lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
status = WLAN_STATUS_NOT_IN_SAME_BSS;
goto error;
}
if (copy_supp_rates(&kde, peer) < 0)
goto error;
if (copy_peer_ht_capab(&kde, peer) < 0)
goto error;
if (copy_peer_vht_capab(&kde, peer) < 0 ||
- copy_peer_he_capab(&kde, peer) < 0)
+ copy_peer_he_capab(&kde, peer) < 0 ||
+ copy_peer_he_6ghz_band_capab(&kde, peer) < 0)
goto error;
if (copy_peer_ext_capab(&kde, peer) < 0)
goto error;
if (copy_peer_supp_channels(&kde, peer) < 0)
goto error;
if (copy_peer_supp_oper_classes(&kde, peer) < 0)
goto error;
peer->qos_info = kde.qosinfo;
/* Overwrite with the qos_info obtained in WMM IE */
if (copy_peer_wmm_capab(&kde, peer) < 0)
goto error;
peer->aid = kde.aid;
if (!wpa_tdls_get_privacy(sm)) {
peer->rsnie_p_len = 0;
peer->cipher = WPA_CIPHER_NONE;
goto skip_rsn;
}
if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie) ||
kde.rsn_ie == NULL) {
wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
status = WLAN_STATUS_INVALID_PARAMETERS;
goto error;
}
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
kde.rsn_ie, kde.rsn_ie_len);
if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
wpa_printf(MSG_INFO,
"TDLS: Too long Responder RSN IE in TPK M2");
status = WLAN_STATUS_INVALID_RSNIE;
goto error;
}
/*
* FIX: bitwise comparison of RSN IE is not the correct way of
* validation this. It can be different, but certain fields must
* match. Since we list only a single pairwise cipher in TPK M1, the
* memcmp is likely to work in most cases, though.
*/
if (kde.rsn_ie_len != peer->rsnie_i_len ||
os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
"not match with RSN IE used in TPK M1");
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Sent in TPK M1",
peer->rsnie_i, peer->rsnie_i_len);
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
kde.rsn_ie, kde.rsn_ie_len);
status = WLAN_STATUS_INVALID_RSNIE;
goto error;
}
if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
status = WLAN_STATUS_INVALID_RSNIE;
goto error;
}
cipher = ie.pairwise_cipher;
if (cipher == WPA_CIPHER_CCMP) {
wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
cipher = WPA_CIPHER_CCMP;
} else {
wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
goto error;
}
wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
kde.ftie, sizeof(*ftie));
ftie = (struct wpa_tdls_ftie *) kde.ftie;
if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
"not match with FTIE SNonce used in TPK M1");
/* Silently discard the frame */
return -1;
}
/* Responder Nonce and RSN IE */
os_memcpy(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN);
os_memcpy(peer->rsnie_p, kde.rsn_ie, kde.rsn_ie_len);
peer->rsnie_p_len = kde.rsn_ie_len;
peer->cipher = cipher;
/* Lifetime */
if (kde.key_lifetime == NULL) {
wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
goto error;
}
timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
lifetime = WPA_GET_LE32(timeoutie->value);
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M2",
lifetime);
if (lifetime != peer->lifetime) {
wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
"TPK M2 (expected %u)", lifetime, peer->lifetime);
status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
goto error;
}
wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
/* Process MIC check to see if TPK M2 is right */
if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
(u8 *) timeoutie, ftie) < 0) {
/* Discard the frame */
wpa_tdls_del_key(sm, peer);
wpa_tdls_disable_peer_link(sm, peer);
return -1;
}
if (wpa_tdls_set_key(sm, peer) < 0) {
/*
* Some drivers may not be able to config the key prior to full
* STA entry having been configured.
*/
wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
"STA entry is complete");
peer->reconfig_key = 1;
}
skip_rsn:
peer->dtoken = dtoken;
/* add supported rates, capabilities, and qos_info to the TDLS peer */
if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
goto error;
wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
"TPK Handshake Message 3");
if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
goto error_no_msg;
if (!peer->tpk_success) {
/*
* Enable Link only when tpk_success is 0, signifying that this
* processing of TPK M2 frame is not because of a retransmission
* during TDLS setup handshake.
*/
ret = wpa_tdls_enable_link(sm, peer);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
wpa_tdls_do_teardown(
sm, peer,
WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
}
}
return ret;
error:
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
status);
error_no_msg:
wpa_tdls_disable_peer_link(sm, peer);
return -1;
}
static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len)
{
struct wpa_tdls_peer *peer;
struct wpa_eapol_ie_parse kde;
struct wpa_tdls_ftie *ftie;
struct wpa_tdls_timeoutie *timeoutie;
struct wpa_tdls_lnkid *lnkid;
int ielen;
u16 status;
const u8 *pos;
u32 lifetime;
int ret = 0;
wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
"(Peer " MACSTR ")", MAC2STR(src_addr));
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0)
break;
}
if (peer == NULL) {
wpa_printf(MSG_INFO, "TDLS: No matching peer found for "
"TPK M3: " MACSTR, MAC2STR(src_addr));
return -1;
}
wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
if (len < 3 + 3)
goto error;
pos = buf;
pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
status = WPA_GET_LE16(pos);
if (status != 0) {
wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
status);
goto error;
}
pos += 2 /* status code */ + 1 /* dialog token */;
ielen = len - (pos - buf); /* start of IE in buf */
/*
* Don't reject the message if failing to parse IEs. The IEs we need are
* explicitly checked below. Some APs piggy-back broken IEs to the end
* of a TDLS Confirm packet, which will fail the link if we don't ignore
* this error.
*/
if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
wpa_printf(MSG_DEBUG,
"TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
}
if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
goto error;
}
wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
(u8 *) kde.lnkid, kde.lnkid_len);
lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
goto error;
}
if (!wpa_tdls_get_privacy(sm))
goto skip_rsn;
if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
goto error;
}
wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
kde.ftie, sizeof(*ftie));
ftie = (struct wpa_tdls_ftie *) kde.ftie;
if (kde.rsn_ie == NULL) {
wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
goto error;
}
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
kde.rsn_ie, kde.rsn_ie_len);
if (kde.rsn_ie_len != peer->rsnie_p_len ||
os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
"with the one sent in TPK M2");
goto error;
}
if (os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
"not match with FTIE ANonce used in TPK M2");
goto error;
}
if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
"match with FTIE SNonce used in TPK M1");
goto error;
}
if (kde.key_lifetime == NULL) {
wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
goto error;
}
timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
(u8 *) timeoutie, sizeof(*timeoutie));
lifetime = WPA_GET_LE32(timeoutie->value);
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds in TPK M3",
lifetime);
if (lifetime != peer->lifetime) {
wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
"TPK M3 (expected %u)", lifetime, peer->lifetime);
goto error;
}
if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
(u8 *) timeoutie, ftie) < 0) {
wpa_tdls_del_key(sm, peer);
goto error;
}
if (wpa_tdls_set_key(sm, peer) < 0) {
/*
* Some drivers may not be able to config the key prior to full
* STA entry having been configured.
*/
wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after "
"STA entry is complete");
peer->reconfig_key = 1;
}
skip_rsn:
/* add supported rates, capabilities, and qos_info to the TDLS peer */
if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
goto error;
if (!peer->tpk_success) {
/*
* Enable Link only when tpk_success is 0, signifying that this
* processing of TPK M3 frame is not because of a retransmission
* during TDLS setup handshake.
*/
ret = wpa_tdls_enable_link(sm, peer);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
goto error;
}
}
return ret;
error:
wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
return -1;
}
static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs)
{
struct wpa_tdls_timeoutie *lifetime = (struct wpa_tdls_timeoutie *) ie;
os_memset(lifetime, 0, ie_len);
lifetime->ie_type = WLAN_EID_TIMEOUT_INTERVAL;
lifetime->ie_len = sizeof(struct wpa_tdls_timeoutie) - 2;
lifetime->interval_type = WLAN_TIMEOUT_KEY_LIFETIME;
WPA_PUT_LE32(lifetime->value, tsecs);
os_memcpy(pos, ie, ie_len);
return pos + ie_len;
}
/**
* wpa_tdls_start - Initiate TDLS handshake (send TPK Handshake Message 1)
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @peer: MAC address of the peer STA
* Returns: 0 on success, or -1 on failure
*
* Send TPK Handshake Message 1 info to driver to start TDLS
* handshake with the peer.
*/
int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
{
struct wpa_tdls_peer *peer;
int tdls_prohibited = sm->tdls_prohibited;
int res;
if (sm->tdls_disabled || !sm->tdls_supported)
return -1;
#ifdef CONFIG_TDLS_TESTING
if ((tdls_testing & TDLS_TESTING_IGNORE_AP_PROHIBIT) &&
tdls_prohibited) {
wpa_printf(MSG_DEBUG, "TDLS: Testing - ignore AP prohibition "
"on TDLS");
tdls_prohibited = 0;
}
#endif /* CONFIG_TDLS_TESTING */
if (tdls_prohibited) {
wpa_printf(MSG_DEBUG, "TDLS: TDLS is prohibited in this BSS - "
"reject request to start setup");
return -1;
}
peer = wpa_tdls_add_peer(sm, addr, NULL);
if (peer == NULL)
return -1;
if (peer->tpk_in_progress) {
wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer");
return 0;
}
peer->initiator = 1;
/* add the peer to the driver as a "setup in progress" peer */
if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
- NULL, NULL, 0, 0, 0, NULL, 0, NULL, 0,
+ NULL, NULL, 0, NULL, 0, 0, NULL, 0, NULL, 0,
NULL, 0)) {
wpa_tdls_disable_peer_link(sm, peer);
return -1;
}
peer->tpk_in_progress = 1;
res = wpa_tdls_send_tpk_m1(sm, peer);
if (res < 0) {
if (res != -2)
wpa_tdls_disable_peer_link(sm, peer);
return -1;
}
return 0;
}
void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr)
{
struct wpa_tdls_peer *peer;
if (sm->tdls_disabled || !sm->tdls_supported)
return;
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
break;
}
if (peer == NULL || !peer->tpk_success)
return;
if (sm->tdls_external_setup) {
/*
* Disable previous link to allow renegotiation to be completed
* on AP path.
*/
wpa_tdls_do_teardown(sm, peer,
WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
}
}
/**
* wpa_supplicant_rx_tdls - Receive TDLS data frame
*
* This function is called to receive TDLS (ethertype = 0x890d) data frames.
*/
static void wpa_supplicant_rx_tdls(void *ctx, const u8 *src_addr,
const u8 *buf, size_t len)
{
struct wpa_sm *sm = ctx;
struct wpa_tdls_frame *tf;
wpa_hexdump(MSG_DEBUG, "TDLS: Received Data frame encapsulation",
buf, len);
if (sm->tdls_disabled || !sm->tdls_supported) {
wpa_printf(MSG_DEBUG, "TDLS: Discard message - TDLS disabled "
"or unsupported by driver");
return;
}
if (os_memcmp(src_addr, sm->own_addr, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG, "TDLS: Discard copy of own message");
return;
}
if (len < sizeof(*tf)) {
wpa_printf(MSG_INFO, "TDLS: Drop too short frame");
return;
}
/* Check to make sure its a valid encapsulated TDLS frame */
tf = (struct wpa_tdls_frame *) buf;
if (tf->payloadtype != 2 /* TDLS_RFTYPE */ ||
tf->category != WLAN_ACTION_TDLS) {
wpa_printf(MSG_INFO, "TDLS: Invalid frame - payloadtype=%u "
"category=%u action=%u",
tf->payloadtype, tf->category, tf->action);
return;
}
switch (tf->action) {
case WLAN_TDLS_SETUP_REQUEST:
wpa_tdls_process_tpk_m1(sm, src_addr, buf, len);
break;
case WLAN_TDLS_SETUP_RESPONSE:
wpa_tdls_process_tpk_m2(sm, src_addr, buf, len);
break;
case WLAN_TDLS_SETUP_CONFIRM:
wpa_tdls_process_tpk_m3(sm, src_addr, buf, len);
break;
case WLAN_TDLS_TEARDOWN:
wpa_tdls_recv_teardown(sm, src_addr, buf, len);
break;
case WLAN_TDLS_DISCOVERY_REQUEST:
wpa_tdls_process_discovery_request(sm, src_addr, buf, len);
break;
default:
/* Kernel code will process remaining frames */
wpa_printf(MSG_DEBUG, "TDLS: Ignore TDLS frame action code %u",
tf->action);
break;
}
}
/**
* wpa_tdls_init - Initialize driver interface parameters for TDLS
* @wpa_s: Pointer to wpa_supplicant data
* Returns: 0 on success, -1 on failure
*
* This function is called to initialize driver interface parameters for TDLS.
* wpa_drv_init() must have been called before this function to initialize the
* driver interface.
*/
int wpa_tdls_init(struct wpa_sm *sm)
{
if (sm == NULL)
return -1;
if (sm->l2_tdls) {
l2_packet_deinit(sm->l2_tdls);
sm->l2_tdls = NULL;
}
sm->l2_tdls = l2_packet_init(sm->bridge_ifname ? sm->bridge_ifname :
sm->ifname,
sm->own_addr,
ETH_P_80211_ENCAP, wpa_supplicant_rx_tdls,
sm, 0);
if (sm->l2_tdls == NULL) {
wpa_printf(MSG_ERROR, "TDLS: Failed to open l2_packet "
"connection");
return -1;
}
/*
* Drivers that support TDLS but don't implement the get_capa callback
* are assumed to perform everything internally
*/
if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
&sm->tdls_external_setup,
&sm->tdls_chan_switch) < 0) {
sm->tdls_supported = 1;
sm->tdls_external_setup = 0;
}
wpa_printf(MSG_DEBUG, "TDLS: TDLS operation%s supported by "
"driver", sm->tdls_supported ? "" : " not");
wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
sm->tdls_external_setup ? "external" : "internal");
wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching",
sm->tdls_chan_switch ? "supports" : "does not support");
return 0;
}
void wpa_tdls_teardown_peers(struct wpa_sm *sm)
{
struct wpa_tdls_peer *peer, *tmp;
if (!sm)
return;
peer = sm->tdls;
wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
while (peer) {
tmp = peer->next;
wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
MAC2STR(peer->addr));
if (sm->tdls_external_setup)
wpa_tdls_do_teardown(sm, peer,
WLAN_REASON_DEAUTH_LEAVING);
else
wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
peer = tmp;
}
}
static void wpa_tdls_remove_peers(struct wpa_sm *sm)
{
struct wpa_tdls_peer *peer, *tmp;
peer = sm->tdls;
while (peer) {
int res;
tmp = peer->next;
res = wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
MAC2STR(peer->addr), res);
wpa_tdls_peer_free(sm, peer);
peer = tmp;
}
}
/**
* wpa_tdls_deinit - Deinitialize driver interface parameters for TDLS
*
* This function is called to recover driver interface parameters for TDLS
* and frees resources allocated for it.
*/
void wpa_tdls_deinit(struct wpa_sm *sm)
{
if (sm == NULL)
return;
if (sm->l2_tdls)
l2_packet_deinit(sm->l2_tdls);
sm->l2_tdls = NULL;
wpa_tdls_remove_peers(sm);
}
void wpa_tdls_assoc(struct wpa_sm *sm)
{
wpa_printf(MSG_DEBUG, "TDLS: Remove peers on association");
wpa_tdls_remove_peers(sm);
}
void wpa_tdls_disassoc(struct wpa_sm *sm)
{
wpa_printf(MSG_DEBUG, "TDLS: Remove peers on disassociation");
wpa_tdls_remove_peers(sm);
}
static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
{
/* bit 38 - TDLS Prohibited */
return !!(elems->ext_capab[4] & 0x40);
}
static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
{
/* bit 39 - TDLS Channel Switch Prohibited */
return !!(elems->ext_capab[4] & 0x80);
}
void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
{
struct ieee802_11_elems elems;
sm->tdls_prohibited = 0;
sm->tdls_chan_switch_prohibited = 0;
if (ies == NULL ||
ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
elems.ext_capab == NULL || elems.ext_capab_len < 5)
return;
sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
sm->tdls_prohibited ? "prohibited" : "allowed");
sm->tdls_chan_switch_prohibited =
wpa_tdls_chan_switch_prohibited(&elems);
wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS",
sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed");
}
void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
{
struct ieee802_11_elems elems;
if (ies == NULL ||
ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
elems.ext_capab == NULL || elems.ext_capab_len < 5)
return;
if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
"(Re)Association Response IEs");
sm->tdls_prohibited = 1;
}
if (!sm->tdls_chan_switch_prohibited &&
wpa_tdls_chan_switch_prohibited(&elems)) {
wpa_printf(MSG_DEBUG,
"TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs");
sm->tdls_chan_switch_prohibited = 1;
}
}
void wpa_tdls_enable(struct wpa_sm *sm, int enabled)
{
wpa_printf(MSG_DEBUG, "TDLS: %s", enabled ? "enabled" : "disabled");
sm->tdls_disabled = !enabled;
}
int wpa_tdls_is_external_setup(struct wpa_sm *sm)
{
return sm->tdls_external_setup;
}
int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
u8 oper_class,
struct hostapd_freq_params *freq_params)
{
struct wpa_tdls_peer *peer;
int ret;
if (sm->tdls_disabled || !sm->tdls_supported)
return -1;
if (!sm->tdls_chan_switch) {
wpa_printf(MSG_DEBUG,
"TDLS: Channel switching not supported by the driver");
return -1;
}
if (sm->tdls_chan_switch_prohibited) {
wpa_printf(MSG_DEBUG,
"TDLS: Channel switching is prohibited in this BSS - reject request to switch channel");
return -1;
}
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
break;
}
if (peer == NULL || !peer->tpk_success) {
wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR
" not found for channel switching", MAC2STR(addr));
return -1;
}
if (peer->chan_switch_enabled) {
wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
" already has channel switching enabled",
MAC2STR(addr));
return 0;
}
ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr,
oper_class, freq_params);
if (!ret)
peer->chan_switch_enabled = 1;
return ret;
}
int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr)
{
struct wpa_tdls_peer *peer;
if (sm->tdls_disabled || !sm->tdls_supported)
return -1;
for (peer = sm->tdls; peer; peer = peer->next) {
if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
break;
}
if (!peer || !peer->chan_switch_enabled) {
wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for "
MACSTR, MAC2STR(addr));
return -1;
}
/* ignore the return value */
wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
peer->chan_switch_enabled = 0;
return 0;
}
diff --git a/contrib/wpa/src/rsn_supp/wpa.c b/contrib/wpa/src/rsn_supp/wpa.c
index e01cd52177d2..0a2f87787504 100644
--- a/contrib/wpa/src/rsn_supp/wpa.c
+++ b/contrib/wpa/src/rsn_supp/wpa.c
@@ -1,5248 +1,5267 @@
/*
* WPA Supplicant - WPA state machine and EAPOL-Key processing
* Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
* Copyright(c) 2015 Intel Deutschland GmbH
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
#include "crypto/aes_siv.h"
#include "crypto/sha256.h"
#include "crypto/sha384.h"
#include "crypto/sha512.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/ocv.h"
#include "common/dpp.h"
#include "common/wpa_ctrl.h"
#include "eap_common/eap_defs.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "drivers/driver.h"
#include "wpa.h"
#include "eloop.h"
#include "preauth.h"
#include "pmksa_cache.h"
#include "wpa_i.h"
#include "wpa_ie.h"
static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
/**
* wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @ptk: PTK for Key Confirmation/Encryption Key
* @ver: Version field from Key Info
* @dest: Destination address for the frame
* @proto: Ethertype (usually ETH_P_EAPOL)
* @msg: EAPOL-Key message
* @msg_len: Length of message
* @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
* Returns: >= 0 on success, < 0 on failure
*/
int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
int ver, const u8 *dest, u16 proto,
u8 *msg, size_t msg_len, u8 *key_mic)
{
int ret = -1;
size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
wpa_printf(MSG_DEBUG, "WPA: Send EAPOL-Key frame to " MACSTR
" ver=%d mic_len=%d key_mgmt=0x%x",
MAC2STR(dest), ver, (int) mic_len, sm->key_mgmt);
if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
/*
* Association event was not yet received; try to fetch
* BSSID from the driver.
*/
if (wpa_sm_get_bssid(sm, sm->bssid) < 0) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Failed to read BSSID for "
"EAPOL-Key destination address");
} else {
dest = sm->bssid;
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Use BSSID (" MACSTR
") as the destination for EAPOL-Key",
MAC2STR(dest));
}
}
if (mic_len) {
if (key_mic && (!ptk || !ptk->kck_len))
goto out;
if (key_mic &&
wpa_eapol_key_mic(ptk->kck, ptk->kck_len, sm->key_mgmt, ver,
msg, msg_len, key_mic)) {
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
ver, sm->key_mgmt);
goto out;
}
if (ptk)
wpa_hexdump_key(MSG_DEBUG, "WPA: KCK",
ptk->kck, ptk->kck_len);
wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC",
key_mic, mic_len);
} else {
#ifdef CONFIG_FILS
/* AEAD cipher - Key MIC field not used */
struct ieee802_1x_hdr *s_hdr, *hdr;
struct wpa_eapol_key *s_key, *key;
u8 *buf, *s_key_data, *key_data;
size_t buf_len = msg_len + AES_BLOCK_SIZE;
size_t key_data_len;
u16 eapol_len;
const u8 *aad[1];
size_t aad_len[1];
if (!ptk || !ptk->kek_len)
goto out;
key_data_len = msg_len - sizeof(struct ieee802_1x_hdr) -
sizeof(struct wpa_eapol_key) - 2;
buf = os_malloc(buf_len);
if (!buf)
goto out;
os_memcpy(buf, msg, msg_len);
hdr = (struct ieee802_1x_hdr *) buf;
key = (struct wpa_eapol_key *) (hdr + 1);
key_data = ((u8 *) (key + 1)) + 2;
/* Update EAPOL header to include AES-SIV overhead */
eapol_len = be_to_host16(hdr->length);
eapol_len += AES_BLOCK_SIZE;
hdr->length = host_to_be16(eapol_len);
/* Update Key Data Length field to include AES-SIV overhead */
WPA_PUT_BE16((u8 *) (key + 1), AES_BLOCK_SIZE + key_data_len);
s_hdr = (struct ieee802_1x_hdr *) msg;
s_key = (struct wpa_eapol_key *) (s_hdr + 1);
s_key_data = ((u8 *) (s_key + 1)) + 2;
wpa_hexdump_key(MSG_DEBUG, "WPA: Plaintext Key Data",
s_key_data, key_data_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
* to Key Data (exclusive). */
aad[0] = buf;
aad_len[0] = key_data - buf;
if (aes_siv_encrypt(ptk->kek, ptk->kek_len,
s_key_data, key_data_len,
1, aad, aad_len, key_data) < 0) {
os_free(buf);
goto out;
}
wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
key_data, AES_BLOCK_SIZE + key_data_len);
os_free(msg);
msg = buf;
msg_len = buf_len;
#else /* CONFIG_FILS */
goto out;
#endif /* CONFIG_FILS */
}
wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
eapol_sm_notify_tx_eapol_key(sm->eapol);
out:
os_free(msg);
return ret;
}
/**
* wpa_sm_key_request - Send EAPOL-Key Request
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @error: Indicate whether this is an Michael MIC error report
* @pairwise: 1 = error report for pairwise packet, 0 = for group packet
*
* Send an EAPOL-Key Request to the current authenticator. This function is
* used to request rekeying and it is usually called when a local Michael MIC
* failure is detected.
*/
void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
int key_info, ver;
u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
if (pairwise && sm->wpa_deny_ptk0_rekey && !sm->use_ext_key_id &&
wpa_sm_get_state(sm) == WPA_COMPLETED) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: PTK0 rekey not allowed, reconnecting");
wpa_sm_reconnect(sm);
return;
}
if (wpa_use_akm_defined(sm->key_mgmt))
ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
else if (wpa_key_mgmt_ft(sm->key_mgmt) ||
wpa_key_mgmt_sha256(sm->key_mgmt))
ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
else if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
else
ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
if (wpa_sm_get_bssid(sm, bssid) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"Failed to read BSSID for EAPOL-Key request");
return;
}
mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
hdrlen, &rlen, (void *) &reply);
if (rbuf == NULL)
return;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info = WPA_KEY_INFO_REQUEST | ver;
if (sm->ptk_set)
key_info |= WPA_KEY_INFO_SECURE;
if (sm->ptk_set && mic_len)
key_info |= WPA_KEY_INFO_MIC;
if (error)
key_info |= WPA_KEY_INFO_ERROR;
if (pairwise)
key_info |= WPA_KEY_INFO_KEY_TYPE;
WPA_PUT_BE16(reply->key_info, key_info);
WPA_PUT_BE16(reply->key_length, 0);
os_memcpy(reply->replay_counter, sm->request_counter,
WPA_REPLAY_COUNTER_LEN);
inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
mic = (u8 *) (reply + 1);
WPA_PUT_BE16(mic + mic_len, 0);
if (!(key_info & WPA_KEY_INFO_MIC))
key_mic = NULL;
else
key_mic = mic;
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Sending EAPOL-Key Request (error=%d "
"pairwise=%d ptk_set=%d len=%lu)",
error, pairwise, sm->ptk_set, (unsigned long) rlen);
wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen,
key_mic);
}
static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm)
{
#ifdef CONFIG_IEEE80211R
if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len))
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Cannot set low order 256 bits of MSK for key management offload");
} else {
#endif /* CONFIG_IEEE80211R */
if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len))
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Cannot set PMK for key management offload");
#ifdef CONFIG_IEEE80211R
}
#endif /* CONFIG_IEEE80211R */
}
static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
const unsigned char *src_addr,
const u8 *pmkid)
{
int abort_cached = 0;
if (pmkid && !sm->cur_pmksa) {
/* When using drivers that generate RSN IE, wpa_supplicant may
* not have enough time to get the association information
* event before receiving this 1/4 message, so try to find a
* matching PMKSA cache entry here. */
sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid,
NULL, 0);
if (sm->cur_pmksa) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: found matching PMKID from PMKSA cache");
} else {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: no matching PMKID found");
abort_cached = 1;
}
}
if (pmkid && sm->cur_pmksa &&
os_memcmp_const(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) {
wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN);
wpa_sm_set_pmk_from_pmksa(sm);
wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache",
sm->pmk, sm->pmk_len);
eapol_sm_notify_cached(sm->eapol);
#ifdef CONFIG_IEEE80211R
sm->xxkey_len = 0;
#ifdef CONFIG_SAE
if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE &&
sm->pmk_len == PMK_LEN) {
/* Need to allow FT key derivation to proceed with
* PMK from SAE being used as the XXKey in cases where
* the PMKID in msg 1/4 matches the PMKSA entry that was
* just added based on SAE authentication for the
* initial mobility domain association. */
os_memcpy(sm->xxkey, sm->pmk, sm->pmk_len);
sm->xxkey_len = sm->pmk_len;
}
#endif /* CONFIG_SAE */
#endif /* CONFIG_IEEE80211R */
} else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
int res, pmk_len;
#ifdef CONFIG_IEEE80211R
u8 buf[2 * PMK_LEN];
#endif /* CONFIG_IEEE80211R */
if (wpa_key_mgmt_sha384(sm->key_mgmt))
pmk_len = PMK_LEN_SUITE_B_192;
else
pmk_len = PMK_LEN;
res = eapol_sm_get_key(sm->eapol, sm->pmk, pmk_len);
if (res) {
if (pmk_len == PMK_LEN) {
/*
* EAP-LEAP is an exception from other EAP
* methods: it uses only 16-byte PMK.
*/
res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
pmk_len = 16;
}
}
#ifdef CONFIG_IEEE80211R
if (res == 0 &&
eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) {
if (wpa_key_mgmt_sha384(sm->key_mgmt)) {
os_memcpy(sm->xxkey, buf, SHA384_MAC_LEN);
sm->xxkey_len = SHA384_MAC_LEN;
} else {
os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN);
sm->xxkey_len = PMK_LEN;
}
forced_memzero(buf, sizeof(buf));
if (sm->proto == WPA_PROTO_RSN &&
wpa_key_mgmt_ft(sm->key_mgmt)) {
struct rsn_pmksa_cache_entry *sa = NULL;
const u8 *fils_cache_id = NULL;
#ifdef CONFIG_FILS
if (sm->fils_cache_id_set)
fils_cache_id = sm->fils_cache_id;
#endif /* CONFIG_FILS */
wpa_hexdump_key(MSG_DEBUG,
"FT: Cache XXKey/MPMK",
sm->xxkey, sm->xxkey_len);
sa = pmksa_cache_add(sm->pmksa,
sm->xxkey, sm->xxkey_len,
NULL, NULL, 0,
src_addr, sm->own_addr,
sm->network_ctx,
sm->key_mgmt,
fils_cache_id);
if (!sm->cur_pmksa)
sm->cur_pmksa = sa;
}
}
#endif /* CONFIG_IEEE80211R */
if (res == 0) {
struct rsn_pmksa_cache_entry *sa = NULL;
const u8 *fils_cache_id = NULL;
#ifdef CONFIG_FILS
if (sm->fils_cache_id_set)
fils_cache_id = sm->fils_cache_id;
#endif /* CONFIG_FILS */
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
"machines", sm->pmk, pmk_len);
sm->pmk_len = pmk_len;
wpa_supplicant_key_mgmt_set_pmk(sm);
if (sm->proto == WPA_PROTO_RSN &&
!wpa_key_mgmt_suite_b(sm->key_mgmt) &&
!wpa_key_mgmt_ft(sm->key_mgmt)) {
sa = pmksa_cache_add(sm->pmksa,
sm->pmk, pmk_len, NULL,
NULL, 0,
src_addr, sm->own_addr,
sm->network_ctx,
sm->key_mgmt,
fils_cache_id);
}
if (!sm->cur_pmksa && pmkid &&
pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL,
0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: the new PMK matches with the "
"PMKID");
abort_cached = 0;
} else if (sa && !sm->cur_pmksa && pmkid) {
/*
* It looks like the authentication server
* derived mismatching MSK. This should not
* really happen, but bugs happen.. There is not
* much we can do here without knowing what
* exactly caused the server to misbehave.
*/
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: PMKID mismatch - authentication server may have derived different MSK?!");
return -1;
}
if (!sm->cur_pmksa)
sm->cur_pmksa = sa;
#ifdef CONFIG_IEEE80211R
} else if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->ft_protocol) {
wpa_printf(MSG_DEBUG,
"FT: Continue 4-way handshake without PMK/PMKID for association using FT protocol");
#endif /* CONFIG_IEEE80211R */
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get master session key from "
"EAPOL state machines - key handshake "
"aborted");
if (sm->cur_pmksa) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Cancelled PMKSA caching "
"attempt");
sm->cur_pmksa = NULL;
abort_cached = 1;
} else if (!abort_cached) {
return -1;
}
}
}
if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
!wpa_key_mgmt_suite_b(sm->key_mgmt) &&
!wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN)
{
/* Send EAPOL-Start to trigger full EAP authentication. */
u8 *buf;
size_t buflen;
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: no PMKSA entry found - trigger "
"full EAP authentication");
buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START,
NULL, 0, &buflen, NULL);
if (buf) {
/* Set and reset eapFail to allow EAP state machine to
* proceed with new authentication. */
eapol_sm_notify_eap_fail(sm->eapol, true);
eapol_sm_notify_eap_fail(sm->eapol, false);
wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL,
buf, buflen);
os_free(buf);
return -2;
}
return -1;
}
return 0;
}
/**
* wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @dst: Destination address for the frame
* @key: Pointer to the EAPOL-Key frame header
* @ver: Version bits from EAPOL-Key Key Info
* @nonce: Nonce value for the EAPOL-Key frame
* @wpa_ie: WPA/RSN IE
* @wpa_ie_len: Length of the WPA/RSN IE
* @ptk: PTK to use for keyed hash and encryption
* Returns: >= 0 on success, < 0 on failure
*/
int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
const struct wpa_eapol_key *key,
int ver, const u8 *nonce,
const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ptk *ptk)
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
u8 *rbuf, *key_mic;
u8 *rsn_ie_buf = NULL;
u16 key_info;
if (wpa_ie == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
"cannot generate msg 2/4");
return -1;
}
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
int res;
wpa_hexdump(MSG_DEBUG, "WPA: WPA IE before FT processing",
wpa_ie, wpa_ie_len);
/*
* Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and
* FTIE from (Re)Association Response.
*/
rsn_ie_buf = os_malloc(wpa_ie_len + 2 + 2 + PMKID_LEN +
sm->assoc_resp_ies_len);
if (rsn_ie_buf == NULL)
return -1;
os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len);
res = wpa_insert_pmkid(rsn_ie_buf, &wpa_ie_len,
sm->pmk_r1_name);
if (res < 0) {
os_free(rsn_ie_buf);
return -1;
}
wpa_hexdump(MSG_DEBUG,
"WPA: WPA IE after PMKID[PMKR1Name] addition into RSNE",
rsn_ie_buf, wpa_ie_len);
if (sm->assoc_resp_ies) {
wpa_hexdump(MSG_DEBUG, "WPA: Add assoc_resp_ies",
sm->assoc_resp_ies,
sm->assoc_resp_ies_len);
os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies,
sm->assoc_resp_ies_len);
wpa_ie_len += sm->assoc_resp_ies_len;
}
wpa_ie = rsn_ie_buf;
}
#endif /* CONFIG_IEEE80211R */
wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
NULL, hdrlen + wpa_ie_len,
&rlen, (void *) &reply);
if (rbuf == NULL) {
os_free(rsn_ie_buf);
return -1;
}
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info = ver | WPA_KEY_INFO_KEY_TYPE;
if (mic_len)
key_info |= WPA_KEY_INFO_MIC;
else
key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
WPA_PUT_BE16(reply->key_info, key_info);
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
WPA_PUT_BE16(reply->key_length, 0);
else
os_memcpy(reply->key_length, key->key_length, 2);
os_memcpy(reply->replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
WPA_REPLAY_COUNTER_LEN);
key_mic = (u8 *) (reply + 1);
WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len); /* Key Data Length */
os_memcpy(key_mic + mic_len + 2, wpa_ie, wpa_ie_len); /* Key Data */
os_free(rsn_ie_buf);
os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
key_mic);
}
static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
{
const u8 *z = NULL;
size_t z_len = 0, kdk_len;
int akmp;
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt))
return wpa_derive_ptk_ft(sm, src_addr, key, ptk);
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_DPP2
if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
z = wpabuf_head(sm->dpp_z);
z_len = wpabuf_len(sm->dpp_z);
}
#endif /* CONFIG_DPP2 */
akmp = sm->key_mgmt;
#ifdef CONFIG_OWE
if (sm->owe_ptk_workaround && akmp == WPA_KEY_MGMT_OWE &&
sm->pmk_len > 32) {
wpa_printf(MSG_DEBUG,
"OWE: Force SHA256 for PTK derivation");
akmp |= WPA_KEY_MGMT_PSK_SHA256;
}
#endif /* CONFIG_OWE */
if (sm->force_kdk_derivation ||
(sm->secure_ltf &&
ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
kdk_len = WPA_KDK_MAX_LEN;
else
kdk_len = 0;
return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
sm->own_addr, sm->bssid, sm->snonce,
key->key_nonce, ptk, akmp,
sm->pairwise_cipher, z, z_len,
kdk_len);
}
static int wpa_handle_ext_key_id(struct wpa_sm *sm,
struct wpa_eapol_ie_parse *kde)
{
if (sm->ext_key_id) {
u16 key_id;
if (!kde->key_id) {
wpa_msg(sm->ctx->msg_ctx,
sm->use_ext_key_id ? MSG_INFO : MSG_DEBUG,
"RSN: No Key ID in Extended Key ID handshake");
sm->keyidx_active = 0;
return sm->use_ext_key_id ? -1 : 0;
}
key_id = kde->key_id[0] & 0x03;
if (key_id > 1) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Invalid Extended Key ID: %d", key_id);
return -1;
}
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Using Extended Key ID %d", key_id);
sm->keyidx_active = key_id;
sm->use_ext_key_id = 1;
} else {
if (kde->key_id && (kde->key_id[0] & 0x03)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Non-zero Extended Key ID Key ID in PTK0 handshake");
return -1;
}
if (kde->key_id) {
/* This is not supposed to be included here, but ignore
* the case of matching Key ID 0 just in case. */
wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Extended Key ID Key ID 0 in PTK0 handshake");
}
sm->keyidx_active = 0;
sm->use_ext_key_id = 0;
}
return 0;
}
static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
const unsigned char *src_addr,
const struct wpa_eapol_key *key,
u16 ver, const u8 *key_data,
size_t key_data_len)
{
struct wpa_eapol_ie_parse ie;
struct wpa_ptk *ptk;
int res;
u8 *kde, *kde_buf = NULL;
size_t kde_len;
if (wpa_sm_get_network_ctx(sm) == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
"found (msg 1 of 4)");
return;
}
if (sm->wpa_deny_ptk0_rekey && !sm->use_ext_key_id &&
wpa_sm_get_state(sm) == WPA_COMPLETED) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: PTK0 rekey not allowed, reconnecting");
wpa_sm_reconnect(sm);
return;
}
wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way "
"Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
os_memset(&ie, 0, sizeof(ie));
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
/* RSN: msg 1/4 should contain PMKID for the selected PMK */
wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
key_data, key_data_len);
if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
goto failed;
if (ie.pmkid) {
wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
"Authenticator", ie.pmkid, PMKID_LEN);
}
}
res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
if (res == -2) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to "
"msg 1/4 - requesting full EAP authentication");
return;
}
if (res)
goto failed;
if (sm->renew_snonce) {
if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get random data for SNonce");
goto failed;
}
sm->renew_snonce = 0;
wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
sm->snonce, WPA_NONCE_LEN);
}
/* Calculate PTK which will be stored as a temporary PTK until it has
* been verified when processing message 3/4. */
ptk = &sm->tptk;
if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
goto failed;
if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
u8 buf[8];
/* Supplicant: swap tx/rx Mic keys */
os_memcpy(buf, &ptk->tk[16], 8);
os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
os_memcpy(&ptk->tk[24], buf, 8);
forced_memzero(buf, sizeof(buf));
}
sm->tptk_set = 1;
kde = sm->assoc_wpa_ie;
kde_len = sm->assoc_wpa_ie_len;
kde_buf = os_malloc(kde_len +
2 + RSN_SELECTOR_LEN + 3 +
sm->assoc_rsnxe_len +
2 + RSN_SELECTOR_LEN + 1 +
2 + RSN_SELECTOR_LEN + 2);
if (!kde_buf)
goto failed;
os_memcpy(kde_buf, kde, kde_len);
kde = kde_buf;
#ifdef CONFIG_OCV
if (wpa_sm_ocv_enabled(sm)) {
struct wpa_channel_info ci;
u8 *pos;
pos = kde + kde_len;
if (wpa_sm_channel_info(sm, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info for OCI element in EAPOL-Key 2/4");
goto failed;
}
#ifdef CONFIG_TESTING_OPTIONS
if (sm->oci_freq_override_eapol) {
wpa_printf(MSG_INFO,
"TEST: Override OCI KDE frequency %d -> %d MHz",
ci.frequency, sm->oci_freq_override_eapol);
ci.frequency = sm->oci_freq_override_eapol;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (ocv_insert_oci_kde(&ci, &pos) < 0)
goto failed;
kde_len = pos - kde;
}
#endif /* CONFIG_OCV */
if (sm->assoc_rsnxe && sm->assoc_rsnxe_len) {
os_memcpy(kde + kde_len, sm->assoc_rsnxe, sm->assoc_rsnxe_len);
kde_len += sm->assoc_rsnxe_len;
}
#ifdef CONFIG_P2P
if (sm->p2p) {
u8 *pos;
wpa_printf(MSG_DEBUG,
"P2P: Add IP Address Request KDE into EAPOL-Key 2/4");
pos = kde + kde_len;
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = RSN_SELECTOR_LEN + 1;
RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ);
pos += RSN_SELECTOR_LEN;
*pos++ = 0x01;
kde_len = pos - kde;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_DPP2
if (DPP_VERSION > 1 && sm->key_mgmt == WPA_KEY_MGMT_DPP) {
u8 *pos;
wpa_printf(MSG_DEBUG, "DPP: Add DPP KDE into EAPOL-Key 2/4");
pos = kde + kde_len;
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = RSN_SELECTOR_LEN + 2;
RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_DPP);
pos += RSN_SELECTOR_LEN;
*pos++ = DPP_VERSION; /* Protocol Version */
*pos = 0; /* Flags */
if (sm->dpp_pfs == 0)
*pos |= DPP_KDE_PFS_ALLOWED;
else if (sm->dpp_pfs == 1)
*pos |= DPP_KDE_PFS_ALLOWED | DPP_KDE_PFS_REQUIRED;
pos++;
kde_len = pos - kde;
}
#endif /* CONFIG_DPP2 */
if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
kde, kde_len, ptk) < 0)
goto failed;
os_free(kde_buf);
os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
return;
failed:
os_free(kde_buf);
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
}
static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_sm *sm = eloop_ctx;
rsn_preauth_candidate_process(sm);
}
static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
const u8 *addr, int secure)
{
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Key negotiation completed with "
MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr),
wpa_cipher_txt(sm->pairwise_cipher),
wpa_cipher_txt(sm->group_cipher));
wpa_sm_cancel_auth_timeout(sm);
wpa_sm_set_state(sm, WPA_COMPLETED);
if (secure) {
wpa_sm_mlme_setprotection(
sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX,
MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
eapol_sm_notify_portValid(sm->eapol, true);
if (wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
sm->key_mgmt == WPA_KEY_MGMT_DPP ||
sm->key_mgmt == WPA_KEY_MGMT_OWE)
eapol_sm_notify_eap_success(sm->eapol, true);
/*
* Start preauthentication after a short wait to avoid a
* possible race condition between the data receive and key
* configuration after the 4-Way Handshake. This increases the
* likelihood of the first preauth EAPOL-Start frame getting to
* the target AP.
*/
if (!dl_list_empty(&sm->pmksa_candidates))
eloop_register_timeout(1, 0, wpa_sm_start_preauth,
sm, NULL);
}
if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Authenticator accepted "
"opportunistic PMKSA entry - marking it valid");
sm->cur_pmksa->opportunistic = 0;
}
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
/* Prepare for the next transition */
wpa_ft_prepare_auth_request(sm, NULL);
}
#endif /* CONFIG_IEEE80211R */
}
static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_sm *sm = eloop_ctx;
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying");
wpa_sm_key_request(sm, 0, 1);
}
static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
enum key_flag key_flag)
{
int keylen, rsclen;
enum wpa_alg alg;
const u8 *key_rsc;
if (sm->ptk.installed) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Do not re-install same PTK to the driver");
return 0;
}
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Installing PTK to the driver");
if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher "
"Suite: NONE - do not use pairwise keys");
return 0;
}
if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported pairwise cipher %d",
sm->pairwise_cipher);
return -1;
}
alg = wpa_cipher_to_alg(sm->pairwise_cipher);
keylen = wpa_cipher_key_len(sm->pairwise_cipher);
if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
wpa_printf(MSG_DEBUG, "WPA: TK length mismatch: %d != %lu",
keylen, (long unsigned int) sm->ptk.tk_len);
return -1;
}
rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
key_rsc = null_rsc;
} else {
key_rsc = key->key_rsc;
wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
}
if (wpa_sm_set_key(sm, alg, sm->bssid, sm->keyidx_active, 1, key_rsc,
rsclen, sm->ptk.tk, keylen,
KEY_FLAG_PAIRWISE | key_flag) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
MACSTR " idx=%d key_flag=0x%x)",
alg, keylen, MAC2STR(sm->bssid),
sm->keyidx_active, key_flag);
return -1;
}
wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher,
sm->dot11RSNAConfigPMKLifetime, &sm->ptk);
/* TK is not needed anymore in supplicant */
os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
sm->ptk.tk_len = 0;
sm->ptk.installed = 1;
if (sm->wpa_ptk_rekey) {
eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk,
sm, NULL);
}
return 0;
}
static int wpa_supplicant_activate_ptk(struct wpa_sm *sm)
{
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Activate PTK (idx=%d bssid=" MACSTR ")",
sm->keyidx_active, MAC2STR(sm->bssid));
if (wpa_sm_set_key(sm, 0, sm->bssid, sm->keyidx_active, 0, NULL, 0,
NULL, 0, KEY_FLAG_PAIRWISE_RX_TX_MODIFY) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to activate PTK for TX (idx=%d bssid="
MACSTR ")", sm->keyidx_active, MAC2STR(sm->bssid));
return -1;
}
return 0;
}
static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm,
int group_cipher,
int keylen, int maxkeylen,
int *key_rsc_len,
enum wpa_alg *alg)
{
int klen;
*alg = wpa_cipher_to_alg(group_cipher);
if (*alg == WPA_ALG_NONE) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported Group Cipher %d",
group_cipher);
return -1;
}
*key_rsc_len = wpa_cipher_rsc_len(group_cipher);
klen = wpa_cipher_key_len(group_cipher);
if (keylen != klen || maxkeylen < klen) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported %s Group Cipher key length %d (%d)",
wpa_cipher_txt(group_cipher), keylen, maxkeylen);
return -1;
}
return 0;
}
struct wpa_gtk_data {
enum wpa_alg alg;
int tx, key_rsc_len, keyidx;
u8 gtk[32];
int gtk_len;
};
static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
const struct wpa_gtk_data *gd,
const u8 *key_rsc, int wnm_sleep)
{
const u8 *_gtk = gd->gtk;
u8 gtk_buf[32];
/* Detect possible key reinstallation */
if ((sm->gtk.gtk_len == (size_t) gd->gtk_len &&
os_memcmp(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len) == 0) ||
(sm->gtk_wnm_sleep.gtk_len == (size_t) gd->gtk_len &&
os_memcmp(sm->gtk_wnm_sleep.gtk, gd->gtk,
sm->gtk_wnm_sleep.gtk_len) == 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Not reinstalling already in-use GTK to the driver (keyidx=%d tx=%d len=%d)",
gd->keyidx, gd->tx, gd->gtk_len);
return 0;
}
wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Installing GTK to the driver (keyidx=%d tx=%d len=%d)",
gd->keyidx, gd->tx, gd->gtk_len);
wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len);
if (sm->group_cipher == WPA_CIPHER_TKIP) {
/* Swap Tx/Rx keys for Michael MIC */
os_memcpy(gtk_buf, gd->gtk, 16);
os_memcpy(gtk_buf + 16, gd->gtk + 24, 8);
os_memcpy(gtk_buf + 24, gd->gtk + 16, 8);
_gtk = gtk_buf;
}
if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
if (wpa_sm_set_key(sm, gd->alg, NULL,
gd->keyidx, 1, key_rsc, gd->key_rsc_len,
_gtk, gd->gtk_len,
KEY_FLAG_GROUP_RX_TX_DEFAULT) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to set GTK to the driver "
"(Group only)");
forced_memzero(gtk_buf, sizeof(gtk_buf));
return -1;
}
} else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
_gtk, gd->gtk_len, KEY_FLAG_GROUP_RX) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to set GTK to "
"the driver (alg=%d keylen=%d keyidx=%d)",
gd->alg, gd->gtk_len, gd->keyidx);
forced_memzero(gtk_buf, sizeof(gtk_buf));
return -1;
}
forced_memzero(gtk_buf, sizeof(gtk_buf));
if (wnm_sleep) {
sm->gtk_wnm_sleep.gtk_len = gd->gtk_len;
os_memcpy(sm->gtk_wnm_sleep.gtk, gd->gtk,
sm->gtk_wnm_sleep.gtk_len);
} else {
sm->gtk.gtk_len = gd->gtk_len;
os_memcpy(sm->gtk.gtk, gd->gtk, sm->gtk.gtk_len);
}
return 0;
}
static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
int tx)
{
if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) {
/* Ignore Tx bit for GTK if a pairwise key is used. One AP
* seemed to set this bit (incorrectly, since Tx is only when
* doing Group Key only APs) and without this workaround, the
* data connection does not work because wpa_supplicant
* configured non-zero keyidx to be used for unicast. */
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Tx bit set for GTK, but pairwise "
"keys are used - ignore Tx bit");
return 0;
}
return tx;
}
static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm,
const u8 *rsc)
{
int rsclen;
if (!sm->wpa_rsc_relaxation)
return 0;
rsclen = wpa_cipher_rsc_len(sm->group_cipher);
/*
* Try to detect RSC (endian) corruption issue where the AP sends
* the RSC bytes in EAPOL-Key message in the wrong order, both if
* it's actually a 6-byte field (as it should be) and if it treats
* it as an 8-byte field.
* An AP model known to have this bug is the Sapido RB-1632.
*/
if (rsclen == 6 && ((rsc[5] && !rsc[0]) || rsc[6] || rsc[7])) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"RSC %02x%02x%02x%02x%02x%02x%02x%02x is likely bogus, using 0",
rsc[0], rsc[1], rsc[2], rsc[3],
rsc[4], rsc[5], rsc[6], rsc[7]);
return 1;
}
return 0;
}
static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
const u8 *gtk, size_t gtk_len,
int key_info)
{
struct wpa_gtk_data gd;
const u8 *key_rsc;
/*
* IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x
* GTK KDE format:
* KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7]
* Reserved [bits 0-7]
* GTK
*/
os_memset(&gd, 0, sizeof(gd));
wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake",
gtk, gtk_len);
if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk))
return -1;
gd.keyidx = gtk[0] & 0x3;
gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
!!(gtk[0] & BIT(2)));
gtk += 2;
gtk_len -= 2;
os_memcpy(gd.gtk, gtk, gtk_len);
gd.gtk_len = gtk_len;
key_rsc = key->key_rsc;
if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
key_rsc = null_rsc;
if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED &&
(wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gtk_len, gtk_len,
&gd.key_rsc_len, &gd.alg) ||
wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0))) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Failed to install GTK");
forced_memzero(&gd, sizeof(gd));
return -1;
}
forced_memzero(&gd, sizeof(gd));
return 0;
}
static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
const struct wpa_igtk_kde *igtk,
int wnm_sleep)
{
size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
u16 keyidx = WPA_GET_LE16(igtk->keyid);
/* Detect possible key reinstallation */
if ((sm->igtk.igtk_len == len &&
os_memcmp(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len) == 0) ||
(sm->igtk_wnm_sleep.igtk_len == len &&
os_memcmp(sm->igtk_wnm_sleep.igtk, igtk->igtk,
sm->igtk_wnm_sleep.igtk_len) == 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Not reinstalling already in-use IGTK to the driver (keyidx=%d)",
keyidx);
return 0;
}
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: IGTK keyid %d pn " COMPACT_MACSTR,
keyidx, MAC2STR(igtk->pn));
wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len);
if (keyidx > 4095) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid IGTK KeyID %d", keyidx);
return -1;
}
if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr,
keyidx, 0, igtk->pn, sizeof(igtk->pn),
igtk->igtk, len, KEY_FLAG_GROUP_RX) < 0) {
if (keyidx == 0x0400 || keyidx == 0x0500) {
/* Assume the AP has broken PMF implementation since it
* seems to have swapped the KeyID bytes. The AP cannot
* be trusted to implement BIP correctly or provide a
* valid IGTK, so do not try to configure this key with
* swapped KeyID bytes. Instead, continue without
* configuring the IGTK so that the driver can drop any
* received group-addressed robust management frames due
* to missing keys.
*
* Normally, this error behavior would result in us
* disconnecting, but there are number of deployed APs
* with this broken behavior, so as an interoperability
* workaround, allow the connection to proceed. */
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Ignore IGTK configuration error due to invalid IGTK KeyID byte order");
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to configure IGTK to the driver");
return -1;
}
}
if (wnm_sleep) {
sm->igtk_wnm_sleep.igtk_len = len;
os_memcpy(sm->igtk_wnm_sleep.igtk, igtk->igtk,
sm->igtk_wnm_sleep.igtk_len);
} else {
sm->igtk.igtk_len = len;
os_memcpy(sm->igtk.igtk, igtk->igtk, sm->igtk.igtk_len);
}
return 0;
}
static int wpa_supplicant_install_bigtk(struct wpa_sm *sm,
const struct wpa_bigtk_kde *bigtk,
int wnm_sleep)
{
size_t len = wpa_cipher_key_len(sm->mgmt_group_cipher);
u16 keyidx = WPA_GET_LE16(bigtk->keyid);
/* Detect possible key reinstallation */
if ((sm->bigtk.bigtk_len == len &&
os_memcmp(sm->bigtk.bigtk, bigtk->bigtk,
sm->bigtk.bigtk_len) == 0) ||
(sm->bigtk_wnm_sleep.bigtk_len == len &&
os_memcmp(sm->bigtk_wnm_sleep.bigtk, bigtk->bigtk,
sm->bigtk_wnm_sleep.bigtk_len) == 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Not reinstalling already in-use BIGTK to the driver (keyidx=%d)",
keyidx);
return 0;
}
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: BIGTK keyid %d pn " COMPACT_MACSTR,
keyidx, MAC2STR(bigtk->pn));
wpa_hexdump_key(MSG_DEBUG, "WPA: BIGTK", bigtk->bigtk, len);
if (keyidx < 6 || keyidx > 7) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid BIGTK KeyID %d", keyidx);
return -1;
}
if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
broadcast_ether_addr,
keyidx, 0, bigtk->pn, sizeof(bigtk->pn),
bigtk->bigtk, len, KEY_FLAG_GROUP_RX) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to configure BIGTK to the driver");
return -1;
}
if (wnm_sleep) {
sm->bigtk_wnm_sleep.bigtk_len = len;
os_memcpy(sm->bigtk_wnm_sleep.bigtk, bigtk->bigtk,
sm->bigtk_wnm_sleep.bigtk_len);
} else {
sm->bigtk.bigtk_len = len;
os_memcpy(sm->bigtk.bigtk, bigtk->bigtk, sm->bigtk.bigtk_len);
}
return 0;
}
static int ieee80211w_set_keys(struct wpa_sm *sm,
struct wpa_eapol_ie_parse *ie)
{
size_t len;
if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) ||
sm->mgmt_group_cipher == WPA_CIPHER_GTK_NOT_USED)
return 0;
if (ie->igtk) {
const struct wpa_igtk_kde *igtk;
len = wpa_cipher_key_len(sm->mgmt_group_cipher);
if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len)
return -1;
igtk = (const struct wpa_igtk_kde *) ie->igtk;
if (wpa_supplicant_install_igtk(sm, igtk, 0) < 0)
return -1;
}
if (ie->bigtk && sm->beacon_prot) {
const struct wpa_bigtk_kde *bigtk;
len = wpa_cipher_key_len(sm->mgmt_group_cipher);
if (ie->bigtk_len != WPA_BIGTK_KDE_PREFIX_LEN + len)
return -1;
bigtk = (const struct wpa_bigtk_kde *) ie->bigtk;
if (wpa_supplicant_install_bigtk(sm, bigtk, 0) < 0)
return -1;
}
return 0;
}
static void wpa_report_ie_mismatch(struct wpa_sm *sm,
const char *reason, const u8 *src_addr,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsn_ie, size_t rsn_ie_len)
{
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")",
reason, MAC2STR(src_addr));
if (sm->ap_wpa_ie) {
wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp",
sm->ap_wpa_ie, sm->ap_wpa_ie_len);
}
if (wpa_ie) {
if (!sm->ap_wpa_ie) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: No WPA IE in Beacon/ProbeResp");
}
wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg",
wpa_ie, wpa_ie_len);
}
if (sm->ap_rsn_ie) {
wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp",
sm->ap_rsn_ie, sm->ap_rsn_ie_len);
}
if (rsn_ie) {
if (!sm->ap_rsn_ie) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: No RSN IE in Beacon/ProbeResp");
}
wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg",
rsn_ie, rsn_ie_len);
}
wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
}
#ifdef CONFIG_IEEE80211R
static int ft_validate_mdie(struct wpa_sm *sm,
const unsigned char *src_addr,
struct wpa_eapol_ie_parse *ie,
const u8 *assoc_resp_mdie)
{
struct rsn_mdie *mdie;
mdie = (struct rsn_mdie *) (ie->mdie + 2);
if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) ||
os_memcmp(mdie->mobility_domain, sm->mobility_domain,
MOBILITY_DOMAIN_ID_LEN) != 0) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did "
"not match with the current mobility domain");
return -1;
}
if (assoc_resp_mdie &&
(assoc_resp_mdie[1] != ie->mdie[1] ||
os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch");
wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4",
ie->mdie, 2 + ie->mdie[1]);
wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response",
assoc_resp_mdie, 2 + assoc_resp_mdie[1]);
return -1;
}
return 0;
}
static int ft_validate_ftie(struct wpa_sm *sm,
const unsigned char *src_addr,
struct wpa_eapol_ie_parse *ie,
const u8 *assoc_resp_ftie)
{
if (ie->ftie == NULL) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"FT: No FTIE in EAPOL-Key msg 3/4");
return -1;
}
if (assoc_resp_ftie == NULL)
return 0;
if (assoc_resp_ftie[1] != ie->ftie[1] ||
os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch");
wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4",
ie->ftie, 2 + ie->ftie[1]);
wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response",
assoc_resp_ftie, 2 + assoc_resp_ftie[1]);
return -1;
}
return 0;
}
static int ft_validate_rsnie(struct wpa_sm *sm,
const unsigned char *src_addr,
struct wpa_eapol_ie_parse *ie)
{
struct wpa_ie_data rsn;
if (!ie->rsn_ie)
return 0;
/*
* Verify that PMKR1Name from EAPOL-Key message 3/4
* matches with the value we derived.
*/
if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 ||
rsn.num_pmkid != 1 || rsn.pmkid == NULL) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in "
"FT 4-way handshake message 3/4");
return -1;
}
if (os_memcmp_const(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0)
{
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"FT: PMKR1Name mismatch in "
"FT 4-way handshake message 3/4");
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator",
rsn.pmkid, WPA_PMK_NAME_LEN);
wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
sm->pmk_r1_name, WPA_PMK_NAME_LEN);
return -1;
}
return 0;
}
static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm,
const unsigned char *src_addr,
struct wpa_eapol_ie_parse *ie)
{
const u8 *pos, *end, *mdie = NULL, *ftie = NULL;
if (sm->assoc_resp_ies) {
pos = sm->assoc_resp_ies;
end = pos + sm->assoc_resp_ies_len;
while (end - pos > 2) {
if (2 + pos[1] > end - pos)
break;
switch (*pos) {
case WLAN_EID_MOBILITY_DOMAIN:
mdie = pos;
break;
case WLAN_EID_FAST_BSS_TRANSITION:
ftie = pos;
break;
}
pos += 2 + pos[1];
}
}
if (ft_validate_mdie(sm, src_addr, ie, mdie) < 0 ||
ft_validate_ftie(sm, src_addr, ie, ftie) < 0 ||
ft_validate_rsnie(sm, src_addr, ie) < 0)
return -1;
return 0;
}
#endif /* CONFIG_IEEE80211R */
static int wpa_supplicant_validate_ie(struct wpa_sm *sm,
const unsigned char *src_addr,
struct wpa_eapol_ie_parse *ie)
{
if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: No WPA/RSN IE for this AP known. "
"Trying to get from scan results");
if (wpa_sm_get_beacon_ie(sm) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Could not find AP from "
"the scan results");
return -1;
}
wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Found the current AP from updated scan results");
}
if (ie->wpa_ie == NULL && ie->rsn_ie == NULL &&
(sm->ap_wpa_ie || sm->ap_rsn_ie)) {
wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
"with IE in Beacon/ProbeResp (no IE?)",
src_addr, ie->wpa_ie, ie->wpa_ie_len,
ie->rsn_ie, ie->rsn_ie_len);
return -1;
}
if ((ie->wpa_ie && sm->ap_wpa_ie &&
(ie->wpa_ie_len != sm->ap_wpa_ie_len ||
os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) ||
(ie->rsn_ie && sm->ap_rsn_ie &&
wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
sm->ap_rsn_ie, sm->ap_rsn_ie_len,
ie->rsn_ie, ie->rsn_ie_len))) {
wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
"with IE in Beacon/ProbeResp",
src_addr, ie->wpa_ie, ie->wpa_ie_len,
ie->rsn_ie, ie->rsn_ie_len);
return -1;
}
if (sm->proto == WPA_PROTO_WPA &&
ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) {
wpa_report_ie_mismatch(sm, "Possible downgrade attack "
"detected - RSN was enabled and RSN IE "
"was in msg 3/4, but not in "
"Beacon/ProbeResp",
src_addr, ie->wpa_ie, ie->wpa_ie_len,
ie->rsn_ie, ie->rsn_ie_len);
return -1;
}
if (sm->proto == WPA_PROTO_RSN &&
((sm->ap_rsnxe && !ie->rsnxe) ||
(!sm->ap_rsnxe && ie->rsnxe) ||
(sm->ap_rsnxe && ie->rsnxe &&
(sm->ap_rsnxe_len != ie->rsnxe_len ||
os_memcmp(sm->ap_rsnxe, ie->rsnxe, sm->ap_rsnxe_len) != 0)))) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: RSNXE mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4");
wpa_hexdump(MSG_INFO, "RSNXE in Beacon/ProbeResp",
sm->ap_rsnxe, sm->ap_rsnxe_len);
wpa_hexdump(MSG_INFO, "RSNXE in EAPOL-Key msg 3/4",
ie->rsnxe, ie->rsnxe_len);
wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
return -1;
}
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt) &&
wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0)
return -1;
#endif /* CONFIG_IEEE80211R */
return 0;
}
/**
* wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @dst: Destination address for the frame
* @key: Pointer to the EAPOL-Key frame header
* @ver: Version bits from EAPOL-Key Key Info
* @key_info: Key Info
* @ptk: PTK to use for keyed hash and encryption
* Returns: >= 0 on success, < 0 on failure
*/
int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
const struct wpa_eapol_key *key,
u16 ver, u16 key_info,
struct wpa_ptk *ptk)
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
u8 *rbuf, *key_mic;
mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
hdrlen, &rlen, (void *) &reply);
if (rbuf == NULL)
return -1;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info &= WPA_KEY_INFO_SECURE;
key_info |= ver | WPA_KEY_INFO_KEY_TYPE;
if (mic_len)
key_info |= WPA_KEY_INFO_MIC;
else
key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
WPA_PUT_BE16(reply->key_info, key_info);
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
WPA_PUT_BE16(reply->key_length, 0);
else
os_memcpy(reply->key_length, key->key_length, 2);
os_memcpy(reply->replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
key_mic = (u8 *) (reply + 1);
WPA_PUT_BE16(key_mic + mic_len, 0);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
key_mic);
}
static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
u16 ver, const u8 *key_data,
size_t key_data_len)
{
u16 key_info, keylen;
struct wpa_eapol_ie_parse ie;
wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way "
"Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
key_info = WPA_GET_BE16(key->key_info);
wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
goto failed;
if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: GTK IE in unencrypted key data");
goto failed;
}
if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: IGTK KDE in unencrypted key data");
goto failed;
}
if (ie.igtk &&
sm->mgmt_group_cipher != WPA_CIPHER_GTK_NOT_USED &&
wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN +
(unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid IGTK KDE length %lu",
(unsigned long) ie.igtk_len);
goto failed;
}
if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
goto failed;
if (wpa_handle_ext_key_id(sm, &ie))
goto failed;
if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: ANonce from message 1 of 4-Way Handshake "
"differs from 3 of 4-Way Handshake - drop packet (src="
MACSTR ")", MAC2STR(sm->bssid));
goto failed;
}
keylen = WPA_GET_BE16(key->key_length);
if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid %s key length %d (src=" MACSTR
")", wpa_cipher_txt(sm->pairwise_cipher), keylen,
MAC2STR(sm->bssid));
goto failed;
}
#ifdef CONFIG_P2P
if (ie.ip_addr_alloc) {
os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4);
wpa_hexdump(MSG_DEBUG, "P2P: IP address info",
sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr));
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_OCV
if (wpa_sm_ocv_enabled(sm)) {
struct wpa_channel_info ci;
if (wpa_sm_channel_info(sm, &ci) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"Failed to get channel info to validate received OCI in EAPOL-Key 3/4");
return;
}
if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx) != OCI_SUCCESS) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
"addr=" MACSTR " frame=eapol-key-m3 error=%s",
MAC2STR(sm->bssid), ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
#ifdef CONFIG_DPP2
if (DPP_VERSION > 1 && ie.dpp_kde) {
wpa_printf(MSG_DEBUG,
"DPP: peer Protocol Version %u Flags 0x%x",
ie.dpp_kde[0], ie.dpp_kde[1]);
if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_pfs != 2 &&
(ie.dpp_kde[1] & DPP_KDE_PFS_ALLOWED) && !sm->dpp_z) {
wpa_printf(MSG_INFO,
"DPP: Peer indicated it supports PFS and local configuration allows this, but PFS was not negotiated for the association");
goto failed;
}
}
#endif /* CONFIG_DPP2 */
if (sm->use_ext_key_id &&
wpa_supplicant_install_ptk(sm, key, KEY_FLAG_RX))
goto failed;
if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
&sm->ptk) < 0) {
goto failed;
}
/* SNonce was successfully used in msg 3/4, so mark it to be renewed
* for the next 4-Way Handshake. If msg 3 is received again, the old
* SNonce will still be used to avoid changing PTK. */
sm->renew_snonce = 1;
if (key_info & WPA_KEY_INFO_INSTALL) {
int res;
if (sm->use_ext_key_id)
res = wpa_supplicant_activate_ptk(sm);
else
res = wpa_supplicant_install_ptk(sm, key,
KEY_FLAG_RX_TX);
if (res)
goto failed;
}
if (key_info & WPA_KEY_INFO_SECURE) {
wpa_sm_mlme_setprotection(
sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
eapol_sm_notify_portValid(sm->eapol, true);
}
wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
/* No GTK to be set to the driver */
} else if (!ie.gtk && sm->proto == WPA_PROTO_RSN) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: No GTK KDE included in EAPOL-Key msg 3/4");
goto failed;
} else if (ie.gtk &&
wpa_supplicant_pairwise_gtk(sm, key,
ie.gtk, ie.gtk_len, key_info) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Failed to configure GTK");
goto failed;
}
if (ieee80211w_set_keys(sm, &ie) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Failed to configure IGTK");
goto failed;
}
if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk)
wpa_supplicant_key_neg_complete(sm, sm->bssid,
key_info & WPA_KEY_INFO_SECURE);
if (ie.gtk)
wpa_sm_set_rekey_offload(sm);
/* Add PMKSA cache entry for Suite B AKMs here since PMKID can be
* calculated only after KCK has been derived. Though, do not replace an
* existing PMKSA entry after each 4-way handshake (i.e., new KCK/PMKID)
* to avoid unnecessary changes of PMKID while continuing to use the
* same PMK. */
if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt) &&
!sm->cur_pmksa) {
struct rsn_pmksa_cache_entry *sa;
sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
sm->ptk.kck, sm->ptk.kck_len,
sm->bssid, sm->own_addr,
sm->network_ctx, sm->key_mgmt, NULL);
if (!sm->cur_pmksa)
sm->cur_pmksa = sa;
}
if (ie.transition_disable)
wpa_sm_transition_disable(sm, ie.transition_disable[0]);
sm->msg_3_of_4_ok = 1;
return;
failed:
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
}
static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
const u8 *keydata,
size_t keydatalen,
u16 key_info,
struct wpa_gtk_data *gd)
{
int maxkeylen;
struct wpa_eapol_ie_parse ie;
u16 gtk_len;
wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data",
keydata, keydatalen);
if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
return -1;
if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: GTK IE in unencrypted key data");
return -1;
}
if (ie.gtk == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: No GTK IE in Group Key msg 1/2");
return -1;
}
gtk_len = ie.gtk_len;
if (gtk_len < 2) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Invalid GTK KDE length (%u) in Group Key msg 1/2",
gtk_len);
return -1;
}
gtk_len -= 2;
if (gtk_len > sizeof(gd->gtk)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Too long GTK in GTK KDE (len=%u)", gtk_len);
return -1;
}
maxkeylen = gd->gtk_len = gtk_len;
#ifdef CONFIG_OCV
if (wpa_sm_ocv_enabled(sm)) {
struct wpa_channel_info ci;
if (wpa_sm_channel_info(sm, &ci) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
return -1;
}
if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx) != OCI_SUCCESS) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
"addr=" MACSTR " frame=eapol-key-g1 error=%s",
MAC2STR(sm->bssid), ocv_errorstr);
return -1;
}
}
#endif /* CONFIG_OCV */
if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gtk_len, maxkeylen,
&gd->key_rsc_len, &gd->alg))
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
ie.gtk, 2 + gtk_len);
gd->keyidx = ie.gtk[0] & 0x3;
gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
!!(ie.gtk[0] & BIT(2)));
os_memcpy(gd->gtk, ie.gtk + 2, gtk_len);
if (ieee80211w_set_keys(sm, &ie) < 0)
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Failed to configure IGTK");
return 0;
}
static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
const u8 *key_data,
size_t key_data_len, u16 key_info,
u16 ver, struct wpa_gtk_data *gd)
{
size_t maxkeylen;
u16 gtk_len;
gtk_len = WPA_GET_BE16(key->key_length);
maxkeylen = key_data_len;
if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
if (maxkeylen < 8) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Too short maxkeylen (%lu)",
(unsigned long) maxkeylen);
return -1;
}
maxkeylen -= 8;
}
if (gtk_len > maxkeylen ||
wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gtk_len, maxkeylen,
&gd->key_rsc_len, &gd->alg))
return -1;
gd->gtk_len = gtk_len;
gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
WPA_KEY_INFO_KEY_INDEX_SHIFT;
if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
#ifdef CONFIG_NO_RC4
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: RC4 not supported in the build");
return -1;
#else /* CONFIG_NO_RC4 */
u8 ek[32];
if (key_data_len > sizeof(gd->gtk)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: RC4 key data too long (%lu)",
(unsigned long) key_data_len);
return -1;
}
os_memcpy(ek, key->key_iv, 16);
os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
os_memcpy(gd->gtk, key_data, key_data_len);
if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) {
forced_memzero(ek, sizeof(ek));
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"WPA: RC4 failed");
return -1;
}
forced_memzero(ek, sizeof(ek));
#endif /* CONFIG_NO_RC4 */
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
if (maxkeylen % 8) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported AES-WRAP len %lu",
(unsigned long) maxkeylen);
return -1;
}
if (maxkeylen > sizeof(gd->gtk)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: AES-WRAP key data "
"too long (keydatalen=%lu maxkeylen=%lu)",
(unsigned long) key_data_len,
(unsigned long) maxkeylen);
return -1;
}
if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
key_data, gd->gtk)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: AES unwrap failed - could not decrypt "
"GTK");
return -1;
}
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported key_info type %d", ver);
return -1;
}
gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
sm, !!(key_info & WPA_KEY_INFO_TXRX));
return 0;
}
static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
int ver, u16 key_info)
{
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
u8 *rbuf, *key_mic;
size_t kde_len = 0;
#ifdef CONFIG_OCV
if (wpa_sm_ocv_enabled(sm))
kde_len = OCV_OCI_KDE_LEN;
#endif /* CONFIG_OCV */
mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
hdrlen + kde_len, &rlen, (void *) &reply);
if (rbuf == NULL)
return -1;
reply->type = (sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
key_info |= ver | WPA_KEY_INFO_SECURE;
if (mic_len)
key_info |= WPA_KEY_INFO_MIC;
else
key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
WPA_PUT_BE16(reply->key_info, key_info);
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
WPA_PUT_BE16(reply->key_length, 0);
else
os_memcpy(reply->key_length, key->key_length, 2);
os_memcpy(reply->replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
key_mic = (u8 *) (reply + 1);
WPA_PUT_BE16(key_mic + mic_len, kde_len); /* Key Data Length */
#ifdef CONFIG_OCV
if (wpa_sm_ocv_enabled(sm)) {
struct wpa_channel_info ci;
u8 *pos;
if (wpa_sm_channel_info(sm, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info for OCI element in EAPOL-Key 2/2");
os_free(rbuf);
return -1;
}
#ifdef CONFIG_TESTING_OPTIONS
if (sm->oci_freq_override_eapol_g2) {
wpa_printf(MSG_INFO,
"TEST: Override OCI KDE frequency %d -> %d MHz",
ci.frequency,
sm->oci_freq_override_eapol_g2);
ci.frequency = sm->oci_freq_override_eapol_g2;
}
#endif /* CONFIG_TESTING_OPTIONS */
pos = key_mic + mic_len + 2; /* Key Data */
if (ocv_insert_oci_kde(&ci, &pos) < 0) {
os_free(rbuf);
return -1;
}
}
#endif /* CONFIG_OCV */
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL,
rbuf, rlen, key_mic);
}
static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
const unsigned char *src_addr,
const struct wpa_eapol_key *key,
const u8 *key_data,
size_t key_data_len, u16 ver)
{
u16 key_info;
int rekey, ret;
struct wpa_gtk_data gd;
const u8 *key_rsc;
if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Group Key Handshake started prior to completion of 4-way handshake");
goto failed;
}
os_memset(&gd, 0, sizeof(gd));
rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of Group Key "
"Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
key_info = WPA_GET_BE16(key->key_info);
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data,
key_data_len, key_info,
&gd);
} else {
ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data,
key_data_len,
key_info, ver, &gd);
}
wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
if (ret)
goto failed;
key_rsc = key->key_rsc;
if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
key_rsc = null_rsc;
if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 0) ||
wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
goto failed;
forced_memzero(&gd, sizeof(gd));
if (rekey) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying "
"completed with " MACSTR " [GTK=%s]",
MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
wpa_sm_cancel_auth_timeout(sm);
wpa_sm_set_state(sm, WPA_COMPLETED);
} else {
wpa_supplicant_key_neg_complete(sm, sm->bssid,
key_info &
WPA_KEY_INFO_SECURE);
}
wpa_sm_set_rekey_offload(sm);
return;
failed:
forced_memzero(&gd, sizeof(gd));
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
}
static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
struct wpa_eapol_key *key,
u16 ver,
const u8 *buf, size_t len)
{
u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
int ok = 0;
size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
os_memcpy(mic, key + 1, mic_len);
if (sm->tptk_set) {
os_memset(key + 1, 0, mic_len);
if (wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len,
sm->key_mgmt,
ver, buf, len, (u8 *) (key + 1)) < 0 ||
os_memcmp_const(mic, key + 1, mic_len) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC "
"when using TPTK - ignoring TPTK");
#ifdef TEST_FUZZ
wpa_printf(MSG_INFO,
"TEST: Ignore Key MIC failure for fuzz testing");
goto continue_fuzz;
#endif /* TEST_FUZZ */
} else {
#ifdef TEST_FUZZ
continue_fuzz:
#endif /* TEST_FUZZ */
ok = 1;
sm->tptk_set = 0;
sm->ptk_set = 1;
os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
/*
* This assures the same TPTK in sm->tptk can never be
* copied twice to sm->ptk as the new PTK. In
* combination with the installed flag in the wpa_ptk
* struct, this assures the same PTK is only installed
* once.
*/
sm->renew_snonce = 1;
}
}
if (!ok && sm->ptk_set) {
os_memset(key + 1, 0, mic_len);
if (wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len,
sm->key_mgmt,
ver, buf, len, (u8 *) (key + 1)) < 0 ||
os_memcmp_const(mic, key + 1, mic_len) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC - "
"dropping packet");
#ifdef TEST_FUZZ
wpa_printf(MSG_INFO,
"TEST: Ignore Key MIC failure for fuzz testing");
goto continue_fuzz2;
#endif /* TEST_FUZZ */
return -1;
}
#ifdef TEST_FUZZ
continue_fuzz2:
#endif /* TEST_FUZZ */
ok = 1;
}
if (!ok) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Could not verify EAPOL-Key MIC - "
"dropping packet");
return -1;
}
os_memcpy(sm->rx_replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
sm->rx_replay_counter_set = 1;
return 0;
}
/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
struct wpa_eapol_key *key,
size_t mic_len, u16 ver,
u8 *key_data, size_t *key_data_len)
{
wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
key_data, *key_data_len);
if (!sm->ptk_set) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: PTK not available, cannot decrypt EAPOL-Key Key "
"Data");
return -1;
}
/* Decrypt key data here so that this operation does not need
* to be implemented separately for each message type. */
if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
#ifdef CONFIG_NO_RC4
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: RC4 not supported in the build");
return -1;
#else /* CONFIG_NO_RC4 */
u8 ek[32];
wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4");
os_memcpy(ek, key->key_iv, 16);
os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
forced_memzero(ek, sizeof(ek));
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"WPA: RC4 failed");
return -1;
}
forced_memzero(ek, sizeof(ek));
#endif /* CONFIG_NO_RC4 */
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
wpa_use_aes_key_wrap(sm->key_mgmt)) {
u8 *buf;
wpa_printf(MSG_DEBUG,
"WPA: Decrypt Key Data using AES-UNWRAP (KEK length %u)",
(unsigned int) sm->ptk.kek_len);
if (*key_data_len < 8 || *key_data_len % 8) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported AES-WRAP len %u",
(unsigned int) *key_data_len);
return -1;
}
*key_data_len -= 8; /* AES-WRAP adds 8 bytes */
buf = os_malloc(*key_data_len);
if (buf == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: No memory for AES-UNWRAP buffer");
return -1;
}
#ifdef TEST_FUZZ
os_memset(buf, 0x11, *key_data_len);
#endif /* TEST_FUZZ */
if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8,
key_data, buf)) {
#ifdef TEST_FUZZ
wpa_printf(MSG_INFO,
"TEST: Ignore AES unwrap failure for fuzz testing");
goto continue_fuzz;
#endif /* TEST_FUZZ */
bin_clear_free(buf, *key_data_len);
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: AES unwrap failed - "
"could not decrypt EAPOL-Key key data");
return -1;
}
#ifdef TEST_FUZZ
continue_fuzz:
#endif /* TEST_FUZZ */
os_memcpy(key_data, buf, *key_data_len);
bin_clear_free(buf, *key_data_len);
WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len);
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Unsupported key_info type %d", ver);
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
key_data, *key_data_len);
return 0;
}
/**
* wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted
* @sm: Pointer to WPA state machine data from wpa_sm_init()
*/
void wpa_sm_aborted_cached(struct wpa_sm *sm)
{
if (sm && sm->cur_pmksa) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Cancelling PMKSA caching attempt");
sm->cur_pmksa = NULL;
}
}
void wpa_sm_aborted_external_cached(struct wpa_sm *sm)
{
if (sm && sm->cur_pmksa && sm->cur_pmksa->external) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: Cancelling external PMKSA caching attempt");
sm->cur_pmksa = NULL;
}
}
static void wpa_eapol_key_dump(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
unsigned int key_data_len,
const u8 *mic, unsigned int mic_len)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
u16 key_info = WPA_GET_BE16(key->key_info);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
" key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s%s%s%s%s%s%s%s)",
key_info, key_info & WPA_KEY_INFO_TYPE_MASK,
(key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
WPA_KEY_INFO_KEY_INDEX_SHIFT,
(key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13,
key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group",
key_info & WPA_KEY_INFO_INSTALL ? " Install" : "",
key_info & WPA_KEY_INFO_ACK ? " Ack" : "",
key_info & WPA_KEY_INFO_MIC ? " MIC" : "",
key_info & WPA_KEY_INFO_SECURE ? " Secure" : "",
key_info & WPA_KEY_INFO_ERROR ? " Error" : "",
key_info & WPA_KEY_INFO_REQUEST ? " Request" : "",
key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
" key_length=%u key_data_length=%u",
WPA_GET_BE16(key->key_length), key_data_len);
wpa_hexdump(MSG_DEBUG, " replay_counter",
key->replay_counter, WPA_REPLAY_COUNTER_LEN);
wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, " key_iv", key->key_iv, 16);
wpa_hexdump(MSG_DEBUG, " key_rsc", key->key_rsc, 8);
wpa_hexdump(MSG_DEBUG, " key_id (reserved)", key->key_id, 8);
wpa_hexdump(MSG_DEBUG, " key_mic", mic, mic_len);
#endif /* CONFIG_NO_STDOUT_DEBUG */
}
#ifdef CONFIG_FILS
static int wpa_supp_aead_decrypt(struct wpa_sm *sm, u8 *buf, size_t buf_len,
size_t *key_data_len)
{
struct wpa_ptk *ptk;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
u8 *pos, *tmp;
const u8 *aad[1];
size_t aad_len[1];
if (*key_data_len < AES_BLOCK_SIZE) {
wpa_printf(MSG_INFO, "No room for AES-SIV data in the frame");
return -1;
}
if (sm->tptk_set)
ptk = &sm->tptk;
else if (sm->ptk_set)
ptk = &sm->ptk;
else
return -1;
hdr = (struct ieee802_1x_hdr *) buf;
key = (struct wpa_eapol_key *) (hdr + 1);
pos = (u8 *) (key + 1);
pos += 2; /* Pointing at the Encrypted Key Data field */
tmp = os_malloc(*key_data_len);
if (!tmp)
return -1;
/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
* to Key Data (exclusive). */
aad[0] = buf;
aad_len[0] = pos - buf;
if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, *key_data_len,
1, aad, aad_len, tmp) < 0) {
wpa_printf(MSG_INFO, "Invalid AES-SIV data in the frame");
bin_clear_free(tmp, *key_data_len);
return -1;
}
/* AEAD decryption and validation completed successfully */
(*key_data_len) -= AES_BLOCK_SIZE;
wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
tmp, *key_data_len);
/* Replace Key Data field with the decrypted version */
os_memcpy(pos, tmp, *key_data_len);
pos -= 2; /* Key Data Length field */
WPA_PUT_BE16(pos, *key_data_len);
bin_clear_free(tmp, *key_data_len);
if (sm->tptk_set) {
sm->tptk_set = 0;
sm->ptk_set = 1;
os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
}
os_memcpy(sm->rx_replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
sm->rx_replay_counter_set = 1;
return 0;
}
#endif /* CONFIG_FILS */
/**
* wpa_sm_rx_eapol - Process received WPA EAPOL frames
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @src_addr: Source MAC address of the EAPOL packet
* @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
* @len: Length of the EAPOL frame
* Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure
*
* This function is called for each received EAPOL frame. Other than EAPOL-Key
* frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is
* only processing WPA and WPA2 EAPOL-Key frames.
*
* The received EAPOL-Key packets are validated and valid packets are replied
* to. In addition, key material (PTK, GTK) is configured at the end of a
* successful key handshake.
*/
int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len)
{
size_t plen, data_len, key_data_len;
const struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
u16 key_info, ver;
u8 *tmp = NULL;
int ret = -1;
u8 *mic, *key_data;
size_t mic_len, keyhdrlen, pmk_len;
#ifdef CONFIG_IEEE80211R
sm->ft_completed = 0;
#endif /* CONFIG_IEEE80211R */
pmk_len = sm->pmk_len;
if (!pmk_len && sm->cur_pmksa)
pmk_len = sm->cur_pmksa->pmk_len;
mic_len = wpa_mic_len(sm->key_mgmt, pmk_len);
keyhdrlen = sizeof(*key) + mic_len + 2;
if (len < sizeof(*hdr) + keyhdrlen) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: EAPOL frame too short to be a WPA "
"EAPOL-Key (len %lu, expecting at least %lu)",
(unsigned long) len,
(unsigned long) sizeof(*hdr) + keyhdrlen);
return 0;
}
hdr = (const struct ieee802_1x_hdr *) buf;
plen = be_to_host16(hdr->length);
data_len = plen + sizeof(*hdr);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"IEEE 802.1X RX: version=%d type=%d length=%lu",
hdr->version, hdr->type, (unsigned long) plen);
if (hdr->version < EAPOL_VERSION) {
/* TODO: backwards compatibility */
}
if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: EAPOL frame (type %u) discarded, "
"not a Key frame", hdr->type);
ret = 0;
goto out;
}
wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
if (plen > len - sizeof(*hdr) || plen < keyhdrlen) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: EAPOL frame payload size %lu "
"invalid (frame size %lu)",
(unsigned long) plen, (unsigned long) len);
ret = 0;
goto out;
}
if (data_len < len) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: ignoring %lu bytes after the IEEE 802.1X data",
(unsigned long) len - data_len);
}
/*
* Make a copy of the frame since we need to modify the buffer during
* MAC validation and Key Data decryption.
*/
tmp = os_memdup(buf, data_len);
if (tmp == NULL)
goto out;
key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
mic = (u8 *) (key + 1);
key_data = mic + mic_len + 2;
if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
{
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: EAPOL-Key type (%d) unknown, discarded",
key->type);
ret = 0;
goto out;
}
key_data_len = WPA_GET_BE16(mic + mic_len);
wpa_eapol_key_dump(sm, key, key_data_len, mic, mic_len);
if (key_data_len > plen - keyhdrlen) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
"frame - key_data overflow (%u > %u)",
(unsigned int) key_data_len,
(unsigned int) (plen - keyhdrlen));
goto out;
}
eapol_sm_notify_lower_layer_success(sm->eapol, 0);
key_info = WPA_GET_BE16(key->key_info);
ver = key_info & WPA_KEY_INFO_TYPE_MASK;
if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
!wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Unsupported EAPOL-Key descriptor version %d",
ver);
goto out;
}
if (wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
ver);
goto out;
}
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
/* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
!wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"FT: AP did not use AES-128-CMAC");
goto out;
}
} else
#endif /* CONFIG_IEEE80211R */
if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
!wpa_use_akm_defined(sm->key_mgmt)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: AP did not use the "
"negotiated AES-128-CMAC");
goto out;
}
} else if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
!wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: CCMP is used, but EAPOL-Key "
"descriptor version (%d) is not 2", ver);
if (sm->group_cipher != WPA_CIPHER_CCMP &&
!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
/* Earlier versions of IEEE 802.11i did not explicitly
* require version 2 descriptor for all EAPOL-Key
* packets, so allow group keys to use version 1 if
* CCMP is not used for them. */
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Backwards compatibility: allow invalid "
"version for non-CCMP group keys");
} else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
} else
goto out;
} else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
!wpa_use_akm_defined(sm->key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: GCMP is used, but EAPOL-Key "
"descriptor version (%d) is not 2", ver);
goto out;
}
if (sm->rx_replay_counter_set &&
os_memcmp(key->replay_counter, sm->rx_replay_counter,
WPA_REPLAY_COUNTER_LEN) <= 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: EAPOL-Key Replay Counter did not increase - "
"dropping packet");
goto out;
}
if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: Unsupported SMK bit in key_info");
goto out;
}
if (!(key_info & WPA_KEY_INFO_ACK)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: No Ack bit in key_info");
goto out;
}
if (key_info & WPA_KEY_INFO_REQUEST) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"WPA: EAPOL-Key with Request bit - dropped");
goto out;
}
if ((key_info & WPA_KEY_INFO_MIC) &&
wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
goto out;
#ifdef CONFIG_FILS
if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
if (wpa_supp_aead_decrypt(sm, tmp, data_len, &key_data_len))
goto out;
}
#endif /* CONFIG_FILS */
if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
(key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) {
/*
* Only decrypt the Key Data field if the frame's authenticity
* was verified. When using AES-SIV (FILS), the MIC flag is not
* set, so this check should only be performed if mic_len != 0
* which is the case in this code branch.
*/
if (!(key_info & WPA_KEY_INFO_MIC)) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Ignore EAPOL-Key with encrypted but unauthenticated data");
goto out;
}
if (wpa_supplicant_decrypt_key_data(sm, key, mic_len,
ver, key_data,
&key_data_len))
goto out;
}
if (key_info & WPA_KEY_INFO_KEY_TYPE) {
if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Ignored EAPOL-Key (Pairwise) with "
"non-zero key index");
goto out;
}
if (key_info & (WPA_KEY_INFO_MIC |
WPA_KEY_INFO_ENCR_KEY_DATA)) {
/* 3/4 4-Way Handshake */
wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
key_data_len);
} else {
/* 1/4 4-Way Handshake */
wpa_supplicant_process_1_of_4(sm, src_addr, key,
ver, key_data,
key_data_len);
}
} else {
if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) ||
(!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) {
/* 1/2 Group Key Handshake */
wpa_supplicant_process_1_of_2(sm, src_addr, key,
key_data, key_data_len,
ver);
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: EAPOL-Key (Group) without Mic/Encr bit - "
"dropped");
}
}
ret = 1;
out:
bin_clear_free(tmp, data_len);
return ret;
}
#ifdef CONFIG_CTRL_IFACE
static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
{
switch (sm->key_mgmt) {
case WPA_KEY_MGMT_IEEE8021X:
return ((sm->proto == WPA_PROTO_RSN ||
sm->proto == WPA_PROTO_OSEN) ?
RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
case WPA_KEY_MGMT_PSK:
return (sm->proto == WPA_PROTO_RSN ?
RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X :
WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
#ifdef CONFIG_IEEE80211R
case WPA_KEY_MGMT_FT_IEEE8021X:
return RSN_AUTH_KEY_MGMT_FT_802_1X;
case WPA_KEY_MGMT_FT_PSK:
return RSN_AUTH_KEY_MGMT_FT_PSK;
#endif /* CONFIG_IEEE80211R */
case WPA_KEY_MGMT_IEEE8021X_SHA256:
return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
case WPA_KEY_MGMT_PSK_SHA256:
return RSN_AUTH_KEY_MGMT_PSK_SHA256;
case WPA_KEY_MGMT_CCKM:
return (sm->proto == WPA_PROTO_RSN ?
RSN_AUTH_KEY_MGMT_CCKM:
WPA_AUTH_KEY_MGMT_CCKM);
case WPA_KEY_MGMT_WPA_NONE:
return WPA_AUTH_KEY_MGMT_NONE;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
default:
return 0;
}
}
#define RSN_SUITE "%02x-%02x-%02x-%d"
#define RSN_SUITE_ARG(s) \
((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
/**
* wpa_sm_get_mib - Dump text list of MIB entries
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @buf: Buffer for the list
* @buflen: Length of the buffer
* Returns: Number of bytes written to buffer
*
* This function is used fetch dot11 MIB variables.
*/
int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
{
char pmkid_txt[PMKID_LEN * 2 + 1];
bool rsna;
int ret;
size_t len;
if (sm->cur_pmksa) {
wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
sm->cur_pmksa->pmkid, PMKID_LEN);
} else
pmkid_txt[0] = '\0';
rsna = (wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) &&
sm->proto == WPA_PROTO_RSN;
ret = os_snprintf(buf, buflen,
"dot11RSNAOptionImplemented=TRUE\n"
"dot11RSNAPreauthenticationImplemented=TRUE\n"
"dot11RSNAEnabled=%s\n"
"dot11RSNAPreauthenticationEnabled=%s\n"
"dot11RSNAConfigVersion=%d\n"
"dot11RSNAConfigPairwiseKeysSupported=5\n"
"dot11RSNAConfigGroupCipherSize=%d\n"
"dot11RSNAConfigPMKLifetime=%d\n"
"dot11RSNAConfigPMKReauthThreshold=%d\n"
"dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n"
"dot11RSNAConfigSATimeout=%d\n",
rsna ? "TRUE" : "FALSE",
rsna ? "TRUE" : "FALSE",
RSN_VERSION,
wpa_cipher_key_len(sm->group_cipher) * 8,
sm->dot11RSNAConfigPMKLifetime,
sm->dot11RSNAConfigPMKReauthThreshold,
sm->dot11RSNAConfigSATimeout);
if (os_snprintf_error(buflen, ret))
return 0;
len = ret;
ret = os_snprintf(
buf + len, buflen - len,
"dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
"dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
"dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
"dot11RSNAPMKIDUsed=%s\n"
"dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
"dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
"dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
"dot11RSNA4WayHandshakeFailures=%u\n",
RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
sm->pairwise_cipher)),
RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
sm->group_cipher)),
pmkid_txt,
RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
sm->pairwise_cipher)),
RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
sm->group_cipher)),
sm->dot11RSNA4WayHandshakeFailures);
if (!os_snprintf_error(buflen - len, ret))
len += ret;
return (int) len;
}
#endif /* CONFIG_CTRL_IFACE */
static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
void *ctx, enum pmksa_free_reason reason)
{
struct wpa_sm *sm = ctx;
int deauth = 0;
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: "
MACSTR " reason=%d", MAC2STR(entry->aa), reason);
if (sm->cur_pmksa == entry) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: %s current PMKSA entry",
reason == PMKSA_REPLACE ? "replaced" : "removed");
pmksa_cache_clear_current(sm);
/*
* If an entry is simply being replaced, there's no need to
* deauthenticate because it will be immediately re-added.
* This happens when EAP authentication is completed again
* (reauth or failed PMKSA caching attempt).
*/
if (reason != PMKSA_REPLACE)
deauth = 1;
}
if (reason == PMKSA_EXPIRE &&
(sm->pmk_len == entry->pmk_len &&
os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: deauthenticating due to expired PMK");
pmksa_cache_clear_current(sm);
deauth = 1;
}
if (deauth) {
sm->pmk_len = 0;
os_memset(sm->pmk, 0, sizeof(sm->pmk));
wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
}
}
+static bool wpa_sm_pmksa_is_current_cb(struct rsn_pmksa_cache_entry *entry,
+ void *ctx)
+{
+ struct wpa_sm *sm = ctx;
+
+ return sm->cur_pmksa == entry;
+}
+
+
/**
* wpa_sm_init - Initialize WPA state machine
* @ctx: Context pointer for callbacks; this needs to be an allocated buffer
* Returns: Pointer to the allocated WPA state machine data
*
* This function is used to allocate a new WPA state machine and the returned
* value is passed to all WPA state machine calls.
*/
struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
{
struct wpa_sm *sm;
sm = os_zalloc(sizeof(*sm));
if (sm == NULL)
return NULL;
dl_list_init(&sm->pmksa_candidates);
sm->renew_snonce = 1;
sm->ctx = ctx;
sm->dot11RSNAConfigPMKLifetime = 43200;
sm->dot11RSNAConfigPMKReauthThreshold = 70;
sm->dot11RSNAConfigSATimeout = 60;
- sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
+ sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb,
+ wpa_sm_pmksa_is_current_cb, sm, sm);
if (sm->pmksa == NULL) {
wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
"RSN: PMKSA cache initialization failed");
os_free(sm);
return NULL;
}
return sm;
}
/**
* wpa_sm_deinit - Deinitialize WPA state machine
* @sm: Pointer to WPA state machine data from wpa_sm_init()
*/
void wpa_sm_deinit(struct wpa_sm *sm)
{
if (sm == NULL)
return;
pmksa_cache_deinit(sm->pmksa);
eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
os_free(sm->assoc_wpa_ie);
os_free(sm->assoc_rsnxe);
os_free(sm->ap_wpa_ie);
os_free(sm->ap_rsn_ie);
os_free(sm->ap_rsnxe);
wpa_sm_drop_sa(sm);
os_free(sm->ctx);
#ifdef CONFIG_IEEE80211R
os_free(sm->assoc_resp_ies);
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_TESTING_OPTIONS
wpabuf_free(sm->test_assoc_ie);
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_FILS_SK_PFS
crypto_ecdh_deinit(sm->fils_ecdh);
#endif /* CONFIG_FILS_SK_PFS */
#ifdef CONFIG_FILS
wpabuf_free(sm->fils_ft_ies);
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
crypto_ecdh_deinit(sm->owe_ecdh);
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
wpabuf_clear_free(sm->dpp_z);
#endif /* CONFIG_DPP2 */
os_free(sm);
}
/**
* wpa_sm_notify_assoc - Notify WPA state machine about association
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @bssid: The BSSID of the new association
*
* This function is called to let WPA state machine know that the connection
* was established.
*/
void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
{
int clear_keys = 1;
if (sm == NULL)
return;
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: Association event - clear replay counter");
os_memcpy(sm->bssid, bssid, ETH_ALEN);
os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
sm->rx_replay_counter_set = 0;
sm->renew_snonce = 1;
if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0)
rsn_preauth_deinit(sm);
#ifdef CONFIG_IEEE80211R
if (wpa_ft_is_completed(sm)) {
/*
* Clear portValid to kick EAPOL state machine to re-enter
* AUTHENTICATED state to get the EAPOL port Authorized.
*/
eapol_sm_notify_portValid(sm->eapol, false);
wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
/* Prepare for the next transition */
wpa_ft_prepare_auth_request(sm, NULL);
clear_keys = 0;
sm->ft_protocol = 1;
} else {
sm->ft_protocol = 0;
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_FILS
if (sm->fils_completed) {
/*
* Clear portValid to kick EAPOL state machine to re-enter
* AUTHENTICATED state to get the EAPOL port Authorized.
*/
wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
clear_keys = 0;
}
#endif /* CONFIG_FILS */
if (clear_keys) {
/*
* IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
* this is not part of a Fast BSS Transition.
*/
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
sm->ptk_set = 0;
os_memset(&sm->ptk, 0, sizeof(sm->ptk));
sm->tptk_set = 0;
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
os_memset(&sm->gtk, 0, sizeof(sm->gtk));
os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
os_memset(&sm->igtk, 0, sizeof(sm->igtk));
os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
}
#ifdef CONFIG_TDLS
wpa_tdls_assoc(sm);
#endif /* CONFIG_TDLS */
#ifdef CONFIG_P2P
os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr));
#endif /* CONFIG_P2P */
sm->keyidx_active = 0;
}
/**
* wpa_sm_notify_disassoc - Notify WPA state machine about disassociation
* @sm: Pointer to WPA state machine data from wpa_sm_init()
*
* This function is called to let WPA state machine know that the connection
* was lost. This will abort any existing pre-authentication session.
*/
void wpa_sm_notify_disassoc(struct wpa_sm *sm)
{
eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
rsn_preauth_deinit(sm);
pmksa_cache_clear_current(sm);
if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
sm->dot11RSNA4WayHandshakeFailures++;
#ifdef CONFIG_TDLS
wpa_tdls_disassoc(sm);
#endif /* CONFIG_TDLS */
#ifdef CONFIG_FILS
sm->fils_completed = 0;
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
sm->ft_reassoc_completed = 0;
sm->ft_protocol = 0;
#endif /* CONFIG_IEEE80211R */
/* Keys are not needed in the WPA state machine anymore */
wpa_sm_drop_sa(sm);
sm->keyidx_active = 0;
sm->msg_3_of_4_ok = 0;
os_memset(sm->bssid, 0, ETH_ALEN);
}
/**
* wpa_sm_set_pmk - Set PMK
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @pmk: The new PMK
* @pmk_len: The length of the new PMK in bytes
* @pmkid: Calculated PMKID
* @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK
*
* Configure the PMK for WPA state machine.
*/
void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *bssid)
{
if (sm == NULL)
return;
wpa_hexdump_key(MSG_DEBUG, "WPA: Set PMK based on external data",
pmk, pmk_len);
sm->pmk_len = pmk_len;
os_memcpy(sm->pmk, pmk, pmk_len);
#ifdef CONFIG_IEEE80211R
/* Set XXKey to be PSK for FT key derivation */
sm->xxkey_len = pmk_len;
os_memcpy(sm->xxkey, pmk, pmk_len);
#endif /* CONFIG_IEEE80211R */
if (bssid) {
- pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
- bssid, sm->own_addr,
- sm->network_ctx, sm->key_mgmt, NULL);
+ sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len,
+ pmkid, NULL, 0, bssid,
+ sm->own_addr,
+ sm->network_ctx, sm->key_mgmt,
+ NULL);
}
}
/**
* wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA
* @sm: Pointer to WPA state machine data from wpa_sm_init()
*
* Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK
* will be cleared.
*/
void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
{
if (sm == NULL)
return;
if (sm->cur_pmksa) {
wpa_hexdump_key(MSG_DEBUG,
"WPA: Set PMK based on current PMKSA",
sm->cur_pmksa->pmk, sm->cur_pmksa->pmk_len);
sm->pmk_len = sm->cur_pmksa->pmk_len;
os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
} else {
wpa_printf(MSG_DEBUG, "WPA: No current PMKSA - clear PMK");
sm->pmk_len = 0;
os_memset(sm->pmk, 0, PMK_LEN_MAX);
}
}
/**
* wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @fast_reauth: Whether fast reauthentication (EAP) is allowed
*/
void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
{
if (sm)
sm->fast_reauth = fast_reauth;
}
/**
* wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @scard_ctx: Context pointer for smartcard related callback functions
*/
void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
{
if (sm == NULL)
return;
sm->scard_ctx = scard_ctx;
if (sm->preauth_eapol)
eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx);
}
/**
* wpa_sm_set_config - Notification of current configuration change
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @config: Pointer to current network configuration
*
* Notify WPA state machine that configuration has changed. config will be
* stored as a backpointer to network configuration. This can be %NULL to clear
* the stored pointed.
*/
void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
{
if (!sm)
return;
if (config) {
sm->network_ctx = config->network_ctx;
sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher;
sm->proactive_key_caching = config->proactive_key_caching;
sm->eap_workaround = config->eap_workaround;
sm->eap_conf_ctx = config->eap_conf_ctx;
if (config->ssid) {
os_memcpy(sm->ssid, config->ssid, config->ssid_len);
sm->ssid_len = config->ssid_len;
} else
sm->ssid_len = 0;
sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
sm->p2p = config->p2p;
sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation;
sm->owe_ptk_workaround = config->owe_ptk_workaround;
sm->force_kdk_derivation = config->force_kdk_derivation;
#ifdef CONFIG_FILS
if (config->fils_cache_id) {
sm->fils_cache_id_set = 1;
os_memcpy(sm->fils_cache_id, config->fils_cache_id,
FILS_CACHE_ID_LEN);
} else {
sm->fils_cache_id_set = 0;
}
#endif /* CONFIG_FILS */
sm->beacon_prot = config->beacon_prot;
} else {
sm->network_ctx = NULL;
sm->allowed_pairwise_cipher = 0;
sm->proactive_key_caching = 0;
sm->eap_workaround = 0;
sm->eap_conf_ctx = NULL;
sm->ssid_len = 0;
sm->wpa_ptk_rekey = 0;
sm->p2p = 0;
sm->wpa_rsc_relaxation = 0;
sm->owe_ptk_workaround = 0;
sm->beacon_prot = 0;
sm->force_kdk_derivation = false;
}
}
/**
* wpa_sm_set_own_addr - Set own MAC address
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @addr: Own MAC address
*/
void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
{
if (sm)
os_memcpy(sm->own_addr, addr, ETH_ALEN);
}
/**
* wpa_sm_set_ifname - Set network interface name
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @ifname: Interface name
* @bridge_ifname: Optional bridge interface name (for pre-auth)
*/
void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
const char *bridge_ifname)
{
if (sm) {
sm->ifname = ifname;
sm->bridge_ifname = bridge_ifname;
}
}
/**
* wpa_sm_set_eapol - Set EAPOL state machine pointer
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init()
*/
void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
{
if (sm)
sm->eapol = eapol;
}
/**
* wpa_sm_set_param - Set WPA state machine parameters
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @param: Parameter field
* @value: Parameter value
* Returns: 0 on success, -1 on failure
*/
int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
unsigned int value)
{
int ret = 0;
if (sm == NULL)
return -1;
switch (param) {
case RSNA_PMK_LIFETIME:
if (value > 0)
sm->dot11RSNAConfigPMKLifetime = value;
else
ret = -1;
break;
case RSNA_PMK_REAUTH_THRESHOLD:
if (value > 0 && value <= 100)
sm->dot11RSNAConfigPMKReauthThreshold = value;
else
ret = -1;
break;
case RSNA_SA_TIMEOUT:
if (value > 0)
sm->dot11RSNAConfigSATimeout = value;
else
ret = -1;
break;
case WPA_PARAM_PROTO:
sm->proto = value;
break;
case WPA_PARAM_PAIRWISE:
sm->pairwise_cipher = value;
break;
case WPA_PARAM_GROUP:
sm->group_cipher = value;
break;
case WPA_PARAM_KEY_MGMT:
sm->key_mgmt = value;
break;
case WPA_PARAM_MGMT_GROUP:
sm->mgmt_group_cipher = value;
break;
case WPA_PARAM_RSN_ENABLED:
sm->rsn_enabled = value;
break;
case WPA_PARAM_MFP:
sm->mfp = value;
break;
case WPA_PARAM_OCV:
sm->ocv = value;
break;
case WPA_PARAM_SAE_PWE:
sm->sae_pwe = value;
break;
case WPA_PARAM_SAE_PK:
sm->sae_pk = value;
break;
case WPA_PARAM_DENY_PTK0_REKEY:
sm->wpa_deny_ptk0_rekey = value;
break;
case WPA_PARAM_EXT_KEY_ID:
sm->ext_key_id = value;
break;
case WPA_PARAM_USE_EXT_KEY_ID:
sm->use_ext_key_id = value;
break;
#ifdef CONFIG_TESTING_OPTIONS
case WPA_PARAM_FT_RSNXE_USED:
sm->ft_rsnxe_used = value;
break;
case WPA_PARAM_OCI_FREQ_EAPOL:
sm->oci_freq_override_eapol = value;
break;
case WPA_PARAM_OCI_FREQ_EAPOL_G2:
sm->oci_freq_override_eapol_g2 = value;
break;
case WPA_PARAM_OCI_FREQ_FT_ASSOC:
sm->oci_freq_override_ft_assoc = value;
break;
case WPA_PARAM_OCI_FREQ_FILS_ASSOC:
sm->oci_freq_override_fils_assoc = value;
break;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_DPP2
case WPA_PARAM_DPP_PFS:
sm->dpp_pfs = value;
break;
#endif /* CONFIG_DPP2 */
default:
break;
}
return ret;
}
/**
* wpa_sm_get_status - Get WPA state machine
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @buf: Buffer for status information
* @buflen: Maximum buffer length
* @verbose: Whether to include verbose status information
* Returns: Number of bytes written to buf.
*
* Query WPA state machine for status information. This function fills in
* a text area with current status information. If the buffer (buf) is not
* large enough, status information will be truncated to fit the buffer.
*/
int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
int verbose)
{
char *pos = buf, *end = buf + buflen;
int ret;
ret = os_snprintf(pos, end - pos,
"pairwise_cipher=%s\n"
"group_cipher=%s\n"
"key_mgmt=%s\n",
wpa_cipher_txt(sm->pairwise_cipher),
wpa_cipher_txt(sm->group_cipher),
wpa_key_mgmt_txt(sm->key_mgmt, sm->proto));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
#ifdef CONFIG_DPP2
if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
ret = os_snprintf(pos, end - pos, "dpp_pfs=1\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#endif /* CONFIG_DPP2 */
if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) {
struct wpa_ie_data rsn;
if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn)
>= 0 &&
rsn.capabilities & (WPA_CAPABILITY_MFPR |
WPA_CAPABILITY_MFPC)) {
ret = os_snprintf(pos, end - pos, "pmf=%d\n"
"mgmt_group_cipher=%s\n",
(rsn.capabilities &
WPA_CAPABILITY_MFPR) ? 2 : 1,
wpa_cipher_txt(
sm->mgmt_group_cipher));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
}
return pos - buf;
}
int wpa_sm_pmf_enabled(struct wpa_sm *sm)
{
struct wpa_ie_data rsn;
if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie)
return 0;
if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 &&
rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC))
return 1;
return 0;
}
int wpa_sm_ext_key_id(struct wpa_sm *sm)
{
return sm ? sm->ext_key_id : 0;
}
int wpa_sm_ext_key_id_active(struct wpa_sm *sm)
{
return sm ? sm->use_ext_key_id : 0;
}
int wpa_sm_ocv_enabled(struct wpa_sm *sm)
{
struct wpa_ie_data rsn;
if (!sm->ocv || !sm->ap_rsn_ie)
return 0;
return wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len,
&rsn) >= 0 &&
(rsn.capabilities & WPA_CAPABILITY_OCVC);
}
/**
* wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @wpa_ie: Pointer to buffer for WPA/RSN IE
* @wpa_ie_len: Pointer to the length of the wpa_ie buffer
* Returns: 0 on success, -1 on failure
*/
int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
size_t *wpa_ie_len)
{
int res;
if (sm == NULL)
return -1;
#ifdef CONFIG_TESTING_OPTIONS
if (sm->test_assoc_ie) {
wpa_printf(MSG_DEBUG,
"TESTING: Replace association WPA/RSN IE");
if (*wpa_ie_len < wpabuf_len(sm->test_assoc_ie))
return -1;
os_memcpy(wpa_ie, wpabuf_head(sm->test_assoc_ie),
wpabuf_len(sm->test_assoc_ie));
res = wpabuf_len(sm->test_assoc_ie);
} else
#endif /* CONFIG_TESTING_OPTIONS */
res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
if (res < 0)
return -1;
*wpa_ie_len = res;
wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default",
wpa_ie, *wpa_ie_len);
if (sm->assoc_wpa_ie == NULL) {
/*
* Make a copy of the WPA/RSN IE so that 4-Way Handshake gets
* the correct version of the IE even if PMKSA caching is
* aborted (which would remove PMKID from IE generation).
*/
sm->assoc_wpa_ie = os_memdup(wpa_ie, *wpa_ie_len);
if (sm->assoc_wpa_ie == NULL)
return -1;
sm->assoc_wpa_ie_len = *wpa_ie_len;
} else {
wpa_hexdump(MSG_DEBUG,
"WPA: Leave previously set WPA IE default",
sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
}
return 0;
}
/**
* wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @ie: Pointer to IE data (starting from id)
* @len: IE length
* Returns: 0 on success, -1 on failure
*
* Inform WPA state machine about the WPA/RSN IE used in (Re)Association
* Request frame. The IE will be used to override the default value generated
* with wpa_sm_set_assoc_wpa_ie_default().
*/
int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
{
if (sm == NULL)
return -1;
os_free(sm->assoc_wpa_ie);
if (ie == NULL || len == 0) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: clearing own WPA/RSN IE");
sm->assoc_wpa_ie = NULL;
sm->assoc_wpa_ie_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len);
sm->assoc_wpa_ie = os_memdup(ie, len);
if (sm->assoc_wpa_ie == NULL)
return -1;
sm->assoc_wpa_ie_len = len;
}
return 0;
}
/**
* wpa_sm_set_assoc_rsnxe_default - Generate own RSNXE from configuration
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @rsnxe: Pointer to buffer for RSNXE
* @rsnxe_len: Pointer to the length of the rsne buffer
* Returns: 0 on success, -1 on failure
*/
int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe,
size_t *rsnxe_len)
{
int res;
if (!sm)
return -1;
res = wpa_gen_rsnxe(sm, rsnxe, *rsnxe_len);
if (res < 0)
return -1;
*rsnxe_len = res;
wpa_hexdump(MSG_DEBUG, "RSN: Set own RSNXE default", rsnxe, *rsnxe_len);
if (sm->assoc_rsnxe) {
wpa_hexdump(MSG_DEBUG,
"RSN: Leave previously set RSNXE default",
sm->assoc_rsnxe, sm->assoc_rsnxe_len);
} else if (*rsnxe_len > 0) {
/*
* Make a copy of the RSNXE so that 4-Way Handshake gets the
* correct version of the IE even if it gets changed.
*/
sm->assoc_rsnxe = os_memdup(rsnxe, *rsnxe_len);
if (!sm->assoc_rsnxe)
return -1;
sm->assoc_rsnxe_len = *rsnxe_len;
}
return 0;
}
/**
* wpa_sm_set_assoc_rsnxe - Set own RSNXE from (Re)AssocReq
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @ie: Pointer to IE data (starting from id)
* @len: IE length
* Returns: 0 on success, -1 on failure
*
* Inform WPA state machine about the RSNXE used in (Re)Association Request
* frame. The IE will be used to override the default value generated
* with wpa_sm_set_assoc_rsnxe_default().
*/
int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len)
{
if (!sm)
return -1;
os_free(sm->assoc_rsnxe);
if (!ie || len == 0) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"RSN: clearing own RSNXE");
sm->assoc_rsnxe = NULL;
sm->assoc_rsnxe_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "RSN: set own RSNXE", ie, len);
sm->assoc_rsnxe = os_memdup(ie, len);
if (!sm->assoc_rsnxe)
return -1;
sm->assoc_rsnxe_len = len;
}
return 0;
}
/**
* wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @ie: Pointer to IE data (starting from id)
* @len: IE length
* Returns: 0 on success, -1 on failure
*
* Inform WPA state machine about the WPA IE used in Beacon / Probe Response
* frame.
*/
int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
{
if (sm == NULL)
return -1;
os_free(sm->ap_wpa_ie);
if (ie == NULL || len == 0) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: clearing AP WPA IE");
sm->ap_wpa_ie = NULL;
sm->ap_wpa_ie_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len);
sm->ap_wpa_ie = os_memdup(ie, len);
if (sm->ap_wpa_ie == NULL)
return -1;
sm->ap_wpa_ie_len = len;
}
return 0;
}
/**
* wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @ie: Pointer to IE data (starting from id)
* @len: IE length
* Returns: 0 on success, -1 on failure
*
* Inform WPA state machine about the RSN IE used in Beacon / Probe Response
* frame.
*/
int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
{
if (sm == NULL)
return -1;
os_free(sm->ap_rsn_ie);
if (ie == NULL || len == 0) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: clearing AP RSN IE");
sm->ap_rsn_ie = NULL;
sm->ap_rsn_ie_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
sm->ap_rsn_ie = os_memdup(ie, len);
if (sm->ap_rsn_ie == NULL)
return -1;
sm->ap_rsn_ie_len = len;
}
return 0;
}
/**
* wpa_sm_set_ap_rsnxe - Set AP RSNXE from Beacon/ProbeResp
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @ie: Pointer to IE data (starting from id)
* @len: IE length
* Returns: 0 on success, -1 on failure
*
* Inform WPA state machine about the RSNXE used in Beacon / Probe Response
* frame.
*/
int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len)
{
if (!sm)
return -1;
os_free(sm->ap_rsnxe);
if (!ie || len == 0) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: clearing AP RSNXE");
sm->ap_rsnxe = NULL;
sm->ap_rsnxe_len = 0;
} else {
wpa_hexdump(MSG_DEBUG, "WPA: set AP RSNXE", ie, len);
sm->ap_rsnxe = os_memdup(ie, len);
if (!sm->ap_rsnxe)
return -1;
sm->ap_rsnxe_len = len;
}
return 0;
}
/**
* wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE
* @sm: Pointer to WPA state machine data from wpa_sm_init()
* @data: Pointer to data area for parsing results
* Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure
*
* Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the
* parsed data into data.
*/
int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data)
{
if (sm == NULL)
return -1;
if (sm->assoc_wpa_ie == NULL) {
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
"WPA: No WPA/RSN IE available from association info");
return -1;
}
if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data))
return -2;
return 0;
}
int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
{
return pmksa_cache_list(sm->pmksa, buf, len);
}
struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm)
{
return pmksa_cache_head(sm->pmksa);
}
struct rsn_pmksa_cache_entry *
wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
struct rsn_pmksa_cache_entry * entry)
{
return pmksa_cache_add_entry(sm->pmksa, entry);
}
void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *bssid,
const u8 *fils_cache_id)
{
sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
bssid, sm->own_addr, sm->network_ctx,
sm->key_mgmt, fils_cache_id);
}
int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
const void *network_ctx)
{
return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx, 0) != NULL;
}
struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm,
const u8 *aa,
const u8 *pmkid,
const void *network_ctx,
int akmp)
{
return pmksa_cache_get(sm->pmksa, aa, pmkid, network_ctx, akmp);
}
void wpa_sm_drop_sa(struct wpa_sm *sm)
{
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
sm->ptk_set = 0;
sm->tptk_set = 0;
sm->pmk_len = 0;
os_memset(sm->pmk, 0, sizeof(sm->pmk));
os_memset(&sm->ptk, 0, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
os_memset(&sm->gtk, 0, sizeof(sm->gtk));
os_memset(&sm->gtk_wnm_sleep, 0, sizeof(sm->gtk_wnm_sleep));
os_memset(&sm->igtk, 0, sizeof(sm->igtk));
os_memset(&sm->igtk_wnm_sleep, 0, sizeof(sm->igtk_wnm_sleep));
#ifdef CONFIG_IEEE80211R
os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
sm->xxkey_len = 0;
os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
sm->pmk_r0_len = 0;
os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
sm->pmk_r1_len = 0;
#ifdef CONFIG_PASN
os_free(sm->pasn_r1kh);
sm->pasn_r1kh = NULL;
sm->n_pasn_r1kh = 0;
#endif /* CONFIG_PASN */
#endif /* CONFIG_IEEE80211R */
}
int wpa_sm_has_ptk(struct wpa_sm *sm)
{
if (sm == NULL)
return 0;
return sm->ptk_set;
}
int wpa_sm_has_ptk_installed(struct wpa_sm *sm)
{
if (!sm)
return 0;
return sm->ptk.installed;
}
void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr)
{
os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN);
}
void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
{
pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, false);
}
void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
{
pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0, true);
}
#ifdef CONFIG_WNM
int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
{
u16 keyinfo;
u8 keylen; /* plaintext key len */
u8 *key_rsc;
if (subelem_id == WNM_SLEEP_SUBELEM_GTK) {
struct wpa_gtk_data gd;
os_memset(&gd, 0, sizeof(gd));
keylen = wpa_cipher_key_len(sm->group_cipher);
gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
gd.alg = wpa_cipher_to_alg(sm->group_cipher);
if (gd.alg == WPA_ALG_NONE) {
wpa_printf(MSG_DEBUG, "Unsupported group cipher suite");
return -1;
}
key_rsc = buf + 5;
keyinfo = WPA_GET_LE16(buf + 2);
gd.gtk_len = keylen;
if (gd.gtk_len != buf[4]) {
wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d",
gd.gtk_len, buf[4]);
return -1;
}
gd.keyidx = keyinfo & 0x03; /* B0 - B1 */
gd.tx = wpa_supplicant_gtk_tx_bit_workaround(
sm, !!(keyinfo & WPA_KEY_INFO_TXRX));
os_memcpy(gd.gtk, buf + 13, gd.gtk_len);
wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
gd.gtk, gd.gtk_len);
if (wpa_supplicant_install_gtk(sm, &gd, key_rsc, 1)) {
forced_memzero(&gd, sizeof(gd));
wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
"WNM mode");
return -1;
}
forced_memzero(&gd, sizeof(gd));
} else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
const struct wpa_igtk_kde *igtk;
igtk = (const struct wpa_igtk_kde *) (buf + 2);
if (wpa_supplicant_install_igtk(sm, igtk, 1) < 0)
return -1;
} else if (subelem_id == WNM_SLEEP_SUBELEM_BIGTK) {
const struct wpa_bigtk_kde *bigtk;
bigtk = (const struct wpa_bigtk_kde *) (buf + 2);
if (sm->beacon_prot &&
wpa_supplicant_install_bigtk(sm, bigtk, 1) < 0)
return -1;
} else {
wpa_printf(MSG_DEBUG, "Unknown element id");
return -1;
}
return 0;
}
#endif /* CONFIG_WNM */
#ifdef CONFIG_P2P
int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
{
if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0)
return -1;
os_memcpy(buf, sm->p2p_ip_addr, 3 * 4);
return 0;
}
#endif /* CONFIG_P2P */
void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter)
{
if (rx_replay_counter == NULL)
return;
os_memcpy(sm->rx_replay_counter, rx_replay_counter,
WPA_REPLAY_COUNTER_LEN);
sm->rx_replay_counter_set = 1;
wpa_printf(MSG_DEBUG, "Updated key replay counter");
}
void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
const u8 *ptk_kck, size_t ptk_kck_len,
const u8 *ptk_kek, size_t ptk_kek_len)
{
if (ptk_kck && ptk_kck_len <= WPA_KCK_MAX_LEN) {
os_memcpy(sm->ptk.kck, ptk_kck, ptk_kck_len);
sm->ptk.kck_len = ptk_kck_len;
wpa_printf(MSG_DEBUG, "Updated PTK KCK");
}
if (ptk_kek && ptk_kek_len <= WPA_KEK_MAX_LEN) {
os_memcpy(sm->ptk.kek, ptk_kek, ptk_kek_len);
sm->ptk.kek_len = ptk_kek_len;
wpa_printf(MSG_DEBUG, "Updated PTK KEK");
}
sm->ptk_set = 1;
}
#ifdef CONFIG_TESTING_OPTIONS
void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf)
{
wpabuf_free(sm->test_assoc_ie);
sm->test_assoc_ie = buf;
}
const u8 * wpa_sm_get_anonce(struct wpa_sm *sm)
{
return sm->anonce;
}
#endif /* CONFIG_TESTING_OPTIONS */
unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm)
{
return sm->key_mgmt;
}
#ifdef CONFIG_FILS
struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md)
{
struct wpabuf *buf = NULL;
struct wpabuf *erp_msg;
struct wpabuf *pub = NULL;
erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol);
if (!erp_msg && !sm->cur_pmksa) {
wpa_printf(MSG_DEBUG,
"FILS: Neither ERP EAP-Initiate/Re-auth nor PMKSA cache entry is available - skip FILS");
goto fail;
}
wpa_printf(MSG_DEBUG, "FILS: Try to use FILS (erp=%d pmksa_cache=%d)",
erp_msg != NULL, sm->cur_pmksa != NULL);
sm->fils_completed = 0;
if (!sm->assoc_wpa_ie) {
wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS");
goto fail;
}
if (random_get_bytes(sm->fils_nonce, FILS_NONCE_LEN) < 0 ||
random_get_bytes(sm->fils_session, FILS_SESSION_LEN) < 0)
goto fail;
wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Nonce",
sm->fils_nonce, FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session",
sm->fils_session, FILS_SESSION_LEN);
#ifdef CONFIG_FILS_SK_PFS
sm->fils_dh_group = dh_group;
if (dh_group) {
crypto_ecdh_deinit(sm->fils_ecdh);
sm->fils_ecdh = crypto_ecdh_init(dh_group);
if (!sm->fils_ecdh) {
wpa_printf(MSG_INFO,
"FILS: Could not initialize ECDH with group %d",
dh_group);
goto fail;
}
pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
if (!pub)
goto fail;
wpa_hexdump_buf(MSG_DEBUG, "FILS: Element (DH public key)",
pub);
sm->fils_dh_elem_len = wpabuf_len(pub);
}
#endif /* CONFIG_FILS_SK_PFS */
buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len +
(pub ? wpabuf_len(pub) : 0));
if (!buf)
goto fail;
/* Fields following the Authentication algorithm number field */
/* Authentication Transaction seq# */
wpabuf_put_le16(buf, 1);
/* Status Code */
wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
/* TODO: FILS PK */
#ifdef CONFIG_FILS_SK_PFS
if (dh_group) {
/* Finite Cyclic Group */
wpabuf_put_le16(buf, dh_group);
/* Element */
wpabuf_put_buf(buf, pub);
}
#endif /* CONFIG_FILS_SK_PFS */
/* RSNE */
wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame",
sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
wpabuf_put_data(buf, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
if (md) {
/* MDE when using FILS for FT initial association */
struct rsn_mdie *mdie;
wpabuf_put_u8(buf, WLAN_EID_MOBILITY_DOMAIN);
wpabuf_put_u8(buf, sizeof(*mdie));
mdie = wpabuf_put(buf, sizeof(*mdie));
os_memcpy(mdie->mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
mdie->ft_capab = 0;
}
/* FILS Nonce */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); /* Length */
/* Element ID Extension */
wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
wpabuf_put_data(buf, sm->fils_nonce, FILS_NONCE_LEN);
/* FILS Session */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
/* Element ID Extension */
wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN);
/* Wrapped Data */
sm->fils_erp_pmkid_set = 0;
if (erp_msg) {
wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg)); /* Length */
/* Element ID Extension */
wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
wpabuf_put_buf(buf, erp_msg);
/* Calculate pending PMKID here so that we do not need to
* maintain a copy of the EAP-Initiate/Reauth message. */
if (fils_pmkid_erp(sm->key_mgmt, wpabuf_head(erp_msg),
wpabuf_len(erp_msg),
sm->fils_erp_pmkid) == 0)
sm->fils_erp_pmkid_set = 1;
}
wpa_hexdump_buf(MSG_DEBUG, "RSN: FILS fields for Authentication frame",
buf);
fail:
wpabuf_free(erp_msg);
wpabuf_free(pub);
return buf;
}
int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
size_t len)
{
const u8 *pos, *end;
struct ieee802_11_elems elems;
struct wpa_ie_data rsn;
int pmkid_match = 0;
u8 ick[FILS_ICK_MAX_LEN];
size_t ick_len;
int res;
struct wpabuf *dh_ss = NULL;
const u8 *g_sta = NULL;
size_t g_sta_len = 0;
const u8 *g_ap = NULL;
size_t g_ap_len = 0, kdk_len;
struct wpabuf *pub = NULL;
os_memcpy(sm->bssid, bssid, ETH_ALEN);
wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
data, len);
pos = data;
end = data + len;
/* TODO: FILS PK */
#ifdef CONFIG_FILS_SK_PFS
if (sm->fils_dh_group) {
u16 group;
/* Using FILS PFS */
/* Finite Cyclic Group */
if (end - pos < 2) {
wpa_printf(MSG_DEBUG,
"FILS: No room for Finite Cyclic Group");
goto fail;
}
group = WPA_GET_LE16(pos);
pos += 2;
if (group != sm->fils_dh_group) {
wpa_printf(MSG_DEBUG,
"FILS: Unexpected change in Finite Cyclic Group: %u (expected %u)",
group, sm->fils_dh_group);
goto fail;
}
/* Element */
if ((size_t) (end - pos) < sm->fils_dh_elem_len) {
wpa_printf(MSG_DEBUG, "FILS: No room for Element");
goto fail;
}
if (!sm->fils_ecdh) {
wpa_printf(MSG_DEBUG, "FILS: No ECDH state available");
goto fail;
}
dh_ss = crypto_ecdh_set_peerkey(sm->fils_ecdh, 1, pos,
sm->fils_dh_elem_len);
if (!dh_ss) {
wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed");
goto fail;
}
wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", dh_ss);
g_ap = pos;
g_ap_len = sm->fils_dh_elem_len;
pos += sm->fils_dh_elem_len;
}
#endif /* CONFIG_FILS_SK_PFS */
wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
goto fail;
}
/* RSNE */
wpa_hexdump(MSG_DEBUG, "FILS: RSN element", elems.rsn_ie,
elems.rsn_ie_len);
if (!elems.rsn_ie ||
wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
&rsn) < 0) {
wpa_printf(MSG_DEBUG, "FILS: No RSN element");
goto fail;
}
if (!elems.fils_nonce) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
goto fail;
}
os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN);
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt)) {
struct wpa_ft_ies parse;
if (!elems.mdie || !elems.ftie) {
wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE");
goto fail;
}
if (wpa_ft_parse_ies(pos, end - pos, &parse,
wpa_key_mgmt_sha384(sm->key_mgmt)) < 0) {
wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs");
goto fail;
}
if (!parse.r0kh_id) {
wpa_printf(MSG_DEBUG,
"FILS+FT: No R0KH-ID subelem in FTE");
goto fail;
}
os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
sm->r0kh_id_len = parse.r0kh_id_len;
wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
if (!parse.r1kh_id) {
wpa_printf(MSG_DEBUG,
"FILS+FT: No R1KH-ID subelem in FTE");
goto fail;
}
os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
wpa_hexdump(MSG_DEBUG, "FILS+FT: R1KH-ID",
sm->r1kh_id, FT_R1KH_ID_LEN);
/* TODO: Check MDE and FTE payload */
wpabuf_free(sm->fils_ft_ies);
sm->fils_ft_ies = wpabuf_alloc(2 + elems.mdie_len +
2 + elems.ftie_len);
if (!sm->fils_ft_ies)
goto fail;
wpabuf_put_data(sm->fils_ft_ies, elems.mdie - 2,
2 + elems.mdie_len);
wpabuf_put_data(sm->fils_ft_ies, elems.ftie - 2,
2 + elems.ftie_len);
} else {
wpabuf_free(sm->fils_ft_ies);
sm->fils_ft_ies = NULL;
}
#endif /* CONFIG_IEEE80211R */
/* PMKID List */
if (rsn.pmkid && rsn.num_pmkid > 0) {
wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
if (rsn.num_pmkid != 1) {
wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN);
if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0)
{
wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch");
wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID",
sm->cur_pmksa->pmkid, PMKID_LEN);
goto fail;
}
wpa_printf(MSG_DEBUG,
"FILS: Matching PMKID - continue using PMKSA caching");
pmkid_match = 1;
}
if (!pmkid_match && sm->cur_pmksa) {
wpa_printf(MSG_DEBUG,
"FILS: No PMKID match - cannot use cached PMKSA entry");
sm->cur_pmksa = NULL;
}
/* FILS Session */
if (!elems.fils_session) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
FILS_SESSION_LEN);
if (os_memcmp(sm->fils_session, elems.fils_session, FILS_SESSION_LEN)
!= 0) {
wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
sm->fils_session, FILS_SESSION_LEN);
goto fail;
}
/* Wrapped Data */
if (!sm->cur_pmksa && elems.wrapped_data) {
u8 rmsk[ERP_MAX_KEY_LEN];
size_t rmsk_len;
wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
elems.wrapped_data,
elems.wrapped_data_len);
eapol_sm_process_erp_finish(sm->eapol, elems.wrapped_data,
elems.wrapped_data_len);
if (eapol_sm_failed(sm->eapol))
goto fail;
rmsk_len = ERP_MAX_KEY_LEN;
res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
if (res == PMK_LEN) {
rmsk_len = PMK_LEN;
res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
}
if (res)
goto fail;
res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len,
sm->fils_nonce, sm->fils_anonce,
dh_ss ? wpabuf_head(dh_ss) : NULL,
dh_ss ? wpabuf_len(dh_ss) : 0,
sm->pmk, &sm->pmk_len);
forced_memzero(rmsk, sizeof(rmsk));
/* Don't use DHss in PTK derivation if PMKSA caching is not
* used. */
wpabuf_clear_free(dh_ss);
dh_ss = NULL;
if (res)
goto fail;
if (!sm->fils_erp_pmkid_set) {
wpa_printf(MSG_DEBUG, "FILS: PMKID not available");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid,
PMKID_LEN);
wpa_printf(MSG_DEBUG, "FILS: ERP processing succeeded - add PMKSA cache entry for the result");
sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len,
sm->fils_erp_pmkid, NULL, 0,
sm->bssid, sm->own_addr,
sm->network_ctx, sm->key_mgmt,
NULL);
}
if (!sm->cur_pmksa) {
wpa_printf(MSG_DEBUG,
"FILS: No remaining options to continue FILS authentication");
goto fail;
}
if (sm->force_kdk_derivation ||
(sm->secure_ltf &&
ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
kdk_len = WPA_KDK_MAX_LEN;
else
kdk_len = 0;
if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
sm->fils_nonce, sm->fils_anonce,
dh_ss ? wpabuf_head(dh_ss) : NULL,
dh_ss ? wpabuf_len(dh_ss) : 0,
&sm->ptk, ick, &ick_len,
sm->key_mgmt, sm->pairwise_cipher,
sm->fils_ft, &sm->fils_ft_len,
kdk_len) < 0) {
wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
goto fail;
}
wpabuf_clear_free(dh_ss);
dh_ss = NULL;
sm->ptk_set = 1;
sm->tptk_set = 0;
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
#ifdef CONFIG_FILS_SK_PFS
if (sm->fils_dh_group) {
if (!sm->fils_ecdh) {
wpa_printf(MSG_INFO, "FILS: ECDH not initialized");
goto fail;
}
pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1);
if (!pub)
goto fail;
wpa_hexdump_buf(MSG_DEBUG, "FILS: gSTA", pub);
g_sta = wpabuf_head(pub);
g_sta_len = wpabuf_len(pub);
if (!g_ap) {
wpa_printf(MSG_INFO, "FILS: gAP not available");
goto fail;
}
wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len);
}
#endif /* CONFIG_FILS_SK_PFS */
res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce,
sm->fils_anonce, sm->own_addr, sm->bssid,
g_sta, g_sta_len, g_ap, g_ap_len,
sm->key_mgmt, sm->fils_key_auth_sta,
sm->fils_key_auth_ap,
&sm->fils_key_auth_len);
wpabuf_free(pub);
forced_memzero(ick, sizeof(ick));
return res;
fail:
wpabuf_free(pub);
wpabuf_clear_free(dh_ss);
return -1;
}
#ifdef CONFIG_IEEE80211R
static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
{
struct rsn_ie_hdr *rsnie;
u16 capab;
u8 *pos;
int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
/* RSNIE[PMKR0Name/PMKR1Name] */
rsnie = wpabuf_put(buf, sizeof(*rsnie));
rsnie->elem_id = WLAN_EID_RSN;
WPA_PUT_LE16(rsnie->version, RSN_VERSION);
/* Group Suite Selector */
if (!wpa_cipher_valid_group(sm->group_cipher)) {
wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
sm->group_cipher);
return -1;
}
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
sm->group_cipher));
/* Pairwise Suite Count */
wpabuf_put_le16(buf, 1);
/* Pairwise Suite List */
if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) {
wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
sm->pairwise_cipher);
return -1;
}
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
sm->pairwise_cipher));
/* Authenticated Key Management Suite Count */
wpabuf_put_le16(buf, 1);
/* Authenticated Key Management Suite List */
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
else {
wpa_printf(MSG_WARNING,
"FILS+FT: Invalid key management type (%d)",
sm->key_mgmt);
return -1;
}
/* RSN Capabilities */
capab = 0;
if (sm->mfp)
capab |= WPA_CAPABILITY_MFPC;
if (sm->mfp == 2)
capab |= WPA_CAPABILITY_MFPR;
if (sm->ocv)
capab |= WPA_CAPABILITY_OCVC;
if (sm->ext_key_id)
capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
wpabuf_put_le16(buf, capab);
/* PMKID Count */
wpabuf_put_le16(buf, 1);
/* PMKID List [PMKR1Name] */
wpa_hexdump_key(MSG_DEBUG, "FILS+FT: XXKey (FILS-FT)",
sm->fils_ft, sm->fils_ft_len);
wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: SSID", sm->ssid, sm->ssid_len);
wpa_hexdump(MSG_DEBUG, "FILS+FT: MDID",
sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN);
wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID",
sm->r0kh_id, sm->r0kh_id_len);
if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid,
sm->ssid_len, sm->mobility_domain,
sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) {
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0");
return -1;
}
sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR,
MAC2STR(sm->r1kh_id));
pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr,
sm->pmk_r1_name, use_sha384) < 0) {
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
return -1;
}
os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN);
if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
/* Management Group Cipher Suite */
pos = wpabuf_put(buf, RSN_SELECTOR_LEN);
RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
}
rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2;
return 0;
}
#endif /* CONFIG_IEEE80211R */
struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
size_t *kek_len, const u8 **snonce,
const u8 **anonce,
const struct wpabuf **hlp,
unsigned int num_hlp)
{
struct wpabuf *buf;
size_t len;
unsigned int i;
len = 1000;
#ifdef CONFIG_IEEE80211R
if (sm->fils_ft_ies)
len += wpabuf_len(sm->fils_ft_ies);
if (wpa_key_mgmt_ft(sm->key_mgmt))
len += 256;
#endif /* CONFIG_IEEE80211R */
for (i = 0; hlp && i < num_hlp; i++)
len += 10 + wpabuf_len(hlp[i]);
buf = wpabuf_alloc(len);
if (!buf)
return NULL;
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) {
/* MDE and FTE when using FILS+FT */
wpabuf_put_buf(buf, sm->fils_ft_ies);
/* RSNE with PMKR1Name in PMKID field */
if (fils_ft_build_assoc_req_rsne(sm, buf) < 0) {
wpabuf_free(buf);
return NULL;
}
}
#endif /* CONFIG_IEEE80211R */
/* FILS Session */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
/* Element ID Extension */
wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN);
/* Everything after FILS Session element gets encrypted in the driver
* with KEK. The buffer returned from here is the plaintext version. */
/* TODO: FILS Public Key */
/* FILS Key Confirm */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
wpabuf_put_u8(buf, 1 + sm->fils_key_auth_len); /* Length */
/* Element ID Extension */
wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM);
wpabuf_put_data(buf, sm->fils_key_auth_sta, sm->fils_key_auth_len);
/* FILS HLP Container */
for (i = 0; hlp && i < num_hlp; i++) {
const u8 *pos = wpabuf_head(hlp[i]);
size_t left = wpabuf_len(hlp[i]);
wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
if (left <= 254)
len = 1 + left;
else
len = 255;
wpabuf_put_u8(buf, len); /* Length */
/* Element ID Extension */
wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_HLP_CONTAINER);
/* Destination MAC Address, Source MAC Address, HLP Packet.
* HLP Packet is in MSDU format (i.e., included the LLC/SNAP
* header when LPD is used). */
wpabuf_put_data(buf, pos, len - 1);
pos += len - 1;
left -= len - 1;
while (left) {
wpabuf_put_u8(buf, WLAN_EID_FRAGMENT);
len = left > 255 ? 255 : left;
wpabuf_put_u8(buf, len);
wpabuf_put_data(buf, pos, len);
pos += len;
left -= len;
}
}
/* TODO: FILS IP Address Assignment */
#ifdef CONFIG_OCV
if (wpa_sm_ocv_enabled(sm)) {
struct wpa_channel_info ci;
u8 *pos;
if (wpa_sm_channel_info(sm, &ci) != 0) {
wpa_printf(MSG_WARNING,
"FILS: Failed to get channel info for OCI element");
wpabuf_free(buf);
return NULL;
}
#ifdef CONFIG_TESTING_OPTIONS
if (sm->oci_freq_override_fils_assoc) {
wpa_printf(MSG_INFO,
"TEST: Override OCI KDE frequency %d -> %d MHz",
ci.frequency,
sm->oci_freq_override_fils_assoc);
ci.frequency = sm->oci_freq_override_fils_assoc;
}
#endif /* CONFIG_TESTING_OPTIONS */
pos = wpabuf_put(buf, OCV_OCI_EXTENDED_LEN);
if (ocv_insert_extended_oci(&ci, pos) < 0) {
wpabuf_free(buf);
return NULL;
}
}
#endif /* CONFIG_OCV */
wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf);
*kek = sm->ptk.kek;
*kek_len = sm->ptk.kek_len;
wpa_hexdump_key(MSG_DEBUG, "FILS: KEK for AEAD", *kek, *kek_len);
*snonce = sm->fils_nonce;
wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD",
*snonce, FILS_NONCE_LEN);
*anonce = sm->fils_anonce;
wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD",
*anonce, FILS_NONCE_LEN);
return buf;
}
static void fils_process_hlp_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
{
const u8 *pos, *end;
wpa_hexdump(MSG_MSGDUMP, "FILS: HLP response", resp, len);
if (len < 2 * ETH_ALEN)
return;
pos = resp + 2 * ETH_ALEN;
end = resp + len;
if (end - pos >= 6 &&
os_memcmp(pos, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
pos += 6; /* Remove SNAP/LLC header */
wpa_sm_fils_hlp_rx(sm, resp, resp + ETH_ALEN, pos, end - pos);
}
static void fils_process_hlp_container(struct wpa_sm *sm, const u8 *pos,
size_t len)
{
const u8 *end = pos + len;
u8 *tmp, *tmp_pos;
/* Check if there are any FILS HLP Container elements */
while (end - pos >= 2) {
if (2 + pos[1] > end - pos)
return;
if (pos[0] == WLAN_EID_EXTENSION &&
pos[1] >= 1 + 2 * ETH_ALEN &&
pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
break;
pos += 2 + pos[1];
}
if (end - pos < 2)
return; /* No FILS HLP Container elements */
tmp = os_malloc(end - pos);
if (!tmp)
return;
while (end - pos >= 2) {
if (2 + pos[1] > end - pos ||
pos[0] != WLAN_EID_EXTENSION ||
pos[1] < 1 + 2 * ETH_ALEN ||
pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
break;
tmp_pos = tmp;
os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
tmp_pos += pos[1] - 1;
pos += 2 + pos[1];
/* Add possible fragments */
while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
2 + pos[1] <= end - pos) {
os_memcpy(tmp_pos, pos + 2, pos[1]);
tmp_pos += pos[1];
pos += 2 + pos[1];
}
fils_process_hlp_resp(sm, tmp, tmp_pos - tmp);
}
os_free(tmp);
}
int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
{
const struct ieee80211_mgmt *mgmt;
const u8 *end, *ie_start;
struct ieee802_11_elems elems;
int keylen, rsclen;
enum wpa_alg alg;
struct wpa_gtk_data gd;
int maxkeylen;
struct wpa_eapol_ie_parse kde;
if (!sm || !sm->ptk_set) {
wpa_printf(MSG_DEBUG, "FILS: No KEK available");
return -1;
}
if (!wpa_key_mgmt_fils(sm->key_mgmt)) {
wpa_printf(MSG_DEBUG, "FILS: Not a FILS AKM");
return -1;
}
if (sm->fils_completed) {
wpa_printf(MSG_DEBUG,
"FILS: Association has already been completed for this FILS authentication - ignore unexpected retransmission");
return -1;
}
wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame",
resp, len);
mgmt = (const struct ieee80211_mgmt *) resp;
if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp))
return -1;
end = resp + len;
/* Same offset for Association Response and Reassociation Response */
ie_start = mgmt->u.assoc_resp.variable;
if (ieee802_11_parse_elems(ie_start, end - ie_start, &elems, 1) ==
ParseFailed) {
wpa_printf(MSG_DEBUG,
"FILS: Failed to parse decrypted elements");
goto fail;
}
if (!elems.fils_session) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
return -1;
}
if (os_memcmp(elems.fils_session, sm->fils_session,
FILS_SESSION_LEN) != 0) {
wpa_printf(MSG_DEBUG, "FILS: FILS Session mismatch");
wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
elems.fils_session, FILS_SESSION_LEN);
wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
sm->fils_session, FILS_SESSION_LEN);
}
if (!elems.rsn_ie) {
wpa_printf(MSG_DEBUG,
"FILS: No RSNE in (Re)Association Response");
/* As an interop workaround, allow this for now since IEEE Std
* 802.11ai-2016 did not include all the needed changes to make
* a FILS AP include RSNE in the frame. This workaround might
* eventually be removed and replaced with rejection (goto fail)
* to follow a strict interpretation of the standard. */
} else if (wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
sm->ap_rsn_ie, sm->ap_rsn_ie_len,
elems.rsn_ie - 2, elems.rsn_ie_len + 2)) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
"FILS: RSNE mismatch between Beacon/Probe Response and (Re)Association Response");
wpa_hexdump(MSG_DEBUG, "FILS: RSNE in Beacon/Probe Response",
sm->ap_rsn_ie, sm->ap_rsn_ie_len);
wpa_hexdump(MSG_DEBUG, "FILS: RSNE in (Re)Association Response",
elems.rsn_ie, elems.rsn_ie_len);
goto fail;
}
/* TODO: FILS Public Key */
if (!elems.fils_key_confirm) {
wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
goto fail;
}
if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
wpa_printf(MSG_DEBUG,
"FILS: Unexpected Key-Auth length %d (expected %d)",
elems.fils_key_confirm_len,
(int) sm->fils_key_auth_len);
goto fail;
}
if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_ap,
sm->fils_key_auth_len) != 0) {
wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
elems.fils_key_confirm,
elems.fils_key_confirm_len);
wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
sm->fils_key_auth_ap, sm->fils_key_auth_len);
goto fail;
}
#ifdef CONFIG_OCV
if (wpa_sm_ocv_enabled(sm)) {
struct wpa_channel_info ci;
if (wpa_sm_channel_info(sm, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Failed to get channel info to validate received OCI in FILS (Re)Association Response frame");
goto fail;
}
if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
channel_width_to_int(ci.chanwidth),
ci.seg1_idx) != OCI_SUCCESS) {
wpa_msg(sm->ctx->msg_ctx, MSG_INFO, OCV_FAILURE
"addr=" MACSTR " frame=fils-assoc error=%s",
MAC2STR(sm->bssid), ocv_errorstr);
goto fail;
}
}
#endif /* CONFIG_OCV */
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) {
struct wpa_ie_data rsn;
/* Check that PMKR1Name derived by the AP matches */
if (!elems.rsn_ie ||
wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
&rsn) < 0 ||
!rsn.pmkid || rsn.num_pmkid != 1 ||
os_memcmp(rsn.pmkid, sm->pmk_r1_name,
WPA_PMK_NAME_LEN) != 0) {
wpa_printf(MSG_DEBUG,
"FILS+FT: No RSNE[PMKR1Name] match in AssocResp");
goto fail;
}
}
#endif /* CONFIG_IEEE80211R */
/* Key Delivery */
if (!elems.key_delivery) {
wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element");
goto fail;
}
/* Parse GTK and set the key to the driver */
os_memset(&gd, 0, sizeof(gd));
if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN,
elems.key_delivery_len - WPA_KEY_RSC_LEN,
&kde) < 0) {
wpa_printf(MSG_DEBUG, "FILS: Failed to parse KDEs");
goto fail;
}
if (!kde.gtk) {
wpa_printf(MSG_DEBUG, "FILS: No GTK KDE");
goto fail;
}
maxkeylen = gd.gtk_len = kde.gtk_len - 2;
if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gd.gtk_len, maxkeylen,
&gd.key_rsc_len, &gd.alg))
goto fail;
wpa_hexdump_key(MSG_DEBUG, "FILS: Received GTK", kde.gtk, kde.gtk_len);
gd.keyidx = kde.gtk[0] & 0x3;
gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
!!(kde.gtk[0] & BIT(2)));
if (kde.gtk_len - 2 > sizeof(gd.gtk)) {
wpa_printf(MSG_DEBUG, "FILS: Too long GTK in GTK KDE (len=%lu)",
(unsigned long) kde.gtk_len - 2);
goto fail;
}
os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2);
wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver");
if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery, 0) < 0) {
wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK");
goto fail;
}
if (ieee80211w_set_keys(sm, &kde) < 0) {
wpa_printf(MSG_DEBUG, "FILS: Failed to set IGTK");
goto fail;
}
alg = wpa_cipher_to_alg(sm->pairwise_cipher);
keylen = wpa_cipher_key_len(sm->pairwise_cipher);
if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
wpa_printf(MSG_DEBUG, "FILS: TK length mismatch: %u != %lu",
keylen, (long unsigned int) sm->ptk.tk_len);
goto fail;
}
rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
sm->ptk.tk, keylen);
if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen,
sm->ptk.tk, keylen, KEY_FLAG_PAIRWISE_RX_TX) < 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
MACSTR ")",
alg, keylen, MAC2STR(sm->bssid));
goto fail;
}
wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher,
sm->dot11RSNAConfigPMKLifetime, &sm->ptk);
/* TODO: TK could be cleared after auth frame exchange now that driver
* takes care of association frame encryption/decryption. */
/* TK is not needed anymore in supplicant */
os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
sm->ptk.tk_len = 0;
sm->ptk.installed = 1;
/* FILS HLP Container */
fils_process_hlp_container(sm, ie_start, end - ie_start);
/* TODO: FILS IP Address Assignment */
wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully");
sm->fils_completed = 1;
forced_memzero(&gd, sizeof(gd));
if (kde.transition_disable)
wpa_sm_transition_disable(sm, kde.transition_disable[0]);
return 0;
fail:
forced_memzero(&gd, sizeof(gd));
return -1;
}
void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set)
{
if (sm)
sm->fils_completed = !!set;
}
#endif /* CONFIG_FILS */
int wpa_fils_is_completed(struct wpa_sm *sm)
{
#ifdef CONFIG_FILS
return sm && sm->fils_completed;
#else /* CONFIG_FILS */
return 0;
#endif /* CONFIG_FILS */
}
#ifdef CONFIG_OWE
struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group)
{
struct wpabuf *ie = NULL, *pub = NULL;
size_t prime_len;
if (group == 19)
prime_len = 32;
else if (group == 20)
prime_len = 48;
else if (group == 21)
prime_len = 66;
else
return NULL;
crypto_ecdh_deinit(sm->owe_ecdh);
sm->owe_ecdh = crypto_ecdh_init(group);
if (!sm->owe_ecdh)
goto fail;
sm->owe_group = group;
pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0);
pub = wpabuf_zeropad(pub, prime_len);
if (!pub)
goto fail;
ie = wpabuf_alloc(5 + wpabuf_len(pub));
if (!ie)
goto fail;
wpabuf_put_u8(ie, WLAN_EID_EXTENSION);
wpabuf_put_u8(ie, 1 + 2 + wpabuf_len(pub));
wpabuf_put_u8(ie, WLAN_EID_EXT_OWE_DH_PARAM);
wpabuf_put_le16(ie, group);
wpabuf_put_buf(ie, pub);
wpabuf_free(pub);
wpa_hexdump_buf(MSG_DEBUG, "OWE: Diffie-Hellman Parameter element",
ie);
return ie;
fail:
wpabuf_free(pub);
crypto_ecdh_deinit(sm->owe_ecdh);
sm->owe_ecdh = NULL;
return NULL;
}
int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
const u8 *resp_ies, size_t resp_ies_len)
{
struct ieee802_11_elems elems;
u16 group;
struct wpabuf *secret, *pub, *hkey;
int res;
u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN];
const char *info = "OWE Key Generation";
const u8 *addr[2];
size_t len[2];
size_t hash_len, prime_len;
struct wpa_ie_data data;
if (!resp_ies ||
ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 1) ==
ParseFailed) {
wpa_printf(MSG_INFO,
"OWE: Could not parse Association Response frame elements");
return -1;
}
if (sm->cur_pmksa && elems.rsn_ie &&
wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, 2 + elems.rsn_ie_len,
&data) == 0 &&
data.num_pmkid == 1 && data.pmkid &&
os_memcmp(sm->cur_pmksa->pmkid, data.pmkid, PMKID_LEN) == 0) {
wpa_printf(MSG_DEBUG, "OWE: Use PMKSA caching");
wpa_sm_set_pmk_from_pmksa(sm);
return 0;
}
if (!elems.owe_dh) {
wpa_printf(MSG_INFO,
"OWE: No Diffie-Hellman Parameter element found in Association Response frame");
return -1;
}
group = WPA_GET_LE16(elems.owe_dh);
if (group != sm->owe_group) {
wpa_printf(MSG_INFO,
"OWE: Unexpected Diffie-Hellman group in response: %u",
group);
return -1;
}
if (!sm->owe_ecdh) {
wpa_printf(MSG_INFO, "OWE: No ECDH state available");
return -1;
}
if (group == 19)
prime_len = 32;
else if (group == 20)
prime_len = 48;
else if (group == 21)
prime_len = 66;
else
return -1;
secret = crypto_ecdh_set_peerkey(sm->owe_ecdh, 0,
elems.owe_dh + 2,
elems.owe_dh_len - 2);
secret = wpabuf_zeropad(secret, prime_len);
if (!secret) {
wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key");
return -1;
}
wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret);
/* prk = HKDF-extract(C | A | group, z) */
pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0);
if (!pub) {
wpabuf_clear_free(secret);
return -1;
}
/* PMKID = Truncate-128(Hash(C | A)) */
addr[0] = wpabuf_head(pub);
len[0] = wpabuf_len(pub);
addr[1] = elems.owe_dh + 2;
len[1] = elems.owe_dh_len - 2;
if (group == 19) {
res = sha256_vector(2, addr, len, pmkid);
hash_len = SHA256_MAC_LEN;
} else if (group == 20) {
res = sha384_vector(2, addr, len, pmkid);
hash_len = SHA384_MAC_LEN;
} else if (group == 21) {
res = sha512_vector(2, addr, len, pmkid);
hash_len = SHA512_MAC_LEN;
} else {
res = -1;
hash_len = 0;
}
pub = wpabuf_zeropad(pub, prime_len);
if (res < 0 || !pub) {
wpabuf_free(pub);
wpabuf_clear_free(secret);
return -1;
}
hkey = wpabuf_alloc(wpabuf_len(pub) + elems.owe_dh_len - 2 + 2);
if (!hkey) {
wpabuf_free(pub);
wpabuf_clear_free(secret);
return -1;
}
wpabuf_put_buf(hkey, pub); /* C */
wpabuf_free(pub);
wpabuf_put_data(hkey, elems.owe_dh + 2, elems.owe_dh_len - 2); /* A */
wpabuf_put_le16(hkey, sm->owe_group); /* group */
if (group == 19)
res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey),
wpabuf_head(secret), wpabuf_len(secret), prk);
else if (group == 20)
res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey),
wpabuf_head(secret), wpabuf_len(secret), prk);
else if (group == 21)
res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey),
wpabuf_head(secret), wpabuf_len(secret), prk);
wpabuf_clear_free(hkey);
wpabuf_clear_free(secret);
if (res < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len);
/* PMK = HKDF-expand(prk, "OWE Key Generation", n) */
if (group == 19)
res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info,
os_strlen(info), sm->pmk, hash_len);
else if (group == 20)
res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info,
os_strlen(info), sm->pmk, hash_len);
else if (group == 21)
res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info,
os_strlen(info), sm->pmk, hash_len);
forced_memzero(prk, SHA512_MAC_LEN);
if (res < 0) {
sm->pmk_len = 0;
return -1;
}
sm->pmk_len = hash_len;
wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, sm->pmk_len);
wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN);
pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, pmkid, NULL, 0,
bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt,
NULL);
return 0;
}
#endif /* CONFIG_OWE */
void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id)
{
#ifdef CONFIG_FILS
if (sm && fils_cache_id) {
sm->fils_cache_id_set = 1;
os_memcpy(sm->fils_cache_id, fils_cache_id, FILS_CACHE_ID_LEN);
}
#endif /* CONFIG_FILS */
}
#ifdef CONFIG_DPP2
void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z)
{
if (sm) {
wpabuf_clear_free(sm->dpp_z);
sm->dpp_z = z ? wpabuf_dup(z) : NULL;
}
}
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_PASN
void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *bssid, int key_mgmt)
{
sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
bssid, sm->own_addr, NULL,
key_mgmt, 0);
}
#endif /* CONFIG_PASN */
+
+
+void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm)
+{
+ if (sm)
+ pmksa_cache_reconfig(sm->pmksa);
+}
diff --git a/contrib/wpa/src/rsn_supp/wpa.h b/contrib/wpa/src/rsn_supp/wpa.h
index ff8a85b6e29b..41daaae2cf72 100644
--- a/contrib/wpa/src/rsn_supp/wpa.h
+++ b/contrib/wpa/src/rsn_supp/wpa.h
@@ -1,557 +1,563 @@
/*
* wpa_supplicant - WPA definitions
* Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPA_H
#define WPA_H
#include "common/defs.h"
#include "common/eapol_common.h"
#include "common/wpa_common.h"
#include "common/ieee802_11_defs.h"
struct wpa_sm;
struct eapol_sm;
struct wpa_config_blob;
struct hostapd_freq_params;
struct wpa_channel_info;
struct wpa_sm_ctx {
void *ctx; /* pointer to arbitrary upper level context */
void *msg_ctx; /* upper level context for wpa_msg() calls */
void (*set_state)(void *ctx, enum wpa_states state);
enum wpa_states (*get_state)(void *ctx);
void (*deauthenticate)(void * ctx, u16 reason_code);
void (*reconnect)(void *ctx);
int (*set_key)(void *ctx, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len, enum key_flag key_flag);
void * (*get_network_ctx)(void *ctx);
int (*get_bssid)(void *ctx, u8 *bssid);
int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
size_t len);
int (*get_beacon_ie)(void *ctx);
void (*cancel_auth_timeout)(void *ctx);
u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len,
size_t *msg_len, void **data_pos);
int (*add_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
const u8 *pmkid, const u8 *fils_cache_id,
const u8 *pmk, size_t pmk_len, u32 pmk_lifetime,
u8 pmk_reauth_threshold, int akmp);
int (*remove_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
const u8 *pmkid, const u8 *fils_cache_id);
void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
const struct wpa_config_blob * (*get_config_blob)(void *ctx,
const char *name);
int (*mlme_setprotection)(void *ctx, const u8 *addr,
int protection_type, int key_type);
int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies,
size_t ies_len);
int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap,
const u8 *ies, size_t ies_len);
int (*mark_authenticated)(void *ctx, const u8 *target_ap);
#ifdef CONFIG_TDLS
int (*tdls_get_capa)(void *ctx, int *tdls_supported,
int *tdls_ext_setup, int *tdls_chan_switch);
int (*send_tdls_mgmt)(void *ctx, const u8 *dst,
u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capab,
int initiator, const u8 *buf, size_t len);
int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid,
u16 capability, const u8 *supp_rates,
size_t supp_rates_len,
const struct ieee80211_ht_capabilities *ht_capab,
const struct ieee80211_vht_capabilities *vht_capab,
const struct ieee80211_he_capabilities *he_capab,
size_t he_capab_len,
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u8 qosinfo, int wmm, const u8 *ext_capab,
size_t ext_capab_len, const u8 *supp_channels,
size_t supp_channels_len,
const u8 *supp_oper_classes,
size_t supp_oper_classes_len);
int (*tdls_enable_channel_switch)(
void *ctx, const u8 *addr, u8 oper_class,
const struct hostapd_freq_params *params);
int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr);
#endif /* CONFIG_TDLS */
void (*set_rekey_offload)(void *ctx, const u8 *kek, size_t kek_len,
const u8 *kck, size_t kck_len,
const u8 *replay_ctr);
int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len);
void (*fils_hlp_rx)(void *ctx, const u8 *dst, const u8 *src,
const u8 *pkt, size_t pkt_len);
int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
void (*transition_disable)(void *ctx, u8 bitmap);
void (*store_ptk)(void *ctx, u8 *addr, int cipher,
u32 life_time, const struct wpa_ptk *ptk);
};
enum wpa_sm_conf_params {
RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */,
RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */,
RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */,
WPA_PARAM_PROTO,
WPA_PARAM_PAIRWISE,
WPA_PARAM_GROUP,
WPA_PARAM_KEY_MGMT,
WPA_PARAM_MGMT_GROUP,
WPA_PARAM_RSN_ENABLED,
WPA_PARAM_MFP,
WPA_PARAM_OCV,
WPA_PARAM_SAE_PWE,
WPA_PARAM_SAE_PK,
WPA_PARAM_DENY_PTK0_REKEY,
WPA_PARAM_EXT_KEY_ID,
WPA_PARAM_USE_EXT_KEY_ID,
WPA_PARAM_FT_RSNXE_USED,
WPA_PARAM_DPP_PFS,
WPA_PARAM_OCI_FREQ_EAPOL,
WPA_PARAM_OCI_FREQ_EAPOL_G2,
WPA_PARAM_OCI_FREQ_FT_ASSOC,
WPA_PARAM_OCI_FREQ_FILS_ASSOC,
};
struct rsn_supp_config {
void *network_ctx;
int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
int proactive_key_caching;
int eap_workaround;
void *eap_conf_ctx;
const u8 *ssid;
size_t ssid_len;
int wpa_ptk_rekey;
int wpa_deny_ptk0_rekey;
int p2p;
int wpa_rsc_relaxation;
int owe_ptk_workaround;
const u8 *fils_cache_id;
int beacon_prot;
bool force_kdk_derivation;
};
#ifndef CONFIG_NO_WPA
struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx);
void wpa_sm_deinit(struct wpa_sm *sm);
void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid);
void wpa_sm_notify_disassoc(struct wpa_sm *sm);
void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *bssid);
void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm);
void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config);
void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr);
void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
const char *bridge_ifname);
void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol);
int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
size_t *wpa_ie_len);
int wpa_sm_set_assoc_rsnxe_default(struct wpa_sm *sm, u8 *rsnxe,
size_t *rsnxe_len);
int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len);
int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len);
int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen);
int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
unsigned int value);
int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
int verbose);
int wpa_sm_pmf_enabled(struct wpa_sm *sm);
int wpa_sm_ext_key_id(struct wpa_sm *sm);
int wpa_sm_ext_key_id_active(struct wpa_sm *sm);
int wpa_sm_ocv_enabled(struct wpa_sm *sm);
void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise);
int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ie_data *data);
void wpa_sm_aborted_cached(struct wpa_sm *sm);
void wpa_sm_aborted_external_cached(struct wpa_sm *sm);
int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len);
int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm);
struct rsn_pmksa_cache_entry *
wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
struct rsn_pmksa_cache_entry * entry);
void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *bssid,
const u8 *fils_cache_id);
int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid,
const void *network_ctx);
void wpa_sm_drop_sa(struct wpa_sm *sm);
struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_get(struct wpa_sm *sm,
const u8 *aa,
const u8 *pmkid,
const void *network_ctx,
int akmp);
int wpa_sm_has_ptk(struct wpa_sm *sm);
int wpa_sm_has_ptk_installed(struct wpa_sm *sm);
void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter);
void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
const u8 *ptk_kck, size_t ptk_kck_len,
const u8 *ptk_kek, size_t ptk_kek_len);
int wpa_fils_is_completed(struct wpa_sm *sm);
+void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm);
#else /* CONFIG_NO_WPA */
static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
{
return (struct wpa_sm *) 1;
}
static inline void wpa_sm_deinit(struct wpa_sm *sm)
{
}
static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
{
}
static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm)
{
}
static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk,
size_t pmk_len, const u8 *pmkid,
const u8 *bssid)
{
}
static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
{
}
static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
{
}
static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
{
}
static inline void wpa_sm_set_config(struct wpa_sm *sm,
struct rsn_supp_config *config)
{
}
static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
{
}
static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
const char *bridge_ifname)
{
}
static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
{
}
static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie,
size_t len)
{
return -1;
}
static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm,
u8 *wpa_ie,
size_t *wpa_ie_len)
{
return -1;
}
static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie,
size_t len)
{
return -1;
}
static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie,
size_t len)
{
return -1;
}
static inline int wpa_sm_set_ap_rsnxe(struct wpa_sm *sm, const u8 *ie,
size_t len)
{
return -1;
}
static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
{
return 0;
}
static inline int wpa_sm_set_param(struct wpa_sm *sm,
enum wpa_sm_conf_params param,
unsigned int value)
{
return -1;
}
static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf,
size_t buflen, int verbose)
{
return 0;
}
static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm)
{
return 0;
}
static inline int wpa_sm_ext_key_id(struct wpa_sm *sm)
{
return 0;
}
static inline int wpa_sm_ext_key_id_active(struct wpa_sm *sm)
{
return 0;
}
static inline int wpa_sm_ocv_enabled(struct wpa_sm *sm)
{
return 0;
}
static inline void wpa_sm_key_request(struct wpa_sm *sm, int error,
int pairwise)
{
}
static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ie_data *data)
{
return -1;
}
static inline void wpa_sm_aborted_cached(struct wpa_sm *sm)
{
}
static inline void wpa_sm_aborted_external_cached(struct wpa_sm *sm)
{
}
static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len)
{
return -1;
}
static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm,
struct wpa_ie_data *data)
{
return -1;
}
static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf,
size_t len)
{
return -1;
}
static inline void wpa_sm_drop_sa(struct wpa_sm *sm)
{
}
static inline struct rsn_pmksa_cache_entry *
wpa_sm_pmksa_cache_get(struct wpa_sm *sm, const u8 *aa, const u8 *pmkid,
const void *network_ctx, int akmp)
{
return NULL;
}
static inline int wpa_sm_has_ptk(struct wpa_sm *sm)
{
return 0;
}
static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm,
const u8 *replay_ctr)
{
}
static inline void wpa_sm_external_pmksa_cache_flush(struct wpa_sm *sm,
void *network_ctx)
{
}
static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm,
void *network_ctx)
{
}
static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm,
const u8 *rx_replay_counter)
{
}
static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
size_t ptk_kck_len,
const u8 *ptk_kek, size_t ptk_kek_len)
{
}
static inline int wpa_fils_is_completed(struct wpa_sm *sm)
{
return 0;
}
+static inline void wpa_sm_pmksa_cache_reconfig(struct wpa_sm *sm)
+{
+}
+
#endif /* CONFIG_NO_WPA */
#ifdef CONFIG_IEEE80211R
int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len);
int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie);
int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len,
const u8 *mdie);
const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm);
int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
int ft_action, const u8 *target_ap,
const u8 *ric_ies, size_t ric_ies_len);
int wpa_ft_is_completed(struct wpa_sm *sm);
void wpa_reset_ft_completed(struct wpa_sm *sm);
int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
size_t ies_len, const u8 *src_addr);
int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
const u8 *mdie);
#ifdef CONFIG_PASN
int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name);
#endif /* CONFIG_PASN */
#else /* CONFIG_IEEE80211R */
static inline int
wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
{
return 0;
}
static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm,
const u8 *mdie)
{
return 0;
}
static inline int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len,
const u8 *mdie)
{
return 0;
}
static inline int
wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
int ft_action, const u8 *target_ap)
{
return 0;
}
static inline int wpa_ft_is_completed(struct wpa_sm *sm)
{
return 0;
}
static inline void wpa_reset_ft_completed(struct wpa_sm *sm)
{
}
static inline int
wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
const u8 *src_addr)
{
return -1;
}
#ifdef CONFIG_PASN
int wpa_pasn_ft_derive_pmk_r1(struct wpa_sm *sm, int akmp, const u8 *r1kh_id,
u8 *pmk_r1, size_t *pmk_r1_len, u8 *pmk_r1_name)
{
return -1;
}
#endif /* CONFIG_PASN */
#endif /* CONFIG_IEEE80211R */
/* tdls.c */
void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr);
void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr);
int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr);
int wpa_tdls_init(struct wpa_sm *sm);
void wpa_tdls_teardown_peers(struct wpa_sm *sm);
void wpa_tdls_deinit(struct wpa_sm *sm);
void wpa_tdls_enable(struct wpa_sm *sm, int enabled);
void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr);
const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr);
int wpa_tdls_is_external_setup(struct wpa_sm *sm);
int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
u8 oper_class,
struct hostapd_freq_params *freq_params);
int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr);
#ifdef CONFIG_TDLS_TESTING
extern unsigned int tdls_testing;
#endif /* CONFIG_TDLS_TESTING */
int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf);
const u8 * wpa_sm_get_anonce(struct wpa_sm *sm);
unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm);
struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md);
int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
size_t len);
struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
size_t *kek_len, const u8 **snonce,
const u8 **anonce,
const struct wpabuf **hlp,
unsigned int num_hlp);
int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len);
struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group);
int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
const u8 *resp_ies, size_t resp_ies_len);
void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set);
void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id);
void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z);
void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *bssid, int key_mgmt);
#endif /* WPA_H */
diff --git a/contrib/wpa/src/rsn_supp/wpa_i.h b/contrib/wpa/src/rsn_supp/wpa_i.h
index e7281bf3b1d8..6cdce321da3b 100644
--- a/contrib/wpa/src/rsn_supp/wpa_i.h
+++ b/contrib/wpa/src/rsn_supp/wpa_i.h
@@ -1,502 +1,503 @@
/*
* Internal WPA/RSN supplicant state machine definitions
* Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPA_I_H
#define WPA_I_H
#include "utils/list.h"
struct wpa_tdls_peer;
struct wpa_eapol_key;
struct pasn_ft_r1kh {
u8 bssid[ETH_ALEN];
u8 r1kh_id[FT_R1KH_ID_LEN];
};
/**
* struct wpa_sm - Internal WPA state machine data
*/
struct wpa_sm {
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
struct wpa_ptk ptk, tptk;
int ptk_set, tptk_set;
unsigned int msg_3_of_4_ok:1;
u8 snonce[WPA_NONCE_LEN];
u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
int renew_snonce;
u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
int rx_replay_counter_set;
u8 request_counter[WPA_REPLAY_COUNTER_LEN];
struct wpa_gtk gtk;
struct wpa_gtk gtk_wnm_sleep;
struct wpa_igtk igtk;
struct wpa_igtk igtk_wnm_sleep;
struct wpa_bigtk bigtk;
struct wpa_bigtk bigtk_wnm_sleep;
struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
struct rsn_pmksa_cache *pmksa; /* PMKSA cache */
struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */
struct dl_list pmksa_candidates;
struct l2_packet_data *l2_preauth;
struct l2_packet_data *l2_preauth_br;
struct l2_packet_data *l2_tdls;
u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or
* 00:00:00:00:00:00 if no pre-auth is
* in progress */
struct eapol_sm *preauth_eapol;
struct wpa_sm_ctx *ctx;
void *scard_ctx; /* context for smartcard callbacks */
int fast_reauth; /* whether EAP fast re-authentication is enabled */
void *network_ctx;
int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
int proactive_key_caching;
int eap_workaround;
void *eap_conf_ctx;
u8 ssid[32];
size_t ssid_len;
int wpa_ptk_rekey;
int wpa_deny_ptk0_rekey:1;
int p2p;
int wpa_rsc_relaxation;
int owe_ptk_workaround;
int beacon_prot;
int ext_key_id; /* whether Extended Key ID is enabled */
int use_ext_key_id; /* whether Extended Key ID has been detected
* to be used */
int keyidx_active; /* Key ID for the active TK */
/*
* If set Key Derivation Key should be derived as part of PMK to
* PTK derivation regardless of advertised capabilities.
*/
bool force_kdk_derivation;
u8 own_addr[ETH_ALEN];
const char *ifname;
const char *bridge_ifname;
u8 bssid[ETH_ALEN];
unsigned int dot11RSNAConfigPMKLifetime;
unsigned int dot11RSNAConfigPMKReauthThreshold;
unsigned int dot11RSNAConfigSATimeout;
unsigned int dot11RSNA4WayHandshakeFailures;
/* Selected configuration (based on Beacon/ProbeResp WPA IE) */
unsigned int proto;
unsigned int pairwise_cipher;
unsigned int group_cipher;
unsigned int key_mgmt;
unsigned int mgmt_group_cipher;
int rsn_enabled; /* Whether RSN is enabled in configuration */
int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
int ocv; /* Operating Channel Validation */
int sae_pwe; /* SAE PWE generation options */
unsigned int sae_pk:1; /* whether SAE-PK is used */
unsigned int secure_ltf:1;
unsigned int secure_rtt:1;
unsigned int prot_range_neg:1;
u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
size_t assoc_wpa_ie_len;
u8 *assoc_rsnxe; /* Own RSNXE from (Re)AssocReq */
size_t assoc_rsnxe_len;
u8 *ap_wpa_ie, *ap_rsn_ie, *ap_rsnxe;
size_t ap_wpa_ie_len, ap_rsn_ie_len, ap_rsnxe_len;
#ifdef CONFIG_TDLS
struct wpa_tdls_peer *tdls;
int tdls_prohibited;
int tdls_chan_switch_prohibited;
int tdls_disabled;
/* The driver supports TDLS */
int tdls_supported;
/*
* The driver requires explicit discovery/setup/teardown frames sent
* to it via tdls_mgmt.
*/
int tdls_external_setup;
/* The driver supports TDLS channel switching */
int tdls_chan_switch;
#endif /* CONFIG_TDLS */
#ifdef CONFIG_IEEE80211R
u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
* first 384 bits of MSK */
size_t xxkey_len;
u8 pmk_r0[PMK_LEN_MAX];
size_t pmk_r0_len;
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
u8 pmk_r1[PMK_LEN_MAX];
size_t pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
size_t r0kh_id_len;
u8 r1kh_id[FT_R1KH_ID_LEN];
unsigned int ft_completed:1;
unsigned int ft_reassoc_completed:1;
unsigned int ft_protocol:1;
int over_the_ds_in_progress;
u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */
int set_ptk_after_assoc;
u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */
u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
size_t assoc_resp_ies_len;
#ifdef CONFIG_PASN
/*
* Currently, the WPA state machine stores the PMK-R1, PMK-R1-Name and
* R1KH-ID only for the current association. As PMK-R1 is required to
* perform PASN authentication with FT, store the R1KH-ID for previous
* associations, which would later be used to derive the PMK-R1 as part
* of the PASN authentication flow.
*/
struct pasn_ft_r1kh *pasn_r1kh;
unsigned int n_pasn_r1kh;
#endif /* CONFIG_PASN */
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_P2P
u8 p2p_ip_addr[3 * 4];
#endif /* CONFIG_P2P */
#ifdef CONFIG_TESTING_OPTIONS
struct wpabuf *test_assoc_ie;
int ft_rsnxe_used;
unsigned int oci_freq_override_eapol;
unsigned int oci_freq_override_eapol_g2;
unsigned int oci_freq_override_ft_assoc;
unsigned int oci_freq_override_fils_assoc;
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_FILS
u8 fils_nonce[FILS_NONCE_LEN];
u8 fils_session[FILS_SESSION_LEN];
u8 fils_anonce[FILS_NONCE_LEN];
u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
size_t fils_key_auth_len;
unsigned int fils_completed:1;
unsigned int fils_erp_pmkid_set:1;
unsigned int fils_cache_id_set:1;
u8 fils_erp_pmkid[PMKID_LEN];
u8 fils_cache_id[FILS_CACHE_ID_LEN];
struct crypto_ecdh *fils_ecdh;
int fils_dh_group;
size_t fils_dh_elem_len;
struct wpabuf *fils_ft_ies;
u8 fils_ft[FILS_FT_MAX_LEN];
size_t fils_ft_len;
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
struct crypto_ecdh *owe_ecdh;
u16 owe_group;
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
struct wpabuf *dpp_z;
int dpp_pfs;
#endif /* CONFIG_DPP2 */
};
static inline void wpa_sm_set_state(struct wpa_sm *sm, enum wpa_states state)
{
WPA_ASSERT(sm->ctx->set_state);
sm->ctx->set_state(sm->ctx->ctx, state);
}
static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm)
{
WPA_ASSERT(sm->ctx->get_state);
return sm->ctx->get_state(sm->ctx->ctx);
}
static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, u16 reason_code)
{
WPA_ASSERT(sm->ctx->deauthenticate);
sm->ctx->deauthenticate(sm->ctx->ctx, reason_code);
}
static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len,
enum key_flag key_flag)
{
WPA_ASSERT(sm->ctx->set_key);
return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx,
seq, seq_len, key, key_len, key_flag);
}
static inline void wpa_sm_reconnect(struct wpa_sm *sm)
{
WPA_ASSERT(sm->ctx->reconnect);
sm->ctx->reconnect(sm->ctx->ctx);
}
static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm)
{
WPA_ASSERT(sm->ctx->get_network_ctx);
return sm->ctx->get_network_ctx(sm->ctx->ctx);
}
static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid)
{
WPA_ASSERT(sm->ctx->get_bssid);
return sm->ctx->get_bssid(sm->ctx->ctx, bssid);
}
static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest,
u16 proto, const u8 *buf, size_t len)
{
WPA_ASSERT(sm->ctx->ether_send);
return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len);
}
static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm)
{
WPA_ASSERT(sm->ctx->get_beacon_ie);
return sm->ctx->get_beacon_ie(sm->ctx->ctx);
}
static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm)
{
WPA_ASSERT(sm->ctx->cancel_auth_timeout);
sm->ctx->cancel_auth_timeout(sm->ctx->ctx);
}
static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type,
const void *data, u16 data_len,
size_t *msg_len, void **data_pos)
{
WPA_ASSERT(sm->ctx->alloc_eapol);
return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len,
msg_len, data_pos);
}
static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, void *network_ctx,
const u8 *bssid, const u8 *pmkid,
const u8 *cache_id, const u8 *pmk,
size_t pmk_len, u32 pmk_lifetime,
u8 pmk_reauth_threshold, int akmp)
{
WPA_ASSERT(sm->ctx->add_pmkid);
return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid,
cache_id, pmk, pmk_len, pmk_lifetime,
pmk_reauth_threshold, akmp);
}
static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, void *network_ctx,
const u8 *bssid, const u8 *pmkid,
const u8 *cache_id)
{
WPA_ASSERT(sm->ctx->remove_pmkid);
return sm->ctx->remove_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid,
cache_id);
}
static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr,
int protect_type, int key_type)
{
WPA_ASSERT(sm->ctx->mlme_setprotection);
return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type,
key_type);
}
static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md,
const u8 *ies, size_t ies_len)
{
if (sm->ctx->update_ft_ies)
return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len);
return -1;
}
static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action,
const u8 *target_ap,
const u8 *ies, size_t ies_len)
{
if (sm->ctx->send_ft_action)
return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap,
ies, ies_len);
return -1;
}
static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm,
const u8 *target_ap)
{
if (sm->ctx->mark_authenticated)
return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap);
return -1;
}
static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm)
{
if (!sm->ctx->set_rekey_offload)
return;
sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, sm->ptk.kek_len,
sm->ptk.kck, sm->ptk.kck_len,
sm->rx_replay_counter);
}
#ifdef CONFIG_TDLS
static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm,
int *tdls_supported,
int *tdls_ext_setup,
int *tdls_chan_switch)
{
if (sm->ctx->tdls_get_capa)
return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported,
tdls_ext_setup, tdls_chan_switch);
return -1;
}
static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capab,
int initiator, const u8 *buf,
size_t len)
{
if (sm->ctx->send_tdls_mgmt)
return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code,
dialog_token, status_code,
peer_capab, initiator, buf,
len);
return -1;
}
static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper,
const u8 *peer)
{
if (sm->ctx->tdls_oper)
return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer);
return -1;
}
static inline int
wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add,
u16 aid, u16 capability, const u8 *supp_rates,
size_t supp_rates_len,
const struct ieee80211_ht_capabilities *ht_capab,
const struct ieee80211_vht_capabilities *vht_capab,
const struct ieee80211_he_capabilities *he_capab,
size_t he_capab_len,
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_capab,
u8 qosinfo, int wmm, const u8 *ext_capab,
size_t ext_capab_len, const u8 *supp_channels,
size_t supp_channels_len, const u8 *supp_oper_classes,
size_t supp_oper_classes_len)
{
if (sm->ctx->tdls_peer_addset)
return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add,
aid, capability, supp_rates,
supp_rates_len, ht_capab,
vht_capab,
he_capab, he_capab_len,
- qosinfo, wmm,
+ he_6ghz_capab, qosinfo, wmm,
ext_capab, ext_capab_len,
supp_channels,
supp_channels_len,
supp_oper_classes,
supp_oper_classes_len);
return -1;
}
static inline int
wpa_sm_tdls_enable_channel_switch(struct wpa_sm *sm, const u8 *addr,
u8 oper_class,
const struct hostapd_freq_params *freq_params)
{
if (sm->ctx->tdls_enable_channel_switch)
return sm->ctx->tdls_enable_channel_switch(sm->ctx->ctx, addr,
oper_class,
freq_params);
return -1;
}
static inline int
wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr)
{
if (sm->ctx->tdls_disable_channel_switch)
return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr);
return -1;
}
#endif /* CONFIG_TDLS */
static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm,
const u8 *pmk, size_t pmk_len)
{
if (!sm->ctx->key_mgmt_set_pmk)
return -1;
return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
}
static inline void wpa_sm_fils_hlp_rx(struct wpa_sm *sm,
const u8 *dst, const u8 *src,
const u8 *pkt, size_t pkt_len)
{
if (sm->ctx->fils_hlp_rx)
sm->ctx->fils_hlp_rx(sm->ctx->ctx, dst, src, pkt, pkt_len);
}
static inline int wpa_sm_channel_info(struct wpa_sm *sm,
struct wpa_channel_info *ci)
{
if (!sm->ctx->channel_info)
return -1;
return sm->ctx->channel_info(sm->ctx->ctx, ci);
}
static inline void wpa_sm_transition_disable(struct wpa_sm *sm, u8 bitmap)
{
if (sm->ctx->transition_disable)
sm->ctx->transition_disable(sm->ctx->ctx, bitmap);
}
static inline void wpa_sm_store_ptk(struct wpa_sm *sm,
u8 *addr, int cipher,
u32 life_time, struct wpa_ptk *ptk)
{
if (sm->ctx->store_ptk)
sm->ctx->store_ptk(sm->ctx->ctx, addr, cipher, life_time,
ptk);
}
int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
int ver, const u8 *dest, u16 proto,
u8 *msg, size_t msg_len, u8 *key_mic);
int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
const struct wpa_eapol_key *key,
int ver, const u8 *nonce,
const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ptk *ptk);
int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
const struct wpa_eapol_key *key,
u16 ver, u16 key_info,
struct wpa_ptk *ptk);
int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key, struct wpa_ptk *ptk);
void wpa_tdls_assoc(struct wpa_sm *sm);
void wpa_tdls_disassoc(struct wpa_sm *sm);
#endif /* WPA_I_H */
diff --git a/contrib/wpa/src/utils/config.c b/contrib/wpa/src/utils/config.c
index 22aa2216eb3f..ba26c2c9a71c 100644
--- a/contrib/wpa/src/utils/config.c
+++ b/contrib/wpa/src/utils/config.c
@@ -1,97 +1,105 @@
/*
* Configuration parsing
* Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "utils/config.h"
#include "common.h"
static int newline_terminated(const char *buf, size_t buflen)
{
size_t len = os_strlen(buf);
if (len == 0)
return 0;
if (len == buflen - 1 && buf[buflen - 1] != '\r' &&
buf[len - 1] != '\n')
return 0;
return 1;
}
static void skip_line_end(FILE *stream)
{
char buf[100];
while (fgets(buf, sizeof(buf), stream)) {
buf[sizeof(buf) - 1] = '\0';
if (newline_terminated(buf, sizeof(buf)))
return;
}
}
char * wpa_config_get_line(char *s, int size, FILE *stream, int *line,
char **_pos)
{
char *pos, *end, *sstart;
while (fgets(s, size, stream)) {
(*line)++;
s[size - 1] = '\0';
if (!newline_terminated(s, size)) {
/*
* The line was truncated - skip rest of it to avoid
* confusing error messages.
*/
wpa_printf(MSG_INFO, "Long line in configuration file "
"truncated");
skip_line_end(stream);
}
pos = s;
/* Skip white space from the beginning of line. */
while (*pos == ' ' || *pos == '\t' || *pos == '\r')
pos++;
/* Skip comment lines and empty lines */
if (*pos == '#' || *pos == '\n' || *pos == '\0')
continue;
/*
* Remove # comments unless they are within a double quoted
* string.
*/
- sstart = os_strchr(pos, '"');
- if (sstart)
- sstart = os_strrchr(sstart + 1, '"');
- if (!sstart)
- sstart = pos;
+ sstart = pos;
end = os_strchr(sstart, '#');
+ while (end) {
+ sstart = os_strchr(sstart, '"');
+ if (!sstart || sstart > end)
+ break;
+ sstart = os_strchr(sstart + 1, '"');
+ if (!sstart)
+ break;
+ sstart++;
+ if (sstart > end)
+ end = os_strchr(sstart, '#');
+ }
+
if (end)
*end-- = '\0';
else
end = pos + os_strlen(pos) - 1;
/* Remove trailing white space. */
while (end > pos &&
(*end == '\n' || *end == ' ' || *end == '\t' ||
*end == '\r'))
*end-- = '\0';
if (*pos == '\0')
continue;
if (_pos)
*_pos = pos;
return pos;
}
if (_pos)
*_pos = NULL;
return NULL;
}
diff --git a/contrib/wpa/src/utils/eloop.c b/contrib/wpa/src/utils/eloop.c
index b353ab0e467f..00b0beff0b78 100644
--- a/contrib/wpa/src/utils/eloop.c
+++ b/contrib/wpa/src/utils/eloop.c
@@ -1,1354 +1,1359 @@
/*
* Event loop based on select() loop
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include <assert.h>
#include "common.h"
#include "trace.h"
#include "list.h"
#include "eloop.h"
#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL)
#error Do not define both of poll and epoll
#endif
#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_KQUEUE)
#error Do not define both of poll and kqueue
#endif
#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL) && \
!defined(CONFIG_ELOOP_KQUEUE)
#define CONFIG_ELOOP_SELECT
#endif
#ifdef CONFIG_ELOOP_POLL
#include <poll.h>
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_EPOLL
#include <sys/epoll.h>
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
#include <sys/event.h>
#endif /* CONFIG_ELOOP_KQUEUE */
struct eloop_sock {
int sock;
void *eloop_data;
void *user_data;
eloop_sock_handler handler;
WPA_TRACE_REF(eloop);
WPA_TRACE_REF(user);
WPA_TRACE_INFO
};
struct eloop_timeout {
struct dl_list list;
struct os_reltime time;
void *eloop_data;
void *user_data;
eloop_timeout_handler handler;
WPA_TRACE_REF(eloop);
WPA_TRACE_REF(user);
WPA_TRACE_INFO
};
struct eloop_signal {
int sig;
void *user_data;
eloop_signal_handler handler;
int signaled;
};
struct eloop_sock_table {
size_t count;
struct eloop_sock *table;
eloop_event_type type;
int changed;
};
struct eloop_data {
int max_sock;
size_t count; /* sum of all table counts */
#ifdef CONFIG_ELOOP_POLL
size_t max_pollfd_map; /* number of pollfds_map currently allocated */
size_t max_poll_fds; /* number of pollfds currently allocated */
struct pollfd *pollfds;
struct pollfd **pollfds_map;
#endif /* CONFIG_ELOOP_POLL */
#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
int max_fd;
struct eloop_sock *fd_table;
#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
#ifdef CONFIG_ELOOP_EPOLL
int epollfd;
size_t epoll_max_event_num;
struct epoll_event *epoll_events;
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
int kqueuefd;
size_t kqueue_nevents;
struct kevent *kqueue_events;
#endif /* CONFIG_ELOOP_KQUEUE */
struct eloop_sock_table readers;
struct eloop_sock_table writers;
struct eloop_sock_table exceptions;
struct dl_list timeout;
size_t signal_count;
struct eloop_signal *signals;
int signaled;
int pending_terminate;
int terminate;
};
static struct eloop_data eloop;
#ifdef WPA_TRACE
static void eloop_sigsegv_handler(int sig)
{
wpa_trace_show("eloop SIGSEGV");
abort();
}
static void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
{
size_t i;
if (table == NULL || table->table == NULL)
return;
for (i = 0; i < table->count; i++) {
wpa_trace_add_ref(&table->table[i], eloop,
table->table[i].eloop_data);
wpa_trace_add_ref(&table->table[i], user,
table->table[i].user_data);
}
}
static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
{
size_t i;
if (table == NULL || table->table == NULL)
return;
for (i = 0; i < table->count; i++) {
wpa_trace_remove_ref(&table->table[i], eloop,
table->table[i].eloop_data);
wpa_trace_remove_ref(&table->table[i], user,
table->table[i].user_data);
}
}
#else /* WPA_TRACE */
#define eloop_trace_sock_add_ref(table) do { } while (0)
#define eloop_trace_sock_remove_ref(table) do { } while (0)
#endif /* WPA_TRACE */
int eloop_init(void)
{
os_memset(&eloop, 0, sizeof(eloop));
dl_list_init(&eloop.timeout);
#ifdef CONFIG_ELOOP_EPOLL
eloop.epollfd = epoll_create1(0);
if (eloop.epollfd < 0) {
wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s",
__func__, strerror(errno));
return -1;
}
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
eloop.kqueuefd = kqueue();
if (eloop.kqueuefd < 0) {
wpa_printf(MSG_ERROR, "%s: kqueue failed: %s",
__func__, strerror(errno));
return -1;
}
#endif /* CONFIG_ELOOP_KQUEUE */
#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
eloop.readers.type = EVENT_TYPE_READ;
eloop.writers.type = EVENT_TYPE_WRITE;
eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
#ifdef WPA_TRACE
signal(SIGSEGV, eloop_sigsegv_handler);
#endif /* WPA_TRACE */
return 0;
}
#ifdef CONFIG_ELOOP_EPOLL
static int eloop_sock_queue(int sock, eloop_event_type type)
{
struct epoll_event ev;
os_memset(&ev, 0, sizeof(ev));
switch (type) {
case EVENT_TYPE_READ:
ev.events = EPOLLIN;
break;
case EVENT_TYPE_WRITE:
ev.events = EPOLLOUT;
break;
/*
* Exceptions are always checked when using epoll, but I suppose it's
* possible that someone registered a socket *only* for exception
* handling.
*/
case EVENT_TYPE_EXCEPTION:
ev.events = EPOLLERR | EPOLLHUP;
break;
}
ev.data.fd = sock;
if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d failed: %s",
__func__, sock, strerror(errno));
return -1;
}
return 0;
}
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
static short event_type_kevent_filter(eloop_event_type type)
{
switch (type) {
case EVENT_TYPE_READ:
return EVFILT_READ;
case EVENT_TYPE_WRITE:
return EVFILT_WRITE;
default:
return 0;
}
}
static int eloop_sock_queue(int sock, eloop_event_type type)
{
struct kevent ke;
EV_SET(&ke, sock, event_type_kevent_filter(type), EV_ADD, 0, 0, 0);
if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) == -1) {
wpa_printf(MSG_ERROR, "%s: kevent(ADD) for fd=%d failed: %s",
__func__, sock, strerror(errno));
return -1;
}
return 0;
}
#endif /* CONFIG_ELOOP_KQUEUE */
static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
int sock, eloop_sock_handler handler,
void *eloop_data, void *user_data)
{
#ifdef CONFIG_ELOOP_EPOLL
struct epoll_event *temp_events;
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
struct kevent *temp_events;
#endif /* CONFIG_ELOOP_EPOLL */
#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
struct eloop_sock *temp_table;
size_t next;
#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
struct eloop_sock *tmp;
int new_max_sock;
if (sock > eloop.max_sock)
new_max_sock = sock;
else
new_max_sock = eloop.max_sock;
if (table == NULL)
return -1;
#ifdef CONFIG_ELOOP_POLL
if ((size_t) new_max_sock >= eloop.max_pollfd_map) {
struct pollfd **nmap;
nmap = os_realloc_array(eloop.pollfds_map, new_max_sock + 50,
sizeof(struct pollfd *));
if (nmap == NULL)
return -1;
eloop.max_pollfd_map = new_max_sock + 50;
eloop.pollfds_map = nmap;
}
if (eloop.count + 1 > eloop.max_poll_fds) {
struct pollfd *n;
size_t nmax = eloop.count + 1 + 50;
n = os_realloc_array(eloop.pollfds, nmax,
sizeof(struct pollfd));
if (n == NULL)
return -1;
eloop.max_poll_fds = nmax;
eloop.pollfds = n;
}
#endif /* CONFIG_ELOOP_POLL */
#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
if (new_max_sock >= eloop.max_fd) {
next = new_max_sock + 16;
temp_table = os_realloc_array(eloop.fd_table, next,
sizeof(struct eloop_sock));
if (temp_table == NULL)
return -1;
eloop.max_fd = next;
eloop.fd_table = temp_table;
}
#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
#ifdef CONFIG_ELOOP_EPOLL
if (eloop.count + 1 > eloop.epoll_max_event_num) {
next = eloop.epoll_max_event_num == 0 ? 8 :
eloop.epoll_max_event_num * 2;
temp_events = os_realloc_array(eloop.epoll_events, next,
sizeof(struct epoll_event));
if (temp_events == NULL) {
wpa_printf(MSG_ERROR, "%s: malloc for epoll failed: %s",
__func__, strerror(errno));
return -1;
}
eloop.epoll_max_event_num = next;
eloop.epoll_events = temp_events;
}
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
if (eloop.count + 1 > eloop.kqueue_nevents) {
next = eloop.kqueue_nevents == 0 ? 8 : eloop.kqueue_nevents * 2;
temp_events = os_malloc(next * sizeof(*temp_events));
if (!temp_events) {
wpa_printf(MSG_ERROR,
"%s: malloc for kqueue failed: %s",
__func__, strerror(errno));
return -1;
}
os_free(eloop.kqueue_events);
eloop.kqueue_events = temp_events;
eloop.kqueue_nevents = next;
}
#endif /* CONFIG_ELOOP_KQUEUE */
eloop_trace_sock_remove_ref(table);
tmp = os_realloc_array(table->table, table->count + 1,
sizeof(struct eloop_sock));
if (tmp == NULL) {
eloop_trace_sock_add_ref(table);
return -1;
}
tmp[table->count].sock = sock;
tmp[table->count].eloop_data = eloop_data;
tmp[table->count].user_data = user_data;
tmp[table->count].handler = handler;
wpa_trace_record(&tmp[table->count]);
table->count++;
table->table = tmp;
eloop.max_sock = new_max_sock;
eloop.count++;
table->changed = 1;
eloop_trace_sock_add_ref(table);
#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
if (eloop_sock_queue(sock, table->type) < 0)
return -1;
os_memcpy(&eloop.fd_table[sock], &table->table[table->count - 1],
sizeof(struct eloop_sock));
#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
return 0;
}
static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
int sock)
{
#ifdef CONFIG_ELOOP_KQUEUE
struct kevent ke;
#endif /* CONFIG_ELOOP_KQUEUE */
size_t i;
if (table == NULL || table->table == NULL || table->count == 0)
return;
for (i = 0; i < table->count; i++) {
if (table->table[i].sock == sock)
break;
}
if (i == table->count)
return;
eloop_trace_sock_remove_ref(table);
if (i != table->count - 1) {
os_memmove(&table->table[i], &table->table[i + 1],
(table->count - i - 1) *
sizeof(struct eloop_sock));
}
table->count--;
eloop.count--;
table->changed = 1;
eloop_trace_sock_add_ref(table);
#ifdef CONFIG_ELOOP_EPOLL
if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d failed: %s",
__func__, sock, strerror(errno));
return;
}
os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
EV_SET(&ke, sock, event_type_kevent_filter(table->type), EV_DELETE, 0,
0, 0);
if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) < 0) {
wpa_printf(MSG_ERROR, "%s: kevent(DEL) for fd=%d failed: %s",
__func__, sock, strerror(errno));
return;
}
os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
#endif /* CONFIG_ELOOP_KQUEUE */
}
#ifdef CONFIG_ELOOP_POLL
static struct pollfd * find_pollfd(struct pollfd **pollfds_map, int fd, int mx)
{
if (fd < mx && fd >= 0)
return pollfds_map[fd];
return NULL;
}
static int eloop_sock_table_set_fds(struct eloop_sock_table *readers,
struct eloop_sock_table *writers,
struct eloop_sock_table *exceptions,
struct pollfd *pollfds,
struct pollfd **pollfds_map,
int max_pollfd_map)
{
size_t i;
int nxt = 0;
int fd;
struct pollfd *pfd;
/* Clear pollfd lookup map. It will be re-populated below. */
os_memset(pollfds_map, 0, sizeof(struct pollfd *) * max_pollfd_map);
if (readers && readers->table) {
for (i = 0; i < readers->count; i++) {
fd = readers->table[i].sock;
assert(fd >= 0 && fd < max_pollfd_map);
pollfds[nxt].fd = fd;
pollfds[nxt].events = POLLIN;
pollfds[nxt].revents = 0;
pollfds_map[fd] = &(pollfds[nxt]);
nxt++;
}
}
if (writers && writers->table) {
for (i = 0; i < writers->count; i++) {
/*
* See if we already added this descriptor, update it
* if so.
*/
fd = writers->table[i].sock;
assert(fd >= 0 && fd < max_pollfd_map);
pfd = pollfds_map[fd];
if (!pfd) {
pfd = &(pollfds[nxt]);
pfd->events = 0;
pfd->fd = fd;
pollfds[i].revents = 0;
pollfds_map[fd] = pfd;
nxt++;
}
pfd->events |= POLLOUT;
}
}
/*
* Exceptions are always checked when using poll, but I suppose it's
* possible that someone registered a socket *only* for exception
* handling. Set the POLLIN bit in this case.
*/
if (exceptions && exceptions->table) {
for (i = 0; i < exceptions->count; i++) {
/*
* See if we already added this descriptor, just use it
* if so.
*/
fd = exceptions->table[i].sock;
assert(fd >= 0 && fd < max_pollfd_map);
pfd = pollfds_map[fd];
if (!pfd) {
pfd = &(pollfds[nxt]);
pfd->events = POLLIN;
pfd->fd = fd;
pollfds[i].revents = 0;
pollfds_map[fd] = pfd;
nxt++;
}
}
}
return nxt;
}
static int eloop_sock_table_dispatch_table(struct eloop_sock_table *table,
struct pollfd **pollfds_map,
int max_pollfd_map,
short int revents)
{
size_t i;
struct pollfd *pfd;
if (!table || !table->table)
return 0;
table->changed = 0;
for (i = 0; i < table->count; i++) {
pfd = find_pollfd(pollfds_map, table->table[i].sock,
max_pollfd_map);
if (!pfd)
continue;
if (!(pfd->revents & revents))
continue;
table->table[i].handler(table->table[i].sock,
table->table[i].eloop_data,
table->table[i].user_data);
if (table->changed)
return 1;
}
return 0;
}
static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
struct eloop_sock_table *writers,
struct eloop_sock_table *exceptions,
struct pollfd **pollfds_map,
int max_pollfd_map)
{
if (eloop_sock_table_dispatch_table(readers, pollfds_map,
max_pollfd_map, POLLIN | POLLERR |
POLLHUP))
return; /* pollfds may be invalid at this point */
if (eloop_sock_table_dispatch_table(writers, pollfds_map,
max_pollfd_map, POLLOUT))
return; /* pollfds may be invalid at this point */
eloop_sock_table_dispatch_table(exceptions, pollfds_map,
max_pollfd_map, POLLERR | POLLHUP);
}
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
fd_set *fds)
{
size_t i;
FD_ZERO(fds);
if (table->table == NULL)
return;
for (i = 0; i < table->count; i++) {
assert(table->table[i].sock >= 0);
FD_SET(table->table[i].sock, fds);
}
}
static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
fd_set *fds)
{
size_t i;
if (table == NULL || table->table == NULL)
return;
table->changed = 0;
for (i = 0; i < table->count; i++) {
if (FD_ISSET(table->table[i].sock, fds)) {
table->table[i].handler(table->table[i].sock,
table->table[i].eloop_data,
table->table[i].user_data);
if (table->changed)
break;
}
}
}
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds)
{
struct eloop_sock *table;
int i;
for (i = 0; i < nfds; i++) {
table = &eloop.fd_table[events[i].data.fd];
if (table->handler == NULL)
continue;
table->handler(table->sock, table->eloop_data,
table->user_data);
if (eloop.readers.changed ||
eloop.writers.changed ||
eloop.exceptions.changed)
break;
}
}
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
static void eloop_sock_table_dispatch(struct kevent *events, int nfds)
{
struct eloop_sock *table;
int i;
for (i = 0; i < nfds; i++) {
table = &eloop.fd_table[events[i].ident];
if (table->handler == NULL)
continue;
table->handler(table->sock, table->eloop_data,
table->user_data);
if (eloop.readers.changed ||
eloop.writers.changed ||
eloop.exceptions.changed)
break;
}
}
static int eloop_sock_table_requeue(struct eloop_sock_table *table)
{
size_t i;
int r;
r = 0;
for (i = 0; i < table->count && table->table; i++) {
if (eloop_sock_queue(table->table[i].sock, table->type) == -1)
r = -1;
}
return r;
}
#endif /* CONFIG_ELOOP_KQUEUE */
int eloop_sock_requeue(void)
{
int r = 0;
#ifdef CONFIG_ELOOP_KQUEUE
close(eloop.kqueuefd);
eloop.kqueuefd = kqueue();
if (eloop.kqueuefd < 0) {
wpa_printf(MSG_ERROR, "%s: kqueue failed: %s",
__func__, strerror(errno));
return -1;
}
if (eloop_sock_table_requeue(&eloop.readers) < 0)
r = -1;
if (eloop_sock_table_requeue(&eloop.writers) < 0)
r = -1;
if (eloop_sock_table_requeue(&eloop.exceptions) < 0)
r = -1;
#endif /* CONFIG_ELOOP_KQUEUE */
return r;
}
static void eloop_sock_table_destroy(struct eloop_sock_table *table)
{
if (table) {
size_t i;
for (i = 0; i < table->count && table->table; i++) {
wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
"sock=%d eloop_data=%p user_data=%p "
"handler=%p",
table->table[i].sock,
table->table[i].eloop_data,
table->table[i].user_data,
table->table[i].handler);
wpa_trace_dump_funcname("eloop unregistered socket "
"handler",
table->table[i].handler);
wpa_trace_dump("eloop sock", &table->table[i]);
}
os_free(table->table);
}
}
int eloop_register_read_sock(int sock, eloop_sock_handler handler,
void *eloop_data, void *user_data)
{
return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
eloop_data, user_data);
}
void eloop_unregister_read_sock(int sock)
{
eloop_unregister_sock(sock, EVENT_TYPE_READ);
}
static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
{
switch (type) {
case EVENT_TYPE_READ:
return &eloop.readers;
case EVENT_TYPE_WRITE:
return &eloop.writers;
case EVENT_TYPE_EXCEPTION:
return &eloop.exceptions;
}
return NULL;
}
int eloop_register_sock(int sock, eloop_event_type type,
eloop_sock_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_sock_table *table;
assert(sock >= 0);
table = eloop_get_sock_table(type);
return eloop_sock_table_add_sock(table, sock, handler,
eloop_data, user_data);
}
void eloop_unregister_sock(int sock, eloop_event_type type)
{
struct eloop_sock_table *table;
table = eloop_get_sock_table(type);
eloop_sock_table_remove_sock(table, sock);
}
int eloop_register_timeout(unsigned int secs, unsigned int usecs,
eloop_timeout_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *tmp;
os_time_t now_sec;
timeout = os_zalloc(sizeof(*timeout));
if (timeout == NULL)
return -1;
if (os_get_reltime(&timeout->time) < 0) {
os_free(timeout);
return -1;
}
now_sec = timeout->time.sec;
timeout->time.sec += secs;
- if (timeout->time.sec < now_sec) {
- /*
- * Integer overflow - assume long enough timeout to be assumed
- * to be infinite, i.e., the timeout would never happen.
- */
- wpa_printf(MSG_DEBUG, "ELOOP: Too long timeout (secs=%u) to "
- "ever happen - ignore it", secs);
- os_free(timeout);
- return 0;
- }
+ if (timeout->time.sec < now_sec)
+ goto overflow;
timeout->time.usec += usecs;
while (timeout->time.usec >= 1000000) {
timeout->time.sec++;
timeout->time.usec -= 1000000;
}
+ if (timeout->time.sec < now_sec)
+ goto overflow;
timeout->eloop_data = eloop_data;
timeout->user_data = user_data;
timeout->handler = handler;
wpa_trace_add_ref(timeout, eloop, eloop_data);
wpa_trace_add_ref(timeout, user, user_data);
wpa_trace_record(timeout);
/* Maintain timeouts in order of increasing time */
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
if (os_reltime_before(&timeout->time, &tmp->time)) {
dl_list_add(tmp->list.prev, &timeout->list);
return 0;
}
}
dl_list_add_tail(&eloop.timeout, &timeout->list);
return 0;
+
+overflow:
+ /*
+ * Integer overflow - assume long enough timeout to be assumed
+ * to be infinite, i.e., the timeout would never happen.
+ */
+ wpa_printf(MSG_DEBUG,
+ "ELOOP: Too long timeout (secs=%u usecs=%u) to ever happen - ignore it",
+ secs,usecs);
+ os_free(timeout);
+ return 0;
}
static void eloop_remove_timeout(struct eloop_timeout *timeout)
{
dl_list_del(&timeout->list);
wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
wpa_trace_remove_ref(timeout, user, timeout->user_data);
os_free(timeout);
}
int eloop_cancel_timeout(eloop_timeout_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_timeout *timeout, *prev;
int removed = 0;
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
struct eloop_timeout, list) {
if (timeout->handler == handler &&
(timeout->eloop_data == eloop_data ||
eloop_data == ELOOP_ALL_CTX) &&
(timeout->user_data == user_data ||
user_data == ELOOP_ALL_CTX)) {
eloop_remove_timeout(timeout);
removed++;
}
}
return removed;
}
int eloop_cancel_timeout_one(eloop_timeout_handler handler,
void *eloop_data, void *user_data,
struct os_reltime *remaining)
{
struct eloop_timeout *timeout, *prev;
int removed = 0;
struct os_reltime now;
os_get_reltime(&now);
remaining->sec = remaining->usec = 0;
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
struct eloop_timeout, list) {
if (timeout->handler == handler &&
(timeout->eloop_data == eloop_data) &&
(timeout->user_data == user_data)) {
removed = 1;
if (os_reltime_before(&now, &timeout->time))
os_reltime_sub(&timeout->time, &now, remaining);
eloop_remove_timeout(timeout);
break;
}
}
return removed;
}
int eloop_is_timeout_registered(eloop_timeout_handler handler,
void *eloop_data, void *user_data)
{
struct eloop_timeout *tmp;
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
if (tmp->handler == handler &&
tmp->eloop_data == eloop_data &&
tmp->user_data == user_data)
return 1;
}
return 0;
}
int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
eloop_timeout_handler handler, void *eloop_data,
void *user_data)
{
struct os_reltime now, requested, remaining;
struct eloop_timeout *tmp;
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
if (tmp->handler == handler &&
tmp->eloop_data == eloop_data &&
tmp->user_data == user_data) {
requested.sec = req_secs;
requested.usec = req_usecs;
os_get_reltime(&now);
os_reltime_sub(&tmp->time, &now, &remaining);
if (os_reltime_before(&requested, &remaining)) {
eloop_cancel_timeout(handler, eloop_data,
user_data);
eloop_register_timeout(requested.sec,
requested.usec,
handler, eloop_data,
user_data);
return 1;
}
return 0;
}
}
return -1;
}
int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
eloop_timeout_handler handler, void *eloop_data,
void *user_data)
{
struct os_reltime now, requested, remaining;
struct eloop_timeout *tmp;
dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
if (tmp->handler == handler &&
tmp->eloop_data == eloop_data &&
tmp->user_data == user_data) {
requested.sec = req_secs;
requested.usec = req_usecs;
os_get_reltime(&now);
os_reltime_sub(&tmp->time, &now, &remaining);
if (os_reltime_before(&remaining, &requested)) {
eloop_cancel_timeout(handler, eloop_data,
user_data);
eloop_register_timeout(requested.sec,
requested.usec,
handler, eloop_data,
user_data);
return 1;
}
return 0;
}
}
return -1;
}
#ifndef CONFIG_NATIVE_WINDOWS
static void eloop_handle_alarm(int sig)
{
wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
"two seconds. Looks like there\n"
"is a bug that ends up in a busy loop that "
"prevents clean shutdown.\n"
"Killing program forcefully.\n");
exit(1);
}
#endif /* CONFIG_NATIVE_WINDOWS */
static void eloop_handle_signal(int sig)
{
size_t i;
#ifndef CONFIG_NATIVE_WINDOWS
if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
/* Use SIGALRM to break out from potential busy loops that
* would not allow the program to be killed. */
eloop.pending_terminate = 1;
signal(SIGALRM, eloop_handle_alarm);
alarm(2);
}
#endif /* CONFIG_NATIVE_WINDOWS */
eloop.signaled++;
for (i = 0; i < eloop.signal_count; i++) {
if (eloop.signals[i].sig == sig) {
eloop.signals[i].signaled++;
break;
}
}
}
static void eloop_process_pending_signals(void)
{
size_t i;
if (eloop.signaled == 0)
return;
eloop.signaled = 0;
if (eloop.pending_terminate) {
#ifndef CONFIG_NATIVE_WINDOWS
alarm(0);
#endif /* CONFIG_NATIVE_WINDOWS */
eloop.pending_terminate = 0;
}
for (i = 0; i < eloop.signal_count; i++) {
if (eloop.signals[i].signaled) {
eloop.signals[i].signaled = 0;
eloop.signals[i].handler(eloop.signals[i].sig,
eloop.signals[i].user_data);
}
}
}
int eloop_register_signal(int sig, eloop_signal_handler handler,
void *user_data)
{
struct eloop_signal *tmp;
tmp = os_realloc_array(eloop.signals, eloop.signal_count + 1,
sizeof(struct eloop_signal));
if (tmp == NULL)
return -1;
tmp[eloop.signal_count].sig = sig;
tmp[eloop.signal_count].user_data = user_data;
tmp[eloop.signal_count].handler = handler;
tmp[eloop.signal_count].signaled = 0;
eloop.signal_count++;
eloop.signals = tmp;
signal(sig, eloop_handle_signal);
return 0;
}
int eloop_register_signal_terminate(eloop_signal_handler handler,
void *user_data)
{
int ret = eloop_register_signal(SIGINT, handler, user_data);
if (ret == 0)
ret = eloop_register_signal(SIGTERM, handler, user_data);
return ret;
}
int eloop_register_signal_reconfig(eloop_signal_handler handler,
void *user_data)
{
#ifdef CONFIG_NATIVE_WINDOWS
return 0;
#else /* CONFIG_NATIVE_WINDOWS */
return eloop_register_signal(SIGHUP, handler, user_data);
#endif /* CONFIG_NATIVE_WINDOWS */
}
void eloop_run(void)
{
#ifdef CONFIG_ELOOP_POLL
int num_poll_fds;
int timeout_ms = 0;
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
fd_set *rfds, *wfds, *efds;
struct timeval _tv;
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
int timeout_ms = -1;
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
struct timespec ts;
#endif /* CONFIG_ELOOP_KQUEUE */
int res;
struct os_reltime tv, now;
#ifdef CONFIG_ELOOP_SELECT
rfds = os_malloc(sizeof(*rfds));
wfds = os_malloc(sizeof(*wfds));
efds = os_malloc(sizeof(*efds));
if (rfds == NULL || wfds == NULL || efds == NULL)
goto out;
#endif /* CONFIG_ELOOP_SELECT */
while (!eloop.terminate &&
(!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
struct eloop_timeout *timeout;
if (eloop.pending_terminate) {
/*
* This may happen in some corner cases where a signal
* is received during a blocking operation. We need to
* process the pending signals and exit if requested to
* avoid hitting the SIGALRM limit if the blocking
* operation took more than two seconds.
*/
eloop_process_pending_signals();
if (eloop.terminate)
break;
}
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
list);
if (timeout) {
os_get_reltime(&now);
if (os_reltime_before(&now, &timeout->time))
os_reltime_sub(&timeout->time, &now, &tv);
else
tv.sec = tv.usec = 0;
#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
timeout_ms = tv.sec * 1000 + tv.usec / 1000;
#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
#ifdef CONFIG_ELOOP_SELECT
_tv.tv_sec = tv.sec;
_tv.tv_usec = tv.usec;
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_KQUEUE
ts.tv_sec = tv.sec;
ts.tv_nsec = tv.usec * 1000L;
#endif /* CONFIG_ELOOP_KQUEUE */
}
#ifdef CONFIG_ELOOP_POLL
num_poll_fds = eloop_sock_table_set_fds(
&eloop.readers, &eloop.writers, &eloop.exceptions,
eloop.pollfds, eloop.pollfds_map,
eloop.max_pollfd_map);
res = poll(eloop.pollfds, num_poll_fds,
timeout ? timeout_ms : -1);
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
eloop_sock_table_set_fds(&eloop.readers, rfds);
eloop_sock_table_set_fds(&eloop.writers, wfds);
eloop_sock_table_set_fds(&eloop.exceptions, efds);
res = select(eloop.max_sock + 1, rfds, wfds, efds,
timeout ? &_tv : NULL);
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
if (eloop.count == 0) {
res = 0;
} else {
res = epoll_wait(eloop.epollfd, eloop.epoll_events,
eloop.count, timeout_ms);
}
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
if (eloop.count == 0) {
res = 0;
} else {
res = kevent(eloop.kqueuefd, NULL, 0,
eloop.kqueue_events, eloop.kqueue_nevents,
timeout ? &ts : NULL);
}
#endif /* CONFIG_ELOOP_KQUEUE */
if (res < 0 && errno != EINTR && errno != 0) {
wpa_printf(MSG_ERROR, "eloop: %s: %s",
#ifdef CONFIG_ELOOP_POLL
"poll"
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
"select"
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
"epoll"
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
"kqueue"
#endif /* CONFIG_ELOOP_EKQUEUE */
, strerror(errno));
goto out;
}
eloop.readers.changed = 0;
eloop.writers.changed = 0;
eloop.exceptions.changed = 0;
eloop_process_pending_signals();
/* check if some registered timeouts have occurred */
timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
list);
if (timeout) {
os_get_reltime(&now);
if (!os_reltime_before(&now, &timeout->time)) {
void *eloop_data = timeout->eloop_data;
void *user_data = timeout->user_data;
eloop_timeout_handler handler =
timeout->handler;
eloop_remove_timeout(timeout);
handler(eloop_data, user_data);
}
}
if (res <= 0)
continue;
if (eloop.readers.changed ||
eloop.writers.changed ||
eloop.exceptions.changed) {
/*
* Sockets may have been closed and reopened with the
* same FD in the signal or timeout handlers, so we
* must skip the previous results and check again
* whether any of the currently registered sockets have
* events.
*/
continue;
}
#ifdef CONFIG_ELOOP_POLL
eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
&eloop.exceptions, eloop.pollfds_map,
eloop.max_pollfd_map);
#endif /* CONFIG_ELOOP_POLL */
#ifdef CONFIG_ELOOP_SELECT
eloop_sock_table_dispatch(&eloop.readers, rfds);
eloop_sock_table_dispatch(&eloop.writers, wfds);
eloop_sock_table_dispatch(&eloop.exceptions, efds);
#endif /* CONFIG_ELOOP_SELECT */
#ifdef CONFIG_ELOOP_EPOLL
eloop_sock_table_dispatch(eloop.epoll_events, res);
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
eloop_sock_table_dispatch(eloop.kqueue_events, res);
#endif /* CONFIG_ELOOP_KQUEUE */
}
eloop.terminate = 0;
out:
#ifdef CONFIG_ELOOP_SELECT
os_free(rfds);
os_free(wfds);
os_free(efds);
#endif /* CONFIG_ELOOP_SELECT */
return;
}
void eloop_terminate(void)
{
eloop.terminate = 1;
}
void eloop_destroy(void)
{
struct eloop_timeout *timeout, *prev;
struct os_reltime now;
os_get_reltime(&now);
dl_list_for_each_safe(timeout, prev, &eloop.timeout,
struct eloop_timeout, list) {
int sec, usec;
sec = timeout->time.sec - now.sec;
usec = timeout->time.usec - now.usec;
if (timeout->time.usec < now.usec) {
sec--;
usec += 1000000;
}
wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
"eloop_data=%p user_data=%p handler=%p",
sec, usec, timeout->eloop_data, timeout->user_data,
timeout->handler);
wpa_trace_dump_funcname("eloop unregistered timeout handler",
timeout->handler);
wpa_trace_dump("eloop timeout", timeout);
eloop_remove_timeout(timeout);
}
eloop_sock_table_destroy(&eloop.readers);
eloop_sock_table_destroy(&eloop.writers);
eloop_sock_table_destroy(&eloop.exceptions);
os_free(eloop.signals);
#ifdef CONFIG_ELOOP_POLL
os_free(eloop.pollfds);
os_free(eloop.pollfds_map);
#endif /* CONFIG_ELOOP_POLL */
#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
os_free(eloop.fd_table);
#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
#ifdef CONFIG_ELOOP_EPOLL
os_free(eloop.epoll_events);
close(eloop.epollfd);
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
os_free(eloop.kqueue_events);
close(eloop.kqueuefd);
#endif /* CONFIG_ELOOP_KQUEUE */
}
int eloop_terminated(void)
{
return eloop.terminate || eloop.pending_terminate;
}
void eloop_wait_for_read_sock(int sock)
{
#ifdef CONFIG_ELOOP_POLL
struct pollfd pfd;
if (sock < 0)
return;
os_memset(&pfd, 0, sizeof(pfd));
pfd.fd = sock;
pfd.events = POLLIN;
poll(&pfd, 1, -1);
#endif /* CONFIG_ELOOP_POLL */
#if defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL)
/*
* We can use epoll() here. But epoll() requres 4 system calls.
* epoll_create1(), epoll_ctl() for ADD, epoll_wait, and close() for
* epoll fd. So select() is better for performance here.
*/
fd_set rfds;
if (sock < 0)
return;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
select(sock + 1, &rfds, NULL, NULL, NULL);
#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
#ifdef CONFIG_ELOOP_KQUEUE
int kfd;
struct kevent ke1, ke2;
kfd = kqueue();
if (kfd == -1)
return;
EV_SET(&ke1, sock, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0);
kevent(kfd, &ke1, 1, &ke2, 1, NULL);
close(kfd);
#endif /* CONFIG_ELOOP_KQUEUE */
}
#ifdef CONFIG_ELOOP_SELECT
#undef CONFIG_ELOOP_SELECT
#endif /* CONFIG_ELOOP_SELECT */
diff --git a/contrib/wpa/src/wps/wps.c b/contrib/wpa/src/wps/wps.c
index 484df262c3ca..1fe38063b253 100644
--- a/contrib/wpa/src/wps/wps.c
+++ b/contrib/wpa/src/wps/wps.c
@@ -1,664 +1,664 @@
/*
* Wi-Fi Protected Setup
* Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "crypto/dh_group5.h"
#include "common/ieee802_11_defs.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
#ifdef CONFIG_WPS_TESTING
int wps_version_number = 0x20;
-int wps_testing_dummy_cred = 0;
+int wps_testing_stub_cred = 0;
int wps_corrupt_pkhash = 0;
int wps_force_auth_types_in_use = 0;
u16 wps_force_auth_types = 0;
int wps_force_encr_types_in_use = 0;
u16 wps_force_encr_types = 0;
#endif /* CONFIG_WPS_TESTING */
/**
* wps_init - Initialize WPS Registration protocol data
* @cfg: WPS configuration
* Returns: Pointer to allocated data or %NULL on failure
*
* This function is used to initialize WPS data for a registration protocol
* instance (i.e., each run of registration protocol as a Registrar of
* Enrollee. The caller is responsible for freeing this data after the
* registration run has been completed by calling wps_deinit().
*/
struct wps_data * wps_init(const struct wps_config *cfg)
{
struct wps_data *data = os_zalloc(sizeof(*data));
if (data == NULL)
return NULL;
data->wps = cfg->wps;
data->registrar = cfg->registrar;
if (cfg->registrar) {
os_memcpy(data->uuid_r, cfg->wps->uuid, WPS_UUID_LEN);
} else {
os_memcpy(data->mac_addr_e, cfg->wps->dev.mac_addr, ETH_ALEN);
os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
}
if (cfg->pin) {
data->dev_pw_id = cfg->dev_pw_id;
data->dev_password = os_memdup(cfg->pin, cfg->pin_len);
if (data->dev_password == NULL) {
os_free(data);
return NULL;
}
data->dev_password_len = cfg->pin_len;
wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password",
data->dev_password, data->dev_password_len);
}
#ifdef CONFIG_WPS_NFC
if (cfg->pin == NULL &&
cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
data->dev_pw_id = cfg->dev_pw_id;
if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
/* Keep AP PIN as alternative Device Password */
data->alt_dev_pw_id = data->dev_pw_id;
data->alt_dev_password = data->dev_password;
data->alt_dev_password_len = data->dev_password_len;
data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id;
data->dev_password =
os_memdup(wpabuf_head(cfg->wps->ap_nfc_dev_pw),
wpabuf_len(cfg->wps->ap_nfc_dev_pw));
if (data->dev_password == NULL) {
os_free(data);
return NULL;
}
data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw);
wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password",
data->dev_password, data->dev_password_len);
}
#endif /* CONFIG_WPS_NFC */
data->pbc = cfg->pbc;
if (cfg->pbc) {
/* Use special PIN '00000000' for PBC */
data->dev_pw_id = DEV_PW_PUSHBUTTON;
bin_clear_free(data->dev_password, data->dev_password_len);
data->dev_password = (u8 *) os_strdup("00000000");
if (data->dev_password == NULL) {
os_free(data);
return NULL;
}
data->dev_password_len = 8;
}
data->state = data->registrar ? RECV_M1 : SEND_M1;
if (cfg->assoc_wps_ie) {
struct wps_parse_attr attr;
wpa_hexdump_buf(MSG_DEBUG, "WPS: WPS IE from (Re)AssocReq",
cfg->assoc_wps_ie);
if (wps_parse_msg(cfg->assoc_wps_ie, &attr) < 0) {
wpa_printf(MSG_DEBUG, "WPS: Failed to parse WPS IE "
"from (Re)AssocReq");
} else if (attr.request_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Request Type attribute "
"in (Re)AssocReq WPS IE");
} else {
wpa_printf(MSG_DEBUG, "WPS: Request Type (from WPS IE "
"in (Re)AssocReq WPS IE): %d",
*attr.request_type);
data->request_type = *attr.request_type;
}
}
if (cfg->new_ap_settings) {
data->new_ap_settings =
os_memdup(cfg->new_ap_settings,
sizeof(*data->new_ap_settings));
if (data->new_ap_settings == NULL) {
bin_clear_free(data->dev_password,
data->dev_password_len);
os_free(data);
return NULL;
}
}
if (cfg->peer_addr)
os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN);
if (cfg->p2p_dev_addr)
os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN);
data->use_psk_key = cfg->use_psk_key;
data->pbc_in_m1 = cfg->pbc_in_m1;
if (cfg->peer_pubkey_hash) {
os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash,
WPS_OOB_PUBKEY_HASH_LEN);
data->peer_pubkey_hash_set = 1;
}
data->multi_ap_backhaul_sta = cfg->multi_ap_backhaul_sta;
return data;
}
/**
* wps_deinit - Deinitialize WPS Registration protocol data
* @data: WPS Registration protocol data from wps_init()
*/
void wps_deinit(struct wps_data *data)
{
#ifdef CONFIG_WPS_NFC
if (data->registrar && data->nfc_pw_token)
wps_registrar_remove_nfc_pw_token(data->wps->registrar,
data->nfc_pw_token);
#endif /* CONFIG_WPS_NFC */
if (data->wps_pin_revealed) {
wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
"negotiation failed");
if (data->registrar)
wps_registrar_invalidate_pin(data->wps->registrar,
data->uuid_e);
} else if (data->registrar)
wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e);
wpabuf_clear_free(data->dh_privkey);
wpabuf_free(data->dh_pubkey_e);
wpabuf_free(data->dh_pubkey_r);
wpabuf_free(data->last_msg);
bin_clear_free(data->dev_password, data->dev_password_len);
bin_clear_free(data->alt_dev_password, data->alt_dev_password_len);
bin_clear_free(data->new_psk, data->new_psk_len);
wps_device_data_free(&data->peer_dev);
bin_clear_free(data->new_ap_settings, sizeof(*data->new_ap_settings));
dh5_free(data->dh_ctx);
os_free(data);
}
/**
* wps_process_msg - Process a WPS message
* @wps: WPS Registration protocol data from wps_init()
* @op_code: Message OP Code
* @msg: Message data
* Returns: Processing result
*
* This function is used to process WPS messages with OP Codes WSC_ACK,
* WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is
* responsible for reassembling the messages before calling this function.
* Response to this message is built by calling wps_get_msg().
*/
enum wps_process_res wps_process_msg(struct wps_data *wps,
enum wsc_op_code op_code,
const struct wpabuf *msg)
{
if (wps->registrar)
return wps_registrar_process_msg(wps, op_code, msg);
else
return wps_enrollee_process_msg(wps, op_code, msg);
}
/**
* wps_get_msg - Build a WPS message
* @wps: WPS Registration protocol data from wps_init()
* @op_code: Buffer for returning message OP Code
* Returns: The generated WPS message or %NULL on failure
*
* This function is used to build a response to a message processed by calling
* wps_process_msg(). The caller is responsible for freeing the buffer.
*/
struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code)
{
if (wps->registrar)
return wps_registrar_get_msg(wps, op_code);
else
return wps_enrollee_get_msg(wps, op_code);
}
/**
* wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC
* @msg: WPS IE contents from Beacon or Probe Response frame
* Returns: 1 if PBC Registrar is active, 0 if not
*/
int wps_is_selected_pbc_registrar(const struct wpabuf *msg)
{
struct wps_parse_attr attr;
/*
* In theory, this could also verify that attr.sel_reg_config_methods
* includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations
* do not set Selected Registrar Config Methods attribute properly, so
* it is safer to just use Device Password ID here.
*/
if (wps_parse_msg(msg, &attr) < 0 ||
!attr.selected_registrar || *attr.selected_registrar == 0 ||
!attr.dev_password_id ||
WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
return 0;
#ifdef CONFIG_WPS_STRICT
if (!attr.sel_reg_config_methods ||
!(WPA_GET_BE16(attr.sel_reg_config_methods) &
WPS_CONFIG_PUSHBUTTON))
return 0;
#endif /* CONFIG_WPS_STRICT */
return 1;
}
static int is_selected_pin_registrar(struct wps_parse_attr *attr)
{
/*
* In theory, this could also verify that attr.sel_reg_config_methods
* includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
* but some deployed AP implementations do not set Selected Registrar
* Config Methods attribute properly, so it is safer to just use
* Device Password ID here.
*/
if (!attr->selected_registrar || *attr->selected_registrar == 0)
return 0;
if (attr->dev_password_id != NULL &&
WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON)
return 0;
#ifdef CONFIG_WPS_STRICT
if (!attr->sel_reg_config_methods ||
!(WPA_GET_BE16(attr->sel_reg_config_methods) &
(WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD)))
return 0;
#endif /* CONFIG_WPS_STRICT */
return 1;
}
/**
* wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
* @msg: WPS IE contents from Beacon or Probe Response frame
* Returns: 1 if PIN Registrar is active, 0 if not
*/
int wps_is_selected_pin_registrar(const struct wpabuf *msg)
{
struct wps_parse_attr attr;
if (wps_parse_msg(msg, &attr) < 0)
return 0;
return is_selected_pin_registrar(&attr);
}
/**
* wps_is_addr_authorized - Check whether WPS IE authorizes MAC address
* @msg: WPS IE contents from Beacon or Probe Response frame
* @addr: MAC address to search for
* @ver1_compat: Whether to use version 1 compatibility mode
* Returns: 2 if the specified address is explicit authorized, 1 if address is
* authorized (broadcast), 0 if not
*/
int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr,
int ver1_compat)
{
struct wps_parse_attr attr;
unsigned int i;
const u8 *pos;
const u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
if (wps_parse_msg(msg, &attr) < 0)
return 0;
if (!attr.version2 && ver1_compat) {
/*
* Version 1.0 AP - AuthorizedMACs not used, so revert back to
* old mechanism of using SelectedRegistrar.
*/
return is_selected_pin_registrar(&attr);
}
if (!attr.authorized_macs)
return 0;
pos = attr.authorized_macs;
for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) {
if (os_memcmp(pos, addr, ETH_ALEN) == 0)
return 2;
if (os_memcmp(pos, bcast, ETH_ALEN) == 0)
return 1;
pos += ETH_ALEN;
}
return 0;
}
/**
* wps_ap_priority_compar - Prioritize WPS IE from two APs
* @wps_a: WPS IE contents from Beacon or Probe Response frame
* @wps_b: WPS IE contents from Beacon or Probe Response frame
* Returns: 1 if wps_b is considered more likely selection for WPS
* provisioning, -1 if wps_a is considered more like, or 0 if no preference
*/
int wps_ap_priority_compar(const struct wpabuf *wps_a,
const struct wpabuf *wps_b)
{
struct wps_parse_attr attr;
int sel_a, sel_b;
if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0)
return 1;
sel_a = attr.selected_registrar && *attr.selected_registrar != 0;
if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0)
return -1;
sel_b = attr.selected_registrar && *attr.selected_registrar != 0;
if (sel_a && !sel_b)
return -1;
if (!sel_a && sel_b)
return 1;
return 0;
}
/**
* wps_get_uuid_e - Get UUID-E from WPS IE
* @msg: WPS IE contents from Beacon or Probe Response frame
* Returns: Pointer to UUID-E or %NULL if not included
*
* The returned pointer is to the msg contents and it remains valid only as
* long as the msg buffer is valid.
*/
const u8 * wps_get_uuid_e(const struct wpabuf *msg)
{
struct wps_parse_attr attr;
if (wps_parse_msg(msg, &attr) < 0)
return NULL;
return attr.uuid_e;
}
/**
* wps_is_20 - Check whether WPS attributes claim support for WPS 2.0
*/
int wps_is_20(const struct wpabuf *msg)
{
struct wps_parse_attr attr;
if (msg == NULL || wps_parse_msg(msg, &attr) < 0)
return 0;
return attr.version2 != NULL;
}
/**
* wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
* @req_type: Value for Request Type attribute
* Returns: WPS IE or %NULL on failure
*
* The caller is responsible for freeing the buffer.
*/
struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
{
struct wpabuf *ie;
u8 *len;
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
"Request");
ie = wpabuf_alloc(100);
if (ie == NULL)
return NULL;
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
len = wpabuf_put(ie, 1);
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
if (wps_build_version(ie) ||
wps_build_req_type(ie, req_type) ||
wps_build_wfa_ext(ie, 0, NULL, 0, 0)) {
wpabuf_free(ie);
return NULL;
}
*len = wpabuf_len(ie) - 2;
return ie;
}
/**
* wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response
* Returns: WPS IE or %NULL on failure
*
* The caller is responsible for freeing the buffer.
*/
struct wpabuf * wps_build_assoc_resp_ie(void)
{
struct wpabuf *ie;
u8 *len;
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
"Response");
ie = wpabuf_alloc(100);
if (ie == NULL)
return NULL;
wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
len = wpabuf_put(ie, 1);
wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
if (wps_build_version(ie) ||
wps_build_resp_type(ie, WPS_RESP_AP) ||
wps_build_wfa_ext(ie, 0, NULL, 0, 0)) {
wpabuf_free(ie);
return NULL;
}
*len = wpabuf_len(ie) - 2;
return ie;
}
/**
* wps_build_probe_req_ie - Build WPS IE for Probe Request
* @pw_id: Password ID (DEV_PW_PUSHBUTTON for active PBC and DEV_PW_DEFAULT for
* most other use cases)
* @dev: Device attributes
* @uuid: Own UUID
* @req_type: Value for Request Type attribute
* @num_req_dev_types: Number of requested device types
* @req_dev_types: Requested device types (8 * num_req_dev_types octets) or
* %NULL if none
* Returns: WPS IE or %NULL on failure
*
* The caller is responsible for freeing the buffer.
*/
struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
const u8 *uuid,
enum wps_request_type req_type,
unsigned int num_req_dev_types,
const u8 *req_dev_types)
{
struct wpabuf *ie;
wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
ie = wpabuf_alloc(500);
if (ie == NULL)
return NULL;
if (wps_build_version(ie) ||
wps_build_req_type(ie, req_type) ||
wps_build_config_methods(ie, dev->config_methods) ||
wps_build_uuid_e(ie, uuid) ||
wps_build_primary_dev_type(dev, ie) ||
wps_build_rf_bands(dev, ie, 0) ||
wps_build_assoc_state(NULL, ie) ||
wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(ie, pw_id) ||
wps_build_manufacturer(dev, ie) ||
wps_build_model_name(dev, ie) ||
wps_build_model_number(dev, ie) ||
wps_build_dev_name(dev, ie) ||
wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0, 0) ||
wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
||
wps_build_secondary_dev_type(dev, ie)
) {
wpabuf_free(ie);
return NULL;
}
return wps_ie_encapsulate(ie);
}
void wps_free_pending_msgs(struct upnp_pending_message *msgs)
{
struct upnp_pending_message *p, *prev;
p = msgs;
while (p) {
prev = p;
p = p->next;
wpabuf_free(prev->msg);
os_free(prev);
}
}
int wps_attr_text(struct wpabuf *data, char *buf, char *end)
{
struct wps_parse_attr attr;
char *pos = buf;
int ret;
if (wps_parse_msg(data, &attr) < 0)
return -1;
if (attr.wps_state) {
if (*attr.wps_state == WPS_STATE_NOT_CONFIGURED)
ret = os_snprintf(pos, end - pos,
"wps_state=unconfigured\n");
else if (*attr.wps_state == WPS_STATE_CONFIGURED)
ret = os_snprintf(pos, end - pos,
"wps_state=configured\n");
else
ret = 0;
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (attr.ap_setup_locked && *attr.ap_setup_locked) {
ret = os_snprintf(pos, end - pos,
"wps_ap_setup_locked=1\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (attr.selected_registrar && *attr.selected_registrar) {
ret = os_snprintf(pos, end - pos,
"wps_selected_registrar=1\n");
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (attr.dev_password_id) {
ret = os_snprintf(pos, end - pos,
"wps_device_password_id=%u\n",
WPA_GET_BE16(attr.dev_password_id));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (attr.sel_reg_config_methods) {
ret = os_snprintf(pos, end - pos,
"wps_selected_registrar_config_methods="
"0x%04x\n",
WPA_GET_BE16(attr.sel_reg_config_methods));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (attr.primary_dev_type) {
char devtype[WPS_DEV_TYPE_BUFSIZE];
ret = os_snprintf(pos, end - pos,
"wps_primary_device_type=%s\n",
wps_dev_type_bin2str(attr.primary_dev_type,
devtype,
sizeof(devtype)));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (attr.dev_name) {
char *str = os_malloc(attr.dev_name_len + 1);
size_t i;
if (str == NULL)
return pos - buf;
for (i = 0; i < attr.dev_name_len; i++) {
if (attr.dev_name[i] == 0 ||
is_ctrl_char(attr.dev_name[i]))
str[i] = '_';
else
str[i] = attr.dev_name[i];
}
str[i] = '\0';
ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str);
os_free(str);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
if (attr.config_methods) {
ret = os_snprintf(pos, end - pos,
"wps_config_methods=0x%04x\n",
WPA_GET_BE16(attr.config_methods));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
return pos - buf;
}
const char * wps_ei_str(enum wps_error_indication ei)
{
switch (ei) {
case WPS_EI_NO_ERROR:
return "No Error";
case WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED:
return "TKIP Only Prohibited";
case WPS_EI_SECURITY_WEP_PROHIBITED:
return "WEP Prohibited";
case WPS_EI_AUTH_FAILURE:
return "Authentication Failure";
default:
return "Unknown";
}
}
diff --git a/contrib/wpa/src/wps/wps_defs.h b/contrib/wpa/src/wps/wps_defs.h
index 9fccb4eeb5c1..ddaeda56d317 100644
--- a/contrib/wpa/src/wps/wps_defs.h
+++ b/contrib/wpa/src/wps/wps_defs.h
@@ -1,384 +1,384 @@
/*
* Wi-Fi Protected Setup - message definitions
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPS_DEFS_H
#define WPS_DEFS_H
#ifdef CONFIG_WPS_TESTING
extern int wps_version_number;
-extern int wps_testing_dummy_cred;
+extern int wps_testing_stub_cred;
extern int wps_corrupt_pkhash;
extern int wps_force_auth_types_in_use;
extern u16 wps_force_auth_types;
extern int wps_force_encr_types_in_use;
extern u16 wps_force_encr_types;
#define WPS_VERSION wps_version_number
#else /* CONFIG_WPS_TESTING */
#define WPS_VERSION 0x20
#endif /* CONFIG_WPS_TESTING */
/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
#define WPS_DH_GROUP 5
#define WPS_UUID_LEN 16
#define WPS_NONCE_LEN 16
#define WPS_AUTHENTICATOR_LEN 8
#define WPS_AUTHKEY_LEN 32
#define WPS_KEYWRAPKEY_LEN 16
#define WPS_EMSK_LEN 32
#define WPS_PSK_LEN 16
#define WPS_SECRET_NONCE_LEN 16
#define WPS_HASH_LEN 32
#define WPS_KWA_LEN 8
#define WPS_MGMTAUTHKEY_LEN 32
#define WPS_MGMTENCKEY_LEN 16
#define WPS_MGMT_KEY_ID_LEN 16
#define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16
#define WPS_OOB_DEVICE_PASSWORD_LEN 32
#define WPS_OOB_PUBKEY_HASH_LEN 20
#define WPS_DEV_NAME_MAX_LEN 32
#define WPS_MANUFACTURER_MAX_LEN 64
#define WPS_MODEL_NAME_MAX_LEN 32
#define WPS_MODEL_NUMBER_MAX_LEN 32
#define WPS_SERIAL_NUMBER_MAX_LEN 32
/* Attribute Types */
enum wps_attribute {
ATTR_AP_CHANNEL = 0x1001,
ATTR_ASSOC_STATE = 0x1002,
ATTR_AUTH_TYPE = 0x1003,
ATTR_AUTH_TYPE_FLAGS = 0x1004,
ATTR_AUTHENTICATOR = 0x1005,
ATTR_CONFIG_METHODS = 0x1008,
ATTR_CONFIG_ERROR = 0x1009,
ATTR_CONFIRM_URL4 = 0x100a,
ATTR_CONFIRM_URL6 = 0x100b,
ATTR_CONN_TYPE = 0x100c,
ATTR_CONN_TYPE_FLAGS = 0x100d,
ATTR_CRED = 0x100e,
ATTR_ENCR_TYPE = 0x100f,
ATTR_ENCR_TYPE_FLAGS = 0x1010,
ATTR_DEV_NAME = 0x1011,
ATTR_DEV_PASSWORD_ID = 0x1012,
ATTR_E_HASH1 = 0x1014,
ATTR_E_HASH2 = 0x1015,
ATTR_E_SNONCE1 = 0x1016,
ATTR_E_SNONCE2 = 0x1017,
ATTR_ENCR_SETTINGS = 0x1018,
ATTR_ENROLLEE_NONCE = 0x101a,
ATTR_FEATURE_ID = 0x101b,
ATTR_IDENTITY = 0x101c,
ATTR_IDENTITY_PROOF = 0x101d,
ATTR_KEY_WRAP_AUTH = 0x101e,
ATTR_KEY_ID = 0x101f,
ATTR_MAC_ADDR = 0x1020,
ATTR_MANUFACTURER = 0x1021,
ATTR_MSG_TYPE = 0x1022,
ATTR_MODEL_NAME = 0x1023,
ATTR_MODEL_NUMBER = 0x1024,
ATTR_NETWORK_INDEX = 0x1026,
ATTR_NETWORK_KEY = 0x1027,
ATTR_NETWORK_KEY_INDEX = 0x1028,
ATTR_NEW_DEVICE_NAME = 0x1029,
ATTR_NEW_PASSWORD = 0x102a,
ATTR_OOB_DEVICE_PASSWORD = 0x102c,
ATTR_OS_VERSION = 0x102d,
ATTR_POWER_LEVEL = 0x102f,
ATTR_PSK_CURRENT = 0x1030,
ATTR_PSK_MAX = 0x1031,
ATTR_PUBLIC_KEY = 0x1032,
ATTR_RADIO_ENABLE = 0x1033,
ATTR_REBOOT = 0x1034,
ATTR_REGISTRAR_CURRENT = 0x1035,
ATTR_REGISTRAR_ESTABLISHED = 0x1036,
ATTR_REGISTRAR_LIST = 0x1037,
ATTR_REGISTRAR_MAX = 0x1038,
ATTR_REGISTRAR_NONCE = 0x1039,
ATTR_REQUEST_TYPE = 0x103a,
ATTR_RESPONSE_TYPE = 0x103b,
ATTR_RF_BANDS = 0x103c,
ATTR_R_HASH1 = 0x103d,
ATTR_R_HASH2 = 0x103e,
ATTR_R_SNONCE1 = 0x103f,
ATTR_R_SNONCE2 = 0x1040,
ATTR_SELECTED_REGISTRAR = 0x1041,
ATTR_SERIAL_NUMBER = 0x1042,
ATTR_WPS_STATE = 0x1044,
ATTR_SSID = 0x1045,
ATTR_TOTAL_NETWORKS = 0x1046,
ATTR_UUID_E = 0x1047,
ATTR_UUID_R = 0x1048,
ATTR_VENDOR_EXT = 0x1049,
ATTR_VERSION = 0x104a,
ATTR_X509_CERT_REQ = 0x104b,
ATTR_X509_CERT = 0x104c,
ATTR_EAP_IDENTITY = 0x104d,
ATTR_MSG_COUNTER = 0x104e,
ATTR_PUBKEY_HASH = 0x104f,
ATTR_REKEY_KEY = 0x1050,
ATTR_KEY_LIFETIME = 0x1051,
ATTR_PERMITTED_CFG_METHODS = 0x1052,
ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
ATTR_PRIMARY_DEV_TYPE = 0x1054,
ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055,
ATTR_PORTABLE_DEV = 0x1056,
ATTR_AP_SETUP_LOCKED = 0x1057,
ATTR_APPLICATION_EXT = 0x1058,
ATTR_EAP_TYPE = 0x1059,
ATTR_IV = 0x1060,
ATTR_KEY_PROVIDED_AUTO = 0x1061,
ATTR_802_1X_ENABLED = 0x1062,
ATTR_APPSESSIONKEY = 0x1063,
ATTR_WEPTRANSMITKEY = 0x1064,
ATTR_REQUESTED_DEV_TYPE = 0x106a,
ATTR_EXTENSIBILITY_TEST = 0x10fa /* _NOT_ defined in the spec */
};
#define WPS_VENDOR_ID_WFA 14122
/* WFA Vendor Extension subelements */
enum {
WFA_ELEM_VERSION2 = 0x00,
WFA_ELEM_AUTHORIZEDMACS = 0x01,
WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
WFA_ELEM_SETTINGS_DELAY_TIME = 0x04,
WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05,
WFA_ELEM_MULTI_AP = 0x06
};
/* Device Password ID */
enum wps_dev_password_id {
DEV_PW_DEFAULT = 0x0000,
DEV_PW_USER_SPECIFIED = 0x0001,
DEV_PW_MACHINE_SPECIFIED = 0x0002,
DEV_PW_REKEY = 0x0003,
DEV_PW_PUSHBUTTON = 0x0004,
DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007,
DEV_PW_P2PS_DEFAULT = 0x0008
};
/* Message Type */
enum wps_msg_type {
WPS_Beacon = 0x01,
WPS_ProbeRequest = 0x02,
WPS_ProbeResponse = 0x03,
WPS_M1 = 0x04,
WPS_M2 = 0x05,
WPS_M2D = 0x06,
WPS_M3 = 0x07,
WPS_M4 = 0x08,
WPS_M5 = 0x09,
WPS_M6 = 0x0a,
WPS_M7 = 0x0b,
WPS_M8 = 0x0c,
WPS_WSC_ACK = 0x0d,
WPS_WSC_NACK = 0x0e,
WPS_WSC_DONE = 0x0f
};
/* Authentication Type Flags */
#define WPS_AUTH_OPEN 0x0001
#define WPS_AUTH_WPAPSK 0x0002
#define WPS_AUTH_SHARED 0x0004 /* deprecated */
#define WPS_AUTH_WPA 0x0008
#define WPS_AUTH_WPA2 0x0010
#define WPS_AUTH_WPA2PSK 0x0020
#define WPS_AUTH_TYPES (WPS_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
/* Encryption Type Flags */
#define WPS_ENCR_NONE 0x0001
#define WPS_ENCR_WEP 0x0002 /* deprecated */
#define WPS_ENCR_TKIP 0x0004
#define WPS_ENCR_AES 0x0008
#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
WPS_ENCR_AES)
/* Configuration Error */
enum wps_config_error {
WPS_CFG_NO_ERROR = 0,
WPS_CFG_OOB_IFACE_READ_ERROR = 1,
WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
WPS_CFG_SIGNAL_TOO_WEAK = 5,
WPS_CFG_NETWORK_AUTH_FAILURE = 6,
WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
WPS_CFG_NO_DHCP_RESPONSE = 8,
WPS_CFG_FAILED_DHCP_CONFIG = 9,
WPS_CFG_IP_ADDR_CONFLICT = 10,
WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
WPS_CFG_ROGUE_SUSPECTED = 13,
WPS_CFG_DEVICE_BUSY = 14,
WPS_CFG_SETUP_LOCKED = 15,
WPS_CFG_MSG_TIMEOUT = 16,
WPS_CFG_REG_SESS_TIMEOUT = 17,
WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18,
WPS_CFG_60G_CHAN_NOT_SUPPORTED = 19,
WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20
};
/* Vendor specific Error Indication for WPS event messages */
enum wps_error_indication {
WPS_EI_NO_ERROR,
WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED,
WPS_EI_SECURITY_WEP_PROHIBITED,
WPS_EI_AUTH_FAILURE,
NUM_WPS_EI_VALUES
};
/* RF Bands */
#define WPS_RF_24GHZ 0x01
#define WPS_RF_50GHZ 0x02
#define WPS_RF_60GHZ 0x04
/* Config Methods */
#define WPS_CONFIG_USBA 0x0001
#define WPS_CONFIG_ETHERNET 0x0002
#define WPS_CONFIG_LABEL 0x0004
#define WPS_CONFIG_DISPLAY 0x0008
#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
#define WPS_CONFIG_NFC_INTERFACE 0x0040
#define WPS_CONFIG_PUSHBUTTON 0x0080
#define WPS_CONFIG_KEYPAD 0x0100
#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
#define WPS_CONFIG_P2PS 0x1000
#define WPS_CONFIG_VIRT_DISPLAY 0x2008
#define WPS_CONFIG_PHY_DISPLAY 0x4008
/* Connection Type Flags */
#define WPS_CONN_ESS 0x01
#define WPS_CONN_IBSS 0x02
/* Wi-Fi Protected Setup State */
enum wps_state {
WPS_STATE_NOT_CONFIGURED = 1,
WPS_STATE_CONFIGURED = 2
};
/* Association State */
enum wps_assoc_state {
WPS_ASSOC_NOT_ASSOC = 0,
WPS_ASSOC_CONN_SUCCESS = 1,
WPS_ASSOC_CFG_FAILURE = 2,
WPS_ASSOC_FAILURE = 3,
WPS_ASSOC_IP_FAILURE = 4
};
#define WPS_DEV_OUI_WFA 0x0050f204
enum wps_dev_categ {
WPS_DEV_COMPUTER = 1,
WPS_DEV_INPUT = 2,
WPS_DEV_PRINTER = 3,
WPS_DEV_CAMERA = 4,
WPS_DEV_STORAGE = 5,
WPS_DEV_NETWORK_INFRA = 6,
WPS_DEV_DISPLAY = 7,
WPS_DEV_MULTIMEDIA = 8,
WPS_DEV_GAMING = 9,
WPS_DEV_PHONE = 10,
WPS_DEV_AUDIO = 11,
};
enum wps_dev_subcateg {
WPS_DEV_COMPUTER_PC = 1,
WPS_DEV_COMPUTER_SERVER = 2,
WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
WPS_DEV_COMPUTER_ULTRA_MOBILE = 4,
WPS_DEV_COMPUTER_NOTEBOOK = 5,
WPS_DEV_COMPUTER_DESKTOP = 6,
WPS_DEV_COMPUTER_MID = 7,
WPS_DEV_COMPUTER_NETBOOK = 8,
WPS_DEV_COMPUTER_TABLET = 9,
WPS_DEV_INPUT_KEYBOARD = 1,
WPS_DEV_INPUT_MOUSE = 2,
WPS_DEV_INPUT_JOYSTICK = 3,
WPS_DEV_INPUT_TRACKBALL = 4,
WPS_DEV_INPUT_GAMING = 5,
WPS_DEV_INPUT_REMOTE = 6,
WPS_DEV_INPUT_TOUCHSCREEN = 7,
WPS_DEV_INPUT_BIOMETRIC_READER = 8,
WPS_DEV_INPUT_BARCODE_READER = 9,
WPS_DEV_PRINTER_PRINTER = 1,
WPS_DEV_PRINTER_SCANNER = 2,
WPS_DEV_PRINTER_FAX = 3,
WPS_DEV_PRINTER_COPIER = 4,
WPS_DEV_PRINTER_ALL_IN_ONE = 5,
WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
WPS_DEV_CAMERA_VIDEO = 2,
WPS_DEV_CAMERA_WEB = 3,
WPS_DEV_CAMERA_SECURITY = 4,
WPS_DEV_STORAGE_NAS = 1,
WPS_DEV_NETWORK_INFRA_AP = 1,
WPS_DEV_NETWORK_INFRA_ROUTER = 2,
WPS_DEV_NETWORK_INFRA_SWITCH = 3,
WPS_DEV_NETWORK_INFRA_GATEWAY = 4,
WPS_DEV_NETWORK_INFRA_BRIDGE = 5,
WPS_DEV_DISPLAY_TV = 1,
WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
WPS_DEV_DISPLAY_PROJECTOR = 3,
WPS_DEV_DISPLAY_MONITOR = 4,
WPS_DEV_MULTIMEDIA_DAR = 1,
WPS_DEV_MULTIMEDIA_PVR = 2,
WPS_DEV_MULTIMEDIA_MCX = 3,
WPS_DEV_MULTIMEDIA_SET_TOP_BOX = 4,
WPS_DEV_MULTIMEDIA_MEDIA_SERVER = 5,
WPS_DEV_MULTIMEDIA_PORTABLE_VIDEO_PLAYER = 6,
WPS_DEV_GAMING_XBOX = 1,
WPS_DEV_GAMING_XBOX360 = 2,
WPS_DEV_GAMING_PLAYSTATION = 3,
WPS_DEV_GAMING_GAME_CONSOLE = 4,
WPS_DEV_GAMING_PORTABLE_DEVICE = 5,
WPS_DEV_PHONE_WINDOWS_MOBILE = 1,
WPS_DEV_PHONE_SINGLE_MODE = 2,
WPS_DEV_PHONE_DUAL_MODE = 3,
WPS_DEV_PHONE_SP_SINGLE_MODE = 4,
WPS_DEV_PHONE_SP_DUAL_MODE = 5,
WPS_DEV_AUDIO_TUNER_RECV = 1,
WPS_DEV_AUDIO_SPEAKERS = 2,
WPS_DEV_AUDIO_PMP = 3,
WPS_DEV_AUDIO_HEADSET = 4,
WPS_DEV_AUDIO_HEADPHONES = 5,
WPS_DEV_AUDIO_MICROPHONE = 6,
WPS_DEV_AUDIO_HOME_THEATRE = 7,
};
/* Request Type */
enum wps_request_type {
WPS_REQ_ENROLLEE_INFO = 0,
WPS_REQ_ENROLLEE = 1,
WPS_REQ_REGISTRAR = 2,
WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
};
/* Response Type */
enum wps_response_type {
WPS_RESP_ENROLLEE_INFO = 0,
WPS_RESP_ENROLLEE = 1,
WPS_RESP_REGISTRAR = 2,
WPS_RESP_AP = 3
};
/* Walk Time for push button configuration (in seconds) */
#define WPS_PBC_WALK_TIME 120
#define WPS_MAX_AUTHORIZED_MACS 5
#endif /* WPS_DEFS_H */
diff --git a/contrib/wpa/src/wps/wps_registrar.c b/contrib/wpa/src/wps/wps_registrar.c
index 173fbbd68aa0..9587293d0f88 100644
--- a/contrib/wpa/src/wps/wps_registrar.c
+++ b/contrib/wpa/src/wps/wps_registrar.c
@@ -1,3795 +1,3795 @@
/*
* Wi-Fi Protected Setup - Registrar
* Copyright (c) 2008-2016, 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"
#include "utils/common.h"
#include "utils/base64.h"
#include "utils/eloop.h"
#include "utils/uuid.h"
#include "utils/list.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_common.h"
#include "wps_i.h"
#include "wps_dev_attr.h"
#include "wps_upnp.h"
#include "wps_upnp_i.h"
#ifndef CONFIG_WPS_STRICT
#define WPS_WORKAROUNDS
#endif /* CONFIG_WPS_STRICT */
#ifdef CONFIG_WPS_NFC
struct wps_nfc_pw_token {
struct dl_list list;
u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
unsigned int peer_pk_hash_known:1;
u16 pw_id;
u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1];
size_t dev_pw_len;
int pk_hash_provided_oob; /* whether own PK hash was provided OOB */
};
static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token)
{
dl_list_del(&token->list);
bin_clear_free(token, sizeof(*token));
}
static void wps_free_nfc_pw_tokens(struct dl_list *tokens, u16 pw_id)
{
struct wps_nfc_pw_token *token, *prev;
dl_list_for_each_safe(token, prev, tokens, struct wps_nfc_pw_token,
list) {
if (pw_id == 0 || pw_id == token->pw_id)
wps_remove_nfc_pw_token(token);
}
}
static struct wps_nfc_pw_token * wps_get_nfc_pw_token(struct dl_list *tokens,
u16 pw_id)
{
struct wps_nfc_pw_token *token;
dl_list_for_each(token, tokens, struct wps_nfc_pw_token, list) {
if (pw_id == token->pw_id)
return token;
}
return NULL;
}
#else /* CONFIG_WPS_NFC */
#define wps_free_nfc_pw_tokens(t, p) do { } while (0)
#endif /* CONFIG_WPS_NFC */
struct wps_uuid_pin {
struct dl_list list;
u8 uuid[WPS_UUID_LEN];
int wildcard_uuid;
u8 *pin;
size_t pin_len;
#define PIN_LOCKED BIT(0)
#define PIN_EXPIRES BIT(1)
int flags;
struct os_reltime expiration;
u8 enrollee_addr[ETH_ALEN];
};
static void wps_free_pin(struct wps_uuid_pin *pin)
{
bin_clear_free(pin->pin, pin->pin_len);
os_free(pin);
}
static void wps_remove_pin(struct wps_uuid_pin *pin)
{
dl_list_del(&pin->list);
wps_free_pin(pin);
}
static void wps_free_pins(struct dl_list *pins)
{
struct wps_uuid_pin *pin, *prev;
dl_list_for_each_safe(pin, prev, pins, struct wps_uuid_pin, list)
wps_remove_pin(pin);
}
struct wps_pbc_session {
struct wps_pbc_session *next;
u8 addr[ETH_ALEN];
u8 uuid_e[WPS_UUID_LEN];
struct os_reltime timestamp;
};
static void wps_free_pbc_sessions(struct wps_pbc_session *pbc)
{
struct wps_pbc_session *prev;
while (pbc) {
prev = pbc;
pbc = pbc->next;
os_free(prev);
}
}
struct wps_registrar_device {
struct wps_registrar_device *next;
struct wps_device_data dev;
u8 uuid[WPS_UUID_LEN];
};
struct wps_registrar {
struct wps_context *wps;
int pbc;
int selected_registrar;
int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
const u8 *psk, size_t psk_len);
int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
struct wpabuf *probe_resp_ie);
void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
const struct wps_device_data *dev);
void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
const u8 *uuid_e, const u8 *dev_pw,
size_t dev_pw_len);
void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
u16 sel_reg_config_methods);
void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
const u8 *pri_dev_type, u16 config_methods,
u16 dev_password_id, u8 request_type,
const char *dev_name);
int (*lookup_pskfile_cb)(void *ctx, const u8 *mac_addr, const u8 **psk);
void *cb_ctx;
struct dl_list pins;
struct dl_list nfc_pw_tokens;
struct wps_pbc_session *pbc_sessions;
int skip_cred_build;
struct wpabuf *extra_cred;
int disable_auto_conf;
int sel_reg_union;
int sel_reg_dev_password_id_override;
int sel_reg_config_methods_override;
int dualband;
int force_per_enrollee_psk;
struct wps_registrar_device *devices;
int force_pbc_overlap;
u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN];
u8 p2p_dev_addr[ETH_ALEN];
u8 pbc_ignore_uuid[WPS_UUID_LEN];
#ifdef WPS_WORKAROUNDS
struct os_reltime pbc_ignore_start;
#endif /* WPS_WORKAROUNDS */
/**
* multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul
* enrollee
*
* This SSID is used by the Registrar to fill in information for
* Credentials when the enrollee advertises it is a Multi-AP backhaul
* STA.
*/
u8 multi_ap_backhaul_ssid[SSID_MAX_LEN];
/**
* multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in
* octets
*/
size_t multi_ap_backhaul_ssid_len;
/**
* multi_ap_backhaul_network_key - The Network Key (PSK) for the
* Multi-AP backhaul enrollee.
*
* This key can be either the ASCII passphrase (8..63 characters) or the
* 32-octet PSK (64 hex characters).
*/
u8 *multi_ap_backhaul_network_key;
/**
* multi_ap_backhaul_network_key_len - Length of
* multi_ap_backhaul_network_key in octets
*/
size_t multi_ap_backhaul_network_key_len;
};
static int wps_set_ie(struct wps_registrar *reg);
static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
static void wps_registrar_set_selected_timeout(void *eloop_ctx,
void *timeout_ctx);
static void wps_registrar_remove_pin(struct wps_registrar *reg,
struct wps_uuid_pin *pin);
static void wps_registrar_add_authorized_mac(struct wps_registrar *reg,
const u8 *addr)
{
int i;
wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC " MACSTR,
MAC2STR(addr));
for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was "
"already in the list");
return; /* already in list */
}
for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--)
os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1],
ETH_ALEN);
os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN);
wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
(u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
}
static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg,
const u8 *addr)
{
int i;
wpa_printf(MSG_DEBUG, "WPS: Remove authorized MAC " MACSTR,
MAC2STR(addr));
for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) {
if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0)
break;
}
if (i == WPS_MAX_AUTHORIZED_MACS) {
wpa_printf(MSG_DEBUG, "WPS: Authorized MAC was not in the "
"list");
return; /* not in the list */
}
for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++)
os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1],
ETH_ALEN);
os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0,
ETH_ALEN);
wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs",
(u8 *) reg->authorized_macs, sizeof(reg->authorized_macs));
}
static void wps_free_devices(struct wps_registrar_device *dev)
{
struct wps_registrar_device *prev;
while (dev) {
prev = dev;
dev = dev->next;
wps_device_data_free(&prev->dev);
os_free(prev);
}
}
static struct wps_registrar_device * wps_device_get(struct wps_registrar *reg,
const u8 *addr)
{
struct wps_registrar_device *dev;
for (dev = reg->devices; dev; dev = dev->next) {
if (os_memcmp(dev->dev.mac_addr, addr, ETH_ALEN) == 0)
return dev;
}
return NULL;
}
static void wps_device_clone_data(struct wps_device_data *dst,
struct wps_device_data *src)
{
os_memcpy(dst->mac_addr, src->mac_addr, ETH_ALEN);
os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
#define WPS_STRDUP(n) \
os_free(dst->n); \
dst->n = src->n ? os_strdup(src->n) : NULL
WPS_STRDUP(device_name);
WPS_STRDUP(manufacturer);
WPS_STRDUP(model_name);
WPS_STRDUP(model_number);
WPS_STRDUP(serial_number);
#undef WPS_STRDUP
}
int wps_device_store(struct wps_registrar *reg,
struct wps_device_data *dev, const u8 *uuid)
{
struct wps_registrar_device *d;
d = wps_device_get(reg, dev->mac_addr);
if (d == NULL) {
d = os_zalloc(sizeof(*d));
if (d == NULL)
return -1;
d->next = reg->devices;
reg->devices = d;
}
wps_device_clone_data(&d->dev, dev);
os_memcpy(d->uuid, uuid, WPS_UUID_LEN);
return 0;
}
static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
const u8 *addr, const u8 *uuid_e)
{
struct wps_pbc_session *pbc, *prev = NULL;
struct os_reltime now;
os_get_reltime(&now);
pbc = reg->pbc_sessions;
while (pbc) {
if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
if (prev)
prev->next = pbc->next;
else
reg->pbc_sessions = pbc->next;
break;
}
prev = pbc;
pbc = pbc->next;
}
if (!pbc) {
pbc = os_zalloc(sizeof(*pbc));
if (pbc == NULL)
return;
os_memcpy(pbc->addr, addr, ETH_ALEN);
if (uuid_e)
os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN);
}
pbc->next = reg->pbc_sessions;
reg->pbc_sessions = pbc;
pbc->timestamp = now;
/* remove entries that have timed out */
prev = pbc;
pbc = pbc->next;
while (pbc) {
if (os_reltime_expired(&now, &pbc->timestamp,
WPS_PBC_WALK_TIME)) {
prev->next = NULL;
wps_free_pbc_sessions(pbc);
break;
}
prev = pbc;
pbc = pbc->next;
}
}
static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
const u8 *uuid_e,
const u8 *p2p_dev_addr)
{
struct wps_pbc_session *pbc, *prev = NULL, *tmp;
pbc = reg->pbc_sessions;
while (pbc) {
if (os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0 ||
(p2p_dev_addr && !is_zero_ether_addr(reg->p2p_dev_addr) &&
os_memcmp(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
0)) {
if (prev)
prev->next = pbc->next;
else
reg->pbc_sessions = pbc->next;
tmp = pbc;
pbc = pbc->next;
wpa_printf(MSG_DEBUG, "WPS: Removing PBC session for "
"addr=" MACSTR, MAC2STR(tmp->addr));
wpa_hexdump(MSG_DEBUG, "WPS: Removed UUID-E",
tmp->uuid_e, WPS_UUID_LEN);
os_free(tmp);
continue;
}
prev = pbc;
pbc = pbc->next;
}
}
int wps_registrar_pbc_overlap(struct wps_registrar *reg,
const u8 *addr, const u8 *uuid_e)
{
int count = 0;
struct wps_pbc_session *pbc;
struct wps_pbc_session *first = NULL;
struct os_reltime now;
os_get_reltime(&now);
wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap");
if (uuid_e) {
wpa_printf(MSG_DEBUG, "WPS: Add one for the requested UUID");
wpa_hexdump(MSG_DEBUG, "WPS: Requested UUID",
uuid_e, WPS_UUID_LEN);
count++;
}
for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
wpa_printf(MSG_DEBUG, "WPS: Consider PBC session with " MACSTR,
MAC2STR(pbc->addr));
wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
pbc->uuid_e, WPS_UUID_LEN);
if (os_reltime_expired(&now, &pbc->timestamp,
WPS_PBC_WALK_TIME)) {
wpa_printf(MSG_DEBUG, "WPS: PBC walk time has expired");
break;
}
if (first &&
os_memcmp(pbc->uuid_e, first->uuid_e, WPS_UUID_LEN) == 0) {
wpa_printf(MSG_DEBUG, "WPS: Same Enrollee");
continue; /* same Enrollee */
}
if (uuid_e == NULL ||
os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN)) {
wpa_printf(MSG_DEBUG, "WPS: New Enrollee");
count++;
}
if (first == NULL)
first = pbc;
}
wpa_printf(MSG_DEBUG, "WPS: %u active PBC session(s) found", count);
return count > 1 ? 1 : 0;
}
static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * Wi-Fi Protected Setup State (%d)",
wps->wps_state);
wpabuf_put_be16(msg, ATTR_WPS_STATE);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, wps->wps_state);
return 0;
}
#ifdef CONFIG_WPS_UPNP
static void wps_registrar_free_pending_m2(struct wps_context *wps)
{
struct upnp_pending_message *p, *p2, *prev = NULL;
p = wps->upnp_msgs;
while (p) {
if (p->type == WPS_M2 || p->type == WPS_M2D) {
if (prev == NULL)
wps->upnp_msgs = p->next;
else
prev->next = p->next;
wpa_printf(MSG_DEBUG, "WPS UPnP: Drop pending M2/M2D");
p2 = p;
p = p->next;
wpabuf_free(p2->msg);
os_free(p2);
continue;
}
prev = p;
p = p->next;
}
}
#endif /* CONFIG_WPS_UPNP */
static int wps_build_ap_setup_locked(struct wps_context *wps,
struct wpabuf *msg)
{
if (wps->ap_setup_locked && wps->ap_setup_locked != 2) {
wpa_printf(MSG_DEBUG, "WPS: * AP Setup Locked");
wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, 1);
}
return 0;
}
static int wps_build_selected_registrar(struct wps_registrar *reg,
struct wpabuf *msg)
{
if (!reg->sel_reg_union)
return 0;
wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar");
wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, 1);
return 0;
}
static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
struct wpabuf *msg)
{
u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
if (!reg->sel_reg_union)
return 0;
if (reg->sel_reg_dev_password_id_override >= 0)
id = reg->sel_reg_dev_password_id_override;
wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id);
wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, id);
return 0;
}
static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg,
struct wpabuf *msg)
{
u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
if (!reg->sel_reg_union)
return 0;
if (reg->sel_reg_dev_password_id_override >= 0)
id = reg->sel_reg_dev_password_id_override;
if (id != DEV_PW_PUSHBUTTON || !reg->dualband)
return 0;
return wps_build_uuid_e(msg, reg->wps->uuid);
}
static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
{
*methods |= WPS_CONFIG_PUSHBUTTON;
if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
WPS_CONFIG_VIRT_PUSHBUTTON)
*methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
if ((conf_methods & WPS_CONFIG_PHY_PUSHBUTTON) ==
WPS_CONFIG_PHY_PUSHBUTTON)
*methods |= WPS_CONFIG_PHY_PUSHBUTTON;
if ((*methods & WPS_CONFIG_VIRT_PUSHBUTTON) !=
WPS_CONFIG_VIRT_PUSHBUTTON &&
(*methods & WPS_CONFIG_PHY_PUSHBUTTON) !=
WPS_CONFIG_PHY_PUSHBUTTON) {
/*
* Required to include virtual/physical flag, but we were not
* configured with push button type, so have to default to one
* of them.
*/
*methods |= WPS_CONFIG_PHY_PUSHBUTTON;
}
}
static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
struct wpabuf *msg)
{
u16 methods;
if (!reg->sel_reg_union)
return 0;
methods = reg->wps->config_methods;
methods &= ~WPS_CONFIG_PUSHBUTTON;
methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
WPS_CONFIG_PHY_PUSHBUTTON);
if (reg->pbc)
wps_set_pushbutton(&methods, reg->wps->config_methods);
if (reg->sel_reg_config_methods_override >= 0)
methods = reg->sel_reg_config_methods_override;
wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar Config Methods (%x)",
methods);
wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, methods);
return 0;
}
static int wps_build_probe_config_methods(struct wps_registrar *reg,
struct wpabuf *msg)
{
u16 methods;
/*
* These are the methods that the AP supports as an Enrollee for adding
* external Registrars.
*/
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
WPS_CONFIG_PHY_PUSHBUTTON);
wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods);
wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, methods);
return 0;
}
static int wps_build_config_methods_r(struct wps_registrar *reg,
struct wpabuf *msg)
{
return wps_build_config_methods(msg, reg->wps->config_methods);
}
const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count)
{
*count = 0;
while (*count < WPS_MAX_AUTHORIZED_MACS) {
if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
break;
(*count)++;
}
return (const u8 *) reg->authorized_macs_union;
}
/**
* wps_registrar_init - Initialize WPS Registrar data
* @wps: Pointer to longterm WPS context
* @cfg: Registrar configuration
* Returns: Pointer to allocated Registrar data or %NULL on failure
*
* This function is used to initialize WPS Registrar functionality. It can be
* used for a single Registrar run (e.g., when run in a supplicant) or multiple
* runs (e.g., when run as an internal Registrar in an AP). Caller is
* responsible for freeing the returned data with wps_registrar_deinit() when
* Registrar functionality is not needed anymore.
*/
struct wps_registrar *
wps_registrar_init(struct wps_context *wps,
const struct wps_registrar_config *cfg)
{
struct wps_registrar *reg = os_zalloc(sizeof(*reg));
if (reg == NULL)
return NULL;
dl_list_init(&reg->pins);
dl_list_init(&reg->nfc_pw_tokens);
reg->wps = wps;
reg->new_psk_cb = cfg->new_psk_cb;
reg->set_ie_cb = cfg->set_ie_cb;
reg->pin_needed_cb = cfg->pin_needed_cb;
reg->reg_success_cb = cfg->reg_success_cb;
reg->set_sel_reg_cb = cfg->set_sel_reg_cb;
reg->enrollee_seen_cb = cfg->enrollee_seen_cb;
reg->lookup_pskfile_cb = cfg->lookup_pskfile_cb;
reg->cb_ctx = cfg->cb_ctx;
reg->skip_cred_build = cfg->skip_cred_build;
if (cfg->extra_cred) {
reg->extra_cred = wpabuf_alloc_copy(cfg->extra_cred,
cfg->extra_cred_len);
if (reg->extra_cred == NULL) {
os_free(reg);
return NULL;
}
}
reg->disable_auto_conf = cfg->disable_auto_conf;
reg->sel_reg_dev_password_id_override = -1;
reg->sel_reg_config_methods_override = -1;
reg->dualband = cfg->dualband;
reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk;
if (cfg->multi_ap_backhaul_ssid) {
os_memcpy(reg->multi_ap_backhaul_ssid,
cfg->multi_ap_backhaul_ssid,
cfg->multi_ap_backhaul_ssid_len);
reg->multi_ap_backhaul_ssid_len =
cfg->multi_ap_backhaul_ssid_len;
}
if (cfg->multi_ap_backhaul_network_key) {
reg->multi_ap_backhaul_network_key =
os_memdup(cfg->multi_ap_backhaul_network_key,
cfg->multi_ap_backhaul_network_key_len);
if (reg->multi_ap_backhaul_network_key)
reg->multi_ap_backhaul_network_key_len =
cfg->multi_ap_backhaul_network_key_len;
}
if (wps_set_ie(reg)) {
wps_registrar_deinit(reg);
return NULL;
}
return reg;
}
void wps_registrar_flush(struct wps_registrar *reg)
{
if (reg == NULL)
return;
wps_free_pins(&reg->pins);
wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
wps_free_pbc_sessions(reg->pbc_sessions);
reg->pbc_sessions = NULL;
wps_free_devices(reg->devices);
reg->devices = NULL;
#ifdef WPS_WORKAROUNDS
reg->pbc_ignore_start.sec = 0;
#endif /* WPS_WORKAROUNDS */
}
/**
* wps_registrar_deinit - Deinitialize WPS Registrar data
* @reg: Registrar data from wps_registrar_init()
*/
void wps_registrar_deinit(struct wps_registrar *reg)
{
if (reg == NULL)
return;
eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
wps_registrar_flush(reg);
wpabuf_clear_free(reg->extra_cred);
bin_clear_free(reg->multi_ap_backhaul_network_key,
reg->multi_ap_backhaul_network_key_len);
os_free(reg);
}
static void wps_registrar_invalidate_unused(struct wps_registrar *reg)
{
struct wps_uuid_pin *pin;
dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
if (pin->wildcard_uuid == 1 && !(pin->flags & PIN_LOCKED)) {
wpa_printf(MSG_DEBUG, "WPS: Invalidate previously "
"configured wildcard PIN");
wps_registrar_remove_pin(reg, pin);
break;
}
}
}
/**
* wps_registrar_add_pin - Configure a new PIN for Registrar
* @reg: Registrar data from wps_registrar_init()
* @addr: Enrollee MAC address or %NULL if not known
* @uuid: UUID-E or %NULL for wildcard (any UUID)
* @pin: PIN (Device Password)
* @pin_len: Length of pin in octets
* @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout
* Returns: 0 on success, -1 on failure
*/
int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
const u8 *uuid, const u8 *pin, size_t pin_len,
int timeout)
{
struct wps_uuid_pin *p;
p = os_zalloc(sizeof(*p));
if (p == NULL)
return -1;
if (addr)
os_memcpy(p->enrollee_addr, addr, ETH_ALEN);
if (uuid == NULL)
p->wildcard_uuid = 1;
else
os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
p->pin = os_memdup(pin, pin_len);
if (p->pin == NULL) {
os_free(p);
return -1;
}
p->pin_len = pin_len;
if (timeout) {
p->flags |= PIN_EXPIRES;
os_get_reltime(&p->expiration);
p->expiration.sec += timeout;
}
if (p->wildcard_uuid)
wps_registrar_invalidate_unused(reg);
dl_list_add(&reg->pins, &p->list);
wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)",
timeout);
wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN);
wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
reg->selected_registrar = 1;
reg->pbc = 0;
if (addr)
wps_registrar_add_authorized_mac(reg, addr);
else
wps_registrar_add_authorized_mac(
reg, (u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg, 0);
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
wps_registrar_set_selected_timeout,
reg, NULL);
return 0;
}
static void wps_registrar_remove_pin(struct wps_registrar *reg,
struct wps_uuid_pin *pin)
{
u8 *addr;
u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
if (is_zero_ether_addr(pin->enrollee_addr))
addr = bcast;
else
addr = pin->enrollee_addr;
wps_registrar_remove_authorized_mac(reg, addr);
wps_remove_pin(pin);
wps_registrar_selected_registrar_changed(reg, 0);
}
static void wps_registrar_expire_pins(struct wps_registrar *reg)
{
struct wps_uuid_pin *pin, *prev;
struct os_reltime now;
os_get_reltime(&now);
dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
{
if ((pin->flags & PIN_EXPIRES) &&
os_reltime_before(&pin->expiration, &now)) {
wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
pin->uuid, WPS_UUID_LEN);
wps_registrar_remove_pin(reg, pin);
}
}
}
/**
* wps_registrar_invalidate_wildcard_pin - Invalidate a wildcard PIN
* @reg: Registrar data from wps_registrar_init()
* @dev_pw: PIN to search for or %NULL to match any
* @dev_pw_len: Length of dev_pw in octets
* Returns: 0 on success, -1 if not wildcard PIN is enabled
*/
static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg,
const u8 *dev_pw,
size_t dev_pw_len)
{
struct wps_uuid_pin *pin, *prev;
dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
{
if (dev_pw && pin->pin &&
(dev_pw_len != pin->pin_len ||
os_memcmp_const(dev_pw, pin->pin, dev_pw_len) != 0))
continue; /* different PIN */
if (pin->wildcard_uuid) {
wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
pin->uuid, WPS_UUID_LEN);
wps_registrar_remove_pin(reg, pin);
return 0;
}
}
return -1;
}
/**
* wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E
* @reg: Registrar data from wps_registrar_init()
* @uuid: UUID-E
* Returns: 0 on success, -1 on failure (e.g., PIN not found)
*/
int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
{
struct wps_uuid_pin *pin, *prev;
dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
{
if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
pin->uuid, WPS_UUID_LEN);
wps_registrar_remove_pin(reg, pin);
return 0;
}
}
return -1;
}
static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
const u8 *uuid, size_t *pin_len)
{
struct wps_uuid_pin *pin, *found = NULL;
int wildcard = 0;
wps_registrar_expire_pins(reg);
dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
if (!pin->wildcard_uuid &&
os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
found = pin;
break;
}
}
if (!found) {
/* Check for wildcard UUIDs since none of the UUID-specific
* PINs matched */
dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
if (pin->wildcard_uuid == 1 ||
pin->wildcard_uuid == 2) {
wpa_printf(MSG_DEBUG, "WPS: Found a wildcard "
"PIN. Assigned it for this UUID-E");
wildcard = 1;
os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
found = pin;
break;
}
}
}
if (!found)
return NULL;
/*
* Lock the PIN to avoid attacks based on concurrent re-use of the PIN
* that could otherwise avoid PIN invalidations.
*/
if (found->flags & PIN_LOCKED) {
wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not "
"allow concurrent re-use");
return NULL;
}
*pin_len = found->pin_len;
found->flags |= PIN_LOCKED;
if (wildcard)
found->wildcard_uuid++;
return found->pin;
}
/**
* wps_registrar_unlock_pin - Unlock a PIN for a specific UUID-E
* @reg: Registrar data from wps_registrar_init()
* @uuid: UUID-E
* Returns: 0 on success, -1 on failure
*
* PINs are locked to enforce only one concurrent use. This function unlocks a
* PIN to allow it to be used again. If the specified PIN was configured using
* a wildcard UUID, it will be removed instead of allowing multiple uses.
*/
int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid)
{
struct wps_uuid_pin *pin;
dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
if (pin->wildcard_uuid == 3) {
wpa_printf(MSG_DEBUG, "WPS: Invalidating used "
"wildcard PIN");
return wps_registrar_invalidate_pin(reg, uuid);
}
pin->flags &= ~PIN_LOCKED;
return 0;
}
}
return -1;
}
static void wps_registrar_stop_pbc(struct wps_registrar *reg)
{
reg->selected_registrar = 0;
reg->pbc = 0;
os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
wps_registrar_remove_authorized_mac(reg,
(u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg, 0);
}
static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wps_registrar *reg = eloop_ctx;
wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode");
wps_pbc_timeout_event(reg->wps);
wps_registrar_stop_pbc(reg);
}
/**
* wps_registrar_button_pushed - Notify Registrar that AP button was pushed
* @reg: Registrar data from wps_registrar_init()
* @p2p_dev_addr: Limit allowed PBC devices to the specified P2P device, %NULL
* indicates no such filtering
* Returns: 0 on success, -1 on failure, -2 on session overlap
*
* This function is called on an AP when a push button is pushed to activate
* PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout
* or when a PBC registration is completed. If more than one Enrollee in active
* PBC mode has been detected during the monitor time (previous 2 minutes), the
* PBC mode is not activated and -2 is returned to indicate session overlap.
* This is skipped if a specific Enrollee is selected.
*/
int wps_registrar_button_pushed(struct wps_registrar *reg,
const u8 *p2p_dev_addr)
{
if (p2p_dev_addr == NULL &&
wps_registrar_pbc_overlap(reg, NULL, NULL)) {
wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC "
"mode");
wps_pbc_overlap_event(reg->wps);
return -2;
}
wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started");
reg->force_pbc_overlap = 0;
reg->selected_registrar = 1;
reg->pbc = 1;
if (p2p_dev_addr)
os_memcpy(reg->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
else
os_memset(reg->p2p_dev_addr, 0, ETH_ALEN);
wps_registrar_add_authorized_mac(reg,
(u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg, 0);
wps_pbc_active_event(reg->wps);
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
reg, NULL);
return 0;
}
static void wps_registrar_pbc_completed(struct wps_registrar *reg)
{
wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode");
eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
wps_registrar_stop_pbc(reg);
wps_pbc_disable_event(reg->wps);
}
static void wps_registrar_pin_completed(struct wps_registrar *reg)
{
wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar");
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
reg->selected_registrar = 0;
wps_registrar_selected_registrar_changed(reg, 0);
}
void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
const u8 *dev_pw, size_t dev_pw_len)
{
if (registrar->pbc) {
wps_registrar_remove_pbc_session(registrar,
uuid_e, NULL);
wps_registrar_pbc_completed(registrar);
#ifdef WPS_WORKAROUNDS
os_get_reltime(&registrar->pbc_ignore_start);
#endif /* WPS_WORKAROUNDS */
os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN);
} else {
wps_registrar_pin_completed(registrar);
}
if (dev_pw &&
wps_registrar_invalidate_wildcard_pin(registrar, dev_pw,
dev_pw_len) == 0) {
wpa_hexdump_key(MSG_DEBUG, "WPS: Invalidated wildcard PIN",
dev_pw, dev_pw_len);
}
}
int wps_registrar_wps_cancel(struct wps_registrar *reg)
{
if (reg->pbc) {
wpa_printf(MSG_DEBUG, "WPS: PBC is set - cancelling it");
wps_registrar_pbc_timeout(reg, NULL);
eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
return 1;
} else if (reg->selected_registrar) {
/* PIN Method */
wpa_printf(MSG_DEBUG, "WPS: PIN is set - cancelling it");
wps_registrar_pin_completed(reg);
wps_registrar_invalidate_wildcard_pin(reg, NULL, 0);
return 1;
}
return 0;
}
/**
* wps_registrar_probe_req_rx - Notify Registrar of Probe Request
* @reg: Registrar data from wps_registrar_init()
* @addr: MAC address of the Probe Request sender
* @wps_data: WPS IE contents
*
* This function is called on an AP when a Probe Request with WPS IE is
* received. This is used to track PBC mode use and to detect possible overlap
* situation with other WPS APs.
*/
void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
const struct wpabuf *wps_data,
int p2p_wildcard)
{
struct wps_parse_attr attr;
int skip_add = 0;
wpa_hexdump_buf(MSG_MSGDUMP,
"WPS: Probe Request with WPS data received",
wps_data);
if (wps_parse_msg(wps_data, &attr) < 0)
return;
if (attr.config_methods == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
"Probe Request");
return;
}
if (attr.dev_password_id == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Device Password Id attribute "
"in Probe Request");
return;
}
if (reg->enrollee_seen_cb && attr.uuid_e &&
attr.primary_dev_type && attr.request_type && !p2p_wildcard) {
char *dev_name = NULL;
if (attr.dev_name) {
dev_name = os_zalloc(attr.dev_name_len + 1);
if (dev_name) {
os_memcpy(dev_name, attr.dev_name,
attr.dev_name_len);
}
}
reg->enrollee_seen_cb(reg->cb_ctx, addr, attr.uuid_e,
attr.primary_dev_type,
WPA_GET_BE16(attr.config_methods),
WPA_GET_BE16(attr.dev_password_id),
*attr.request_type, dev_name);
os_free(dev_name);
}
if (WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
return; /* Not PBC */
wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from "
MACSTR, MAC2STR(addr));
if (attr.uuid_e == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Probe Request WPS IE: No "
"UUID-E included");
return;
}
wpa_hexdump(MSG_DEBUG, "WPS: UUID-E from Probe Request", attr.uuid_e,
WPS_UUID_LEN);
#ifdef WPS_WORKAROUNDS
if (reg->pbc_ignore_start.sec &&
os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) {
struct os_reltime now, dur;
os_get_reltime(&now);
os_reltime_sub(&now, &reg->pbc_ignore_start, &dur);
if (dur.sec >= 0 && dur.sec < 5) {
wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation "
"based on Probe Request from the Enrollee "
"that just completed PBC provisioning");
skip_add = 1;
} else
reg->pbc_ignore_start.sec = 0;
}
#endif /* WPS_WORKAROUNDS */
if (!skip_add)
wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) {
wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected");
reg->force_pbc_overlap = 1;
wps_pbc_overlap_event(reg->wps);
}
}
int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len)
{
if (reg->new_psk_cb == NULL)
return 0;
return reg->new_psk_cb(reg->cb_ctx, mac_addr, p2p_dev_addr, psk,
psk_len);
}
static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e,
const struct wps_device_data *dev)
{
if (reg->pin_needed_cb == NULL)
return;
reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev);
}
static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
const u8 *uuid_e, const u8 *dev_pw,
size_t dev_pw_len)
{
if (reg->reg_success_cb == NULL)
return;
reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e, dev_pw, dev_pw_len);
}
static int wps_cb_set_ie(struct wps_registrar *reg, struct wpabuf *beacon_ie,
struct wpabuf *probe_resp_ie)
{
return reg->set_ie_cb(reg->cb_ctx, beacon_ie, probe_resp_ie);
}
static void wps_cb_set_sel_reg(struct wps_registrar *reg)
{
u16 methods = 0;
if (reg->set_sel_reg_cb == NULL)
return;
if (reg->selected_registrar) {
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
WPS_CONFIG_PHY_PUSHBUTTON);
if (reg->pbc)
wps_set_pushbutton(&methods, reg->wps->config_methods);
}
wpa_printf(MSG_DEBUG, "WPS: wps_cb_set_sel_reg: sel_reg=%d "
"config_methods=0x%x pbc=%d methods=0x%x",
reg->selected_registrar, reg->wps->config_methods,
reg->pbc, methods);
reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
methods);
}
static int wps_cp_lookup_pskfile(struct wps_registrar *reg, const u8 *mac_addr,
const u8 **psk)
{
if (!reg->lookup_pskfile_cb)
return 0;
return reg->lookup_pskfile_cb(reg->cb_ctx, mac_addr, psk);
}
static int wps_set_ie(struct wps_registrar *reg)
{
struct wpabuf *beacon;
struct wpabuf *probe;
const u8 *auth_macs;
size_t count;
size_t vendor_len = 0;
int i;
if (reg->set_ie_cb == NULL)
return 0;
for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
if (reg->wps->dev.vendor_ext[i]) {
vendor_len += 2 + 2;
vendor_len += wpabuf_len(reg->wps->dev.vendor_ext[i]);
}
}
beacon = wpabuf_alloc(400 + vendor_len);
probe = wpabuf_alloc(500 + vendor_len);
if (!beacon || !probe)
goto fail;
auth_macs = wps_authorized_macs(reg, &count);
wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs");
if (wps_build_version(beacon) ||
wps_build_wps_state(reg->wps, beacon) ||
wps_build_ap_setup_locked(reg->wps, beacon) ||
wps_build_selected_registrar(reg, beacon) ||
wps_build_sel_reg_dev_password_id(reg, beacon) ||
wps_build_sel_reg_config_methods(reg, beacon) ||
wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
(reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon, 0)) ||
wps_build_wfa_ext(beacon, 0, auth_macs, count, 0) ||
wps_build_vendor_ext(&reg->wps->dev, beacon) ||
wps_build_application_ext(&reg->wps->dev, beacon))
goto fail;
#ifdef CONFIG_P2P
if (wps_build_dev_name(&reg->wps->dev, beacon) ||
wps_build_primary_dev_type(&reg->wps->dev, beacon))
goto fail;
#endif /* CONFIG_P2P */
wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs");
if (wps_build_version(probe) ||
wps_build_wps_state(reg->wps, probe) ||
wps_build_ap_setup_locked(reg->wps, probe) ||
wps_build_selected_registrar(reg, probe) ||
wps_build_sel_reg_dev_password_id(reg, probe) ||
wps_build_sel_reg_config_methods(reg, probe) ||
wps_build_resp_type(probe, reg->wps->ap ? WPS_RESP_AP :
WPS_RESP_REGISTRAR) ||
wps_build_uuid_e(probe, reg->wps->uuid) ||
wps_build_device_attrs(&reg->wps->dev, probe) ||
wps_build_probe_config_methods(reg, probe) ||
(reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe, 0)) ||
wps_build_wfa_ext(probe, 0, auth_macs, count, 0) ||
wps_build_vendor_ext(&reg->wps->dev, probe) ||
wps_build_application_ext(&reg->wps->dev, probe))
goto fail;
beacon = wps_ie_encapsulate(beacon);
probe = wps_ie_encapsulate(probe);
if (!beacon || !probe)
goto fail;
return wps_cb_set_ie(reg, beacon, probe);
fail:
wpabuf_free(beacon);
wpabuf_free(probe);
return -1;
}
static int wps_get_dev_password(struct wps_data *wps)
{
const u8 *pin;
size_t pin_len = 0;
bin_clear_free(wps->dev_password, wps->dev_password_len);
wps->dev_password = NULL;
if (wps->pbc) {
wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
pin = (const u8 *) "00000000";
pin_len = 8;
#ifdef CONFIG_WPS_NFC
} else if (wps->nfc_pw_token) {
if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
{
wpa_printf(MSG_DEBUG, "WPS: Using NFC connection "
"handover and abbreviated WPS handshake "
"without Device Password");
return 0;
}
wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
"Password Token");
pin = wps->nfc_pw_token->dev_pw;
pin_len = wps->nfc_pw_token->dev_pw_len;
} else if (wps->dev_pw_id >= 0x10 &&
wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
wps->wps->ap_nfc_dev_pw) {
wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token");
pin = wpabuf_head(wps->wps->ap_nfc_dev_pw);
pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw);
#endif /* CONFIG_WPS_NFC */
} else {
pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
&pin_len);
if (pin && wps->dev_pw_id >= 0x10) {
wpa_printf(MSG_DEBUG, "WPS: No match for OOB Device "
"Password ID, but PIN found");
/*
* See whether Enrollee is willing to use PIN instead.
*/
wps->dev_pw_id = DEV_PW_DEFAULT;
}
}
if (pin == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
"the Enrollee (context %p registrar %p)",
wps->wps, wps->wps->registrar);
wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e,
&wps->peer_dev);
return -1;
}
wps->dev_password = os_memdup(pin, pin_len);
if (wps->dev_password == NULL)
return -1;
wps->dev_password_len = pin_len;
return 0;
}
static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * UUID-R");
wpabuf_put_be16(msg, ATTR_UUID_R);
wpabuf_put_be16(msg, WPS_UUID_LEN);
wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN);
return 0;
}
static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
{
u8 *hash;
const u8 *addr[4];
size_t len[4];
if (random_get_bytes(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
return -1;
wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
"R-Hash derivation");
return -1;
}
wpa_printf(MSG_DEBUG, "WPS: * R-Hash1");
wpabuf_put_be16(msg, ATTR_R_HASH1);
wpabuf_put_be16(msg, SHA256_MAC_LEN);
hash = wpabuf_put(msg, SHA256_MAC_LEN);
/* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
addr[0] = wps->snonce;
len[0] = WPS_SECRET_NONCE_LEN;
addr[1] = wps->psk1;
len[1] = WPS_PSK_LEN;
addr[2] = wpabuf_head(wps->dh_pubkey_e);
len[2] = wpabuf_len(wps->dh_pubkey_e);
addr[3] = wpabuf_head(wps->dh_pubkey_r);
len[3] = wpabuf_len(wps->dh_pubkey_r);
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN);
wpa_printf(MSG_DEBUG, "WPS: * R-Hash2");
wpabuf_put_be16(msg, ATTR_R_HASH2);
wpabuf_put_be16(msg, SHA256_MAC_LEN);
hash = wpabuf_put(msg, SHA256_MAC_LEN);
/* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
addr[1] = wps->psk2;
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN);
return 0;
}
static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * R-SNonce1");
wpabuf_put_be16(msg, ATTR_R_SNONCE1);
wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
return 0;
}
static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * R-SNonce2");
wpabuf_put_be16(msg, ATTR_R_SNONCE2);
wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
WPS_SECRET_NONCE_LEN);
return 0;
}
static int wps_build_cred_network_idx(struct wpabuf *msg,
const struct wps_credential *cred)
{
wpa_printf(MSG_DEBUG, "WPS: * Network Index (1)");
wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
wpabuf_put_be16(msg, 1);
wpabuf_put_u8(msg, 1);
return 0;
}
static int wps_build_cred_ssid(struct wpabuf *msg,
const struct wps_credential *cred)
{
wpa_printf(MSG_DEBUG, "WPS: * SSID");
wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID for Credential",
cred->ssid, cred->ssid_len);
wpabuf_put_be16(msg, ATTR_SSID);
wpabuf_put_be16(msg, cred->ssid_len);
wpabuf_put_data(msg, cred->ssid, cred->ssid_len);
return 0;
}
static int wps_build_cred_auth_type(struct wpabuf *msg,
const struct wps_credential *cred)
{
wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)",
cred->auth_type);
wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, cred->auth_type);
return 0;
}
static int wps_build_cred_encr_type(struct wpabuf *msg,
const struct wps_credential *cred)
{
wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)",
cred->encr_type);
wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
wpabuf_put_be16(msg, 2);
wpabuf_put_be16(msg, cred->encr_type);
return 0;
}
static int wps_build_cred_network_key(struct wpabuf *msg,
const struct wps_credential *cred)
{
wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%d)",
(int) cred->key_len);
wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
cred->key, cred->key_len);
wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
wpabuf_put_be16(msg, cred->key_len);
wpabuf_put_data(msg, cred->key, cred->key_len);
return 0;
}
static int wps_build_credential(struct wpabuf *msg,
const struct wps_credential *cred)
{
if (wps_build_cred_network_idx(msg, cred) ||
wps_build_cred_ssid(msg, cred) ||
wps_build_cred_auth_type(msg, cred) ||
wps_build_cred_encr_type(msg, cred) ||
wps_build_cred_network_key(msg, cred) ||
wps_build_mac_addr(msg, cred->mac_addr))
return -1;
return 0;
}
int wps_build_credential_wrap(struct wpabuf *msg,
const struct wps_credential *cred)
{
struct wpabuf *wbuf;
wbuf = wpabuf_alloc(200);
if (wbuf == NULL)
return -1;
if (wps_build_credential(wbuf, cred)) {
wpabuf_clear_free(wbuf);
return -1;
}
wpabuf_put_be16(msg, ATTR_CRED);
wpabuf_put_be16(msg, wpabuf_len(wbuf));
wpabuf_put_buf(msg, wbuf);
wpabuf_clear_free(wbuf);
return 0;
}
int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
{
struct wpabuf *cred;
struct wps_registrar *reg = wps->wps->registrar;
const u8 *pskfile_psk;
char hex[65];
if (wps->wps->registrar->skip_cred_build)
goto skip_cred_build;
wpa_printf(MSG_DEBUG, "WPS: * Credential");
if (wps->use_cred) {
os_memcpy(&wps->cred, wps->use_cred, sizeof(wps->cred));
goto use_provided;
}
os_memset(&wps->cred, 0, sizeof(wps->cred));
if (wps->peer_dev.multi_ap_ext == MULTI_AP_BACKHAUL_STA &&
reg->multi_ap_backhaul_ssid_len) {
wpa_printf(MSG_DEBUG, "WPS: Use backhaul STA credentials");
os_memcpy(wps->cred.ssid, reg->multi_ap_backhaul_ssid,
reg->multi_ap_backhaul_ssid_len);
wps->cred.ssid_len = reg->multi_ap_backhaul_ssid_len;
/* Backhaul is always WPA2PSK */
wps->cred.auth_type = WPS_AUTH_WPA2PSK;
wps->cred.encr_type = WPS_ENCR_AES;
/* Set MAC address in the Credential to be the Enrollee's MAC
* address
*/
os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
if (reg->multi_ap_backhaul_network_key) {
os_memcpy(wps->cred.key,
reg->multi_ap_backhaul_network_key,
reg->multi_ap_backhaul_network_key_len);
wps->cred.key_len =
reg->multi_ap_backhaul_network_key_len;
}
goto use_provided;
}
os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
wps->cred.ssid_len = wps->wps->ssid_len;
/* Select the best authentication and encryption type */
wpa_printf(MSG_DEBUG,
"WPS: Own auth types 0x%x - masked Enrollee auth types 0x%x",
wps->wps->auth_types, wps->auth_type);
if (wps->auth_type & WPS_AUTH_WPA2PSK)
wps->auth_type = WPS_AUTH_WPA2PSK;
#ifndef CONFIG_NO_TKIP
else if (wps->auth_type & WPS_AUTH_WPAPSK)
wps->auth_type = WPS_AUTH_WPAPSK;
#endif /* CONFIG_NO_TKIP */
else if (wps->auth_type & WPS_AUTH_OPEN)
wps->auth_type = WPS_AUTH_OPEN;
else {
wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
wps->auth_type);
return -1;
}
wps->cred.auth_type = wps->auth_type;
wpa_printf(MSG_DEBUG,
"WPS: Own encr types 0x%x (rsn: 0x%x, wpa: 0x%x) - masked Enrollee encr types 0x%x",
wps->wps->encr_types, wps->wps->encr_types_rsn,
wps->wps->encr_types_wpa, wps->encr_type);
if (wps->wps->ap && wps->auth_type == WPS_AUTH_WPA2PSK)
wps->encr_type &= wps->wps->encr_types_rsn;
else if (wps->wps->ap && wps->auth_type == WPS_AUTH_WPAPSK)
wps->encr_type &= wps->wps->encr_types_wpa;
if (wps->auth_type == WPS_AUTH_WPA2PSK ||
wps->auth_type == WPS_AUTH_WPAPSK) {
if (wps->encr_type & WPS_ENCR_AES)
wps->encr_type = WPS_ENCR_AES;
#ifndef CONFIG_NO_TKIP
else if (wps->encr_type & WPS_ENCR_TKIP)
wps->encr_type = WPS_ENCR_TKIP;
#endif /* CONFIG_NO_TKIP */
else {
wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
"type for WPA/WPA2");
return -1;
}
} else {
if (wps->encr_type & WPS_ENCR_NONE)
wps->encr_type = WPS_ENCR_NONE;
#ifdef CONFIG_TESTING_OPTIONS
else if (wps->encr_type & WPS_ENCR_WEP)
wps->encr_type = WPS_ENCR_WEP;
#endif /* CONFIG_TESTING_OPTIONS */
else {
wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
"type for non-WPA/WPA2 mode");
return -1;
}
}
wps->cred.encr_type = wps->encr_type;
/*
* Set MAC address in the Credential to be the Enrollee's MAC address
*/
os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap &&
!wps->wps->registrar->disable_auto_conf) {
u8 r[16];
/* Generate a random passphrase */
if (random_pool_ready() != 1 ||
random_get_bytes(r, sizeof(r)) < 0) {
wpa_printf(MSG_INFO,
"WPS: Could not generate random PSK");
return -1;
}
os_free(wps->new_psk);
wps->new_psk = (u8 *) base64_encode(r, sizeof(r),
&wps->new_psk_len);
if (wps->new_psk == NULL)
return -1;
wps->new_psk_len--; /* remove newline */
while (wps->new_psk_len &&
wps->new_psk[wps->new_psk_len - 1] == '=')
wps->new_psk_len--;
wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated passphrase",
wps->new_psk, wps->new_psk_len);
os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len);
wps->cred.key_len = wps->new_psk_len;
} else if (wps_cp_lookup_pskfile(reg, wps->mac_addr_e, &pskfile_psk)) {
wpa_hexdump_key(MSG_DEBUG, "WPS: Use PSK from wpa_psk_file",
pskfile_psk, PMK_LEN);
wpa_snprintf_hex(hex, sizeof(hex), pskfile_psk, PMK_LEN);
os_memcpy(wps->cred.key, hex, PMK_LEN * 2);
wps->cred.key_len = PMK_LEN * 2;
} else if (!wps->wps->registrar->force_per_enrollee_psk &&
wps->use_psk_key && wps->wps->psk_set) {
wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key");
wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, PMK_LEN);
os_memcpy(wps->cred.key, hex, PMK_LEN * 2);
wps->cred.key_len = PMK_LEN * 2;
} else if ((!wps->wps->registrar->force_per_enrollee_psk ||
wps->wps->use_passphrase) && wps->wps->network_key) {
wpa_printf(MSG_DEBUG,
"WPS: Use passphrase format for Network key");
os_memcpy(wps->cred.key, wps->wps->network_key,
wps->wps->network_key_len);
wps->cred.key_len = wps->wps->network_key_len;
} else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
/* Generate a random per-device PSK */
os_free(wps->new_psk);
wps->new_psk_len = PMK_LEN;
wps->new_psk = os_malloc(wps->new_psk_len);
if (wps->new_psk == NULL)
return -1;
if (random_pool_ready() != 1 ||
random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
wpa_printf(MSG_INFO,
"WPS: Could not generate random PSK");
os_free(wps->new_psk);
wps->new_psk = NULL;
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
wps->new_psk, wps->new_psk_len);
wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk,
wps->new_psk_len);
os_memcpy(wps->cred.key, hex, wps->new_psk_len * 2);
wps->cred.key_len = wps->new_psk_len * 2;
}
use_provided:
#ifdef CONFIG_WPS_TESTING
- if (wps_testing_dummy_cred)
+ if (wps_testing_stub_cred)
cred = wpabuf_alloc(200);
else
cred = NULL;
if (cred) {
- struct wps_credential dummy;
- wpa_printf(MSG_DEBUG, "WPS: Add dummy credential");
- os_memset(&dummy, 0, sizeof(dummy));
- os_memcpy(dummy.ssid, "dummy", 5);
- dummy.ssid_len = 5;
- dummy.auth_type = WPS_AUTH_WPA2PSK;
- dummy.encr_type = WPS_ENCR_AES;
- os_memcpy(dummy.key, "dummy psk", 9);
- dummy.key_len = 9;
- os_memcpy(dummy.mac_addr, wps->mac_addr_e, ETH_ALEN);
- wps_build_credential(cred, &dummy);
- wpa_hexdump_buf(MSG_DEBUG, "WPS: Dummy Credential", cred);
+ struct wps_credential stub;
+ wpa_printf(MSG_DEBUG, "WPS: Add stub credential");
+ os_memset(&stub, 0, sizeof(stub));
+ os_memcpy(stub.ssid, "stub", 5);
+ stub.ssid_len = 5;
+ stub.auth_type = WPS_AUTH_WPA2PSK;
+ stub.encr_type = WPS_ENCR_AES;
+ os_memcpy(stub.key, "stub psk", 9);
+ stub.key_len = 9;
+ os_memcpy(stub.mac_addr, wps->mac_addr_e, ETH_ALEN);
+ wps_build_credential(cred, &stub);
+ wpa_hexdump_buf(MSG_DEBUG, "WPS: Stub Credential", cred);
wpabuf_put_be16(msg, ATTR_CRED);
wpabuf_put_be16(msg, wpabuf_len(cred));
wpabuf_put_buf(msg, cred);
wpabuf_free(cred);
}
#endif /* CONFIG_WPS_TESTING */
cred = wpabuf_alloc(200);
if (cred == NULL)
return -1;
if (wps_build_credential(cred, &wps->cred)) {
wpabuf_clear_free(cred);
return -1;
}
wpabuf_put_be16(msg, ATTR_CRED);
wpabuf_put_be16(msg, wpabuf_len(cred));
wpabuf_put_buf(msg, cred);
wpabuf_clear_free(cred);
skip_cred_build:
if (wps->wps->registrar->extra_cred) {
wpa_printf(MSG_DEBUG, "WPS: * Credential (pre-configured)");
wpabuf_put_buf(msg, wps->wps->registrar->extra_cred);
}
return 0;
}
static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg)
{
wpa_printf(MSG_DEBUG, "WPS: * AP Settings");
if (wps_build_credential(msg, &wps->cred))
return -1;
return 0;
}
static struct wpabuf * wps_build_ap_cred(struct wps_data *wps)
{
struct wpabuf *msg, *plain;
msg = wpabuf_alloc(1000);
if (msg == NULL)
return NULL;
plain = wpabuf_alloc(200);
if (plain == NULL) {
wpabuf_free(msg);
return NULL;
}
if (wps_build_ap_settings(wps, plain)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
return NULL;
}
wpabuf_put_be16(msg, ATTR_CRED);
wpabuf_put_be16(msg, wpabuf_len(plain));
wpabuf_put_buf(msg, plain);
wpabuf_clear_free(plain);
return msg;
}
static struct wpabuf * wps_build_m2(struct wps_data *wps)
{
struct wpabuf *msg;
int config_in_m2 = 0;
if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
return NULL;
wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
wps->nonce_r, WPS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
wpa_printf(MSG_DEBUG, "WPS: Building Message M2");
msg = wpabuf_alloc(1000);
if (msg == NULL)
return NULL;
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_M2) ||
wps_build_enrollee_nonce(wps, msg) ||
wps_build_registrar_nonce(wps, msg) ||
wps_build_uuid_r(wps, msg) ||
wps_build_public_key(wps, msg) ||
wps_derive_keys(wps) ||
wps_build_auth_type_flags(wps, msg) ||
wps_build_encr_type_flags(wps, msg) ||
wps_build_conn_type_flags(wps, msg) ||
wps_build_config_methods_r(wps->wps->registrar, msg) ||
wps_build_device_attrs(&wps->wps->dev, msg) ||
wps_build_rf_bands(&wps->wps->dev, msg,
wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
wps_build_assoc_state(wps, msg) ||
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_os_version(&wps->wps->dev, msg) ||
wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
#ifdef CONFIG_WPS_NFC
if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob &&
wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
/*
* Use abbreviated handshake since public key hash allowed
* Enrollee to validate our public key similarly to how Enrollee
* public key was validated. There is no need to validate Device
* Password in this case.
*/
struct wpabuf *plain = wpabuf_alloc(500);
if (plain == NULL ||
wps_build_cred(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain)) {
wpabuf_free(msg);
wpabuf_clear_free(plain);
return NULL;
}
wpabuf_clear_free(plain);
config_in_m2 = 1;
}
#endif /* CONFIG_WPS_NFC */
if (wps_build_authenticator(wps, msg)) {
wpabuf_free(msg);
return NULL;
}
wps->int_reg = 1;
wps->state = config_in_m2 ? RECV_DONE : RECV_M3;
return msg;
}
static struct wpabuf * wps_build_m2d(struct wps_data *wps)
{
struct wpabuf *msg;
u16 err = wps->config_error;
wpa_printf(MSG_DEBUG, "WPS: Building Message M2D");
msg = wpabuf_alloc(1000);
if (msg == NULL)
return NULL;
if (wps->wps->ap && wps->wps->ap_setup_locked &&
err == WPS_CFG_NO_ERROR)
err = WPS_CFG_SETUP_LOCKED;
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_M2D) ||
wps_build_enrollee_nonce(wps, msg) ||
wps_build_registrar_nonce(wps, msg) ||
wps_build_uuid_r(wps, msg) ||
wps_build_auth_type_flags(wps, msg) ||
wps_build_encr_type_flags(wps, msg) ||
wps_build_conn_type_flags(wps, msg) ||
wps_build_config_methods_r(wps->wps->registrar, msg) ||
wps_build_device_attrs(&wps->wps->dev, msg) ||
wps_build_rf_bands(&wps->wps->dev, msg,
wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
wps_build_assoc_state(wps, msg) ||
wps_build_config_error(msg, err) ||
wps_build_os_version(&wps->wps->dev, msg) ||
wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
wps->state = RECV_M2D_ACK;
return msg;
}
static struct wpabuf * wps_build_m4(struct wps_data *wps)
{
struct wpabuf *msg, *plain;
wpa_printf(MSG_DEBUG, "WPS: Building Message M4");
if (wps_derive_psk(wps, wps->dev_password, wps->dev_password_len) < 0)
return NULL;
plain = wpabuf_alloc(200);
if (plain == NULL)
return NULL;
msg = wpabuf_alloc(1000);
if (msg == NULL) {
wpabuf_free(plain);
return NULL;
}
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_M4) ||
wps_build_enrollee_nonce(wps, msg) ||
wps_build_r_hash(wps, msg) ||
wps_build_r_snonce1(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
return NULL;
}
wpabuf_clear_free(plain);
wps->state = RECV_M5;
return msg;
}
static struct wpabuf * wps_build_m6(struct wps_data *wps)
{
struct wpabuf *msg, *plain;
wpa_printf(MSG_DEBUG, "WPS: Building Message M6");
plain = wpabuf_alloc(200);
if (plain == NULL)
return NULL;
msg = wpabuf_alloc(1000);
if (msg == NULL) {
wpabuf_free(plain);
return NULL;
}
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_M6) ||
wps_build_enrollee_nonce(wps, msg) ||
wps_build_r_snonce2(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
return NULL;
}
wpabuf_clear_free(plain);
wps->wps_pin_revealed = 1;
wps->state = RECV_M7;
return msg;
}
static struct wpabuf * wps_build_m8(struct wps_data *wps)
{
struct wpabuf *msg, *plain;
wpa_printf(MSG_DEBUG, "WPS: Building Message M8");
plain = wpabuf_alloc(500);
if (plain == NULL)
return NULL;
msg = wpabuf_alloc(1000);
if (msg == NULL) {
wpabuf_free(plain);
return NULL;
}
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_M8) ||
wps_build_enrollee_nonce(wps, msg) ||
((wps->wps->ap || wps->er) && wps_build_cred(wps, plain)) ||
(!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_clear_free(msg);
return NULL;
}
wpabuf_clear_free(plain);
wps->state = RECV_DONE;
return msg;
}
struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
enum wsc_op_code *op_code)
{
struct wpabuf *msg;
#ifdef CONFIG_WPS_UPNP
if (!wps->int_reg && wps->wps->wps_upnp) {
struct upnp_pending_message *p, *prev = NULL;
if (wps->ext_reg > 1)
wps_registrar_free_pending_m2(wps->wps);
p = wps->wps->upnp_msgs;
/* TODO: check pending message MAC address */
while (p && p->next) {
prev = p;
p = p->next;
}
if (p) {
wpa_printf(MSG_DEBUG, "WPS: Use pending message from "
"UPnP");
if (prev)
prev->next = NULL;
else
wps->wps->upnp_msgs = NULL;
msg = p->msg;
switch (p->type) {
case WPS_WSC_ACK:
*op_code = WSC_ACK;
break;
case WPS_WSC_NACK:
*op_code = WSC_NACK;
break;
default:
*op_code = WSC_MSG;
break;
}
os_free(p);
if (wps->ext_reg == 0)
wps->ext_reg = 1;
return msg;
}
}
if (wps->ext_reg) {
wpa_printf(MSG_DEBUG, "WPS: Using external Registrar, but no "
"pending message available");
return NULL;
}
#endif /* CONFIG_WPS_UPNP */
switch (wps->state) {
case SEND_M2:
if (wps_get_dev_password(wps) < 0)
msg = wps_build_m2d(wps);
else
msg = wps_build_m2(wps);
*op_code = WSC_MSG;
break;
case SEND_M2D:
msg = wps_build_m2d(wps);
*op_code = WSC_MSG;
break;
case SEND_M4:
msg = wps_build_m4(wps);
*op_code = WSC_MSG;
break;
case SEND_M6:
msg = wps_build_m6(wps);
*op_code = WSC_MSG;
break;
case SEND_M8:
msg = wps_build_m8(wps);
*op_code = WSC_MSG;
break;
case RECV_DONE:
msg = wps_build_wsc_ack(wps);
*op_code = WSC_ACK;
break;
case SEND_WSC_NACK:
msg = wps_build_wsc_nack(wps);
*op_code = WSC_NACK;
break;
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
"a message", wps->state);
msg = NULL;
break;
}
if (*op_code == WSC_MSG && msg) {
/* Save a copy of the last message for Authenticator derivation
*/
wpabuf_free(wps->last_msg);
wps->last_msg = wpabuf_dup(msg);
}
return msg;
}
static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
{
if (e_nonce == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
return -1;
}
os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
wps->nonce_e, WPS_NONCE_LEN);
return 0;
}
static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
{
if (r_nonce == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
return -1;
}
if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce received");
return -1;
}
return 0;
}
static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e)
{
if (uuid_e == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No UUID-E received");
return -1;
}
os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN);
return 0;
}
static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id)
{
if (pw_id == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Device Password ID received");
return -1;
}
wps->dev_pw_id = WPA_GET_BE16(pw_id);
wpa_printf(MSG_DEBUG, "WPS: Device Password ID %d", wps->dev_pw_id);
return 0;
}
static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1)
{
if (e_hash1 == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No E-Hash1 received");
return -1;
}
os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN);
return 0;
}
static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2)
{
if (e_hash2 == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No E-Hash2 received");
return -1;
}
os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN);
wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN);
return 0;
}
static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1)
{
u8 hash[SHA256_MAC_LEN];
const u8 *addr[4];
size_t len[4];
if (e_snonce1 == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No E-SNonce1 received");
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1,
WPS_SECRET_NONCE_LEN);
/* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
addr[0] = e_snonce1;
len[0] = WPS_SECRET_NONCE_LEN;
addr[1] = wps->psk1;
len[1] = WPS_PSK_LEN;
addr[2] = wpabuf_head(wps->dh_pubkey_e);
len[2] = wpabuf_len(wps->dh_pubkey_e);
addr[3] = wpabuf_head(wps->dh_pubkey_r);
len[3] = wpabuf_len(wps->dh_pubkey_r);
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does "
"not match with the pre-committed value");
wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
wps_pwd_auth_fail_event(wps->wps, 0, 1, wps->mac_addr_e);
return -1;
}
wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the first "
"half of the device password");
return 0;
}
static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
{
u8 hash[SHA256_MAC_LEN];
const u8 *addr[4];
size_t len[4];
if (e_snonce2 == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No E-SNonce2 received");
return -1;
}
wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2,
WPS_SECRET_NONCE_LEN);
/* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
addr[0] = e_snonce2;
len[0] = WPS_SECRET_NONCE_LEN;
addr[1] = wps->psk2;
len[1] = WPS_PSK_LEN;
addr[2] = wpabuf_head(wps->dh_pubkey_e);
len[2] = wpabuf_len(wps->dh_pubkey_e);
addr[3] = wpabuf_head(wps->dh_pubkey_r);
len[3] = wpabuf_len(wps->dh_pubkey_r);
hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does "
"not match with the pre-committed value");
wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
wps_pwd_auth_fail_event(wps->wps, 0, 2, wps->mac_addr_e);
return -1;
}
wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second "
"half of the device password");
wps->wps_pin_revealed = 0;
wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e);
/*
* In case wildcard PIN is used and WPS handshake succeeds in the first
* attempt, wps_registrar_unlock_pin() would not free the PIN, so make
* sure the PIN gets invalidated here.
*/
wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
return 0;
}
static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr)
{
if (mac_addr == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No MAC Address received");
return -1;
}
wpa_printf(MSG_DEBUG, "WPS: Enrollee MAC Address " MACSTR,
MAC2STR(mac_addr));
os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN);
os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN);
return 0;
}
static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
size_t pk_len)
{
if (pk == NULL || pk_len == 0) {
wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
return -1;
}
wpabuf_free(wps->dh_pubkey_e);
wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
if (wps->dh_pubkey_e == NULL)
return -1;
return 0;
}
static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth)
{
u16 auth_types;
if (auth == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Authentication Type flags "
"received");
return -1;
}
auth_types = WPA_GET_BE16(auth);
wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x",
auth_types);
#ifdef WPS_WORKAROUNDS
/*
* Some deployed implementations seem to advertise incorrect information
* in this attribute. A value of 0x1b (WPA2 + WPA + WPAPSK + OPEN, but
* no WPA2PSK) has been reported to be used. Add WPA2PSK to the list to
* avoid issues with building Credentials that do not use the strongest
* actually supported authentication option (that device does support
* WPA2PSK even when it does not claim it here).
*/
if ((auth_types &
(WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) ==
(WPS_AUTH_WPA2 | WPS_AUTH_WPAPSK)) {
wpa_printf(MSG_DEBUG,
"WPS: Workaround - assume Enrollee supports WPA2PSK based on claimed WPA2 support");
auth_types |= WPS_AUTH_WPA2PSK;
}
#endif /* WPS_WORKAROUNDS */
wps->auth_type = wps->wps->auth_types & auth_types;
if (wps->auth_type == 0) {
wpa_printf(MSG_DEBUG, "WPS: No match in supported "
"authentication types (own 0x%x Enrollee 0x%x)",
wps->wps->auth_types, auth_types);
#ifdef WPS_WORKAROUNDS
/*
* Some deployed implementations seem to advertise incorrect
* information in this attribute. For example, Linksys WRT350N
* seems to have a byteorder bug that breaks this negotiation.
* In order to interoperate with existing implementations,
* assume that the Enrollee supports everything we do.
*/
wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
"does not advertise supported authentication types "
"correctly");
wps->auth_type = wps->wps->auth_types;
#else /* WPS_WORKAROUNDS */
return -1;
#endif /* WPS_WORKAROUNDS */
}
return 0;
}
static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr)
{
u16 encr_types;
if (encr == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Encryption Type flags "
"received");
return -1;
}
encr_types = WPA_GET_BE16(encr);
wpa_printf(MSG_DEBUG, "WPS: Enrollee Encryption Type flags 0x%x",
encr_types);
wps->encr_type = wps->wps->encr_types & encr_types;
if (wps->encr_type == 0) {
wpa_printf(MSG_DEBUG, "WPS: No match in supported "
"encryption types (own 0x%x Enrollee 0x%x)",
wps->wps->encr_types, encr_types);
#ifdef WPS_WORKAROUNDS
/*
* Some deployed implementations seem to advertise incorrect
* information in this attribute. For example, Linksys WRT350N
* seems to have a byteorder bug that breaks this negotiation.
* In order to interoperate with existing implementations,
* assume that the Enrollee supports everything we do.
*/
wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
"does not advertise supported encryption types "
"correctly");
wps->encr_type = wps->wps->encr_types;
#else /* WPS_WORKAROUNDS */
return -1;
#endif /* WPS_WORKAROUNDS */
}
return 0;
}
static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn)
{
if (conn == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Connection Type flags "
"received");
return -1;
}
wpa_printf(MSG_DEBUG, "WPS: Enrollee Connection Type flags 0x%x",
*conn);
return 0;
}
static int wps_process_config_methods(struct wps_data *wps, const u8 *methods)
{
u16 m;
if (methods == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Config Methods received");
return -1;
}
m = WPA_GET_BE16(methods);
wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x"
"%s%s%s%s%s%s%s%s%s", m,
m & WPS_CONFIG_USBA ? " [USBA]" : "",
m & WPS_CONFIG_ETHERNET ? " [Ethernet]" : "",
m & WPS_CONFIG_LABEL ? " [Label]" : "",
m & WPS_CONFIG_DISPLAY ? " [Display]" : "",
m & WPS_CONFIG_EXT_NFC_TOKEN ? " [Ext NFC Token]" : "",
m & WPS_CONFIG_INT_NFC_TOKEN ? " [Int NFC Token]" : "",
m & WPS_CONFIG_NFC_INTERFACE ? " [NFC]" : "",
m & WPS_CONFIG_PUSHBUTTON ? " [PBC]" : "",
m & WPS_CONFIG_KEYPAD ? " [Keypad]" : "");
if (!(m & WPS_CONFIG_DISPLAY) && !wps->use_psk_key) {
/*
* The Enrollee does not have a display so it is unlikely to be
* able to show the passphrase to a user and as such, could
* benefit from receiving PSK to reduce key derivation time.
*/
wpa_printf(MSG_DEBUG, "WPS: Prefer PSK format key due to "
"Enrollee not supporting display");
wps->use_psk_key = 1;
}
return 0;
}
static int wps_process_wps_state(struct wps_data *wps, const u8 *state)
{
if (state == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Wi-Fi Protected Setup State "
"received");
return -1;
}
wpa_printf(MSG_DEBUG, "WPS: Enrollee Wi-Fi Protected Setup State %d",
*state);
return 0;
}
static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc)
{
u16 a;
if (assoc == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Association State received");
return -1;
}
a = WPA_GET_BE16(assoc);
wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a);
return 0;
}
static int wps_process_config_error(struct wps_data *wps, const u8 *err)
{
u16 e;
if (err == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received");
return -1;
}
e = WPA_GET_BE16(err);
wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e);
return 0;
}
static int wps_registrar_p2p_dev_addr_match(struct wps_data *wps)
{
#ifdef CONFIG_P2P
struct wps_registrar *reg = wps->wps->registrar;
if (is_zero_ether_addr(reg->p2p_dev_addr))
return 1; /* no filtering in use */
if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: No match on P2P Device Address "
"filtering for PBC: expected " MACSTR " was "
MACSTR " - indicate PBC session overlap",
MAC2STR(reg->p2p_dev_addr),
MAC2STR(wps->p2p_dev_addr));
return 0;
}
#endif /* CONFIG_P2P */
return 1;
}
static int wps_registrar_skip_overlap(struct wps_data *wps)
{
#ifdef CONFIG_P2P
struct wps_registrar *reg = wps->wps->registrar;
if (is_zero_ether_addr(reg->p2p_dev_addr))
return 0; /* no specific Enrollee selected */
if (os_memcmp(reg->p2p_dev_addr, wps->p2p_dev_addr, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG, "WPS: Skip PBC overlap due to selected "
"Enrollee match");
return 1;
}
#endif /* CONFIG_P2P */
return 0;
}
static enum wps_process_res wps_process_m1(struct wps_data *wps,
struct wps_parse_attr *attr)
{
wpa_printf(MSG_DEBUG, "WPS: Received M1");
if (wps->state != RECV_M1) {
wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
"receiving M1", wps->state);
return WPS_FAILURE;
}
if (wps_process_uuid_e(wps, attr->uuid_e) ||
wps_process_mac_addr(wps, attr->mac_addr) ||
wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
wps_process_auth_type_flags(wps, attr->auth_type_flags) ||
wps_process_encr_type_flags(wps, attr->encr_type_flags) ||
wps_process_conn_type_flags(wps, attr->conn_type_flags) ||
wps_process_config_methods(wps, attr->config_methods) ||
wps_process_wps_state(wps, attr->wps_state) ||
wps_process_device_attrs(&wps->peer_dev, attr) ||
wps_process_rf_bands(&wps->peer_dev, attr->rf_bands) ||
wps_process_assoc_state(wps, attr->assoc_state) ||
wps_process_dev_password_id(wps, attr->dev_password_id) ||
wps_process_config_error(wps, attr->config_error) ||
wps_process_os_version(&wps->peer_dev, attr->os_version))
return WPS_FAILURE;
if (wps->dev_pw_id < 0x10 &&
wps->dev_pw_id != DEV_PW_DEFAULT &&
wps->dev_pw_id != DEV_PW_P2PS_DEFAULT &&
wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
#ifdef CONFIG_WPS_NFC
wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
#endif /* CONFIG_WPS_NFC */
(wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
!wps->wps->registrar->pbc)) {
wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
wps->dev_pw_id);
wps->state = SEND_M2D;
return WPS_CONTINUE;
}
#ifdef CONFIG_WPS_NFC
if (wps->dev_pw_id >= 0x10 ||
wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
struct wps_nfc_pw_token *token;
const u8 *addr[1];
u8 hash[WPS_HASH_LEN];
wpa_printf(MSG_DEBUG, "WPS: Searching for NFC token match for id=%d (ctx %p registrar %p)",
wps->dev_pw_id, wps->wps, wps->wps->registrar);
token = wps_get_nfc_pw_token(
&wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
if (token && token->peer_pk_hash_known) {
size_t len;
wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
"Password Token");
dl_list_del(&token->list);
wps->nfc_pw_token = token;
addr[0] = attr->public_key;
len = attr->public_key_len;
sha256_vector(1, addr, &len, hash);
if (os_memcmp_const(hash,
wps->nfc_pw_token->pubkey_hash,
WPS_OOB_PUBKEY_HASH_LEN) != 0) {
wpa_printf(MSG_ERROR, "WPS: Public Key hash "
"mismatch");
wps->state = SEND_M2D;
wps->config_error =
WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
return WPS_CONTINUE;
}
} else if (token) {
wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
"Password Token (no peer PK hash)");
wps->nfc_pw_token = token;
} else if (wps->dev_pw_id >= 0x10 &&
wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
wps->wps->ap_nfc_dev_pw) {
wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token");
}
}
#endif /* CONFIG_WPS_NFC */
if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
if ((wps->wps->registrar->force_pbc_overlap ||
wps_registrar_pbc_overlap(wps->wps->registrar,
wps->mac_addr_e, wps->uuid_e) ||
!wps_registrar_p2p_dev_addr_match(wps)) &&
!wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC "
"negotiation");
wps->state = SEND_M2D;
wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
wps_pbc_overlap_event(wps->wps);
wps_fail_event(wps->wps, WPS_M1,
WPS_CFG_MULTIPLE_PBC_DETECTED,
WPS_EI_NO_ERROR, wps->mac_addr_e);
wps->wps->registrar->force_pbc_overlap = 1;
return WPS_CONTINUE;
}
wps_registrar_add_pbc_session(wps->wps->registrar,
wps->mac_addr_e, wps->uuid_e);
wps->pbc = 1;
}
#ifdef WPS_WORKAROUNDS
/*
* It looks like Mac OS X 10.6.3 and 10.6.4 do not like Network Key in
* passphrase format. To avoid interop issues, force PSK format to be
* used.
*/
if (!wps->use_psk_key &&
wps->peer_dev.manufacturer &&
os_strncmp(wps->peer_dev.manufacturer, "Apple ", 6) == 0 &&
wps->peer_dev.model_name &&
os_strcmp(wps->peer_dev.model_name, "AirPort") == 0) {
wpa_printf(MSG_DEBUG, "WPS: Workaround - Force Network Key in "
"PSK format");
wps->use_psk_key = 1;
}
#endif /* WPS_WORKAROUNDS */
wps_process_vendor_ext_m1(&wps->peer_dev, attr->multi_ap_ext);
wps->state = SEND_M2;
return WPS_CONTINUE;
}
static enum wps_process_res wps_process_m3(struct wps_data *wps,
const struct wpabuf *msg,
struct wps_parse_attr *attr)
{
wpa_printf(MSG_DEBUG, "WPS: Received M3");
if (wps->state != RECV_M3) {
wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
"receiving M3", wps->state);
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
!wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
"session overlap");
wps->state = SEND_WSC_NACK;
wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
return WPS_CONTINUE;
}
if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
wps_process_authenticator(wps, attr->authenticator, msg) ||
wps_process_e_hash1(wps, attr->e_hash1) ||
wps_process_e_hash2(wps, attr->e_hash2)) {
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
wps->state = SEND_M4;
return WPS_CONTINUE;
}
static enum wps_process_res wps_process_m5(struct wps_data *wps,
const struct wpabuf *msg,
struct wps_parse_attr *attr)
{
struct wpabuf *decrypted;
struct wps_parse_attr eattr;
wpa_printf(MSG_DEBUG, "WPS: Received M5");
if (wps->state != RECV_M5) {
wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
"receiving M5", wps->state);
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
!wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
"session overlap");
wps->state = SEND_WSC_NACK;
wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
return WPS_CONTINUE;
}
if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
wps_process_authenticator(wps, attr->authenticator, msg)) {
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
attr->encr_settings_len);
if (decrypted == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
"Settings attribute");
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) {
wpabuf_clear_free(decrypted);
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
wps_process_e_snonce1(wps, eattr.e_snonce1)) {
wpabuf_clear_free(decrypted);
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
wpabuf_clear_free(decrypted);
wps->state = SEND_M6;
return WPS_CONTINUE;
}
static void wps_sta_cred_cb(struct wps_data *wps)
{
/*
* Update credential to only include a single authentication and
* encryption type in case the AP configuration includes more than one
* option.
*/
if (wps->cred.auth_type & WPS_AUTH_WPA2PSK)
wps->cred.auth_type = WPS_AUTH_WPA2PSK;
else if (wps->cred.auth_type & WPS_AUTH_WPAPSK)
wps->cred.auth_type = WPS_AUTH_WPAPSK;
if (wps->cred.encr_type & WPS_ENCR_AES)
wps->cred.encr_type = WPS_ENCR_AES;
else if (wps->cred.encr_type & WPS_ENCR_TKIP)
wps->cred.encr_type = WPS_ENCR_TKIP;
wpa_printf(MSG_DEBUG, "WPS: Update local configuration based on the "
"AP configuration");
if (wps->wps->cred_cb)
wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
}
static void wps_cred_update(struct wps_credential *dst,
struct wps_credential *src)
{
os_memcpy(dst->ssid, src->ssid, sizeof(dst->ssid));
dst->ssid_len = src->ssid_len;
dst->auth_type = src->auth_type;
dst->encr_type = src->encr_type;
dst->key_idx = src->key_idx;
os_memcpy(dst->key, src->key, sizeof(dst->key));
dst->key_len = src->key_len;
}
static int wps_process_ap_settings_r(struct wps_data *wps,
struct wps_parse_attr *attr)
{
struct wpabuf *msg;
if (wps->wps->ap || wps->er)
return 0;
/* AP Settings Attributes in M7 when Enrollee is an AP */
if (wps_process_ap_settings(attr, &wps->cred) < 0)
return -1;
wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP");
if (wps->new_ap_settings) {
wpa_printf(MSG_INFO, "WPS: Update AP configuration based on "
"new settings");
wps_cred_update(&wps->cred, wps->new_ap_settings);
return 0;
} else {
/*
* Use the AP PIN only to receive the current AP settings, not
* to reconfigure the AP.
*/
/*
* Clear selected registrar here since we do not get to
* WSC_Done in this protocol run.
*/
wps_registrar_pin_completed(wps->wps->registrar);
msg = wps_build_ap_cred(wps);
if (msg == NULL)
return -1;
wps->cred.cred_attr = wpabuf_head(msg);
wps->cred.cred_attr_len = wpabuf_len(msg);
if (wps->ap_settings_cb) {
wps->ap_settings_cb(wps->ap_settings_cb_ctx,
&wps->cred);
wpabuf_free(msg);
return 1;
}
wps_sta_cred_cb(wps);
wps->cred.cred_attr = NULL;
wps->cred.cred_attr_len = 0;
wpabuf_free(msg);
return 1;
}
}
static enum wps_process_res wps_process_m7(struct wps_data *wps,
const struct wpabuf *msg,
struct wps_parse_attr *attr)
{
struct wpabuf *decrypted;
struct wps_parse_attr eattr;
wpa_printf(MSG_DEBUG, "WPS: Received M7");
if (wps->state != RECV_M7) {
wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
"receiving M7", wps->state);
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
if (wps->pbc && wps->wps->registrar->force_pbc_overlap &&
!wps_registrar_skip_overlap(wps)) {
wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
"session overlap");
wps->state = SEND_WSC_NACK;
wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
return WPS_CONTINUE;
}
if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
wps_process_authenticator(wps, attr->authenticator, msg)) {
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
attr->encr_settings_len);
if (decrypted == NULL) {
wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt Encrypted "
"Settings attribute");
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er,
attr->version2 != NULL) < 0) {
wpabuf_clear_free(decrypted);
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
"attribute");
if (wps_parse_msg(decrypted, &eattr) < 0 ||
wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
wps_process_e_snonce2(wps, eattr.e_snonce2) ||
wps_process_ap_settings_r(wps, &eattr)) {
wpabuf_clear_free(decrypted);
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
wpabuf_clear_free(decrypted);
wps->state = SEND_M8;
return WPS_CONTINUE;
}
static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
const struct wpabuf *msg)
{
struct wps_parse_attr attr;
enum wps_process_res ret = WPS_CONTINUE;
wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
wps->state = SEND_WSC_NACK;
return WPS_CONTINUE;
}
if (*attr.msg_type != WPS_M1 &&
(attr.registrar_nonce == NULL ||
os_memcmp(wps->nonce_r, attr.registrar_nonce,
WPS_NONCE_LEN) != 0)) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
return WPS_FAILURE;
}
switch (*attr.msg_type) {
case WPS_M1:
if (wps_validate_m1(msg) < 0)
return WPS_FAILURE;
#ifdef CONFIG_WPS_UPNP
if (wps->wps->wps_upnp && attr.mac_addr) {
/* Remove old pending messages when starting new run */
wps_free_pending_msgs(wps->wps->upnp_msgs);
wps->wps->upnp_msgs = NULL;
upnp_wps_device_send_wlan_event(
wps->wps->wps_upnp, attr.mac_addr,
UPNP_WPS_WLANEVENT_TYPE_EAP, msg);
}
#endif /* CONFIG_WPS_UPNP */
ret = wps_process_m1(wps, &attr);
break;
case WPS_M3:
if (wps_validate_m3(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m3(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
wps_fail_event(wps->wps, WPS_M3, wps->config_error,
wps->error_indication, wps->mac_addr_e);
break;
case WPS_M5:
if (wps_validate_m5(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m5(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
wps_fail_event(wps->wps, WPS_M5, wps->config_error,
wps->error_indication, wps->mac_addr_e);
break;
case WPS_M7:
if (wps_validate_m7(msg) < 0)
return WPS_FAILURE;
ret = wps_process_m7(wps, msg, &attr);
if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
wps_fail_event(wps->wps, WPS_M7, wps->config_error,
wps->error_indication, wps->mac_addr_e);
break;
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
*attr.msg_type);
return WPS_FAILURE;
}
if (ret == WPS_CONTINUE) {
/* Save a copy of the last message for Authenticator derivation
*/
wpabuf_free(wps->last_msg);
wps->last_msg = wpabuf_dup(msg);
}
return ret;
}
static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
const struct wpabuf *msg)
{
struct wps_parse_attr attr;
wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
}
if (*attr.msg_type != WPS_WSC_ACK) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
*attr.msg_type);
return WPS_FAILURE;
}
#ifdef CONFIG_WPS_UPNP
if (wps->wps->wps_upnp && wps->ext_reg && wps->state == RECV_M2D_ACK &&
upnp_wps_subscribers(wps->wps->wps_upnp)) {
if (wps->wps->upnp_msgs)
return WPS_CONTINUE;
wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
"external Registrar");
return WPS_PENDING;
}
#endif /* CONFIG_WPS_UPNP */
if (attr.registrar_nonce == NULL ||
os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
{
wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
return WPS_FAILURE;
}
if (attr.enrollee_nonce == NULL ||
os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
return WPS_FAILURE;
}
if (wps->state == RECV_M2D_ACK) {
#ifdef CONFIG_WPS_UPNP
if (wps->wps->wps_upnp &&
upnp_wps_subscribers(wps->wps->wps_upnp)) {
if (wps->wps->upnp_msgs)
return WPS_CONTINUE;
if (wps->ext_reg == 0)
wps->ext_reg = 1;
wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
"external Registrar");
return WPS_PENDING;
}
#endif /* CONFIG_WPS_UPNP */
wpa_printf(MSG_DEBUG, "WPS: No more registrars available - "
"terminate negotiation");
}
return WPS_FAILURE;
}
static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
const struct wpabuf *msg)
{
struct wps_parse_attr attr;
int old_state;
u16 config_error;
wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
old_state = wps->state;
wps->state = SEND_WSC_NACK;
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
}
if (*attr.msg_type != WPS_WSC_NACK) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
*attr.msg_type);
return WPS_FAILURE;
}
#ifdef CONFIG_WPS_UPNP
if (wps->wps->wps_upnp && wps->ext_reg) {
wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
"Registrar terminated by the Enrollee");
return WPS_FAILURE;
}
#endif /* CONFIG_WPS_UPNP */
if (attr.registrar_nonce == NULL ||
os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
{
wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
return WPS_FAILURE;
}
if (attr.enrollee_nonce == NULL ||
os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
return WPS_FAILURE;
}
if (attr.config_error == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
"in WSC_NACK");
return WPS_FAILURE;
}
config_error = WPA_GET_BE16(attr.config_error);
wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
"Configuration Error %d", config_error);
switch (old_state) {
case RECV_M3:
wps_fail_event(wps->wps, WPS_M2, config_error,
wps->error_indication, wps->mac_addr_e);
break;
case RECV_M5:
wps_fail_event(wps->wps, WPS_M4, config_error,
wps->error_indication, wps->mac_addr_e);
break;
case RECV_M7:
wps_fail_event(wps->wps, WPS_M6, config_error,
wps->error_indication, wps->mac_addr_e);
break;
case RECV_DONE:
wps_fail_event(wps->wps, WPS_M8, config_error,
wps->error_indication, wps->mac_addr_e);
break;
default:
break;
}
return WPS_FAILURE;
}
static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
const struct wpabuf *msg)
{
struct wps_parse_attr attr;
wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done");
if (wps->state != RECV_DONE &&
(!wps->wps->wps_upnp || !wps->ext_reg)) {
wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
"receiving WSC_Done", wps->state);
return WPS_FAILURE;
}
if (wps_parse_msg(msg, &attr) < 0)
return WPS_FAILURE;
if (attr.msg_type == NULL) {
wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
return WPS_FAILURE;
}
if (*attr.msg_type != WPS_WSC_DONE) {
wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
*attr.msg_type);
return WPS_FAILURE;
}
#ifdef CONFIG_WPS_UPNP
if (wps->wps->wps_upnp && wps->ext_reg) {
wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
"Registrar completed successfully");
wps_device_store(wps->wps->registrar, &wps->peer_dev,
wps->uuid_e);
return WPS_DONE;
}
#endif /* CONFIG_WPS_UPNP */
if (attr.registrar_nonce == NULL ||
os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN) != 0)
{
wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
return WPS_FAILURE;
}
if (attr.enrollee_nonce == NULL ||
os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN) != 0) {
wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
return WPS_FAILURE;
}
wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully");
wps_device_store(wps->wps->registrar, &wps->peer_dev,
wps->uuid_e);
if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk &&
wps->wps->ap && !wps->wps->registrar->disable_auto_conf) {
struct wps_credential cred;
wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
"on first Enrollee connection");
os_memset(&cred, 0, sizeof(cred));
os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
cred.ssid_len = wps->wps->ssid_len;
if (wps->wps->rf_band_cb(wps->wps->cb_ctx) == WPS_RF_60GHZ) {
cred.auth_type = WPS_AUTH_WPA2PSK;
cred.encr_type = WPS_ENCR_AES;
} else {
cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
}
os_memcpy(cred.key, wps->new_psk, wps->new_psk_len);
cred.key_len = wps->new_psk_len;
wps->wps->wps_state = WPS_STATE_CONFIGURED;
wpa_hexdump_ascii_key(MSG_DEBUG,
"WPS: Generated random passphrase",
wps->new_psk, wps->new_psk_len);
if (wps->wps->cred_cb)
wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
os_free(wps->new_psk);
wps->new_psk = NULL;
}
if (!wps->wps->ap && !wps->er)
wps_sta_cred_cb(wps);
if (wps->new_psk) {
if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e,
wps->p2p_dev_addr, wps->new_psk,
wps->new_psk_len)) {
wpa_printf(MSG_DEBUG, "WPS: Failed to configure the "
"new PSK");
}
os_free(wps->new_psk);
wps->new_psk = NULL;
}
wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e,
wps->dev_password, wps->dev_password_len);
if (wps->pbc) {
wps_registrar_remove_pbc_session(wps->wps->registrar,
wps->uuid_e,
wps->p2p_dev_addr);
wps_registrar_pbc_completed(wps->wps->registrar);
#ifdef WPS_WORKAROUNDS
os_get_reltime(&wps->wps->registrar->pbc_ignore_start);
#endif /* WPS_WORKAROUNDS */
os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e,
WPS_UUID_LEN);
} else {
wps_registrar_pin_completed(wps->wps->registrar);
}
/* TODO: maintain AuthorizedMACs somewhere separately for each ER and
* merge them into APs own list.. */
wps_success_event(wps->wps, wps->mac_addr_e);
return WPS_DONE;
}
enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
enum wsc_op_code op_code,
const struct wpabuf *msg)
{
enum wps_process_res ret;
wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
"op_code=%d)",
(unsigned long) wpabuf_len(msg), op_code);
#ifdef CONFIG_WPS_UPNP
if (wps->wps->wps_upnp && op_code == WSC_MSG && wps->ext_reg == 1) {
struct wps_parse_attr attr;
if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type &&
*attr.msg_type == WPS_M3)
wps->ext_reg = 2; /* past M2/M2D phase */
}
if (wps->ext_reg > 1)
wps_registrar_free_pending_m2(wps->wps);
if (wps->wps->wps_upnp && wps->ext_reg &&
wps->wps->upnp_msgs == NULL &&
(op_code == WSC_MSG || op_code == WSC_Done || op_code == WSC_NACK))
{
struct wps_parse_attr attr;
int type;
if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL)
type = -1;
else
type = *attr.msg_type;
wpa_printf(MSG_DEBUG, "WPS: Sending received message (type %d)"
" to external Registrar for processing", type);
upnp_wps_device_send_wlan_event(wps->wps->wps_upnp,
wps->mac_addr_e,
UPNP_WPS_WLANEVENT_TYPE_EAP,
msg);
if (op_code == WSC_MSG)
return WPS_PENDING;
} else if (wps->wps->wps_upnp && wps->ext_reg && op_code == WSC_MSG) {
wpa_printf(MSG_DEBUG, "WPS: Skip internal processing - using "
"external Registrar");
return WPS_CONTINUE;
}
#endif /* CONFIG_WPS_UPNP */
switch (op_code) {
case WSC_MSG:
return wps_process_wsc_msg(wps, msg);
case WSC_ACK:
if (wps_validate_wsc_ack(msg) < 0)
return WPS_FAILURE;
return wps_process_wsc_ack(wps, msg);
case WSC_NACK:
if (wps_validate_wsc_nack(msg) < 0)
return WPS_FAILURE;
return wps_process_wsc_nack(wps, msg);
case WSC_Done:
if (wps_validate_wsc_done(msg) < 0)
return WPS_FAILURE;
ret = wps_process_wsc_done(wps, msg);
if (ret == WPS_FAILURE) {
wps->state = SEND_WSC_NACK;
wps_fail_event(wps->wps, WPS_WSC_DONE,
wps->config_error,
wps->error_indication, wps->mac_addr_e);
}
return ret;
default:
wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
return WPS_FAILURE;
}
}
int wps_registrar_update_ie(struct wps_registrar *reg)
{
return wps_set_ie(reg);
}
static void wps_registrar_set_selected_timeout(void *eloop_ctx,
void *timeout_ctx)
{
struct wps_registrar *reg = eloop_ctx;
wpa_printf(MSG_DEBUG, "WPS: Selected Registrar timeout - "
"unselect internal Registrar");
reg->selected_registrar = 0;
reg->pbc = 0;
wps_registrar_expire_pins(reg);
wps_registrar_selected_registrar_changed(reg, 0);
}
#ifdef CONFIG_WPS_UPNP
static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
struct subscription *s)
{
int i, j;
wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d "
"config_methods=0x%x)",
s->dev_password_id, s->config_methods);
reg->sel_reg_union = 1;
if (reg->sel_reg_dev_password_id_override != DEV_PW_PUSHBUTTON)
reg->sel_reg_dev_password_id_override = s->dev_password_id;
if (reg->sel_reg_config_methods_override == -1)
reg->sel_reg_config_methods_override = 0;
reg->sel_reg_config_methods_override |= s->config_methods;
for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++)
if (is_zero_ether_addr(reg->authorized_macs_union[i]))
break;
for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS;
j++) {
if (is_zero_ether_addr(s->authorized_macs[j]))
break;
wpa_printf(MSG_DEBUG, "WPS: Add authorized MAC into union: "
MACSTR, MAC2STR(s->authorized_macs[j]));
os_memcpy(reg->authorized_macs_union[i],
s->authorized_macs[j], ETH_ALEN);
i++;
}
wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union",
(u8 *) reg->authorized_macs_union,
sizeof(reg->authorized_macs_union));
}
#endif /* CONFIG_WPS_UPNP */
static void wps_registrar_sel_reg_union(struct wps_registrar *reg)
{
#ifdef CONFIG_WPS_UPNP
struct subscription *s;
if (reg->wps->wps_upnp == NULL)
return;
dl_list_for_each(s, &reg->wps->wps_upnp->subscriptions,
struct subscription, list) {
struct subscr_addr *sa;
sa = dl_list_first(&s->addr_list, struct subscr_addr, list);
if (sa) {
wpa_printf(MSG_DEBUG, "WPS: External Registrar %s:%d",
inet_ntoa(sa->saddr.sin_addr),
ntohs(sa->saddr.sin_port));
}
if (s->selected_registrar)
wps_registrar_sel_reg_add(reg, s);
else
wpa_printf(MSG_DEBUG, "WPS: External Registrar not "
"selected");
}
#endif /* CONFIG_WPS_UPNP */
}
/**
* wps_registrar_selected_registrar_changed - SetSelectedRegistrar change
* @reg: Registrar data from wps_registrar_init()
*
* This function is called when selected registrar state changes, e.g., when an
* AP receives a SetSelectedRegistrar UPnP message.
*/
void wps_registrar_selected_registrar_changed(struct wps_registrar *reg,
u16 dev_pw_id)
{
wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed");
reg->sel_reg_union = reg->selected_registrar;
reg->sel_reg_dev_password_id_override = -1;
reg->sel_reg_config_methods_override = -1;
os_memcpy(reg->authorized_macs_union, reg->authorized_macs,
WPS_MAX_AUTHORIZED_MACS * ETH_ALEN);
wpa_hexdump(MSG_DEBUG, "WPS: Authorized MACs union (start with own)",
(u8 *) reg->authorized_macs_union,
sizeof(reg->authorized_macs_union));
if (reg->selected_registrar) {
u16 methods;
methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
WPS_CONFIG_PHY_PUSHBUTTON);
if (reg->pbc) {
reg->sel_reg_dev_password_id_override =
DEV_PW_PUSHBUTTON;
wps_set_pushbutton(&methods, reg->wps->config_methods);
} else if (dev_pw_id)
reg->sel_reg_dev_password_id_override = dev_pw_id;
wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected "
"(pbc=%d)", reg->pbc);
reg->sel_reg_config_methods_override = methods;
} else
wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected");
wps_registrar_sel_reg_union(reg);
wps_set_ie(reg);
wps_cb_set_sel_reg(reg);
}
int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
char *buf, size_t buflen)
{
struct wps_registrar_device *d;
int len = 0, ret;
char uuid[40];
char devtype[WPS_DEV_TYPE_BUFSIZE];
d = wps_device_get(reg, addr);
if (d == NULL)
return 0;
if (uuid_bin2str(d->uuid, uuid, sizeof(uuid)))
return 0;
ret = os_snprintf(buf + len, buflen - len,
"wpsUuid=%s\n"
"wpsPrimaryDeviceType=%s\n"
"wpsDeviceName=%s\n"
"wpsManufacturer=%s\n"
"wpsModelName=%s\n"
"wpsModelNumber=%s\n"
"wpsSerialNumber=%s\n",
uuid,
wps_dev_type_bin2str(d->dev.pri_dev_type, devtype,
sizeof(devtype)),
d->dev.device_name ? d->dev.device_name : "",
d->dev.manufacturer ? d->dev.manufacturer : "",
d->dev.model_name ? d->dev.model_name : "",
d->dev.model_number ? d->dev.model_number : "",
d->dev.serial_number ? d->dev.serial_number : "");
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
return len;
}
int wps_registrar_config_ap(struct wps_registrar *reg,
struct wps_credential *cred)
{
wpa_printf(MSG_DEBUG, "WPS: encr_type=0x%x", cred->encr_type);
if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP |
WPS_ENCR_AES))) {
if (cred->encr_type & WPS_ENCR_WEP) {
wpa_printf(MSG_INFO, "WPS: Reject new AP settings "
"due to WEP configuration");
return -1;
}
wpa_printf(MSG_INFO, "WPS: Reject new AP settings due to "
"invalid encr_type 0x%x", cred->encr_type);
return -1;
}
if ((cred->encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) ==
WPS_ENCR_TKIP) {
wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
"TKIP+AES");
cred->encr_type |= WPS_ENCR_AES;
}
if ((cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) ==
WPS_AUTH_WPAPSK) {
wpa_printf(MSG_DEBUG, "WPS: Upgrade auth_type WPAPSK -> "
"WPAPSK+WPA2PSK");
cred->auth_type |= WPS_AUTH_WPA2PSK;
}
if (reg->wps->cred_cb)
return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
return -1;
}
int wps_registrar_update_multi_ap(struct wps_registrar *reg,
const u8 *multi_ap_backhaul_ssid,
size_t multi_ap_backhaul_ssid_len,
const u8 *multi_ap_backhaul_network_key,
size_t multi_ap_backhaul_network_key_len)
{
if (multi_ap_backhaul_ssid) {
os_memcpy(reg->multi_ap_backhaul_ssid,
multi_ap_backhaul_ssid, multi_ap_backhaul_ssid_len);
reg->multi_ap_backhaul_ssid_len = multi_ap_backhaul_ssid_len;
}
os_free(reg->multi_ap_backhaul_network_key);
reg->multi_ap_backhaul_network_key = NULL;
reg->multi_ap_backhaul_network_key_len = 0;
if (multi_ap_backhaul_network_key) {
reg->multi_ap_backhaul_network_key =
os_memdup(multi_ap_backhaul_network_key,
multi_ap_backhaul_network_key_len);
if (!reg->multi_ap_backhaul_network_key)
return -1;
reg->multi_ap_backhaul_network_key_len =
multi_ap_backhaul_network_key_len;
}
return 0;
}
#ifdef CONFIG_WPS_NFC
int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
const u8 *pubkey_hash, u16 pw_id,
const u8 *dev_pw, size_t dev_pw_len,
int pk_hash_provided_oob)
{
struct wps_nfc_pw_token *token;
if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
return -1;
if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER &&
(pubkey_hash == NULL || !pk_hash_provided_oob)) {
wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token "
"addition - missing public key hash");
return -1;
}
wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
token = os_zalloc(sizeof(*token));
if (token == NULL)
return -1;
token->peer_pk_hash_known = pubkey_hash != NULL;
if (pubkey_hash)
os_memcpy(token->pubkey_hash, pubkey_hash,
WPS_OOB_PUBKEY_HASH_LEN);
token->pw_id = pw_id;
token->pk_hash_provided_oob = pk_hash_provided_oob;
if (dev_pw) {
wpa_snprintf_hex_uppercase((char *) token->dev_pw,
sizeof(token->dev_pw),
dev_pw, dev_pw_len);
token->dev_pw_len = dev_pw_len * 2;
}
dl_list_add(&reg->nfc_pw_tokens, &token->list);
reg->selected_registrar = 1;
reg->pbc = 0;
wps_registrar_add_authorized_mac(reg,
(u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg, pw_id);
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
wps_registrar_set_selected_timeout,
reg, NULL);
wpa_printf(MSG_DEBUG, "WPS: Added NFC Device Password %u to Registrar",
pw_id);
return 0;
}
int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
const u8 *oob_dev_pw,
size_t oob_dev_pw_len)
{
const u8 *pos, *hash, *dev_pw;
u16 id;
size_t dev_pw_len;
if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
WPS_OOB_DEVICE_PASSWORD_LEN)
return -1;
hash = oob_dev_pw;
pos = oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN;
id = WPA_GET_BE16(pos);
dev_pw = pos + 2;
dev_pw_len = oob_dev_pw + oob_dev_pw_len - dev_pw;
wpa_printf(MSG_DEBUG, "WPS: Add NFC Password Token for Password ID %u",
id);
wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
hash, WPS_OOB_PUBKEY_HASH_LEN);
wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
dev_pw_len, 0);
}
void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
struct wps_nfc_pw_token *token)
{
wps_registrar_remove_authorized_mac(reg,
(u8 *) "\xff\xff\xff\xff\xff\xff");
wps_registrar_selected_registrar_changed(reg, 0);
/*
* Free the NFC password token if it was used only for a single protocol
* run. The static handover case uses the same password token multiple
* times, so do not free that case here.
*/
if (token->peer_pk_hash_known)
os_free(token);
}
#endif /* CONFIG_WPS_NFC */
diff --git a/contrib/wpa/src/wps/wps_upnp.c b/contrib/wpa/src/wps/wps_upnp.c
index 2ae7ea8dcabd..5a89b5812144 100644
--- a/contrib/wpa/src/wps/wps_upnp.c
+++ b/contrib/wpa/src/wps/wps_upnp.c
@@ -1,1260 +1,1260 @@
/*
* UPnP WPS Device
* Copyright (c) 2000-2003 Intel Corporation
* Copyright (c) 2006-2007 Sony Corporation
* Copyright (c) 2008-2009 Atheros Communications
* Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
*
* See below for more details on licensing and code history.
*/
/*
* This has been greatly stripped down from the original file
* (upnp_wps_device.c) by Ted Merrill, Atheros Communications
* in order to eliminate use of the bulky libupnp library etc.
*
* History:
* upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and
* the libupnp library.
* The layering (by Sony) was well done; only a very minor modification
* to API of upnp_wps_device.c was required.
* libupnp was found to be undesirable because:
* -- It consumed too much code and data space
* -- It uses multiple threads, making debugging more difficult
* and possibly reducing reliability.
* -- It uses static variables and only supports one instance.
* The shim and libupnp are here replaced by special code written
* specifically for the needs of hostapd.
* Various shortcuts can and are taken to keep the code size small.
* Generally, execution time is not as crucial.
*
* BUGS:
* -- UPnP requires that we be able to resolve domain names.
* While uncommon, if we have to do it then it will stall the entire
* hostapd program, which is bad.
* This is because we use the standard linux getaddrinfo() function
* which is syncronous.
* An asyncronous solution would be to use the free "ares" library.
* -- Does not have a robust output buffering scheme. Uses a single
* fixed size output buffer per TCP/HTTP connection, with possible (although
* unlikely) possibility of overflow and likely excessive use of RAM.
* A better solution would be to write the HTTP output as a buffered stream,
* using chunking: (handle header specially, then) generate data with
* a printf-like function into a buffer, catching buffer full condition,
* then send it out surrounded by http chunking.
* -- There is some code that could be separated out into the common
* library to be shared with wpa_supplicant.
* -- Needs renaming with module prefix to avoid polluting the debugger
* namespace and causing possible collisions with other static fncs
* and structure declarations when using the debugger.
* -- The http error code generation is pretty bogus, hopefully no one cares.
*
* Author: Ted Merrill, Atheros Communications, based upon earlier work
* as explained above and below.
*
* Copyright:
* Copyright 2008 Atheros Communications.
*
* The original header (of upnp_wps_device.c) reads:
*
* Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved.
*
* File Name: upnp_wps_device.c
* Description: EAP-WPS UPnP device source
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Sony Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c
* typical header:
*
* Copyright (c) 2000-2003 Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Overview of WPS over UPnP:
*
* UPnP is a protocol that allows devices to discover each other and control
* each other. In UPnP terminology, a device is either a "device" (a server
* that provides information about itself and allows itself to be controlled)
* or a "control point" (a client that controls "devices") or possibly both.
* This file implements a UPnP "device".
*
* For us, we use mostly basic UPnP discovery, but the control part of interest
* is WPS carried via UPnP messages. There is quite a bit of basic UPnP
* discovery to do before we can get to WPS, however.
*
* UPnP discovery begins with "devices" send out multicast UDP packets to a
* certain fixed multicast IP address and port, and "control points" sending
* out other such UDP packets.
*
* The packets sent by devices are NOTIFY packets (not to be confused with TCP
* NOTIFY packets that are used later) and those sent by control points are
* M-SEARCH packets. These packets contain a simple HTTP style header. The
* packets are sent redundantly to get around packet loss. Devices respond to
* M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK
* messages, which give similar information as the UDP NOTIFY packets.
*
* The above UDP packets advertise the (arbitrary) TCP ports that the
* respective parties will listen to. The control point can then do a HTTP
* SUBSCRIBE (something like an HTTP PUT) after which the device can do a
* separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging.
*
* The control point will also do HTTP GET of the "device file" listed in the
* original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE
* data), and based on this will do additional GETs... HTTP POSTs are done to
* cause an action.
*
* Beyond some basic information in HTTP headers, additional information is in
* the HTTP bodies, in a format set by the SOAP and XML standards, a markup
* language related to HTML used for web pages. This language is intended to
* provide the ultimate in self-documentation by providing a universal
* namespace based on pseudo-URLs called URIs. Note that although a URI looks
* like a URL (a web address), they are never accessed as such but are used
* only as identifiers.
*
* The POST of a GetDeviceInfo gets information similar to what might be
* obtained from a probe request or response on Wi-Fi. WPS messages M1-M8
* are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted
* to a bin64 ascii representation for encapsulation. When proxying messages,
* WLANEvent and PutWLANResponse are used.
*
* This of course glosses over a lot of details.
*/
#include "includes.h"
#include <time.h>
#include <net/if.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include "common.h"
#include "uuid.h"
#include "base64.h"
#include "wps.h"
#include "wps_i.h"
#include "wps_upnp.h"
#include "wps_upnp_i.h"
/*
* UPnP allows a client ("control point") to send a server like us ("device")
* a domain name for registration, and we are supposed to resolve it. This is
* bad because, using the standard Linux library, we will stall the entire
* hostapd waiting for resolution.
*
* The "correct" solution would be to use an event driven library for domain
* name resolution such as "ares". However, this would increase code size
* further. Since it is unlikely that we'll actually see such domain names, we
* can just refuse to accept them.
*/
#define NO_DOMAIN_NAME_RESOLUTION 1 /* 1 to allow only dotted ip addresses */
/*
* UPnP does not scale well. If we were in a room with thousands of control
* points then potentially we could be expected to handle subscriptions for
* each of them, which would exhaust our memory. So we must set a limit. In
* practice we are unlikely to see more than one or two.
*/
#define MAX_SUBSCRIPTIONS 4 /* how many subscribing clients we handle */
#define MAX_ADDR_PER_SUBSCRIPTION 8
/* Maximum number of Probe Request events per second */
#define MAX_EVENTS_PER_SEC 5
static struct upnp_wps_device_sm *shared_upnp_device = NULL;
/* Write the current date/time per RFC */
void format_date(struct wpabuf *buf)
{
const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
"Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
struct tm *date;
time_t t;
t = time(NULL);
date = gmtime(&t);
if (date == NULL)
return;
wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
&weekday_str[date->tm_wday * 4], date->tm_mday,
&month_str[date->tm_mon * 4], date->tm_year + 1900,
date->tm_hour, date->tm_min, date->tm_sec);
}
/***************************************************************************
* UUIDs (unique identifiers)
*
* These are supposed to be unique in all the world.
* Sometimes permanent ones are used, sometimes temporary ones
* based on random numbers... there are different rules for valid content
* of different types.
* Each uuid is 16 bytes long.
**************************************************************************/
/* uuid_make -- construct a random UUID
* The UPnP documents don't seem to offer any guidelines as to which method to
* use for constructing UUIDs for subscriptions. Presumably any method from
* rfc4122 is good enough; I've chosen random number method.
*/
static int uuid_make(u8 uuid[UUID_LEN])
{
if (os_get_random(uuid, UUID_LEN) < 0)
return -1;
/* Replace certain bits as specified in rfc4122 or X.667 */
uuid[6] &= 0x0f; uuid[6] |= (4 << 4); /* version 4 == random gen */
uuid[8] &= 0x3f; uuid[8] |= 0x80;
return 0;
}
/*
* Subscriber address handling.
* Since a subscriber may have an arbitrary number of addresses, we have to
* add a bunch of code to handle them.
*
* Addresses are passed in text, and MAY be domain names instead of the (usual
* and expected) dotted IP addresses. Resolving domain names consumes a lot of
* resources. Worse, we are currently using the standard Linux getaddrinfo()
* which will block the entire program until complete or timeout! The proper
* solution would be to use the "ares" library or similar with more state
* machine steps etc. or just disable domain name resolution by setting
* NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file.
*/
/* subscr_addr_delete -- delete single unlinked subscriber address
* (be sure to unlink first if need be)
*/
void subscr_addr_delete(struct subscr_addr *a)
{
/*
* Note: do NOT free domain_and_port or path because they point to
* memory within the allocation of "a".
*/
os_free(a);
}
/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */
static void subscr_addr_free_all(struct subscription *s)
{
struct subscr_addr *a, *tmp;
dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list)
{
dl_list_del(&a->list);
subscr_addr_delete(a);
}
}
static int local_network_addr(struct upnp_wps_device_sm *sm,
struct sockaddr_in *addr)
{
return (addr->sin_addr.s_addr & sm->netmask.s_addr) ==
(sm->ip_addr & sm->netmask.s_addr);
}
/* subscr_addr_add_url -- add address(es) for one url to subscription */
static void subscr_addr_add_url(struct subscription *s, const char *url,
size_t url_len)
{
int alloc_len;
char *scratch_mem = NULL;
char *mem;
char *host;
char *delim;
char *path;
int port = 80; /* port to send to (default is port 80) */
struct addrinfo hints;
struct addrinfo *result = NULL;
struct addrinfo *rp;
int rerr;
size_t host_len, path_len;
/* URL MUST begin with HTTP scheme. In addition, limit the length of
* the URL to 700 characters which is around the limit that was
* implicitly enforced for more than 10 years due to a bug in
* generating the event messages. */
if (url_len < 7 || os_strncasecmp(url, "http://", 7) || url_len > 700) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Reject an unacceptable URL");
goto fail;
}
url += 7;
url_len -= 7;
/* Make a copy of the string to allow modification during parsing */
scratch_mem = dup_binstr(url, url_len);
if (scratch_mem == NULL)
goto fail;
wpa_printf(MSG_DEBUG, "WPS UPnP: Adding URL '%s'", scratch_mem);
host = scratch_mem;
path = os_strchr(host, '/');
if (path)
*path++ = '\0'; /* null terminate host */
/* Process and remove optional port component */
delim = os_strchr(host, ':');
if (delim) {
*delim = '\0'; /* null terminate host name for now */
if (isdigit(delim[1]))
port = atol(delim + 1);
}
/*
* getaddrinfo does the right thing with dotted decimal notations, or
* will resolve domain names. Resolving domain names will unfortunately
* hang the entire program until it is resolved or it times out
* internal to getaddrinfo; fortunately we think that the use of actual
* domain names (vs. dotted decimal notations) should be uncommon.
*/
os_memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET; /* IPv4 */
hints.ai_socktype = SOCK_STREAM;
#if NO_DOMAIN_NAME_RESOLUTION
/* Suppress domain name resolutions that would halt
* the program for periods of time
*/
hints.ai_flags = AI_NUMERICHOST;
#else
/* Allow domain name resolution. */
hints.ai_flags = 0;
#endif
hints.ai_protocol = 0; /* Any protocol? */
rerr = getaddrinfo(host, NULL /* fill in port ourselves */,
&hints, &result);
if (rerr) {
wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
rerr, gai_strerror(rerr), host);
goto fail;
}
if (delim)
*delim = ':'; /* Restore port */
host_len = os_strlen(host);
path_len = path ? os_strlen(path) : 0;
alloc_len = host_len + 1 + 1 + path_len + 1;
for (rp = result; rp; rp = rp->ai_next) {
struct subscr_addr *a;
struct sockaddr_in *addr = (struct sockaddr_in *) rp->ai_addr;
/* Limit no. of address to avoid denial of service attack */
if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
"Ignoring excessive addresses");
break;
}
if (!local_network_addr(s->sm, addr)) {
wpa_printf(MSG_INFO,
"WPS UPnP: Ignore a delivery URL that points to another network %s",
inet_ntoa(addr->sin_addr));
continue;
}
a = os_zalloc(sizeof(*a) + alloc_len);
if (a == NULL)
break;
mem = (char *) (a + 1);
a->domain_and_port = mem;
os_memcpy(mem, host, host_len);
mem += host_len + 1;
a->path = mem;
if (path == NULL || path[0] != '/')
*mem++ = '/';
if (path)
os_memcpy(mem, path, path_len);
os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
a->saddr.sin_port = htons(port);
dl_list_add(&s->addr_list, &a->list);
}
fail:
if (result)
freeaddrinfo(result);
os_free(scratch_mem);
}
/* subscr_addr_list_create -- create list from urls in string.
* Each url is enclosed by angle brackets.
*/
static void subscr_addr_list_create(struct subscription *s,
const char *url_list)
{
const char *end;
wpa_printf(MSG_DEBUG, "WPS UPnP: Parsing URL list '%s'", url_list);
for (;;) {
while (*url_list == ' ' || *url_list == '\t')
url_list++;
if (*url_list != '<')
break;
url_list++;
end = os_strchr(url_list, '>');
if (end == NULL)
break;
subscr_addr_add_url(s, url_list, end - url_list);
url_list = end + 1;
}
}
static void wpabuf_put_property(struct wpabuf *buf, const char *name,
const char *value)
{
wpabuf_put_str(buf, "<e:property>");
wpabuf_printf(buf, "<%s>", name);
if (value)
wpabuf_put_str(buf, value);
wpabuf_printf(buf, "</%s>", name);
wpabuf_put_str(buf, "</e:property>\n");
}
/**
* upnp_wps_device_send_event - Queue event messages for subscribers
* @sm: WPS UPnP state machine from upnp_wps_device_init()
*
* This function queues the last WLANEvent to be sent for all currently
* subscribed UPnP control points. sm->wlanevent must have been set with the
* encoded data before calling this function.
*/
static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
{
/* Enqueue event message for all subscribers */
struct wpabuf *buf; /* holds event message */
int buf_size = 0;
struct subscription *s, *tmp;
/* Actually, utf-8 is the default, but it doesn't hurt to specify it */
const char *format_head =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
const char *format_tail = "</e:propertyset>\n";
struct os_reltime now;
if (dl_list_empty(&sm->subscriptions)) {
/* optimize */
return;
}
if (os_get_reltime(&now) == 0) {
if (now.sec != sm->last_event_sec) {
sm->last_event_sec = now.sec;
sm->num_events_in_sec = 1;
} else {
sm->num_events_in_sec++;
/*
* In theory, this should apply to all WLANEvent
* notifications, but EAP messages are of much higher
* priority and Probe Request notifications should not
* be allowed to drop EAP messages, so only throttle
* Probe Request notifications.
*/
if (sm->num_events_in_sec > MAX_EVENTS_PER_SEC &&
sm->wlanevent_type ==
UPNP_WPS_WLANEVENT_TYPE_PROBE) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Throttle "
"event notifications (%u seen "
"during one second)",
sm->num_events_in_sec);
return;
}
}
}
/* Determine buffer size needed first */
buf_size += os_strlen(format_head);
buf_size += 50 + 2 * os_strlen("WLANEvent");
if (sm->wlanevent)
buf_size += os_strlen(sm->wlanevent);
buf_size += os_strlen(format_tail);
buf = wpabuf_alloc(buf_size);
if (buf == NULL)
return;
wpabuf_put_str(buf, format_head);
wpabuf_put_property(buf, "WLANEvent", sm->wlanevent);
wpabuf_put_str(buf, format_tail);
wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s",
(char *) wpabuf_head(buf));
dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
list) {
wps_upnp_event_add(
s, buf,
sm->wlanevent_type == UPNP_WPS_WLANEVENT_TYPE_PROBE);
}
wpabuf_free(buf);
}
/*
* Event subscription (subscriber machines register with us to receive event
* messages).
* This is the result of an incoming HTTP over TCP SUBSCRIBE request.
*/
/* subscription_destroy -- destroy an unlinked subscription
* Be sure to unlink first if necessary.
*/
void subscription_destroy(struct subscription *s)
{
struct upnp_wps_device_interface *iface;
wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
subscr_addr_free_all(s);
wps_upnp_event_delete_all(s);
dl_list_for_each(iface, &s->sm->interfaces,
struct upnp_wps_device_interface, list)
upnp_er_remove_notification(iface->wps->registrar, s);
os_free(s);
}
/* subscription_list_age -- remove expired subscriptions */
static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now)
{
struct subscription *s, *tmp;
dl_list_for_each_safe(s, tmp, &sm->subscriptions,
struct subscription, list) {
if (s->timeout_time > now)
break;
wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription");
dl_list_del(&s->list);
subscription_destroy(s);
}
}
/* subscription_find -- return existing subscription matching uuid, if any
* returns NULL if not found
*/
struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
const u8 uuid[UUID_LEN])
{
struct subscription *s;
dl_list_for_each(s, &sm->subscriptions, struct subscription, list) {
if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0)
return s; /* Found match */
}
return NULL;
}
static struct wpabuf * build_fake_wsc_ack(void)
{
struct wpabuf *msg = wpabuf_alloc(100);
if (msg == NULL)
return NULL;
wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP);
wpabuf_put_str(msg, "00:00:00:00:00:00");
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_WSC_ACK)) {
wpabuf_free(msg);
return NULL;
}
/* Enrollee Nonce */
wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
wpabuf_put_be16(msg, WPS_NONCE_LEN);
wpabuf_put(msg, WPS_NONCE_LEN);
/* Registrar Nonce */
wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
wpabuf_put_be16(msg, WPS_NONCE_LEN);
wpabuf_put(msg, WPS_NONCE_LEN);
if (wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
return msg;
}
/* subscription_first_event -- send format/queue event that is automatically
* sent on a new subscription.
*/
static int subscription_first_event(struct subscription *s)
{
/*
* Actually, utf-8 is the default, but it doesn't hurt to specify it.
*
* APStatus is apparently a bit set,
* 0x1 = configuration change (but is always set?)
* 0x10 = ap is locked
*
* Per UPnP spec, we send out the last value of each variable, even
* for WLANEvent, whatever it was.
*/
char *wlan_event;
struct wpabuf *buf;
int ap_status = 1; /* TODO: add 0x10 if access point is locked */
const char *head =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
"<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
const char *tail = "</e:propertyset>\n";
char txt[10];
int ret;
if (s->sm->wlanevent == NULL) {
/*
* There has been no events before the subscription. However,
* UPnP device architecture specification requires all the
- * evented variables to be included, so generate a dummy event
+ * evented variables to be included, so generate a stub event
* for this particular case using a WSC_ACK and all-zeros
* nonces. The ER (UPnP control point) will ignore this, but at
* least it will learn that WLANEvent variable will be used in
* event notifications in the future.
*/
struct wpabuf *msg;
wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the "
"initial WLANEvent");
msg = build_fake_wsc_ack();
if (msg) {
s->sm->wlanevent =
base64_encode(wpabuf_head(msg),
wpabuf_len(msg), NULL);
wpabuf_free(msg);
}
}
wlan_event = s->sm->wlanevent;
if (wlan_event == NULL || *wlan_event == '\0') {
wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for "
"initial event message");
wlan_event = "";
}
buf = wpabuf_alloc(500 + os_strlen(wlan_event));
if (buf == NULL)
return -1;
wpabuf_put_str(buf, head);
wpabuf_put_property(buf, "STAStatus", "1");
os_snprintf(txt, sizeof(txt), "%d", ap_status);
wpabuf_put_property(buf, "APStatus", txt);
if (*wlan_event)
wpabuf_put_property(buf, "WLANEvent", wlan_event);
wpabuf_put_str(buf, tail);
ret = wps_upnp_event_add(s, buf, 0);
if (ret) {
wpabuf_free(buf);
return ret;
}
wpabuf_free(buf);
return 0;
}
/**
* subscription_start - Remember a UPnP control point to send events to.
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* @callback_urls: Callback URLs
* Returns: %NULL on error, or pointer to new subscription structure.
*/
struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
const char *callback_urls)
{
struct subscription *s;
time_t now = time(NULL);
time_t expire = now + UPNP_SUBSCRIBE_SEC;
char str[80];
/* Get rid of expired subscriptions so we have room */
subscription_list_age(sm, now);
/* If too many subscriptions, remove oldest */
if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
s = dl_list_first(&sm->subscriptions, struct subscription,
list);
if (s) {
wpa_printf(MSG_INFO,
"WPS UPnP: Too many subscriptions, trashing oldest");
dl_list_del(&s->list);
subscription_destroy(s);
}
}
s = os_zalloc(sizeof(*s));
if (s == NULL)
return NULL;
dl_list_init(&s->addr_list);
dl_list_init(&s->event_queue);
s->sm = sm;
s->timeout_time = expire;
if (uuid_make(s->uuid) < 0) {
subscription_destroy(s);
return NULL;
}
subscr_addr_list_create(s, callback_urls);
if (dl_list_empty(&s->addr_list)) {
wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
"'%s' - drop subscription", callback_urls);
subscription_destroy(s);
return NULL;
}
/* Add to end of list, since it has the highest expiration time */
dl_list_add_tail(&sm->subscriptions, &s->list);
/* Queue up immediate event message (our last event)
* as required by UPnP spec.
*/
if (subscription_first_event(s)) {
wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to "
"event backlog");
dl_list_del(&s->list);
subscription_destroy(s);
return NULL;
}
uuid_bin2str(s->uuid, str, sizeof(str));
wpa_printf(MSG_DEBUG,
"WPS UPnP: Subscription %p (SID %s) started with %s",
s, str, callback_urls);
/* Schedule sending this */
wps_upnp_event_send_all_later(sm);
return s;
}
/* subscription_renew -- find subscription and reset timeout */
struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
const u8 uuid[UUID_LEN])
{
time_t now = time(NULL);
time_t expire = now + UPNP_SUBSCRIBE_SEC;
struct subscription *s = subscription_find(sm, uuid);
if (s == NULL)
return NULL;
wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed");
dl_list_del(&s->list);
s->timeout_time = expire;
/* add back to end of list, since it now has highest expiry */
dl_list_add_tail(&sm->subscriptions, &s->list);
return s;
}
/**
* upnp_wps_device_send_wlan_event - Event notification
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* @from_mac_addr: Source (Enrollee) MAC address for the event
* @ev_type: Event type
* @msg: Event data
* Returns: 0 on success, -1 on failure
*
* Tell external Registrars (UPnP control points) that something happened. In
* particular, events include WPS messages from clients that are proxied to
* external Registrars.
*/
int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
const u8 from_mac_addr[ETH_ALEN],
enum upnp_wps_wlanevent_type ev_type,
const struct wpabuf *msg)
{
int ret = -1;
char type[2];
const u8 *mac = from_mac_addr;
char mac_text[18];
u8 *raw = NULL;
size_t raw_len;
char *val;
size_t val_len;
int pos = 0;
if (!sm)
goto fail;
os_snprintf(type, sizeof(type), "%1u", ev_type);
raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0);
raw = os_zalloc(raw_len);
if (!raw)
goto fail;
*(raw + pos) = (u8) ev_type;
pos += 1;
os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac));
wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s",
mac_text);
os_memcpy(raw + pos, mac_text, 17);
pos += 17;
if (msg) {
os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg));
pos += wpabuf_len(msg);
}
raw_len = pos;
val = base64_encode(raw, raw_len, &val_len);
if (val == NULL)
goto fail;
os_free(sm->wlanevent);
sm->wlanevent = val;
sm->wlanevent_type = ev_type;
upnp_wps_device_send_event(sm);
ret = 0;
fail:
os_free(raw);
return ret;
}
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
#include <sys/sysctl.h>
#include <net/route.h>
#include <net/if_dl.h>
static int eth_get(const char *device, u8 ea[ETH_ALEN])
{
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
u_char *p, *buf;
size_t len;
int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
return -1;
if ((buf = os_malloc(len)) == NULL)
return -1;
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
os_free(buf);
return -1;
}
for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
ifm = (struct if_msghdr *)p;
sdl = (struct sockaddr_dl *)(ifm + 1);
if (ifm->ifm_type != RTM_IFINFO ||
(ifm->ifm_addrs & RTA_IFP) == 0)
continue;
if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
continue;
os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
break;
}
os_free(buf);
if (p >= buf + len) {
errno = ESRCH;
return -1;
}
return 0;
}
#endif /* __FreeBSD__ || __APPLE__ */
/**
* get_netif_info - Get hw and IP addresses for network device
* @net_if: Selected network interface name
* @ip_addr: Buffer for returning IP address in network byte order
* @ip_addr_text: Buffer for returning a pointer to allocated IP address text
* @netmask: Buffer for returning netmask or %NULL if not needed
* @mac: Buffer for returning MAC address
* Returns: 0 on success, -1 on failure
*/
int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
struct in_addr *netmask, u8 mac[ETH_ALEN])
{
struct ifreq req;
int sock = -1;
struct sockaddr_in *addr;
struct in_addr in_addr;
*ip_addr_text = os_zalloc(16);
if (*ip_addr_text == NULL)
goto fail;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
goto fail;
os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
if (ioctl(sock, SIOCGIFADDR, &req) < 0) {
wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)",
errno, strerror(errno));
goto fail;
}
addr = (void *) &req.ifr_addr;
*ip_addr = addr->sin_addr.s_addr;
in_addr.s_addr = *ip_addr;
os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
if (netmask) {
os_memset(&req, 0, sizeof(req));
os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
if (ioctl(sock, SIOCGIFNETMASK, &req) < 0) {
wpa_printf(MSG_ERROR,
"WPS UPnP: SIOCGIFNETMASK failed: %d (%s)",
errno, strerror(errno));
goto fail;
}
addr = (struct sockaddr_in *) &req.ifr_addr;
netmask->s_addr = addr->sin_addr.s_addr;
}
#ifdef __linux__
os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: "
"%d (%s)", errno, strerror(errno));
goto fail;
}
os_memcpy(mac, req.ifr_addr.sa_data, 6);
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__APPLE__) \
|| defined(__DragonFly__)
if (eth_get(net_if, mac) < 0) {
wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
goto fail;
}
#else
#error MAC address fetch not implemented
#endif
close(sock);
return 0;
fail:
if (sock >= 0)
close(sock);
os_free(*ip_addr_text);
*ip_addr_text = NULL;
return -1;
}
static void upnp_wps_free_msearchreply(struct dl_list *head)
{
struct advertisement_state_machine *a, *tmp;
dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine,
list)
msearchreply_state_machine_stop(a);
}
static void upnp_wps_free_subscriptions(struct dl_list *head,
struct wps_registrar *reg)
{
struct subscription *s, *tmp;
dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
if (reg && s->reg != reg)
continue;
dl_list_del(&s->list);
subscription_destroy(s);
}
}
/**
* upnp_wps_device_stop - Stop WPS UPnP operations on an interface
* @sm: WPS UPnP state machine from upnp_wps_device_init()
*/
static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
{
if (!sm || !sm->started)
return;
wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
web_listener_stop(sm);
ssdp_listener_stop(sm);
upnp_wps_free_msearchreply(&sm->msearch_replies);
upnp_wps_free_subscriptions(&sm->subscriptions, NULL);
advertisement_state_machine_stop(sm, 1);
wps_upnp_event_send_stop_all(sm);
os_free(sm->wlanevent);
sm->wlanevent = NULL;
os_free(sm->ip_addr_text);
sm->ip_addr_text = NULL;
if (sm->multicast_sd >= 0)
close(sm->multicast_sd);
sm->multicast_sd = -1;
sm->started = 0;
}
/**
* upnp_wps_device_start - Start WPS UPnP operations on an interface
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* @net_if: Selected network interface name
* Returns: 0 on success, -1 on failure
*/
static int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
{
if (!sm || !net_if)
return -1;
if (sm->started)
upnp_wps_device_stop(sm);
sm->multicast_sd = -1;
sm->ssdp_sd = -1;
sm->started = 1;
sm->advertise_count = 0;
/* Fix up linux multicast handling */
if (add_ssdp_network(net_if))
goto fail;
/* Determine which IP and mac address we're using */
if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
&sm->netmask, sm->mac_addr)) {
wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
"for %s. Does it have IP address?", net_if);
goto fail;
}
wpa_printf(MSG_DEBUG, "WPS UPnP: Local IP address %s netmask %s hwaddr "
MACSTR,
sm->ip_addr_text, inet_ntoa(sm->netmask),
MAC2STR(sm->mac_addr));
/* Listen for incoming TCP connections so that others
* can fetch our "xml files" from us.
*/
if (web_listener_start(sm))
goto fail;
/* Set up for receiving discovery (UDP) packets */
if (ssdp_listener_start(sm))
goto fail;
/* Set up for sending multicast */
if (ssdp_open_multicast(sm) < 0)
goto fail;
/*
* Broadcast NOTIFY messages to let the world know we exist.
* This is done via a state machine since the messages should not be
* all sent out at once.
*/
if (advertisement_state_machine_start(sm))
goto fail;
return 0;
fail:
upnp_wps_device_stop(sm);
return -1;
}
static struct upnp_wps_device_interface *
upnp_wps_get_iface(struct upnp_wps_device_sm *sm, void *priv)
{
struct upnp_wps_device_interface *iface;
dl_list_for_each(iface, &sm->interfaces,
struct upnp_wps_device_interface, list) {
if (iface->priv == priv)
return iface;
}
return NULL;
}
/**
* upnp_wps_device_deinit - Deinitialize WPS UPnP
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* @priv: External context data that was used in upnp_wps_device_init() call
*/
void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
{
struct upnp_wps_device_interface *iface;
struct upnp_wps_peer *peer;
if (!sm)
return;
iface = upnp_wps_get_iface(sm, priv);
if (iface == NULL) {
wpa_printf(MSG_ERROR, "WPS UPnP: Could not find the interface "
"instance to deinit");
return;
}
wpa_printf(MSG_DEBUG, "WPS UPnP: Deinit interface instance %p", iface);
if (dl_list_len(&sm->interfaces) == 1) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Deinitializing last instance "
"- free global device instance");
upnp_wps_device_stop(sm);
} else
upnp_wps_free_subscriptions(&sm->subscriptions,
iface->wps->registrar);
dl_list_del(&iface->list);
while ((peer = dl_list_first(&iface->peers, struct upnp_wps_peer,
list))) {
if (peer->wps)
wps_deinit(peer->wps);
dl_list_del(&peer->list);
os_free(peer);
}
os_free(iface->ctx->ap_pin);
os_free(iface->ctx);
os_free(iface);
if (dl_list_empty(&sm->interfaces)) {
os_free(sm->root_dir);
os_free(sm->desc_url);
os_free(sm);
shared_upnp_device = NULL;
}
}
/**
* upnp_wps_device_init - Initialize WPS UPnP
* @ctx: callback table; we must eventually free it
* @wps: Pointer to longterm WPS context
* @priv: External context data that will be used in callbacks
* @net_if: Selected network interface name
* Returns: WPS UPnP state or %NULL on failure
*/
struct upnp_wps_device_sm *
upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
void *priv, char *net_if)
{
struct upnp_wps_device_sm *sm;
struct upnp_wps_device_interface *iface;
int start = 0;
iface = os_zalloc(sizeof(*iface));
if (iface == NULL) {
os_free(ctx->ap_pin);
os_free(ctx);
return NULL;
}
wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
dl_list_init(&iface->peers);
iface->ctx = ctx;
iface->wps = wps;
iface->priv = priv;
if (shared_upnp_device) {
wpa_printf(MSG_DEBUG, "WPS UPnP: Share existing device "
"context");
sm = shared_upnp_device;
} else {
wpa_printf(MSG_DEBUG, "WPS UPnP: Initialize device context");
sm = os_zalloc(sizeof(*sm));
if (!sm) {
wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init "
"failed");
os_free(iface);
os_free(ctx->ap_pin);
os_free(ctx);
return NULL;
}
shared_upnp_device = sm;
dl_list_init(&sm->msearch_replies);
dl_list_init(&sm->subscriptions);
dl_list_init(&sm->interfaces);
start = 1;
}
dl_list_add(&sm->interfaces, &iface->list);
if (start && upnp_wps_device_start(sm, net_if)) {
upnp_wps_device_deinit(sm, priv);
return NULL;
}
return sm;
}
/**
* upnp_wps_subscribers - Check whether there are any event subscribers
* @sm: WPS UPnP state machine from upnp_wps_device_init()
* Returns: 0 if no subscribers, 1 if subscribers
*/
int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
{
return !dl_list_empty(&sm->subscriptions);
}
int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
{
struct upnp_wps_device_interface *iface;
if (sm == NULL)
return 0;
dl_list_for_each(iface, &sm->interfaces,
struct upnp_wps_device_interface, list) {
os_free(iface->ctx->ap_pin);
if (ap_pin) {
iface->ctx->ap_pin = os_strdup(ap_pin);
if (iface->ctx->ap_pin == NULL)
return -1;
} else
iface->ctx->ap_pin = NULL;
}
return 0;
}
diff --git a/contrib/wpa/wpa_supplicant/Android.mk b/contrib/wpa/wpa_supplicant/Android.mk
index f539ce1348ec..0aacafd4ab96 100644
--- a/contrib/wpa/wpa_supplicant/Android.mk
+++ b/contrib/wpa/wpa_supplicant/Android.mk
@@ -1,1825 +1,1824 @@
#
# Copyright (C) 2008 The Android Open Source Project
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
#
LOCAL_PATH := $(call my-dir)
PKG_CONFIG ?= pkg-config
ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),)
CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y
endif
include $(LOCAL_PATH)/android.config
# To ignore possible wrong network configurations
L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS
L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\"
# Set Android log name
L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\"
# Disable unused parameter warnings
L_CFLAGS += -Wno-unused-parameter
# Set Android extended P2P functionality
L_CFLAGS += -DANDROID_P2P
ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
L_CFLAGS += -DANDROID_LIB_STUB
endif
ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB_EVENT),)
L_CFLAGS += -DANDROID_LIB_EVENT
endif
# Disable roaming in wpa_supplicant
ifdef CONFIG_NO_ROAMING
L_CFLAGS += -DCONFIG_NO_ROAMING
endif
# Use Android specific directory for control interface sockets
L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/misc/wifi/sockets\"
# Use Android specific directory for wpa_cli command completion history
L_CFLAGS += -DCONFIG_WPA_CLI_HISTORY_DIR=\"/data/misc/wifi\"
# To force sizeof(enum) = 4
ifeq ($(TARGET_ARCH),arm)
L_CFLAGS += -mabi=aapcs-linux
endif
# C++ flags for binder interface
L_CPPFLAGS := -std=c++11 -Wall -Werror
# TODO: Remove these allowed warnings later.
L_CPPFLAGS += -Wno-unused-variable -Wno-unused-parameter
L_CPPFLAGS += -Wno-unused-private-field
INCLUDES = $(LOCAL_PATH)
INCLUDES += $(LOCAL_PATH)/src
INCLUDES += $(LOCAL_PATH)/src/common
# INCLUDES += $(LOCAL_PATH)/src/crypto # To force proper includes
INCLUDES += $(LOCAL_PATH)/src/drivers
INCLUDES += $(LOCAL_PATH)/src/eap_common
INCLUDES += $(LOCAL_PATH)/src/eapol_supp
INCLUDES += $(LOCAL_PATH)/src/eap_peer
INCLUDES += $(LOCAL_PATH)/src/eap_server
INCLUDES += $(LOCAL_PATH)/src/hlr_auc_gw
INCLUDES += $(LOCAL_PATH)/src/l2_packet
INCLUDES += $(LOCAL_PATH)/src/radius
INCLUDES += $(LOCAL_PATH)/src/rsn_supp
INCLUDES += $(LOCAL_PATH)/src/tls
INCLUDES += $(LOCAL_PATH)/src/utils
INCLUDES += $(LOCAL_PATH)/src/wps
INCLUDES += system/security/keystore/include
ifdef CONFIG_DRIVER_NL80211
ifneq ($(wildcard external/libnl),)
INCLUDES += external/libnl/include
else
INCLUDES += external/libnl-headers
endif
endif
ifdef CONFIG_FIPS
CONFIG_NO_RANDOM_POOL=
CONFIG_OPENSSL_CMAC=y
endif
OBJS = config.c
OBJS += notify.c
OBJS += bss.c
OBJS += eap_register.c
OBJS += src/utils/common.c
OBJS += src/utils/config.c
OBJS += src/utils/wpa_debug.c
OBJS += src/utils/wpabuf.c
OBJS += src/utils/bitfield.c
OBJS += src/utils/ip_addr.c
OBJS += src/utils/crc32.c
OBJS += wmm_ac.c
OBJS += op_classes.c
OBJS += rrm.c
OBJS += twt.c
OBJS += robust_av.c
OBJS_p = wpa_passphrase.c
OBJS_p += src/utils/common.c
OBJS_p += src/utils/wpa_debug.c
OBJS_p += src/utils/wpabuf.c
OBJS_c = wpa_cli.c src/common/wpa_ctrl.c
OBJS_c += src/utils/wpa_debug.c
OBJS_c += src/utils/common.c
OBJS_c += src/common/cli.c
OBJS_d =
OBJS_priv =
ifndef CONFIG_OS
ifdef CONFIG_NATIVE_WINDOWS
CONFIG_OS=win32
else
CONFIG_OS=unix
endif
endif
ifeq ($(CONFIG_OS), internal)
L_CFLAGS += -DOS_NO_C_LIB_DEFINES
endif
OBJS += src/utils/os_$(CONFIG_OS).c
OBJS_p += src/utils/os_$(CONFIG_OS).c
OBJS_c += src/utils/os_$(CONFIG_OS).c
ifdef CONFIG_WPA_TRACE
L_CFLAGS += -DWPA_TRACE
OBJS += src/utils/trace.c
OBJS_p += src/utils/trace.c
OBJS_c += src/utils/trace.c
LDFLAGS += -rdynamic
L_CFLAGS += -funwind-tables
ifdef CONFIG_WPA_TRACE_BFD
L_CFLAGS += -DWPA_TRACE_BFD
LIBS += -lbfd
LIBS_p += -lbfd
LIBS_c += -lbfd
endif
endif
ifndef CONFIG_ELOOP
CONFIG_ELOOP=eloop
endif
OBJS += src/utils/$(CONFIG_ELOOP).c
OBJS_c += src/utils/$(CONFIG_ELOOP).c
ifdef CONFIG_ELOOP_POLL
L_CFLAGS += -DCONFIG_ELOOP_POLL
endif
ifdef CONFIG_ELOOP_EPOLL
L_CFLAGS += -DCONFIG_ELOOP_EPOLL
endif
ifdef CONFIG_EAPOL_TEST
L_CFLAGS += -Werror -DEAPOL_TEST
endif
ifdef CONFIG_HT_OVERRIDES
L_CFLAGS += -DCONFIG_HT_OVERRIDES
endif
ifdef CONFIG_VHT_OVERRIDES
L_CFLAGS += -DCONFIG_VHT_OVERRIDES
endif
ifdef CONFIG_HE_OVERRIDES
L_CFLAGS += -DCONFIG_HE_OVERRIDES
endif
ifndef CONFIG_BACKEND
CONFIG_BACKEND=file
endif
ifeq ($(CONFIG_BACKEND), file)
OBJS += config_file.c
ifndef CONFIG_NO_CONFIG_BLOBS
NEED_BASE64=y
endif
L_CFLAGS += -DCONFIG_BACKEND_FILE
endif
ifeq ($(CONFIG_BACKEND), winreg)
OBJS += config_winreg.c
endif
ifeq ($(CONFIG_BACKEND), none)
OBJS += config_none.c
endif
ifdef CONFIG_NO_CONFIG_WRITE
L_CFLAGS += -DCONFIG_NO_CONFIG_WRITE
endif
ifdef CONFIG_NO_CONFIG_BLOBS
L_CFLAGS += -DCONFIG_NO_CONFIG_BLOBS
endif
ifdef CONFIG_NO_SCAN_PROCESSING
L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
endif
ifdef CONFIG_SUITEB
L_CFLAGS += -DCONFIG_SUITEB
endif
ifdef CONFIG_SUITEB192
L_CFLAGS += -DCONFIG_SUITEB192
NEED_SHA384=y
endif
ifdef CONFIG_OCV
L_CFLAGS += -DCONFIG_OCV
OBJS += src/common/ocv.c
endif
ifdef CONFIG_IEEE80211R
L_CFLAGS += -DCONFIG_IEEE80211R
OBJS += src/rsn_supp/wpa_ft.c
endif
ifdef CONFIG_MESH
NEED_80211_COMMON=y
NEED_AES_SIV=y
CONFIG_SAE=y
CONFIG_AP=y
L_CFLAGS += -DCONFIG_MESH
OBJS += mesh.c
OBJS += mesh_mpm.c
OBJS += mesh_rsn.c
endif
ifdef CONFIG_SAE
L_CFLAGS += -DCONFIG_SAE
OBJS += src/common/sae.c
ifdef CONFIG_SAE_PK
L_CFLAGS += -DCONFIG_SAE_PK
OBJS += src/common/sae_pk.c
endif
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
NEED_DRAGONFLY=y
ifdef CONFIG_TESTING_OPTIONS
NEED_DH_GROUPS_ALL=y
endif
endif
ifdef CONFIG_DPP
L_CFLAGS += -DCONFIG_DPP
OBJS += src/common/dpp.c
OBJS += src/common/dpp_auth.c
OBJS += src/common/dpp_backup.c
OBJS += src/common/dpp_crypto.c
OBJS += src/common/dpp_pkex.c
OBJS += src/common/dpp_reconfig.c
OBJS += src/common/dpp_tcp.c
OBJS += dpp_supplicant.c
NEED_AES_SIV=y
NEED_HMAC_SHA256_KDF=y
NEED_HMAC_SHA384_KDF=y
NEED_HMAC_SHA512_KDF=y
NEED_SHA384=y
NEED_SHA512=y
NEED_ECC=y
NEED_JSON=y
NEED_GAS_SERVER=y
NEED_BASE64=y
NEED_ASN1=y
ifdef CONFIG_DPP2
L_CFLAGS += -DCONFIG_DPP2
endif
endif
ifdef CONFIG_OWE
L_CFLAGS += -DCONFIG_OWE
NEED_ECC=y
NEED_HMAC_SHA256_KDF=y
NEED_HMAC_SHA384_KDF=y
NEED_HMAC_SHA512_KDF=y
NEED_SHA384=y
NEED_SHA512=y
endif
ifdef CONFIG_FILS
L_CFLAGS += -DCONFIG_FILS
NEED_SHA384=y
NEED_AES_SIV=y
ifdef CONFIG_FILS_SK_PFS
L_CFLAGS += -DCONFIG_FILS_SK_PFS
NEED_ECC=y
endif
endif
ifdef CONFIG_MBO
CONFIG_WNM=y
endif
ifdef CONFIG_WNM
L_CFLAGS += -DCONFIG_WNM
OBJS += wnm_sta.c
endif
ifdef CONFIG_TDLS
L_CFLAGS += -DCONFIG_TDLS
OBJS += src/rsn_supp/tdls.c
endif
ifdef CONFIG_TDLS_TESTING
L_CFLAGS += -DCONFIG_TDLS_TESTING
endif
ifdef CONFIG_PMKSA_CACHE_EXTERNAL
L_CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
endif
ifndef CONFIG_NO_WPA
OBJS += src/rsn_supp/wpa.c
OBJS += src/rsn_supp/preauth.c
OBJS += src/rsn_supp/pmksa_cache.c
OBJS += src/rsn_supp/wpa_ie.c
OBJS += src/common/wpa_common.c
NEED_AES=y
NEED_SHA1=y
NEED_MD5=y
NEED_RC4=y
else
L_CFLAGS += -DCONFIG_NO_WPA
endif
ifdef CONFIG_IBSS_RSN
NEED_RSN_AUTHENTICATOR=y
L_CFLAGS += -DCONFIG_IBSS_RSN
L_CFLAGS += -DCONFIG_NO_VLAN
OBJS += ibss_rsn.c
endif
ifdef CONFIG_P2P
OBJS += p2p_supplicant.c
OBJS += p2p_supplicant_sd.c
OBJS += src/p2p/p2p.c
OBJS += src/p2p/p2p_utils.c
OBJS += src/p2p/p2p_parse.c
OBJS += src/p2p/p2p_build.c
OBJS += src/p2p/p2p_go_neg.c
OBJS += src/p2p/p2p_sd.c
OBJS += src/p2p/p2p_pd.c
OBJS += src/p2p/p2p_invitation.c
OBJS += src/p2p/p2p_dev_disc.c
OBJS += src/p2p/p2p_group.c
OBJS += src/ap/p2p_hostapd.c
L_CFLAGS += -DCONFIG_P2P
NEED_GAS=y
NEED_OFFCHANNEL=y
CONFIG_WPS=y
CONFIG_AP=y
ifdef CONFIG_P2P_STRICT
L_CFLAGS += -DCONFIG_P2P_STRICT
endif
-endif
-
ifdef CONFIG_WIFI_DISPLAY
L_CFLAGS += -DCONFIG_WIFI_DISPLAY
OBJS += wifi_display.c
endif
+endif
ifdef CONFIG_PASN
L_CFLAGS += -DCONFIG_PASN
L_CFLAGS += -DCONFIG_PTKSA_CACHE
NEED_HMAC_SHA256_KDF=y
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
OBJS += src/common/ptksa_cache.c
OBJS += pasn_supplicant.c
endif
ifdef CONFIG_HS20
OBJS += hs20_supplicant.c
L_CFLAGS += -DCONFIG_HS20
CONFIG_INTERWORKING=y
endif
ifdef CONFIG_INTERWORKING
OBJS += interworking.c
L_CFLAGS += -DCONFIG_INTERWORKING
NEED_GAS=y
endif
ifdef CONFIG_FST
L_CFLAGS += -DCONFIG_FST
OBJS += src/fst/fst.c
OBJS += src/fst/fst_session.c
OBJS += src/fst/fst_iface.c
OBJS += src/fst/fst_group.c
OBJS += src/fst/fst_ctrl_aux.c
ifdef CONFIG_FST_TEST
L_CFLAGS += -DCONFIG_FST_TEST
endif
ifdef CONFIG_CTRL_IFACE
OBJS += src/fst/fst_ctrl_iface.c
endif
endif
ifdef CONFIG_WEP
L_CFLAGS += -DCONFIG_WEP
endif
ifdef CONFIG_NO_TKIP
L_CFLAGS += -DCONFIG_NO_TKIP
endif
include $(LOCAL_PATH)/src/drivers/drivers.mk
ifdef CONFIG_AP
OBJS_d += $(DRV_BOTH_OBJS)
L_CFLAGS += $(DRV_BOTH_CFLAGS)
LDFLAGS += $(DRV_BOTH_LDFLAGS)
LIBS += $(DRV_BOTH_LIBS)
else
NEED_AP_MLME=
OBJS_d += $(DRV_WPA_OBJS)
L_CFLAGS += $(DRV_WPA_CFLAGS)
LDFLAGS += $(DRV_WPA_LDFLAGS)
LIBS += $(DRV_WPA_LIBS)
endif
ifndef CONFIG_L2_PACKET
CONFIG_L2_PACKET=linux
endif
OBJS_l2 += src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).c
ifeq ($(CONFIG_L2_PACKET), pcap)
ifdef CONFIG_WINPCAP
L_CFLAGS += -DCONFIG_WINPCAP
LIBS += -lwpcap -lpacket
LIBS_w += -lwpcap
else
LIBS += -ldnet -lpcap
endif
endif
ifeq ($(CONFIG_L2_PACKET), winpcap)
LIBS += -lwpcap -lpacket
LIBS_w += -lwpcap
endif
ifeq ($(CONFIG_L2_PACKET), freebsd)
LIBS += -lpcap
endif
ifdef CONFIG_ERP
L_CFLAGS += -DCONFIG_ERP
NEED_HMAC_SHA256_KDF=y
endif
ifdef CONFIG_EAP_TLS
# EAP-TLS
ifeq ($(CONFIG_EAP_TLS), dyn)
L_CFLAGS += -DEAP_TLS_DYNAMIC
EAPDYN += src/eap_peer/eap_tls.so
else
L_CFLAGS += -DEAP_TLS
OBJS += src/eap_peer/eap_tls.c
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_UNAUTH_TLS
# EAP-UNAUTH-TLS
L_CFLAGS += -DEAP_UNAUTH_TLS
ifndef CONFIG_EAP_TLS
OBJS += src/eap_peer/eap_tls.c
TLS_FUNCS=y
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_PEAP
# EAP-PEAP
ifeq ($(CONFIG_EAP_PEAP), dyn)
L_CFLAGS += -DEAP_PEAP_DYNAMIC
EAPDYN += src/eap_peer/eap_peap.so
else
L_CFLAGS += -DEAP_PEAP
OBJS += src/eap_peer/eap_peap.c
OBJS += src/eap_common/eap_peap_common.c
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_TTLS
# EAP-TTLS
ifeq ($(CONFIG_EAP_TTLS), dyn)
L_CFLAGS += -DEAP_TTLS_DYNAMIC
EAPDYN += src/eap_peer/eap_ttls.so
else
L_CFLAGS += -DEAP_TTLS
OBJS += src/eap_peer/eap_ttls.c
endif
TLS_FUNCS=y
ifndef CONFIG_FIPS
MS_FUNCS=y
CHAP=y
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_MD5
# EAP-MD5
ifeq ($(CONFIG_EAP_MD5), dyn)
L_CFLAGS += -DEAP_MD5_DYNAMIC
EAPDYN += src/eap_peer/eap_md5.so
else
L_CFLAGS += -DEAP_MD5
OBJS += src/eap_peer/eap_md5.c
endif
CHAP=y
CONFIG_IEEE8021X_EAPOL=y
endif
# backwards compatibility for old spelling
ifdef CONFIG_MSCHAPV2
ifndef CONFIG_EAP_MSCHAPV2
CONFIG_EAP_MSCHAPV2=y
endif
endif
ifdef CONFIG_EAP_MSCHAPV2
# EAP-MSCHAPv2
ifeq ($(CONFIG_EAP_MSCHAPV2), dyn)
L_CFLAGS += -DEAP_MSCHAPv2_DYNAMIC
EAPDYN += src/eap_peer/eap_mschapv2.so
EAPDYN += src/eap_peer/mschapv2.so
else
L_CFLAGS += -DEAP_MSCHAPv2
OBJS += src/eap_peer/eap_mschapv2.c
OBJS += src/eap_peer/mschapv2.c
endif
MS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_GTC
# EAP-GTC
ifeq ($(CONFIG_EAP_GTC), dyn)
L_CFLAGS += -DEAP_GTC_DYNAMIC
EAPDYN += src/eap_peer/eap_gtc.so
else
L_CFLAGS += -DEAP_GTC
OBJS += src/eap_peer/eap_gtc.c
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_OTP
# EAP-OTP
ifeq ($(CONFIG_EAP_OTP), dyn)
L_CFLAGS += -DEAP_OTP_DYNAMIC
EAPDYN += src/eap_peer/eap_otp.so
else
L_CFLAGS += -DEAP_OTP
OBJS += src/eap_peer/eap_otp.c
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_SIM
# EAP-SIM
ifeq ($(CONFIG_EAP_SIM), dyn)
L_CFLAGS += -DEAP_SIM_DYNAMIC
EAPDYN += src/eap_peer/eap_sim.so
else
L_CFLAGS += -DEAP_SIM
OBJS += src/eap_peer/eap_sim.c
endif
CONFIG_IEEE8021X_EAPOL=y
CONFIG_EAP_SIM_COMMON=y
NEED_AES_CBC=y
endif
ifdef CONFIG_EAP_LEAP
# EAP-LEAP
ifeq ($(CONFIG_EAP_LEAP), dyn)
L_CFLAGS += -DEAP_LEAP_DYNAMIC
EAPDYN += src/eap_peer/eap_leap.so
else
L_CFLAGS += -DEAP_LEAP
OBJS += src/eap_peer/eap_leap.c
endif
MS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_PSK
# EAP-PSK
ifeq ($(CONFIG_EAP_PSK), dyn)
L_CFLAGS += -DEAP_PSK_DYNAMIC
EAPDYN += src/eap_peer/eap_psk.so
else
L_CFLAGS += -DEAP_PSK
OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_AES=y
NEED_AES_ENCBLOCK=y
NEED_AES_EAX=y
endif
ifdef CONFIG_EAP_AKA
# EAP-AKA
ifeq ($(CONFIG_EAP_AKA), dyn)
L_CFLAGS += -DEAP_AKA_DYNAMIC
EAPDYN += src/eap_peer/eap_aka.so
else
L_CFLAGS += -DEAP_AKA
OBJS += src/eap_peer/eap_aka.c
endif
CONFIG_IEEE8021X_EAPOL=y
CONFIG_EAP_SIM_COMMON=y
NEED_AES_CBC=y
endif
ifdef CONFIG_EAP_PROXY
L_CFLAGS += -DCONFIG_EAP_PROXY
OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c
include $(LOCAL_PATH)/eap_proxy_$(CONFIG_EAP_PROXY).mk
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_AKA_PRIME
# EAP-AKA'
ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
L_CFLAGS += -DEAP_AKA_PRIME_DYNAMIC
else
L_CFLAGS += -DEAP_AKA_PRIME
endif
endif
ifdef CONFIG_EAP_SIM_COMMON
OBJS += src/eap_common/eap_sim_common.c
NEED_AES=y
NEED_FIPS186_2_PRF=y
endif
ifdef CONFIG_EAP_FAST
# EAP-FAST
ifeq ($(CONFIG_EAP_FAST), dyn)
L_CFLAGS += -DEAP_FAST_DYNAMIC
EAPDYN += src/eap_peer/eap_fast.so
EAPDYN += src/eap_common/eap_fast_common.c
else
L_CFLAGS += -DEAP_FAST
OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c
OBJS += src/eap_common/eap_fast_common.c
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
NEED_T_PRF=y
endif
ifdef CONFIG_EAP_TEAP
# EAP-TEAP
ifeq ($(CONFIG_EAP_TEAP), dyn)
L_CFLAGS += -DEAP_YEAP_DYNAMIC
EAPDYN += src/eap_peer/eap_teap.so
EAPDYN += src/eap_common/eap_teap_common.c
else
L_CFLAGS += -DEAP_TEAP
OBJS += src/eap_peer/eap_teap.c src/eap_peer/eap_teap_pac.c
OBJS += src/eap_common/eap_teap_common.c
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
NEED_T_PRF=y
NEED_SHA384=y
NEED_TLS_PRF_SHA256=y
NEED_TLS_PRF_SHA384=y
endif
ifdef CONFIG_EAP_PAX
# EAP-PAX
ifeq ($(CONFIG_EAP_PAX), dyn)
L_CFLAGS += -DEAP_PAX_DYNAMIC
EAPDYN += src/eap_peer/eap_pax.so
else
L_CFLAGS += -DEAP_PAX
OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_SAKE
# EAP-SAKE
ifeq ($(CONFIG_EAP_SAKE), dyn)
L_CFLAGS += -DEAP_SAKE_DYNAMIC
EAPDYN += src/eap_peer/eap_sake.so
else
L_CFLAGS += -DEAP_SAKE
OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_GPSK
# EAP-GPSK
ifeq ($(CONFIG_EAP_GPSK), dyn)
L_CFLAGS += -DEAP_GPSK_DYNAMIC
EAPDYN += src/eap_peer/eap_gpsk.so
else
L_CFLAGS += -DEAP_GPSK
OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c
endif
CONFIG_IEEE8021X_EAPOL=y
ifdef CONFIG_EAP_GPSK_SHA256
L_CFLAGS += -DEAP_GPSK_SHA256
endif
endif
ifdef CONFIG_EAP_PWD
L_CFLAGS += -DEAP_PWD
OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c
CONFIG_IEEE8021X_EAPOL=y
NEED_ECC=y
NEED_DRAGONFLY=y
endif
ifdef CONFIG_EAP_EKE
# EAP-EKE
ifeq ($(CONFIG_EAP_EKE), dyn)
L_CFLAGS += -DEAP_EKE_DYNAMIC
EAPDYN += src/eap_peer/eap_eke.so
else
L_CFLAGS += -DEAP_EKE
OBJS += src/eap_peer/eap_eke.c src/eap_common/eap_eke_common.c
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
NEED_DH_GROUPS_ALL=y
NEED_AES_CBC=y
endif
ifdef CONFIG_WPS
# EAP-WSC
L_CFLAGS += -DCONFIG_WPS -DEAP_WSC
OBJS += wps_supplicant.c
OBJS += src/utils/uuid.c
OBJS += src/eap_peer/eap_wsc.c src/eap_common/eap_wsc_common.c
OBJS += src/wps/wps.c
OBJS += src/wps/wps_common.c
OBJS += src/wps/wps_attr_parse.c
OBJS += src/wps/wps_attr_build.c
OBJS += src/wps/wps_attr_process.c
OBJS += src/wps/wps_dev_attr.c
OBJS += src/wps/wps_enrollee.c
OBJS += src/wps/wps_registrar.c
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
NEED_BASE64=y
NEED_AES_CBC=y
NEED_MODEXP=y
ifdef CONFIG_WPS_NFC
L_CFLAGS += -DCONFIG_WPS_NFC
OBJS += src/wps/ndef.c
NEED_WPS_OOB=y
endif
ifdef NEED_WPS_OOB
L_CFLAGS += -DCONFIG_WPS_OOB
endif
ifdef CONFIG_WPS_ER
CONFIG_WPS_UPNP=y
L_CFLAGS += -DCONFIG_WPS_ER
OBJS += src/wps/wps_er.c
OBJS += src/wps/wps_er_ssdp.c
endif
ifdef CONFIG_WPS_UPNP
L_CFLAGS += -DCONFIG_WPS_UPNP
OBJS += src/wps/wps_upnp.c
OBJS += src/wps/wps_upnp_ssdp.c
OBJS += src/wps/wps_upnp_web.c
OBJS += src/wps/wps_upnp_event.c
OBJS += src/wps/wps_upnp_ap.c
OBJS += src/wps/upnp_xml.c
OBJS += src/wps/httpread.c
OBJS += src/wps/http_client.c
OBJS += src/wps/http_server.c
endif
ifdef CONFIG_WPS_STRICT
L_CFLAGS += -DCONFIG_WPS_STRICT
OBJS += src/wps/wps_validate.c
endif
ifdef CONFIG_WPS_TESTING
L_CFLAGS += -DCONFIG_WPS_TESTING
endif
ifdef CONFIG_WPS_REG_DISABLE_OPEN
L_CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN
endif
endif
ifdef CONFIG_EAP_IKEV2
# EAP-IKEv2
ifeq ($(CONFIG_EAP_IKEV2), dyn)
L_CFLAGS += -DEAP_IKEV2_DYNAMIC
EAPDYN += src/eap_peer/eap_ikev2.so src/eap_peer/ikev2.c
EAPDYN += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
else
L_CFLAGS += -DEAP_IKEV2
OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c
OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
NEED_DH_GROUPS_ALL=y
NEED_MODEXP=y
NEED_CIPHER=y
endif
ifdef CONFIG_EAP_VENDOR_TEST
ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn)
L_CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC
EAPDYN += src/eap_peer/eap_vendor_test.so
else
L_CFLAGS += -DEAP_VENDOR_TEST
OBJS += src/eap_peer/eap_vendor_test.c
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_TNC
# EAP-TNC
L_CFLAGS += -DEAP_TNC
OBJS += src/eap_peer/eap_tnc.c
OBJS += src/eap_peer/tncc.c
NEED_BASE64=y
ifndef CONFIG_NATIVE_WINDOWS
ifndef CONFIG_DRIVER_BSD
LIBS += -ldl
endif
endif
endif
ifdef CONFIG_IEEE8021X_EAPOL
# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
L_CFLAGS += -DIEEE8021X_EAPOL
OBJS += src/eapol_supp/eapol_supp_sm.c
OBJS += src/eap_peer/eap.c src/eap_peer/eap_methods.c
NEED_EAP_COMMON=y
ifdef CONFIG_DYNAMIC_EAP_METHODS
L_CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
LIBS += -ldl -rdynamic
endif
endif
ifdef CONFIG_AP
NEED_EAP_COMMON=y
NEED_RSN_AUTHENTICATOR=y
L_CFLAGS += -DCONFIG_AP
OBJS += ap.c
L_CFLAGS += -DCONFIG_NO_RADIUS
L_CFLAGS += -DCONFIG_NO_ACCOUNTING
L_CFLAGS += -DCONFIG_NO_VLAN
OBJS += src/ap/hostapd.c
OBJS += src/ap/wpa_auth_glue.c
OBJS += src/ap/utils.c
OBJS += src/ap/authsrv.c
OBJS += src/ap/ap_config.c
OBJS += src/ap/sta_info.c
OBJS += src/ap/tkip_countermeasures.c
OBJS += src/ap/ap_mlme.c
OBJS += src/ap/ieee802_1x.c
OBJS += src/eapol_auth/eapol_auth_sm.c
OBJS += src/ap/ieee802_11_auth.c
OBJS += src/ap/ieee802_11_shared.c
OBJS += src/ap/drv_callbacks.c
OBJS += src/ap/ap_drv_ops.c
OBJS += src/ap/beacon.c
OBJS += src/ap/bss_load.c
OBJS += src/ap/eap_user_db.c
OBJS += src/ap/neighbor_db.c
OBJS += src/ap/rrm.c
OBJS += src/ap/ieee802_11_ht.c
ifdef CONFIG_IEEE80211AC
OBJS += src/ap/ieee802_11_vht.c
endif
ifdef CONFIG_IEEE80211AX
OBJS += src/ap/ieee802_11_he.c
endif
ifdef CONFIG_WNM_AP
L_CFLAGS += -DCONFIG_WNM_AP
OBJS += src/ap/wnm_ap.c
endif
ifdef CONFIG_MBO
OBJS += src/ap/mbo_ap.c
endif
ifdef CONFIG_FILS
OBJS += src/ap/fils_hlp.c
endif
ifdef CONFIG_CTRL_IFACE
OBJS += src/ap/ctrl_iface_ap.c
endif
L_CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
OBJS += src/eap_server/eap_server.c
OBJS += src/eap_server/eap_server_identity.c
OBJS += src/eap_server/eap_server_methods.c
ifdef CONFIG_IEEE80211AC
L_CFLAGS += -DCONFIG_IEEE80211AC
endif
ifdef CONFIG_IEEE80211AX
L_CFLAGS += -DCONFIG_IEEE80211AX
endif
ifdef NEED_AP_MLME
OBJS += src/ap/wmm.c
OBJS += src/ap/ap_list.c
OBJS += src/ap/ieee802_11.c
OBJS += src/ap/hw_features.c
OBJS += src/ap/dfs.c
L_CFLAGS += -DNEED_AP_MLME
endif
ifdef CONFIG_WPS
L_CFLAGS += -DEAP_SERVER_WSC
OBJS += src/ap/wps_hostapd.c
OBJS += src/eap_server/eap_server_wsc.c
endif
ifdef CONFIG_DPP
OBJS += src/ap/dpp_hostapd.c
OBJS += src/ap/gas_query_ap.c
NEED_AP_GAS_SERV=y
endif
ifdef CONFIG_INTERWORKING
NEED_AP_GAS_SERV=y
endif
ifdef NEED_AP_GAS_SERV
OBJS += src/ap/gas_serv.c
endif
ifdef CONFIG_HS20
OBJS += src/ap/hs20.c
endif
endif
ifdef CONFIG_MBO
OBJS += mbo.c
L_CFLAGS += -DCONFIG_MBO
endif
ifdef CONFIG_TESTING_OPTIONS
L_CFLAGS += -DCONFIG_TESTING_OPTIONS
endif
ifdef NEED_RSN_AUTHENTICATOR
L_CFLAGS += -DCONFIG_NO_RADIUS
NEED_AES_WRAP=y
OBJS += src/ap/wpa_auth.c
OBJS += src/ap/wpa_auth_ie.c
OBJS += src/ap/pmksa_cache_auth.c
endif
ifdef CONFIG_ACS
L_CFLAGS += -DCONFIG_ACS
OBJS += src/ap/acs.c
LIBS += -lm
endif
ifdef CONFIG_PCSC
# PC/SC interface for smartcards (USIM, GSM SIM)
L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
OBJS += src/utils/pcsc_funcs.c
# -lpthread may not be needed depending on how pcsc-lite was configured
ifdef CONFIG_NATIVE_WINDOWS
#Once MinGW gets support for WinScard, -lwinscard could be used instead of the
#dynamic symbol loading that is now used in pcsc_funcs.c
#LIBS += -lwinscard
else
LIBS += -lpcsclite -lpthread
endif
endif
ifdef CONFIG_SIM_SIMULATOR
L_CFLAGS += -DCONFIG_SIM_SIMULATOR
NEED_MILENAGE=y
endif
ifdef CONFIG_USIM_SIMULATOR
L_CFLAGS += -DCONFIG_USIM_SIMULATOR
NEED_MILENAGE=y
endif
ifdef NEED_MILENAGE
OBJS += src/crypto/milenage.c
NEED_AES_ENCBLOCK=y
endif
ifdef CONFIG_PKCS12
L_CFLAGS += -DPKCS12_FUNCS
endif
ifdef CONFIG_SMARTCARD
L_CFLAGS += -DCONFIG_SMARTCARD
endif
ifdef NEED_DRAGONFLY
OBJS += src/common/dragonfly.c
endif
ifdef MS_FUNCS
OBJS += src/crypto/ms_funcs.c
NEED_DES=y
NEED_MD4=y
endif
ifdef CHAP
OBJS += src/eap_common/chap.c
endif
ifdef TLS_FUNCS
NEED_DES=y
# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
OBJS += src/eap_peer/eap_tls_common.c
ifndef CONFIG_FIPS
NEED_TLS_PRF=y
NEED_SHA1=y
NEED_MD5=y
endif
endif
ifndef CONFIG_TLS
CONFIG_TLS=openssl
endif
ifdef CONFIG_TLSV11
L_CFLAGS += -DCONFIG_TLSV11
endif
ifdef CONFIG_TLSV12
L_CFLAGS += -DCONFIG_TLSV12
endif
ifeq ($(CONFIG_TLS), openssl)
ifdef TLS_FUNCS
L_CFLAGS += -DEAP_TLS_OPENSSL
OBJS += src/crypto/tls_openssl.c
OBJS += src/crypto/tls_openssl_ocsp.c
LIBS += -lssl
endif
OBJS += src/crypto/crypto_openssl.c
OBJS_p += src/crypto/crypto_openssl.c
ifdef NEED_FIPS186_2_PRF
OBJS += src/crypto/fips_prf_openssl.c
endif
NEED_TLS_PRF_SHA256=y
LIBS += -lcrypto
LIBS_p += -lcrypto
ifdef CONFIG_TLS_ADD_DL
LIBS += -ldl
LIBS_p += -ldl
endif
ifndef CONFIG_TLS_DEFAULT_CIPHERS
CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
endif
L_CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
endif
ifeq ($(CONFIG_TLS), gnutls)
ifndef CONFIG_CRYPTO
# default to libgcrypt
CONFIG_CRYPTO=gnutls
endif
ifdef TLS_FUNCS
OBJS += src/crypto/tls_gnutls.c
LIBS += -lgnutls -lgpg-error
endif
OBJS += src/crypto/crypto_$(CONFIG_CRYPTO).c
OBJS_p += src/crypto/crypto_$(CONFIG_CRYPTO).c
ifdef NEED_FIPS186_2_PRF
OBJS += src/crypto/fips_prf_internal.c
OBJS += src/crypto/sha1-internal.c
endif
ifeq ($(CONFIG_CRYPTO), gnutls)
LIBS += -lgcrypt
LIBS_p += -lgcrypt
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
ifeq ($(CONFIG_CRYPTO), nettle)
LIBS += -lnettle -lgmp
LIBS_p += -lnettle -lgmp
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
endif
ifeq ($(CONFIG_TLS), internal)
ifndef CONFIG_CRYPTO
CONFIG_CRYPTO=internal
endif
ifdef TLS_FUNCS
OBJS += src/crypto/crypto_internal-rsa.c
OBJS += src/crypto/tls_internal.c
OBJS += src/tls/tlsv1_common.c
OBJS += src/tls/tlsv1_record.c
OBJS += src/tls/tlsv1_cred.c
OBJS += src/tls/tlsv1_client.c
OBJS += src/tls/tlsv1_client_write.c
OBJS += src/tls/tlsv1_client_read.c
OBJS += src/tls/tlsv1_client_ocsp.c
NEED_ASN1=y
OBJS += src/tls/rsa.c
OBJS += src/tls/x509v3.c
OBJS += src/tls/pkcs1.c
OBJS += src/tls/pkcs5.c
OBJS += src/tls/pkcs8.c
NEED_BASE64=y
NEED_TLS_PRF=y
ifdef CONFIG_TLSV12
NEED_TLS_PRF_SHA256=y
endif
NEED_MODEXP=y
NEED_CIPHER=y
L_CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
endif
ifdef NEED_CIPHER
NEED_DES=y
OBJS += src/crypto/crypto_internal-cipher.c
endif
ifdef NEED_MODEXP
OBJS += src/crypto/crypto_internal-modexp.c
OBJS += src/tls/bignum.c
endif
ifeq ($(CONFIG_CRYPTO), libtomcrypt)
OBJS += src/crypto/crypto_libtomcrypt.c
OBJS_p += src/crypto/crypto_libtomcrypt.c
LIBS += -ltomcrypt -ltfm
LIBS_p += -ltomcrypt -ltfm
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
ifeq ($(CONFIG_CRYPTO), internal)
OBJS += src/crypto/crypto_internal.c
OBJS_p += src/crypto/crypto_internal.c
NEED_AES_ENC=y
L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL
ifdef CONFIG_INTERNAL_LIBTOMMATH
L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
L_CFLAGS += -DLTM_FAST
endif
else
LIBS += -ltommath
LIBS_p += -ltommath
endif
CONFIG_INTERNAL_AES=y
CONFIG_INTERNAL_DES=y
CONFIG_INTERNAL_SHA1=y
CONFIG_INTERNAL_MD4=y
CONFIG_INTERNAL_MD5=y
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_SHA384=y
CONFIG_INTERNAL_SHA512=y
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
ifeq ($(CONFIG_CRYPTO), cryptoapi)
OBJS += src/crypto/crypto_cryptoapi.c
OBJS_p += src/crypto/crypto_cryptoapi.c
L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_RC4=y
endif
endif
ifeq ($(CONFIG_TLS), none)
ifdef TLS_FUNCS
OBJS += src/crypto/tls_none.c
L_CFLAGS += -DEAP_TLS_NONE
CONFIG_INTERNAL_AES=y
CONFIG_INTERNAL_SHA1=y
CONFIG_INTERNAL_MD5=y
endif
OBJS += src/crypto/crypto_none.c
OBJS_p += src/crypto/crypto_none.c
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_RC4=y
endif
ifdef TLS_FUNCS
ifdef CONFIG_SMARTCARD
ifndef CONFIG_NATIVE_WINDOWS
ifneq ($(CONFIG_L2_PACKET), freebsd)
LIBS += -ldl
endif
endif
endif
endif
ifndef TLS_FUNCS
OBJS += src/crypto/tls_none.c
ifeq ($(CONFIG_TLS), internal)
CONFIG_INTERNAL_AES=y
CONFIG_INTERNAL_SHA1=y
CONFIG_INTERNAL_MD5=y
CONFIG_INTERNAL_RC4=y
endif
endif
AESOBJS = # none so far (see below)
ifdef CONFIG_INTERNAL_AES
AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c
endif
ifneq ($(CONFIG_TLS), openssl)
NEED_INTERNAL_AES_WRAP=y
endif
ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
# Seems to be needed at least with BoringSSL
NEED_INTERNAL_AES_WRAP=y
L_CFLAGS += -DCONFIG_OPENSSL_INTERNAL_AES_WRAP
endif
ifdef CONFIG_FIPS
# Have to use internal AES key wrap routines to use OpenSSL EVP since the
# OpenSSL AES_wrap_key()/AES_unwrap_key() API is not available in FIPS mode.
NEED_INTERNAL_AES_WRAP=y
endif
ifdef NEED_INTERNAL_AES_WRAP
AESOBJS += src/crypto/aes-unwrap.c
endif
ifdef NEED_AES_EAX
AESOBJS += src/crypto/aes-eax.c
NEED_AES_CTR=y
endif
ifdef NEED_AES_SIV
AESOBJS += src/crypto/aes-siv.c
NEED_AES_CTR=y
endif
ifdef NEED_AES_CTR
AESOBJS += src/crypto/aes-ctr.c
endif
ifdef NEED_AES_ENCBLOCK
AESOBJS += src/crypto/aes-encblock.c
endif
NEED_AES_ENC=y
ifdef CONFIG_OPENSSL_CMAC
L_CFLAGS += -DCONFIG_OPENSSL_CMAC
else
AESOBJS += src/crypto/aes-omac1.c
endif
ifdef NEED_AES_WRAP
NEED_AES_ENC=y
ifdef NEED_INTERNAL_AES_WRAP
AESOBJS += src/crypto/aes-wrap.c
endif
endif
ifdef NEED_AES_CBC
NEED_AES_ENC=y
ifneq ($(CONFIG_TLS), openssl)
AESOBJS += src/crypto/aes-cbc.c
endif
endif
ifdef NEED_AES_ENC
ifdef CONFIG_INTERNAL_AES
AESOBJS += src/crypto/aes-internal-enc.c
endif
endif
ifdef NEED_AES
OBJS += $(AESOBJS)
endif
SHA1OBJS =
ifdef NEED_SHA1
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), gnutls)
SHA1OBJS += src/crypto/sha1.c
endif
endif
SHA1OBJS += src/crypto/sha1-prf.c
ifdef CONFIG_INTERNAL_SHA1
SHA1OBJS += src/crypto/sha1-internal.c
ifdef NEED_FIPS186_2_PRF
SHA1OBJS += src/crypto/fips_prf_internal.c
endif
endif
ifdef CONFIG_NO_WPA_PASSPHRASE
L_CFLAGS += -DCONFIG_NO_PBKDF2
else
ifneq ($(CONFIG_TLS), openssl)
SHA1OBJS += src/crypto/sha1-pbkdf2.c
endif
endif
ifdef NEED_T_PRF
SHA1OBJS += src/crypto/sha1-tprf.c
endif
ifdef NEED_TLS_PRF
SHA1OBJS += src/crypto/sha1-tlsprf.c
endif
endif
MD5OBJS =
ifndef CONFIG_FIPS
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), gnutls)
MD5OBJS += src/crypto/md5.c
endif
endif
endif
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
MD5OBJS += src/crypto/md5-internal.c
endif
OBJS += $(MD5OBJS)
OBJS_p += $(MD5OBJS)
endif
ifdef NEED_MD4
ifdef CONFIG_INTERNAL_MD4
OBJS += src/crypto/md4-internal.c
endif
endif
DESOBJS = # none needed when not internal
ifdef NEED_DES
ifdef CONFIG_INTERNAL_DES
DESOBJS += src/crypto/des-internal.c
endif
endif
ifdef CONFIG_NO_RC4
L_CFLAGS += -DCONFIG_NO_RC4
endif
ifdef NEED_RC4
ifdef CONFIG_INTERNAL_RC4
ifndef CONFIG_NO_RC4
OBJS += src/crypto/rc4.c
endif
endif
endif
SHA256OBJS = # none by default
L_CFLAGS += -DCONFIG_SHA256
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), gnutls)
SHA256OBJS += src/crypto/sha256.c
endif
endif
SHA256OBJS += src/crypto/sha256-prf.c
ifdef CONFIG_INTERNAL_SHA256
SHA256OBJS += src/crypto/sha256-internal.c
endif
ifdef CONFIG_INTERNAL_SHA384
L_CFLAGS += -DCONFIG_INTERNAL_SHA384
SHA256OBJS += src/crypto/sha384-internal.c
endif
ifdef CONFIG_INTERNAL_SHA512
L_CFLAGS += -DCONFIG_INTERNAL_SHA512
SHA256OBJS += src/crypto/sha512-internal.c
endif
ifdef NEED_TLS_PRF_SHA256
SHA256OBJS += src/crypto/sha256-tlsprf.c
endif
ifdef NEED_TLS_PRF_SHA384
SHA256OBJS += src/crypto/sha384-tlsprf.c
endif
ifdef NEED_HMAC_SHA256_KDF
L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF
SHA256OBJS += src/crypto/sha256-kdf.c
endif
ifdef NEED_HMAC_SHA384_KDF
L_CFLAGS += -DCONFIG_HMAC_SHA384_KDF
SHA256OBJS += src/crypto/sha384-kdf.c
endif
ifdef NEED_HMAC_SHA512_KDF
L_CFLAGS += -DCONFIG_HMAC_SHA512_KDF
SHA256OBJS += src/crypto/sha512-kdf.c
endif
OBJS += $(SHA256OBJS)
ifdef NEED_SHA384
L_CFLAGS += -DCONFIG_SHA384
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), gnutls)
OBJS += src/crypto/sha384.c
endif
endif
OBJS += src/crypto/sha384-prf.c
endif
ifdef NEED_SHA512
L_CFLAGS += -DCONFIG_SHA512
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), gnutls)
OBJS += src/crypto/sha512.c
endif
endif
OBJS += src/crypto/sha512-prf.c
endif
ifdef NEED_ASN1
OBJS += src/tls/asn1.c
endif
ifdef NEED_DH_GROUPS
OBJS += src/crypto/dh_groups.c
endif
ifdef NEED_DH_GROUPS_ALL
L_CFLAGS += -DALL_DH_GROUPS
endif
ifdef CONFIG_INTERNAL_DH_GROUP5
ifdef NEED_DH_GROUPS
OBJS += src/crypto/dh_group5.c
endif
endif
ifdef NEED_ECC
L_CFLAGS += -DCONFIG_ECC
endif
ifdef CONFIG_NO_RANDOM_POOL
L_CFLAGS += -DCONFIG_NO_RANDOM_POOL
else
OBJS += src/crypto/random.c
endif
ifdef CONFIG_CTRL_IFACE
ifeq ($(CONFIG_CTRL_IFACE), y)
ifdef CONFIG_NATIVE_WINDOWS
CONFIG_CTRL_IFACE=named_pipe
else
CONFIG_CTRL_IFACE=unix
endif
endif
L_CFLAGS += -DCONFIG_CTRL_IFACE
ifeq ($(CONFIG_CTRL_IFACE), unix)
L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
OBJS += src/common/ctrl_iface_common.c
endif
ifeq ($(CONFIG_CTRL_IFACE), udp)
L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP
endif
ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
L_CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
endif
ifeq ($(CONFIG_CTRL_IFACE), udp-remote)
CONFIG_CTRL_IFACE=udp
L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP
L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
endif
OBJS += ctrl_iface.c ctrl_iface_$(CONFIG_CTRL_IFACE).c
endif
ifdef CONFIG_CTRL_IFACE_DBUS_NEW
L_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
OBJS += dbus/dbus_dict_helpers.c
OBJS += dbus/dbus_new_helpers.c
OBJS += dbus/dbus_new.c dbus/dbus_new_handlers.c
OBJS += dbus/dbus_common.c
ifdef CONFIG_WPS
OBJS += dbus/dbus_new_handlers_wps.c
endif
ifdef CONFIG_P2P
OBJS += dbus/dbus_new_handlers_p2p.c
endif
ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
OBJS += dbus/dbus_new_introspect.c
L_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
endif
L_CFLAGS += $(DBUS_INCLUDE)
endif
ifdef CONFIG_CTRL_IFACE_BINDER
WPA_SUPPLICANT_USE_BINDER=y
L_CFLAGS += -DCONFIG_BINDER -DCONFIG_CTRL_IFACE_BINDER
endif
ifdef CONFIG_READLINE
OBJS_c += src/utils/edit_readline.c
LIBS_c += -lncurses -lreadline
else
ifdef CONFIG_WPA_CLI_EDIT
OBJS_c += src/utils/edit.c
else
OBJS_c += src/utils/edit_simple.c
endif
endif
ifdef CONFIG_NATIVE_WINDOWS
L_CFLAGS += -DCONFIG_NATIVE_WINDOWS
LIBS += -lws2_32 -lgdi32 -lcrypt32
LIBS_c += -lws2_32
LIBS_p += -lws2_32 -lgdi32
ifeq ($(CONFIG_CRYPTO), cryptoapi)
LIBS_p += -lcrypt32
endif
endif
ifdef CONFIG_NO_STDOUT_DEBUG
L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
ifndef CONFIG_CTRL_IFACE
L_CFLAGS += -DCONFIG_NO_WPA_MSG
endif
endif
ifdef CONFIG_ANDROID_LOG
L_CFLAGS += -DCONFIG_ANDROID_LOG
endif
ifdef CONFIG_IPV6
# for eapol_test only
L_CFLAGS += -DCONFIG_IPV6
endif
ifdef NEED_BASE64
OBJS += src/utils/base64.c
endif
ifdef NEED_SME
OBJS += sme.c
L_CFLAGS += -DCONFIG_SME
endif
OBJS += src/common/ieee802_11_common.c
OBJS += src/common/hw_features_common.c
ifdef NEED_EAP_COMMON
OBJS += src/eap_common/eap_common.c
endif
ifndef CONFIG_MAIN
CONFIG_MAIN=main
endif
ifdef CONFIG_DEBUG_SYSLOG
L_CFLAGS += -DCONFIG_DEBUG_SYSLOG
ifdef CONFIG_DEBUG_SYSLOG_FACILITY
L_CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)"
endif
endif
ifdef CONFIG_DEBUG_LINUX_TRACING
L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
endif
ifdef CONFIG_DEBUG_FILE
L_CFLAGS += -DCONFIG_DEBUG_FILE
endif
ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
L_CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT
endif
ifdef CONFIG_FIPS
L_CFLAGS += -DCONFIG_FIPS
endif
OBJS += $(SHA1OBJS) $(DESOBJS)
OBJS_p += $(SHA1OBJS)
OBJS_p += $(SHA256OBJS)
ifdef CONFIG_BGSCAN_SIMPLE
L_CFLAGS += -DCONFIG_BGSCAN_SIMPLE
OBJS += bgscan_simple.c
NEED_BGSCAN=y
endif
ifdef CONFIG_BGSCAN_LEARN
L_CFLAGS += -DCONFIG_BGSCAN_LEARN
OBJS += bgscan_learn.c
NEED_BGSCAN=y
endif
ifdef NEED_BGSCAN
L_CFLAGS += -DCONFIG_BGSCAN
OBJS += bgscan.c
endif
ifdef CONFIG_AUTOSCAN_EXPONENTIAL
L_CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL
OBJS += autoscan_exponential.c
NEED_AUTOSCAN=y
endif
ifdef CONFIG_AUTOSCAN_PERIODIC
L_CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
OBJS += autoscan_periodic.c
NEED_AUTOSCAN=y
endif
ifdef NEED_AUTOSCAN
L_CFLAGS += -DCONFIG_AUTOSCAN
OBJS += autoscan.c
endif
ifdef CONFIG_EXT_PASSWORD_TEST
OBJS += src/utils/ext_password_test.c
L_CFLAGS += -DCONFIG_EXT_PASSWORD_TEST
NEED_EXT_PASSWORD=y
endif
ifdef CONFIG_EXT_PASSWORD_FILE
OBJS += src/utils/ext_password_file.c
L_CFLAGS += -DCONFIG_EXT_PASSWORD_FILE
NEED_EXT_PASSWORD=y
endif
ifdef NEED_EXT_PASSWORD
OBJS += src/utils/ext_password.c
L_CFLAGS += -DCONFIG_EXT_PASSWORD
endif
ifdef NEED_GAS_SERVER
OBJS += src/common/gas_server.c
L_CFLAGS += -DCONFIG_GAS_SERVER
NEED_GAS=y
endif
ifdef NEED_GAS
OBJS += src/common/gas.c
OBJS += gas_query.c
L_CFLAGS += -DCONFIG_GAS
NEED_OFFCHANNEL=y
endif
ifdef NEED_OFFCHANNEL
OBJS += offchannel.c
L_CFLAGS += -DCONFIG_OFFCHANNEL
endif
ifdef NEED_JSON
OBJS += src/utils/json.c
L_CFLAGS += -DCONFIG_JSON
endif
OBJS += src/drivers/driver_common.c
OBJS += wpa_supplicant.c events.c bssid_ignore.c wpas_glue.c scan.c
OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c
OBJS_t += src/radius/radius_client.c
OBJS_t += src/radius/radius.c
OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.c
OBJS += $(CONFIG_MAIN).c
ifdef CONFIG_PRIVSEP
OBJS_priv += $(OBJS_d) src/drivers/drivers.c
OBJS_priv += $(OBJS_l2)
OBJS_priv += src/utils/os_$(CONFIG_OS).c
OBJS_priv += src/utils/$(CONFIG_ELOOP).c
OBJS_priv += src/utils/common.c
OBJS_priv += src/utils/wpa_debug.c
OBJS_priv += src/utils/wpabuf.c
OBJS_priv += wpa_priv.c
ifdef CONFIG_DRIVER_NL80211
OBJS_priv += src/common/ieee802_11_common.c
endif
OBJS += src/l2_packet/l2_packet_privsep.c
OBJS += src/drivers/driver_privsep.c
EXTRA_progs += wpa_priv
else
OBJS += $(OBJS_d) src/drivers/drivers.c
OBJS += $(OBJS_l2)
endif
ifdef CONFIG_NDIS_EVENTS_INTEGRATED
L_CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED
OBJS += src/drivers/ndis_events.c
EXTRALIBS += -loleaut32 -lole32 -luuid
ifdef PLATFORMSDKLIB
EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib
else
EXTRALIBS += WbemUuid.Lib
endif
endif
ifndef LDO
LDO=$(CC)
endif
########################
include $(CLEAR_VARS)
LOCAL_MODULE := wpa_cli
LOCAL_MODULE_TAGS := debug
LOCAL_SHARED_LIBRARIES := libc libcutils liblog
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := $(OBJS_c)
LOCAL_C_INCLUDES := $(INCLUDES)
include $(BUILD_EXECUTABLE)
########################
include $(CLEAR_VARS)
LOCAL_MODULE := wpa_supplicant
ifdef CONFIG_DRIVER_CUSTOM
LOCAL_STATIC_LIBRARIES := libCustomWifi
endif
ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB)
endif
LOCAL_SHARED_LIBRARIES := libc libcutils liblog
ifdef CONFIG_EAP_PROXY
LOCAL_STATIC_LIBRARIES += $(LIB_STATIC_EAP_PROXY)
LOCAL_SHARED_LIBRARIES += $(LIB_SHARED_EAP_PROXY)
endif
ifeq ($(CONFIG_TLS), openssl)
LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore_binder
endif
# With BoringSSL we need libkeystore-engine in order to provide access to
# keystore keys.
LOCAL_SHARED_LIBRARIES += libkeystore-engine
ifdef CONFIG_DRIVER_NL80211
ifneq ($(wildcard external/libnl),)
LOCAL_SHARED_LIBRARIES += libnl
else
LOCAL_STATIC_LIBRARIES += libnl_2
endif
endif
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_SRC_FILES := $(OBJS)
LOCAL_C_INCLUDES := $(INCLUDES)
ifeq ($(DBUS), y)
LOCAL_SHARED_LIBRARIES += libdbus
endif
ifeq ($(WPA_SUPPLICANT_USE_BINDER), y)
LOCAL_SHARED_LIBRARIES += libbinder libutils
LOCAL_STATIC_LIBRARIES += libwpa_binder libwpa_binder_interface
endif
include $(BUILD_EXECUTABLE)
########################
#
#include $(CLEAR_VARS)
#LOCAL_MODULE := eapol_test
#ifdef CONFIG_DRIVER_CUSTOM
#LOCAL_STATIC_LIBRARIES := libCustomWifi
#endif
#LOCAL_SHARED_LIBRARIES := libc libcrypto libssl
#LOCAL_CFLAGS := $(L_CFLAGS)
#LOCAL_SRC_FILES := $(OBJS_t)
#LOCAL_C_INCLUDES := $(INCLUDES)
#include $(BUILD_EXECUTABLE)
#
########################
#
#local_target_dir := $(TARGET_OUT)/etc/wifi
#
#include $(CLEAR_VARS)
#LOCAL_MODULE := wpa_supplicant.conf
#LOCAL_MODULE_CLASS := ETC
#LOCAL_MODULE_PATH := $(local_target_dir)
#LOCAL_SRC_FILES := $(LOCAL_MODULE)
#include $(BUILD_PREBUILT)
#
########################
include $(CLEAR_VARS)
LOCAL_MODULE = libwpa_client
LOCAL_CFLAGS = $(L_CFLAGS)
LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c
LOCAL_C_INCLUDES = $(INCLUDES)
LOCAL_SHARED_LIBRARIES := libcutils liblog
LOCAL_COPY_HEADERS_TO := libwpa_client
LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h
LOCAL_COPY_HEADERS += src/common/qca-vendor.h
include $(BUILD_SHARED_LIBRARY)
ifeq ($(WPA_SUPPLICANT_USE_BINDER), y)
### Binder interface library ###
########################
include $(CLEAR_VARS)
LOCAL_MODULE := libwpa_binder_interface
LOCAL_AIDL_INCLUDES := \
$(LOCAL_PATH)/binder \
frameworks/native/aidl/binder
LOCAL_EXPORT_C_INCLUDE_DIRS := \
$(LOCAL_PATH)/binder
LOCAL_CPPFLAGS := $(L_CPPFLAGS)
LOCAL_SRC_FILES := \
binder/binder_constants.cpp \
binder/fi/w1/wpa_supplicant/ISupplicant.aidl \
binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl \
binder/fi/w1/wpa_supplicant/IIface.aidl
LOCAL_SHARED_LIBRARIES := libbinder
include $(BUILD_STATIC_LIBRARY)
### Binder service library ###
########################
include $(CLEAR_VARS)
LOCAL_MODULE := libwpa_binder
LOCAL_CPPFLAGS := $(L_CPPFLAGS)
LOCAL_CFLAGS := $(L_CFLAGS)
LOCAL_C_INCLUDES := $(INCLUDES)
LOCAL_SRC_FILES := \
binder/binder.cpp binder/binder_manager.cpp \
binder/supplicant.cpp binder/iface.cpp
LOCAL_SHARED_LIBRARIES := \
libbinder \
libutils
LOCAL_STATIC_LIBRARIES := libwpa_binder_interface
include $(BUILD_STATIC_LIBRARY)
endif # BINDER == y
diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog
index a06a93b22175..5ca82457ad1b 100644
--- a/contrib/wpa/wpa_supplicant/ChangeLog
+++ b/contrib/wpa/wpa_supplicant/ChangeLog
@@ -1,2446 +1,2447 @@
ChangeLog for wpa_supplicant
2019-08-07 - v2.9
* SAE changes
- disable use of groups using Brainpool curves
- improved protection against side channel attacks
[https://w1.fi/security/2019-6/]
* EAP-pwd changes
- disable use of groups using Brainpool curves
- allow the set of groups to be configured (eap_pwd_groups)
- improved protection against side channel attacks
[https://w1.fi/security/2019-6/]
* fixed FT-EAP initial mobility domain association using PMKSA caching
(disabled by default for backwards compatibility; can be enabled
with ft_eap_pmksa_caching=1)
* fixed a regression in OpenSSL 1.1+ engine loading
* added validation of RSNE in (Re)Association Response frames
* fixed DPP bootstrapping URI parser of channel list
* extended EAP-SIM/AKA fast re-authentication to allow use with FILS
* extended ca_cert_blob to support PEM format
* improved robustness of P2P Action frame scheduling
* added support for EAP-SIM/AKA using anonymous@realm identity
* fixed Hotspot 2.0 credential selection based on roaming consortium
to ignore credentials without a specific EAP method
* added experimental support for EAP-TEAP peer (RFC 7170)
* added experimental support for EAP-TLS peer with TLS v1.3
* fixed a regression in WMM parameter configuration for a TDLS peer
* fixed a regression in operation with drivers that offload 802.1X
4-way handshake
* fixed an ECDH operation corner case with OpenSSL
2019-04-21 - v2.8
* SAE changes
- added support for SAE Password Identifier
- changed default configuration to enable only groups 19, 20, 21
(i.e., disable groups 25 and 26) and disable all unsuitable groups
completely based on REVmd changes
- do not regenerate PWE unnecessarily when the AP uses the
anti-clogging token mechanisms
- fixed some association cases where both SAE and FT-SAE were enabled
on both the station and the selected AP
- started to prefer FT-SAE over SAE AKM if both are enabled
- started to prefer FT-SAE over FT-PSK if both are enabled
- fixed FT-SAE when SAE PMKSA caching is used
- reject use of unsuitable groups based on new implementation guidance
in REVmd (allow only FFC groups with prime >= 3072 bits and ECC
groups with prime >= 256)
- minimize timing and memory use differences in PWE derivation
[https://w1.fi/security/2019-1/] (CVE-2019-9494)
* EAP-pwd changes
- minimize timing and memory use differences in PWE derivation
[https://w1.fi/security/2019-2/] (CVE-2019-9495)
- verify server scalar/element
[https://w1.fi/security/2019-4/] (CVE-2019-9499)
- fix message reassembly issue with unexpected fragment
[https://w1.fi/security/2019-5/]
- enforce rand,mask generation rules more strictly
- fix a memory leak in PWE derivation
- disallow ECC groups with a prime under 256 bits (groups 25, 26, and
27)
* fixed CONFIG_IEEE80211R=y (FT) build without CONFIG_FILS=y
* Hotspot 2.0 changes
- do not indicate release number that is higher than the one
AP supports
- added support for release number 3
- enable PMF automatically for network profiles created from
credentials
* fixed OWE network profile saving
* fixed DPP network profile saving
* added support for RSN operating channel validation
(CONFIG_OCV=y and network profile parameter ocv=1)
* added Multi-AP backhaul STA support
* fixed build with LibreSSL
* number of MKA/MACsec fixes and extensions
* extended domain_match and domain_suffix_match to allow list of values
* fixed dNSName matching in domain_match and domain_suffix_match when
using wolfSSL
* started to prefer FT-EAP-SHA384 over WPA-EAP-SUITE-B-192 AKM if both
are enabled
* extended nl80211 Connect and external authentication to support
SAE, FT-SAE, FT-EAP-SHA384
* fixed KEK2 derivation for FILS+FT
* extended client_cert file to allow loading of a chain of PEM
encoded certificates
* extended beacon reporting functionality
* extended D-Bus interface with number of new properties
* fixed a regression in FT-over-DS with mac80211-based drivers
* OpenSSL: allow systemwide policies to be overridden
* extended driver flags indication for separate 802.1X and PSK
4-way handshake offload capability
* added support for random P2P Device/Interface Address use
* extended PEAP to derive EMSK to enable use with ERP/FILS
* extended WPS to allow SAE configuration to be added automatically
for PSK (wps_cred_add_sae=1)
* removed support for the old D-Bus interface (CONFIG_CTRL_IFACE_DBUS)
* extended domain_match and domain_suffix_match to allow list of values
* added a RSN workaround for misbehaving PMF APs that advertise
IGTK/BIP KeyID using incorrect byte order
* fixed PTK rekeying with FILS and FT
2018-12-02 - v2.7
* fixed WPA packet number reuse with replayed messages and key
reinstallation
[https://w1.fi/security/2017-1/] (CVE-2017-13077, CVE-2017-13078,
CVE-2017-13079, CVE-2017-13080, CVE-2017-13081, CVE-2017-13082,
CVE-2017-13086, CVE-2017-13087, CVE-2017-13088)
* fixed unauthenticated EAPOL-Key decryption in wpa_supplicant
[https://w1.fi/security/2018-1/] (CVE-2018-14526)
* added support for FILS (IEEE 802.11ai) shared key authentication
* added support for OWE (Opportunistic Wireless Encryption, RFC 8110;
and transition mode defined by WFA)
* added support for DPP (Wi-Fi Device Provisioning Protocol)
* added support for RSA 3k key case with Suite B 192-bit level
* fixed Suite B PMKSA caching not to update PMKID during each 4-way
handshake
* fixed EAP-pwd pre-processing with PasswordHashHash
* added EAP-pwd client support for salted passwords
* fixed a regression in TDLS prohibited bit validation
* started to use estimated throughput to avoid undesired signal
strength based roaming decision
* MACsec/MKA:
- new macsec_linux driver interface support for the Linux
kernel macsec module
- number of fixes and extensions
* added support for external persistent storage of PMKSA cache
(PMKSA_GET/PMKSA_ADD control interface commands; and
MESH_PMKSA_GET/MESH_PMKSA_SET for the mesh case)
* fixed mesh channel configuration pri/sec switch case
* added support for beacon report
* large number of other fixes, cleanup, and extensions
* added support for randomizing local address for GAS queries
(gas_rand_mac_addr parameter)
* fixed EAP-SIM/AKA/AKA' ext auth cases within TLS tunnel
* added option for using random WPS UUID (auto_uuid=1)
* added SHA256-hash support for OCSP certificate matching
* fixed EAP-AKA' to add AT_KDF into Synchronization-Failure
* fixed a regression in RSN pre-authentication candidate selection
* added option to configure allowed group management cipher suites
(group_mgmt network profile parameter)
* removed all PeerKey functionality
* fixed nl80211 AP and mesh mode configuration regression with
Linux 4.15 and newer
* added ap_isolate configuration option for AP mode
* added support for nl80211 to offload 4-way handshake into the driver
* added support for using wolfSSL cryptographic library
* SAE
- added support for configuring SAE password separately of the
WPA2 PSK/passphrase
- fixed PTK and EAPOL-Key integrity and key-wrap algorithm selection
for SAE;
note: this is not backwards compatible, i.e., both the AP and
station side implementations will need to be update at the same
time to maintain interoperability
- added support for Password Identifier
- fixed FT-SAE PMKID matching
* Hotspot 2.0
- added support for fetching of Operator Icon Metadata ANQP-element
- added support for Roaming Consortium Selection element
- added support for Terms and Conditions
- added support for OSEN connection in a shared RSN BSS
- added support for fetching Venue URL information
* added support for using OpenSSL 1.1.1
* FT
- disabled PMKSA caching with FT since it is not fully functional
- added support for SHA384 based AKM
- added support for BIP ciphers BIP-CMAC-256, BIP-GMAC-128,
BIP-GMAC-256 in addition to previously supported BIP-CMAC-128
- fixed additional IE inclusion in Reassociation Request frame when
using FT protocol
2016-10-02 - v2.6
* fixed WNM Sleep Mode processing when PMF is not enabled
[http://w1.fi/security/2015-6/] (CVE-2015-5310)
* fixed EAP-pwd last fragment validation
[http://w1.fi/security/2015-7/] (CVE-2015-5315)
* fixed EAP-pwd unexpected Confirm message processing
[http://w1.fi/security/2015-8/] (CVE-2015-5316)
* fixed WPS configuration update vulnerability with malformed passphrase
[http://w1.fi/security/2016-1/] (CVE-2016-4476)
* fixed configuration update vulnerability with malformed parameters set
over the local control interface
[http://w1.fi/security/2016-1/] (CVE-2016-4477)
* fixed TK configuration to the driver in EAPOL-Key 3/4 retry case
* extended channel switch support for P2P GO
* started to throttle control interface event message bursts to avoid
issues with monitor sockets running out of buffer space
* mesh mode fixes/improvements
- generate proper AID for peer
- enable WMM by default
- add VHT support
- fix PMKID derivation
- improve robustness on various exchanges
- fix peer link counting in reconnect case
- improve mesh joining behavior
- allow DTIM period to be configured
- allow HT to be disabled (disable_ht=1)
- add MESH_PEER_ADD and MESH_PEER_REMOVE commands
- add support for PMKSA caching
- add minimal support for SAE group negotiation
- allow pairwise/group cipher to be configured in the network profile
- use ieee80211w profile parameter to enable/disable PMF and derive
a separate TX IGTK if PMF is enabled instead of using MGTK
incorrectly
- fix AEK and MTK derivation
- remove GTKdata and IGTKdata from Mesh Peering Confirm/Close
- note: these changes are not fully backwards compatible for secure
(RSN) mesh network
* fixed PMKID derivation with SAE
* added support for requesting and fetching arbitrary ANQP-elements
without internal support in wpa_supplicant for the specific element
(anqp[265]=<hexdump> in "BSS <BSSID>" command output)
* P2P
- filter control characters in group client device names to be
consistent with other P2P peer cases
- support VHT 80+80 MHz and 160 MHz
- indicate group completion in P2P Client role after data association
instead of already after the WPS provisioning step
- improve group-join operation to use SSID, if known, to filter BSS
entries
- added optional ssid=<hexdump> argument to P2P_CONNECT for join case
- added P2P_GROUP_MEMBER command to fetch client interface address
* P2PS
- fix follow-on PD Response behavior
- fix PD Response generation for unknown peer
- fix persistent group reporting
- add channel policy to PD Request
- add group SSID to the P2PS-PROV-DONE event
- allow "P2P_CONNECT <addr> p2ps" to be used without specifying the
default PIN
* BoringSSL
- support for OCSP stapling
- support building of h20-osu-client
* D-Bus
- add ExpectDisconnect()
- add global config parameters as properties
- add SaveConfig()
- add VendorElemAdd(), VendorElemGet(), VendorElemRem()
* fixed Suite B 192-bit AKM to use proper PMK length
(note: this makes old releases incompatible with the fixed behavior)
* improved PMF behavior for cases where the AP and STA has different
configuration by not trying to connect in some corner cases where the
connection cannot succeed
* added option to reopen debug log (e.g., to rotate the file) upon
receipt of SIGHUP signal
* EAP-pwd: added support for Brainpool Elliptic Curves
(with OpenSSL 1.0.2 and newer)
* fixed EAPOL reauthentication after FT protocol run
* fixed FTIE generation for 4-way handshake after FT protocol run
* extended INTERFACE_ADD command to allow certain type (sta/ap)
interface to be created
* fixed and improved various FST operations
* added 80+80 MHz and 160 MHz VHT support for IBSS/mesh
* fixed SIGNAL_POLL in IBSS and mesh cases
* added an option to abort an ongoing scan (used to speed up connection
and can also be done with the new ABORT_SCAN command)
* TLS client
- do not verify CA certificates when ca_cert is not specified
- support validating server certificate hash
- support SHA384 and SHA512 hashes
- add signature_algorithms extension into ClientHello
- support TLS v1.2 signature algorithm with SHA384 and SHA512
- support server certificate probing
- allow specific TLS versions to be disabled with phase2 parameter
- support extKeyUsage
- support PKCS #5 v2.0 PBES2
- support PKCS #5 with PKCS #12 style key decryption
- minimal support for PKCS #12
- support OCSP stapling (including ocsp_multi)
* OpenSSL
- support OpenSSL 1.1 API changes
- drop support for OpenSSL 0.9.8
- drop support for OpenSSL 1.0.0
* added support for multiple schedule scan plans (sched_scan_plans)
* added support for external server certificate chain validation
(tls_ext_cert_check=1 in the network profile phase1 parameter)
* made phase2 parser more strict about correct use of auth=<val> and
autheap=<val> values
* improved GAS offchannel operations with comeback request
* added SIGNAL_MONITOR command to request signal strength monitoring
events
* added command for retrieving HS 2.0 icons with in-memory storage
(REQ_HS20_ICON, GET_HS20_ICON, DEL_HS20_ICON commands and
RX-HS20-ICON event)
* enabled ACS support for AP mode operations with wpa_supplicant
* EAP-PEAP: fixed interoperability issue with Windows 2012r2 server
("Invalid Compound_MAC in cryptobinding TLV")
* EAP-TTLS: fixed success after fragmented final Phase 2 message
* VHT: added interoperability workaround for 80+80 and 160 MHz channels
* WNM: workaround for broken AP operating class behavior
* added kqueue(2) support for eloop (CONFIG_ELOOP_KQUEUE)
* nl80211:
- add support for full station state operations
- do not add NL80211_ATTR_SMPS_MODE attribute if HT is disabled
- add NL80211_ATTR_PREV_BSSID with Connect command
- fix IEEE 802.1X/WEP EAP reauthentication and rekeying to use
unencrypted EAPOL frames
* added initial MBO support; number of extensions to WNM BSS Transition
Management
* added support for PBSS/PCP and P2P on 60 GHz
* Interworking: add credential realm to EAP-TLS identity
* fixed EAPOL-Key Request Secure bit to be 1 if PTK is set
* HS 2.0: add support for configuring frame filters
* added POLL_STA command to check connectivity in AP mode
* added initial functionality for location related operations
* started to ignore pmf=1/2 parameter for non-RSN networks
* added wps_disabled=1 network profile parameter to allow AP mode to
be started without enabling WPS
* wpa_cli: added action script support for AP-ENABLED and AP-DISABLED
events
* improved Public Action frame addressing
- add gas_address3 configuration parameter to control Address 3
behavior
* number of small fixes
2015-09-27 - v2.5
* fixed P2P validation of SSID element length before copying it
[http://w1.fi/security/2015-1/] (CVE-2015-1863)
* fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
[http://w1.fi/security/2015-2/] (CVE-2015-4141)
* fixed WMM Action frame parser (AP mode)
[http://w1.fi/security/2015-3/] (CVE-2015-4142)
* fixed EAP-pwd peer missing payload length validation
[http://w1.fi/security/2015-4/]
(CVE-2015-4143, CVE-2015-4144, CVE-2015-4145, CVE-2015-4146)
* fixed validation of WPS and P2P NFC NDEF record payload length
[http://w1.fi/security/2015-5/]
* nl80211:
- added VHT configuration for IBSS
- fixed vendor command handling to check OUI properly
- allow driver-based roaming to change ESS
* added AVG_BEACON_RSSI to SIGNAL_POLL output
* wpa_cli: added tab completion for number of commands
* removed unmaintained and not yet completed SChannel/CryptoAPI support
* modified Extended Capabilities element use in Probe Request frames to
include all cases if any of the values are non-zero
* added support for dynamically creating/removing a virtual interface
with interface_add/interface_remove
* added support for hashed password (NtHash) in EAP-pwd peer
* added support for memory-only PSK/passphrase (mem_only_psk=1 and
CTRL-REQ/RSP-PSK_PASSPHRASE)
* P2P
- optimize scan frequencies list when re-joining a persistent group
- fixed number of sequences with nl80211 P2P Device interface
- added operating class 125 for P2P use cases (this allows 5 GHz
channels 161 and 169 to be used if they are enabled in the current
regulatory domain)
- number of fixes to P2PS functionality
- do not allow 40 MHz co-ex PRI/SEC switch to force MCC
- extended support for preferred channel listing
* D-Bus:
- fixed WPS property of fi.w1.wpa_supplicant1.BSS interface
- fixed PresenceRequest to use group interface
- added new signals: FindStopped, WPS pbc-overlap,
GroupFormationFailure, WPS timeout, InvitationReceived
- added new methods: WPS Cancel, P2P Cancel, Reconnect, RemoveClient
- added manufacturer info
* added EAP-EKE peer support for deriving Session-Id
* added wps_priority configuration parameter to set the default priority
for all network profiles added by WPS
* added support to request a scan with specific SSIDs with the SCAN
command (optional "ssid <hexdump>" arguments)
* removed support for WEP40/WEP104 as a group cipher with WPA/WPA2
* fixed SAE group selection in an error case
* modified SAE routines to be more robust and PWE generation to be
stronger against timing attacks
* added support for Brainpool Elliptic Curves with SAE
* added support for CCMP-256 and GCMP-256 as group ciphers with FT
* fixed BSS selection based on estimated throughput
* added option to disable TLSv1.0 with OpenSSL
(phase1="tls_disable_tlsv1_0=1")
* added Fast Session Transfer (FST) module
* fixed OpenSSL PKCS#12 extra certificate handling
* fixed key derivation for Suite B 192-bit AKM (this breaks
compatibility with the earlier version)
* added RSN IE to Mesh Peering Open/Confirm frames
* number of small fixes
2015-03-15 - v2.4
* allow OpenSSL cipher configuration to be set for internal EAP server
(openssl_ciphers parameter)
* fixed number of small issues based on hwsim test case failures and
static analyzer reports
* P2P:
- add new=<0/1> flag to P2P-DEVICE-FOUND events
- add passive channels in invitation response from P2P Client
- enable nl80211 P2P_DEVICE support by default
- fix regresssion in disallow_freq preventing search on social
channels
- fix regressions in P2P SD query processing
- try to re-invite with social operating channel if no common channels
in invitation
- allow cross connection on parent interface (this fixes number of
use cases with nl80211)
- add support for P2P services (P2PS)
- add p2p_go_ctwindow configuration parameter to allow GO CTWindow to
be configured
* increase postponing of EAPOL-Start by one second with AP/GO that
supports WPS 2.0 (this makes it less likely to trigger extra roundtrip
of identity frames)
* add support for PMKSA caching with SAE
* add support for control mesh BSS (IEEE 802.11s) operations
* fixed number of issues with D-Bus P2P commands
* fixed regression in ap_scan=2 special case for WPS
* fixed macsec_validate configuration
* add a workaround for incorrectly behaving APs that try to use
EAPOL-Key descriptor version 3 when the station supports PMF even if
PMF is not enabled on the AP
* allow TLS v1.1 and v1.2 to be negotiated by default; previous behavior
of disabling these can be configured to work around issues with broken
servers with phase1="tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1"
* add support for Suite B (128-bit and 192-bit level) key management and
cipher suites
* add WMM-AC support (WMM_AC_ADDTS/WMM_AC_DELTS)
* improved BSS Transition Management processing
* add support for neighbor report
* add support for link measurement
* fixed expiration of BSS entry with all-zeros BSSID
* add optional LAST_ID=x argument to LIST_NETWORK to allow all
configured networks to be listed even with huge number of network
profiles
* add support for EAP Re-Authentication Protocol (ERP)
* fixed EAP-IKEv2 fragmentation reassembly
* improved PKCS#11 configuration for OpenSSL
* set stdout to be line-buffered
* add TDLS channel switch configuration
* add support for MAC address randomization in scans with nl80211
* enable HT for IBSS if supported by the driver
* add BSSID black and white lists (bssid_blacklist, bssid_whitelist)
* add support for domain_suffix_match with GnuTLS
* add OCSP stapling client support with GnuTLS
* include peer certificate in EAP events even without a separate probe
operation; old behavior can be restored with cert_in_cb=0
* add peer ceritficate alt subject name to EAP events
(CTRL-EVENT-EAP-PEER-ALT)
* add domain_match network profile parameter (similar to
domain_suffix_match, but full match is required)
* enable AP/GO mode HT Tx STBC automatically based on driver support
* add ANQP-QUERY-DONE event to provide information on ANQP parsing
status
* allow passive scanning to be forced with passive_scan=1
* add a workaround for Linux packet socket behavior when interface is in
bridge
* increase 5 GHz band preference in BSS selection (estimate SNR, if info
not available from driver; estimate maximum throughput based on common
HT/VHT/specific TX rate support)
* add INTERWORKING_ADD_NETWORK ctrl_iface command; this can be used to
implement Interworking network selection behavior in upper layers
software components
* add optional reassoc_same_bss_optim=1 (disabled by default)
optimization to avoid unnecessary Authentication frame exchange
* extend TDLS frame padding workaround to cover all packets
* allow wpa_supplicant to recover nl80211 functionality if the cfg80211
module gets removed and reloaded without restarting wpa_supplicant
* allow hostapd DFS implementation to be used in wpa_supplicant AP mode
2014-10-09 - v2.3
* fixed number of minor issues identified in static analyzer warnings
* fixed wfd_dev_info to be more careful and not read beyond the buffer
when parsing invalid information for P2P-DEVICE-FOUND
* extended P2P and GAS query operations to support drivers that have
maximum remain-on-channel time below 1000 ms (500 ms is the current
minimum supported value)
* added p2p_search_delay parameter to make the default p2p_find delay
configurable
* improved P2P operating channel selection for various multi-channel
concurrency cases
* fixed some TDLS failure cases to clean up driver state
* fixed dynamic interface addition cases with nl80211 to avoid adding
ifindex values to incorrect interface to skip foreign interface events
properly
* added TDLS workaround for some APs that may add extra data to the
end of a short frame
* fixed EAP-AKA' message parser with multiple AT_KDF attributes
* added configuration option (p2p_passphrase_len) to allow longer
passphrases to be generated for P2P groups
* fixed IBSS channel configuration in some corner cases
* improved HT/VHT/QoS parameter setup for TDLS
* modified D-Bus interface for P2P peers/groups
* started to use constant time comparison for various password and hash
values to reduce possibility of any externally measurable timing
differences
* extended explicit clearing of freed memory and expired keys to avoid
keeping private data in memory longer than necessary
* added optional scan_id parameter to the SCAN command to allow manual
scan requests for active scans for specific configured SSIDs
* fixed CTRL-EVENT-REGDOM-CHANGE event init parameter value
* added option to set Hotspot 2.0 Rel 2 update_identifier in network
configuration to support external configuration
* modified Android PNO functionality to send Probe Request frames only
for hidden SSIDs (based on scan_ssid=1)
* added generic mechanism for adding vendor elements into frames at
runtime (VENDOR_ELEM_ADD, VENDOR_ELEM_GET, VENDOR_ELEM_REMOVE)
* added fields to show unrecognized vendor elements in P2P_PEER
* removed EAP-TTLS/MSCHAPv2 interoperability workaround so that
MS-CHAP2-Success is required to be present regardless of
eap_workaround configuration
* modified EAP fast session resumption to allow results to be used only
with the same network block that generated them
* extended freq_list configuration to apply for sched_scan as well as
normal scan
* modified WPS to merge mixed-WPA/WPA2 credentials from a single session
* fixed nl80211/RTM_DELLINK processing when a P2P GO interface is
removed from a bridge
* fixed number of small P2P issues to make negotiations more robust in
corner cases
* added experimental support for using temporary, random local MAC
address (mac_addr and preassoc_mac_addr parameters); this is disabled
by default (i.e., previous behavior of using permanent address is
maintained if configuration is not changed)
* added D-Bus interface for setting/clearing WFD IEs
* fixed TDLS AID configuration for VHT
* modified -m<conf> configuration file to be used only for the P2P
non-netdev management device and do not load this for the default
station interface or load the station interface configuration for
the P2P management interface
* fixed external MAC address changes while wpa_supplicant is running
* started to enable HT (if supported by the driver) for IBSS
* fixed wpa_cli action script execution to use more robust mechanism
(CVE-2014-3686)
2014-06-04 - v2.2
* added DFS indicator to get_capability freq
* added/fixed nl80211 functionality
- BSSID/frequency hint for driver-based BSS selection
- fix tearing down WDS STA interfaces
- support vendor specific driver command
(VENDOR <vendor id> <sub command id> [<hex formatted data>])
- GO interface teardown optimization
- allow beacon interval to be configured for IBSS
- add SHA256-based AKM suites to CONNECT/ASSOCIATE commands
* removed unused NFC_RX_HANDOVER_REQ and NFC_RX_HANDOVER_SEL control
interface commands (the more generic NFC_REPORT_HANDOVER is now used)
* fixed MSCHAP UTF-8 to UCS-2 conversion for three-byte encoding;
this fixes password with include UTF-8 characters that use
three-byte encoding EAP methods that use NtPasswordHash
* fixed couple of sequences where radio work items could get stuck,
e.g., when rfkill blocking happens during scanning or when
scan-for-auth workaround is used
* P2P enhancements/fixes
- enable enable U-APSD on GO automatically if the driver indicates
support for this
- fixed some service discovery cases with broadcast queries not being
sent to all stations
- fixed Probe Request frame triggering invitation to trigger only a
single invitation instance even if multiple Probe Request frames are
received
- fixed a potential NULL pointer dereference crash when processing an
invalid Invitation Request frame
- add optional configuration file for the P2P_DEVICE parameters
- optimize scan for GO during persistent group invocation
- fix possible segmentation fault when PBC overlap is detected while
using a separate P2P group interface
- improve GO Negotiation robustness by allowing GO Negotiation
Confirmation to be retransmitted
- do use freed memory on device found event when P2P NFC
* added phase1 network parameter options for disabling TLS v1.1 and v1.2
to allow workarounds with misbehaving AAA servers
(tls_disable_tlsv1_1=1 and tls_disable_tlsv1_2=1)
* added support for OCSP stapling to validate AAA server certificate
during TLS exchange
* Interworking/Hotspot 2.0 enhancements
- prefer the last added network in Interworking connection to make the
behavior more consistent with likely user expectation
- roaming partner configuration (roaming_partner within a cred block)
- support Hotspot 2.0 Release 2
* "hs20_anqp_get <BSSID> 8" to request OSU Providers list
* "hs20_icon_request <BSSID> <icon filename>" to request icon files
* "fetch_osu" and "cancel_osu_fetch" to start/stop full OSU provider
search (all suitable APs in scan results)
* OSEN network for online signup connection
* min_{dl,ul}_bandwidth_{home,roaming} cred parameters
* max_bss_load cred parameter
* req_conn_capab cred parameter
* sp_priority cred parameter
* ocsp cred parameter
* slow down automatic connection attempts on EAP failure to meet
required behavior (no more than 10 retries within a 10-minute
interval)
* sample implementation of online signup client (both SPP and
OMA-DM protocols) (hs20/client/*)
- fixed GAS indication for additional comeback delay with status
code 95
- extend ANQP_GET to accept Hotspot 2.0 subtypes
ANQP_GET <addr> <info id>[,<info id>]...
[,hs20:<subtype>][...,hs20:<subtype>]
- add control interface events CRED-ADDED <id>,
CRED-MODIFIED <id> <field>, CRED-REMOVED <id>
- add "GET_CRED <id> <field>" command
- enable FT for the connection automatically if the AP advertises
support for this
- fix a case where auto_interworking=1 could end up stopping scanning
* fixed TDLS interoperability issues with supported operating class in
some deployed stations
* internal TLS implementation enhancements/fixes
- add SHA256-based cipher suites
- add DHE-RSA cipher suites
- fix X.509 validation of PKCS#1 signature to check for extra data
* fixed PTK derivation for CCMP-256 and GCMP-256
* added "reattach" command for fast reassociate-back-to-same-BSS
* allow PMF to be enabled for AP mode operation with the ieee80211w
parameter
* added "get_capability tdls" command
* added option to set config blobs through control interface with
"SET blob <name> <hexdump>"
* D-Bus interface extensions/fixes
- make p2p_no_group_iface configurable
- declare ServiceDiscoveryRequest method properly
- export peer's device address as a property
- make reassociate command behave like the control interface one,
i.e., to allow connection from disconnected state
* added optional "freq=<channel ranges>" parameter to SET pno
* added optional "freq=<channel ranges>" parameter to SELECT_NETWORK
* fixed OBSS scan result processing for 20/40 MHz co-ex report
* remove WPS 1.0 only support, i.e., WSC 2.0 support is now enabled
whenever CONFIG_WPS=y is set
* fixed regression in parsing of WNM Sleep Mode exit key data
* fixed potential segmentation fault and memory leaks in WNM neighbor
report processing
* EAP-pwd fixes
- fragmentation of PWD-Confirm-Resp
- fix memory leak when fragmentation is used
- fix possible segmentation fault on EAP method deinit if an invalid
group is negotiated
* added MACsec/IEEE Std 802.1X-2010 PAE implementation (currently
available only with the macsec_qca driver wrapper)
* fixed EAP-SIM counter-too-small message
* added 'dup_network <id_s> <id_d> <name>' command; this can be used to
clone the psk field without having toextract it from wpa_supplicant
* fixed GSM authentication on USIM
* added support for using epoll in eloop (CONFIG_ELOOP_EPOLL=y)
* fixed some concurrent virtual interface cases with dedicated P2P
management interface to not catch events from removed interface (this
could result in the management interface getting disabled)
* fixed a memory leak in SAE random number generation
* fixed off-by-one bounds checking in printf_encode()
- this could result in some control interface ATTACH command cases
terminating wpa_supplicant
* fixed EAPOL-Key exchange when GCMP is used with SHA256-based AKM
* various bug fixes
2014-02-04 - v2.1
* added support for simultaneous authentication of equals (SAE) for
stronger password-based authentication with WPA2-Personal
* improved P2P negotiation and group formation robustness
- avoid unnecessary Dialog Token value changes during retries
- avoid more concurrent scanning cases during full group formation
sequence
- do not use potentially obsolete scan result data from driver
cache for peer discovery/updates
- avoid undesired re-starting of GO negotiation based on Probe
Request frames
- increase GO Negotiation and Invitation timeouts to address busy
environments and peers that take long time to react to messages,
e.g., due to power saving
- P2P Device interface type
* improved P2P channel selection (use more peer information and allow
more local options)
* added support for optional per-device PSK assignment by P2P GO
(wpa_cli p2p_set per_sta_psk <0/1>)
* added P2P_REMOVE_CLIENT for removing a client from P2P groups
(including persistent groups); this can be used to securely remove
a client from a group if per-device PSKs are used
* added more configuration flexibility for allowed P2P GO/client
channels (p2p_no_go_freq list and p2p_add_cli_chan=0/1)
* added nl80211 functionality
- VHT configuration for nl80211
- MFP (IEEE 802.11w) information for nl80211 command API
- support split wiphy dump
- FT (IEEE 802.11r) with driver-based SME
- use advertised number of supported concurrent channels
- QoS Mapping configuration
* improved TDLS negotiation robustness
* added more TDLS peer parameters to be configured to the driver
* optimized connection time by allowing recently received scan results
to be used instead of having to run through a new scan
* fixed ctrl_iface BSS command iteration with RANGE argument and no
exact matches; also fixed argument parsing for some cases with
multiple arguments
* added 'SCAN TYPE=ONLY' ctrl_iface command to request manual scan
without executing roaming/network re-selection on scan results
* added Session-Id derivation for EAP peer methods
* added fully automated regression testing with mac80211_hwsim
* changed configuration parser to reject invalid integer values
* allow AP/Enrollee to be specified with BSSID instead of UUID for
WPS ER operations
* disable network block temporarily on repeated connection failures
* changed the default driver interface from wext to nl80211 if both are
included in the build
* remove duplicate networks if WPS provisioning is run multiple times
* remove duplicate networks when Interworking network selection uses the
same network
* added global freq_list configuration to allow scan frequencies to be
limited for all cases instead of just for a specific network block
* added support for BSS Transition Management
* added option to use "IFNAME=<ifname> " prefix to use the global
control interface connection to perform per-interface commands;
similarly, allow global control interface to be used as a monitor
interface to receive events from all interfaces
* fixed OKC-based PMKSA cache entry clearing
* fixed TKIP group key configuration with FT
* added support for using OCSP stapling to validate server certificate
(ocsp=1 as optional and ocsp=2 as mandatory)
* added EAP-EKE peer
* added peer restart detection for IBSS RSN
* added domain_suffix_match (and domain_suffix_match2 for Phase 2
EAP-TLS) to specify additional constraint for the server certificate
domain name
* added support for external SIM/USIM processing in EAP-SIM, EAP-AKA,
and EAP-AKA' (CTRL-REQ-SIM and CTRL-RSP-SIM commands over control
interface)
* added global bgscan configuration option as a default for all network
blocks that do not specify their own bgscan parameters
* added D-Bus methods for TDLS
* added more control to scan requests
- "SCAN freq=<freq list>" can be used to specify which channels are
scanned (comma-separated frequency ranges in MHz)
- "SCAN passive=1" can be used to request a passive scan (no Probe
Request frames are sent)
- "SCAN use_id" can be used to request a scan id to be returned and
included in event messages related to this specific scan operation
- "SCAN only_new=1" can be used to request the driver/cfg80211 to
report only BSS entries that have been updated during this scan
round
- these optional arguments to the SCAN command can be combined with
each other
* modified behavior on externally triggered scans
- avoid concurrent operations requiring full control of the radio when
an externally triggered scan is detected
- do not use results for internal roaming decision
* added a new cred block parameter 'temporary' to allow credential
blocks to be stored separately even if wpa_supplicant configuration
file is used to maintain other network information
* added "radio work" framework to schedule exclusive radio operations
for off-channel functionality
- reduce issues with concurrent operations that try to control which
channel is used
- allow external programs to request exclusive radio control in a way
that avoids conflicts with wpa_supplicant
* added support for using Protected Dual of Public Action frames for
GAS/ANQP exchanges when associated with PMF
* added support for WPS+NFC updates and P2P+NFC
- improved protocol for WPS
- P2P group formation/join based on NFC connection handover
- new IPv4 address assignment for P2P groups (ip_addr_* configuration
parameters on the GO) to replace DHCP
- option to fetch and report alternative carrier records for external
NFC operations
* various bug fixes
2013-01-12 - v2.0
* removed Qt3-based wpa_gui (obsoleted by wpa_qui-qt4)
* removed unmaintained driver wrappers broadcom, iphone, osx, ralink,
hostap, madwifi (hostap and madwifi remain available for hostapd;
their wpa_supplicant functionality is obsoleted by wext)
* improved debug logging (human readable event names, interface name
included in more entries)
* changed AP mode behavior to enable WPS only for open and
WPA/WPA2-Personal configuration
* improved P2P concurrency operations
- better coordination of concurrent scan and P2P search operations
- avoid concurrent remain-on-channel operation requests by canceling
previous operations prior to starting a new one
- reject operations that would require multi-channel concurrency if
the driver does not support it
- add parameter to select whether STA or P2P connection is preferred
if the driver cannot support both at the same time
- allow driver to indicate channel changes
- added optional delay=<search delay in milliseconds> parameter for
p2p_find to avoid taking all radio resources
- use 500 ms p2p_find search delay by default during concurrent
operations
- allow all channels in GO Negotiation if the driver supports
multi-channel concurrency
* added number of small changes to make it easier for static analyzers
to understand the implementation
* fixed number of small bugs (see git logs for more details)
* nl80211: number of updates to use new cfg80211/nl80211 functionality
- replace monitor interface with nl80211 commands for AP mode
- additional information for driver-based AP SME
- STA entry authorization in RSN IBSS
* EAP-pwd:
- fixed KDF for group 21 and zero-padding
- added support for fragmentation
- increased maximum number of hunting-and-pecking iterations
* avoid excessive Probe Response retries for broadcast Probe Request
frames (only with drivers using wpa_supplicant AP mode SME/MLME)
* added "GET country" ctrl_iface command
* do not save an invalid network block in wpa_supplicant.conf to avoid
problems reading the file on next start
* send STA connected/disconnected ctrl_iface events to both the P2P
group and parent interfaces
* added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y)
* added "SET pno <1/0>" ctrl_iface command to start/stop preferred
network offload with sched_scan driver command
* merged in number of changes from Android repository for P2P, nl80211,
and build parameters
* changed P2P GO mode configuration to use driver capabilities to
automatically enable HT operations when supported
* added "wpa_cli status wps" command to fetch WPA2-Personal passhrase
for WPS use cases in AP mode
* EAP-AKA: keep pseudonym identity across EAP exchanges to match EAP-SIM
behavior
* improved reassociation behavior in cases where association is rejected
or when an AP disconnects us to handle common load balancing
mechanisms
- try to avoid extra scans when the needed information is available
* added optional "join" argument for p2p_prov_disc ctrl_iface command
* added group ifname to P2P-PROV-DISC-* events
* added P2P Device Address to AP-STA-DISCONNECTED event and use
p2p_dev_addr parameter name with AP-STA-CONNECTED
* added workarounds for WPS PBC overlap detection for some P2P use cases
where deployed stations work incorrectly
* optimize WPS connection speed by disconnecting prior to WPS scan and
by using single channel scans when AP channel is known
* PCSC and SIM/USIM improvements:
- accept 0x67 (Wrong length) as a response to READ RECORD to fix
issues with some USIM cards
- try to read MNC length from SIM/USIM
- build realm according to 3GPP TS 23.003 with identity from the SIM
- allow T1 protocol to be enabled
* added more WPS and P2P information available through D-Bus
* improve P2P negotiation robustness
- extra waits to get ACK frames through
- longer timeouts for cases where deployed devices have been
identified have issues meeting the specification requirements
- more retries for some P2P frames
- handle race conditions in GO Negotiation start by both devices
- ignore unexpected GO Negotiation Response frame
* added support for libnl 3.2 and newer
* added P2P persistent group info to P2P_PEER data
* maintain a list of P2P Clients for persistent group on GO
* AP: increased initial group key handshake retransmit timeout to 500 ms
* added optional dev_id parameter for p2p_find
* added P2P-FIND-STOPPED ctrl_iface event
* fixed issues in WPA/RSN element validation when roaming with ap_scan=1
and driver-based BSS selection
* do not expire P2P peer entries while connected with the peer in a
group
* fixed WSC element inclusion in cases where P2P is disabled
* AP: added a WPS workaround for mixed mode AP Settings with Windows 7
* EAP-SIM: fixed AT_COUNTER_TOO_SMALL use
* EAP-SIM/AKA: append realm to pseudonym identity
* EAP-SIM/AKA: store pseudonym identity in network configuration to
allow it to persist over multiple EAP sessions and wpa_supplicant
restarts
* EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this
breaks interoperability with older versions
* added support for WFA Hotspot 2.0
- GAS/ANQP to fetch network information
- credential configuration and automatic network selections based on
credential match with ANQP information
* limited PMKSA cache entries to be used only with the network context
that was used to create them
* improved PMKSA cache expiration to avoid unnecessary disconnections
* adjusted bgscan_simple fast-scan backoff to avoid too frequent
background scans
* removed ctrl_iface event on P2P PD Response in join-group case
* added option to fetch BSS table entry based on P2P Device Address
("BSS p2p_dev_addr=<P2P Device Address>")
* added BSS entry age to ctrl_iface BSS command output
* added optional MASK=0xH option for ctrl_iface BSS command to select
which fields are included in the response
* added optional RANGE=ALL|N1-N2 option for ctrl_iface BSS command to
fetch information about several BSSes in one call
* simplified licensing terms by selecting the BSD license as the only
alternative
* added "P2P_SET disallow_freq <freq list>" ctrl_iface command to
disable channels from P2P use
* added p2p_pref_chan configuration parameter to allow preferred P2P
channels to be specified
* added support for advertising immediate availability of a WPS
credential for P2P use cases
* optimized scan operations for P2P use cases (use single channel scan
for a specific SSID when possible)
* EAP-TTLS: fixed peer challenge generation for MSCHAPv2
* SME: do not use reassociation after explicit disconnection request
(local or a notification from an AP)
* added support for sending debug info to Linux tracing (-T on command
line)
* added support for using Deauthentication reason code 3 as an
indication of P2P group termination
* added wps_vendor_ext_m1 configuration parameter to allow vendor
specific attributes to be added to WPS M1
* started using separate TLS library context for tunneled TLS
(EAP-PEAP/TLS, EAP-TTLS/TLS, EAP-FAST/TLS) to support different CA
certificate configuration between Phase 1 and Phase 2
* added optional "auto" parameter for p2p_connect to request automatic
GO Negotiation vs. join-a-group selection
* added disabled_scan_offload parameter to disable automatic scan
offloading (sched_scan)
* added optional persistent=<network id> parameter for p2p_connect to
allow forcing of a specific SSID/passphrase for GO Negotiation
* added support for OBSS scan requests and 20/40 BSS coexistence reports
* reject PD Request for unknown group
* removed scripts and notes related to Windows binary releases (which
have not been used starting from 1.x)
* added initial support for WNM operations
- Keep-alive based on BSS max idle period
- WNM-Sleep Mode
- minimal BSS Transition Management processing
* added autoscan module to control scanning behavior while not connected
- autoscan_periodic and autoscan_exponential modules
* added new WPS NFC ctrl_iface mechanism
- added initial support NFC connection handover
- removed obsoleted WPS_OOB command (including support for deprecated
UFD config_method)
* added optional framework for external password storage ("ext:<name>")
* wpa_cli: added optional support for controlling wpa_supplicant
remotely over UDP (CONFIG_CTRL_IFACE=udp-remote) for testing purposes
* wpa_cli: extended tab completion to more commands
* changed SSID output to use printf-escaped strings instead of masking
of non-ASCII characters
- SSID can now be configured in the same format: ssid=P"abc\x00test"
* removed default ACM=1 from AC_VO and AC_VI
* added optional "ht40" argument for P2P ctrl_iface commands to allow
40 MHz channels to be requested on the 5 GHz band
* added optional parameters for p2p_invite command to specify channel
when reinvoking a persistent group as the GO
* improved FIPS mode builds with OpenSSL
- "make fips" with CONFIG_FIPS=y to build wpa_supplicant with the
OpenSSL FIPS object module
- replace low level OpenSSL AES API calls to use EVP
- use OpenSSL keying material exporter when possible
- do not export TLS keys in FIPS mode
- remove MD5 from CONFIG_FIPS=y builds
- use OpenSSL function for PKBDF2 passphrase-to-PSK
- use OpenSSL HMAC implementation
- mix RAND_bytes() output into random_get_bytes() to force OpenSSL
DRBG to be used in FIPS mode
- use OpenSSL CMAC implementation
* added mechanism to disable TLS Session Ticket extension
- a workaround for servers that do not support TLS extensions that
was enabled by default in recent OpenSSL versions
- tls_disable_session_ticket=1
- automatically disable TLS Session Ticket extension by default when
using EAP-TLS/PEAP/TTLS (i.e., only use it with EAP-FAST)
* changed VENDOR-TEST EAP method to use proper private enterprise number
(this will not interoperate with older versions)
* disable network block temporarily on authentication failures
* improved WPS AP selection during WPS PIN iteration
* added support for configuring GCMP cipher for IEEE 802.11ad
* added support for Wi-Fi Display extensions
- WFD_SUBELEMENT_SET ctrl_iface command to configure WFD subelements
- SET wifi_display <0/1> to disable/enable WFD support
- WFD service discovery
- an external program is needed to manage the audio/video streaming
and codecs
* optimized scan result use for network selection
- use the internal BSS table instead of raw scan results
- allow unnecessary scans to be skipped if fresh information is
available (e.g., after GAS/ANQP round for Interworking)
* added support for 256-bit AES with internal TLS implementation
* allow peer to propose channel in P2P invitation process for a
persistent group
* added disallow_aps parameter to allow BSSIDs/SSIDs to be disallowed
from network selection
* re-enable the networks disabled during WPS operations
* allow P2P functionality to be disabled per interface (p2p_disabled=1)
* added secondary device types into P2P_PEER output
* added an option to disable use of a separate P2P group interface
(p2p_no_group_iface=1)
* fixed P2P Bonjour SD to match entries with both compressed and not
compressed domain name format and support multiple Bonjour PTR matches
for the same key
* use deauthentication instead of disassociation for all disconnection
operations; this removes the now unused disassociate() wpa_driver_ops
callback
* optimized PSK generation on P2P GO by caching results to avoid
multiple PBKDF2 operations
* added okc=1 global configuration parameter to allow OKC to be enabled
by default for all network blocks
* added a workaround for WPS PBC session overlap detection to avoid
interop issues with deployed station implementations that do not
remove active PBC indication from Probe Request frames properly
* added basic support for 60 GHz band
* extend EAPOL frames processing workaround for roaming cases
(postpone processing of unexpected EAPOL frame until association
event to handle reordered events)
2012-05-10 - v1.0
* bsd: Add support for setting HT values in IFM_MMASK.
* Delay STA entry removal until Deauth/Disassoc TX status in AP mode.
This allows the driver to use PS buffering of Deauthentication and
Disassociation frames when the STA is in power save sleep. Only
available with drivers that provide TX status events for Deauth/
Disassoc frames (nl80211).
* Drop oldest unknown BSS table entries first. This makes it less
likely to hit connection issues in environments with huge number
of visible APs.
* Add systemd support.
* Add support for setting the syslog facility from the config file
at build time.
* atheros: Add support for IEEE 802.11w configuration.
* AP mode: Allow enable HT20 if driver supports it, by setting the
config parameter ieee80211n.
* Allow AP mode to disconnect STAs based on low ACK condition (when
the data connection is not working properly, e.g., due to the STA
going outside the range of the AP). Disabled by default, enable by
config option disassoc_low_ack.
* nl80211:
- Support GTK rekey offload.
- Support PMKSA candidate events. This adds support for RSN
pre-authentication with nl80211 interface and drivers that handle
roaming internally.
* dbus:
- Add a DBus signal for EAP SM requests, emitted on the Interface
object.
- Export max scan ssids supported by the driver as MaxScanSSID.
- Add signal Certification for information about server certification.
- Add BSSExpireAge and BSSExpireCount interface properties and
support set/get, which allows for setting BSS cache expiration age
and expiration scan count.
- Add ConfigFile to AddInterface properties.
- Add Interface.Country property and support to get/set the value.
- Add DBus property CurrentAuthMode.
- P2P DBus API added.
- Emit property changed events (for property BSSs) when adding/
removing BSSs.
- Treat '' in SSIDs of Interface.Scan as a request for broadcast
scan, instead of ignoring it.
- Add DBus getter/setter for FastReauth.
- Raise PropertiesChanged on org.freedesktop.DBus.Properties.
* wpa_cli:
- Send AP-STA-DISCONNECTED event when an AP disconnects a station
due to inactivity.
- Make second argument to set command optional. This can be used to
indicate a zero length value.
- Add signal_poll command.
- Add bss_expire_age and bss_expire_count commands to set/get BSS
cache expiration age and expiration scan count.
- Add ability to set scan interval (the time in seconds wpa_s waits
before requesting a new scan after failing to find a suitable
network in scan results) using scan_interval command.
- Add event CTRL-EVENT-ASSOC-REJECT for association rejected.
- Add command get version, that returns wpa_supplicant version string.
- Add command sta_autoconnect for disabling automatic reconnection
on receiving disconnection event.
- Setting bssid parameter to an empty string "" or any can now be
used to clear the bssid_set flag in a network block, i.e., to remove
bssid filtering.
- Add tdls_testing command to add a special testing feature for
changing TDLS behavior. Build param CONFIG_TDLS_TESTING must be
enabled as well.
- For interworking, add wpa_cli commands interworking_select,
interworking_connect, anqp_get, fetch_anqp, and stop_fetch_anqp.
- Many P2P commands were added. See README-P2P.
- Many WPS/WPS ER commands - see WPS/WPS ER sections for details.
- Allow set command to change global config parameters.
- Add log_level command, which can be used to display the current
debugging level and to change the log level during run time.
- Add note command, which can be used to insert notes to the debug
log.
- Add internal line edit implementation. CONFIG_WPA_CLI_EDIT=y
can now be used to build wpa_cli with internal implementation of
line editing and history support. This can be used as a replacement
for CONFIG_READLINE=y.
* AP mode: Add max_num_sta config option, which can be used to limit
the number of stations allowed to connect to the AP.
* Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad
config file.
* wext: Increase scan timeout from 5 to 10 seconds.
* Add blacklist command, allowing an external program to
manage the BSS blacklist and display its current contents.
* WPS:
- Add wpa_cli wps_pin get command for generating random PINs. This can
be used in a UI to generate a PIN without starting WPS (or P2P)
operation.
- Set RF bands based on driver capabilities, instead of hardcoding
them.
- Add mechanism for indicating non-standard WPS errors.
- Add CONFIG_WPS_REG_DISABLE_OPEN=y option to disable open networks
by default.
- Add wps_ap_pin cli command for wpa_supplicant AP mode.
- Add wps_check_pin cli command for processing PIN from user input.
UIs can use this command to process a PIN entered by a user and to
validate the checksum digit (if present).
- Cancel WPS operation on PBC session overlap detection.
- New wps_cancel command in wpa_cli will cancel a pending WPS
operation.
- wpa_cli action: Add WPS_EVENT_SUCCESS and WPS_EVENT_FAIL handlers.
- Trigger WPS config update on Manufacturer, Model Name, Model
Number, and Serial Number changes.
- Fragment size is now configurable for EAP-WSC peer. Use
wpa_cli set wps_fragment_size <val>.
- Disable AP PIN after 10 consecutive failures. Slow down attacks on
failures up to 10.
- Allow AP to start in Enrollee mode without AP PIN for probing, to
be compatible with Windows 7.
- Add Config Error into WPS-FAIL events to provide more info to the
user on how to resolve the issue.
- Label and Display config methods are not allowed to be enabled
at the same time, since it is unclear which PIN to use if both
methods are advertised.
- When controlling multiple interfaces:
- apply WPS commands to all interfaces configured to use WPS
- apply WPS config changes to all interfaces that use WPS
- when an attack is detected on any interface, disable AP PIN on
all interfaces
* WPS ER:
- Add special AP Setup Locked mode to allow read only ER.
ap_setup_locked=2 can now be used to enable a special mode where
WPS ER can learn the current AP settings, but cannot change them.
- Show SetSelectedRegistrar events as ctrl_iface events
- Add wps_er_set_config to enroll a network based on a local
network configuration block instead of having to (re-)learn the
current AP settings with wps_er_learn.
- Allow AP filtering based on IP address, add ctrl_iface event for
learned AP settings, add wps_er_config command to configure an AP.
* WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2)
- Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool
for testing protocol extensibility.
- Add build option CONFIG_WPS_STRICT to allow disabling of WPS
workarounds.
- Add support for AuthorizedMACs attribute.
* TDLS:
- Propagate TDLS related nl80211 capability flags from kernel and
add them as driver capability flags. If the driver doesn't support
capabilities, assume TDLS is supported internally. When TDLS is
explicitly not supported, disable all user facing TDLS operations.
- Allow TDLS to be disabled at runtime (mostly for testing).
Use set tdls_disabled.
- Honor AP TDLS settings that prohibit/allow TDLS.
- Add a special testing feature for changing TDLS behavior. Use
CONFIG_TDLS_TESTING build param to enable. Configure at runtime
with tdls_testing cli command.
- Add support for TDLS 802.11z.
* wlantest: Add a tool wlantest for IEEE802.11 protocol testing.
wlantest can be used to capture frames from a monitor interface
for realtime capturing or from pcap files for offline analysis.
* Interworking: Support added for 802.11u. Enable in .config with
CONFIG_INTERWORKING. See wpa_supplicant.conf for config parameters
for interworking. wpa_cli commands added to support this are
interworking_select, interworking_connect, anqp_get, fetch_anqp,
and stop_fetch_anqp.
* Android: Add build and runtime support for Android wpa_supplicant.
* bgscan learn: Add new bgscan that learns BSS information based on
previous scans, and uses that information to dynamically generate
the list of channels for background scans.
* Add a new debug message level for excessive information. Use
-ddd to enable.
* TLS: Add support for tls_disable_time_checks=1 in client mode.
* Internal TLS:
- Add support for TLS v1.1 (RFC 4346). Enable with build parameter
CONFIG_TLSV11.
- Add domainComponent parser for X.509 names.
* Linux: Add RFKill support by adding an interface state "disabled".
* Reorder some IEs to get closer to IEEE 802.11 standard. Move
WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames.
Move HT IEs to be later in (Re)Assoc Resp.
* Solaris: Add support for wired 802.1X client.
* Wi-Fi Direct support. See README-P2P for more information.
* Many bugfixes.
2010-04-18 - v0.7.2
* nl80211: fixed number of issues with roaming
* avoid unnecessary roaming if multiple APs with similar signal
strength are present in scan results
* add TLS client events and server probing to ease design of
automatic detection of EAP parameters
* add option for server certificate matching (SHA256 hash of the
certificate) instead of trusted CA certificate configuration
* bsd: Cleaned up driver wrapper and added various low-level
configuration options
* wpa_gui-qt4: do not show too frequent WPS AP available events as
tray messages
* TNC: fixed issues with fragmentation
* EAP-TNC: add Flags field into fragment acknowledgement (needed to
interoperate with other implementations; may potentially breaks
compatibility with older wpa_supplicant/hostapd versions)
* wpa_cli: added option for using a separate process to receive event
messages to reduce latency in showing these
(CFLAGS += -DCONFIG_WPA_CLI_FORK=y in .config to enable this)
* maximum BSS table size can now be configured (bss_max_count)
* BSSes to be included in the BSS table can be filtered based on
configured SSIDs to save memory (filter_ssids)
* fix number of issues with IEEE 802.11r/FT; this version is not
backwards compatible with old versions
* nl80211: add support for IEEE 802.11r/FT protocol (both over-the-air
and over-the-DS)
* add freq_list network configuration parameter to allow the AP
selection to filter out entries based on the operating channel
* add signal strength change events for bgscan; this allows more
dynamic changes to background scanning interval based on changes in
the signal strength with the current AP; this improves roaming within
ESS quite a bit, e.g., with bgscan="simple:30:-45:300" in the network
configuration block to request background scans less frequently when
signal strength remains good and to automatically trigger background
scans whenever signal strength drops noticeably
(this is currently only available with nl80211)
* add BSSID and reason code (if available) to disconnect event messages
* wpa_gui-qt4: more complete support for translating the GUI with
linguist and add German translation
* fix DH padding with internal crypto code (mainly, for WPS)
* do not trigger initial scan automatically anymore if there are no
enabled networks
2010-01-16 - v0.7.1
* cleaned up driver wrapper API (struct wpa_driver_ops); the new API
is not fully backwards compatible, so out-of-tree driver wrappers
will need modifications
* cleaned up various module interfaces
* merge hostapd and wpa_supplicant developers' documentation into a
single document
* nl80211: use explicit deauthentication to clear cfg80211 state to
avoid issues when roaming between APs
* dbus: major design changes in the new D-Bus API
(fi.w1.wpa_supplicant1)
* nl80211: added support for IBSS networks
* added internal debugging mechanism with backtrace support and memory
allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y)
* added WPS ER unsubscription command to more cleanly unregister from
receiving UPnP events when ER is terminated
* cleaned up AP mode operations to avoid need for virtual driver_ops
wrapper
* added BSS table to maintain more complete scan result information
over multiple scans (that may include only partial results)
* wpa_gui-qt4: update Peers dialog information more dynamically while
the dialog is kept open
* fixed PKCS#12 use with OpenSSL 1.0.0
* driver_wext: Added cfg80211-specific optimization to avoid some
unnecessary scans and to speed up association
2009-11-21 - v0.7.0
* increased wpa_cli ping interval to 5 seconds and made this
configurable with a new command line options (-G<seconds>)
* fixed scan buffer processing with WEXT to handle up to 65535
byte result buffer (previously, limited to 32768 bytes)
* allow multiple driver wrappers to be specified on command line
(e.g., -Dnl80211,wext); the first one that is able to initialize the
interface will be used
* added support for multiple SSIDs per scan request to optimize
scan_ssid=1 operations in ap_scan=1 mode (i.e., search for hidden
SSIDs); this requires driver support and can currently be used only
with nl80211
* added support for WPS USBA out-of-band mechanism with USB Flash
Drives (UFD) (CONFIG_WPS_UFD=y)
* driver_ndis: add PAE group address to the multicast address list to
fix wired IEEE 802.1X authentication
* fixed IEEE 802.11r key derivation function to match with the standard
(note: this breaks interoperability with previous version) [Bug 303]
* added better support for drivers that allow separate authentication
and association commands (e.g., mac80211-based Linux drivers with
nl80211; SME in wpa_supplicant); this allows over-the-air FT protocol
to be used (IEEE 802.11r)
* fixed SHA-256 based key derivation function to match with the
standard when using CCMP (for IEEE 802.11r and IEEE 802.11w)
(note: this breaks interoperability with previous version) [Bug 307]
* use shared driver wrapper files with hostapd
* added AP mode functionality (CONFIG_AP=y) with mode=2 in the network
block; this can be used for open and WPA2-Personal networks
(optionally, with WPS); this links in parts of hostapd functionality
into wpa_supplicant
* wpa_gui-qt4: added new Peers dialog to show information about peers
(other devices, including APs and stations, etc. in the neighborhood)
* added support for WPS External Registrar functionality (configure APs
and enroll new devices); can be used with wpa_gui-qt4 Peers dialog
and wpa_cli commands wps_er_start, wps_er_stop, wps_er_pin,
wps_er_pbc, wps_er_learn
(this can also be used with a new 'none' driver wrapper if no
wireless device or IEEE 802.1X on wired is needed)
* driver_nl80211: multiple updates to provide support for new Linux
nl80211/mac80211 functionality
* updated management frame protection to use IEEE Std 802.11w-2009
* fixed number of small WPS issues and added workarounds to
interoperate with common deployed broken implementations
* added support for NFC out-of-band mechanism with WPS
* driver_ndis: fixed wired IEEE 802.1X authentication with PAE group
address frames
* added preliminary support for IEEE 802.11r RIC processing
* added support for specifying subset of enabled frequencies to scan
(scan_freq option in the network configuration block); this can speed
up scanning process considerably if it is known that only a small
subset of channels is actually used in the network (this is currently
supported only with -Dnl80211)
* added a workaround for race condition between receiving the
association event and the following EAPOL-Key
* added background scan and roaming infrastructure to allow
network-specific optimizations to be used to improve roaming within
an ESS (same SSID)
* added new DBus interface (fi.w1.wpa_supplicant1)
2009-01-06 - v0.6.7
* added support for Wi-Fi Protected Setup (WPS)
(wpa_supplicant can now be configured to act as a WPS Enrollee to
enroll credentials for a network using PIN and PBC methods; in
addition, wpa_supplicant can act as a wireless WPS Registrar to
configure an AP); WPS support can be enabled by adding CONFIG_WPS=y
into .config and setting the runtime configuration variables in
wpa_supplicant.conf (see WPS section in the example configuration
file); new wpa_cli commands wps_pin, wps_pbc, and wps_reg are used to
manage WPS negotiation; see README-WPS for more details
* added support for EAP-AKA' (draft-arkko-eap-aka-kdf)
* added support for using driver_test over UDP socket
* fixed PEAPv0 Cryptobinding interoperability issue with Windows Server
2008 NPS; optional cryptobinding is now enabled (again) by default
* fixed PSK editing in wpa_gui
* changed EAP-GPSK to use the IANA assigned EAP method type 51
* added a Windows installer that includes WinPcap and all the needed
DLLs; in addition, it set up the registry automatically so that user
will only need start wpa_gui to get prompted to start the wpasvc
servide and add a new interface if needed through wpa_gui dialog
* updated management frame protection to use IEEE 802.11w/D7.0
2008-11-23 - v0.6.6
* added Milenage SIM/USIM emulator for EAP-SIM/EAP-AKA
(can be used to simulate test SIM/USIM card with a known private key;
enable with CONFIG_SIM_SIMULATOR=y/CONFIG_USIM_SIMULATOR=y in .config
and password="Ki:OPc"/password="Ki:OPc:SQN" in network configuration)
* added a new network configuration option, wpa_ptk_rekey, that can be
used to enforce frequent PTK rekeying, e.g., to mitigate some attacks
against TKIP deficiencies
* added an optional mitigation mechanism for certain attacks against
TKIP by delaying Michael MIC error reports by a random amount of time
between 0 and 60 seconds; this can be enabled with a build option
CONFIG_DELAYED_MIC_ERROR_REPORT=y in .config
* fixed EAP-AKA to use RES Length field in AT_RES as length in bits,
not bytes
* updated OpenSSL code for EAP-FAST to use an updated version of the
session ticket overriding API that was included into the upstream
OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is
needed with that version anymore)
* updated userspace MLME instructions to match with the current Linux
mac80211 implementation; please also note that this can only be used
with driver_nl80211.c (the old code from driver_wext.c was removed)
* added support (Linux only) for RoboSwitch chipsets (often found in
consumer grade routers); driver interface 'roboswitch'
* fixed canceling of PMKSA caching when using drivers that generate
RSN IE and refuse to drop PMKIDs that wpa_supplicant does not know
about
2008-11-01 - v0.6.5
* added support for SHA-256 as X.509 certificate digest when using the
internal X.509/TLSv1 implementation
* updated management frame protection to use IEEE 802.11w/D6.0
* added support for using SHA256-based stronger key derivation for WPA2
(IEEE 802.11w)
* fixed FT (IEEE 802.11r) authentication after a failed association to
use correct FTIE
* added support for configuring Phase 2 (inner/tunneled) authentication
method with wpa_gui-qt4
2008-08-10 - v0.6.4
* added support for EAP Sequences in EAP-FAST Phase 2
* added support for using TNC with EAP-FAST
* added driver_ps3 for the PS3 Linux wireless driver
* added support for optional cryptobinding with PEAPv0
* fixed the OpenSSL patches (0.9.8g and 0.9.9) for EAP-FAST to
allow fallback to full handshake if server rejects PAC-Opaque
* added fragmentation support for EAP-TNC
* added support for parsing PKCS #8 formatted private keys into the
internal TLS implementation (both PKCS #1 RSA key and PKCS #8
encapsulated RSA key can now be used)
* added option of using faster, but larger, routines in the internal
LibTomMath (for internal TLS implementation) to speed up DH and RSA
calculations (CONFIG_INTERNAL_LIBTOMMATH_FAST=y)
* fixed race condition between disassociation event and group key
handshake to avoid getting stuck in incorrect state [Bug 261]
* fixed opportunistic key caching (proactive_key_caching)
2008-02-22 - v0.6.3
* removed 'nai' and 'eappsk' network configuration variables that were
previously used for configuring user identity and key for EAP-PSK,
EAP-PAX, EAP-SAKE, and EAP-GPSK. 'identity' field is now used as the
replacement for 'nai' (if old configuration used a separate
'identity' value, that would now be configured as
'anonymous_identity'). 'password' field is now used as the
replacement for 'eappsk' (it can also be set using hexstring to
present random binary data)
* removed '-w' command line parameter (wait for interface to be added,
if needed); cleaner way of handling this functionality is to use an
external mechanism (e.g., hotplug scripts) that start wpa_supplicant
when an interface is added
* updated FT support to use the latest draft, IEEE 802.11r/D9.0
* added ctrl_iface monitor event (CTRL-EVENT-SCAN-RESULTS) for
indicating when new scan results become available
* added new ctrl_iface command, BSS, to allow scan results to be
fetched without hitting the message size limits (this command
can be used to iterate through the scan results one BSS at the time)
* fixed EAP-SIM not to include AT_NONCE_MT and AT_SELECTED_VERSION
attributes in EAP-SIM Start/Response when using fast reauthentication
* fixed EAPOL not to end up in infinite loop when processing dynamic
WEP keys with IEEE 802.1X
* fixed problems in getting NDIS events from WMI on Windows 2000
2008-01-01 - v0.6.2
* added support for Makefile builds to include debug-log-to-a-file
functionality (CONFIG_DEBUG_FILE=y and -f<path> on command line)
* fixed EAP-SIM and EAP-AKA message parser to validate attribute
lengths properly to avoid potential crash caused by invalid messages
* added data structure for storing allocated buffers (struct wpabuf);
this does not affect wpa_supplicant usage, but many of the APIs
changed and various interfaces (e.g., EAP) is not compatible with old
versions
* added support for protecting EAP-AKA/Identity messages with
AT_CHECKCODE (optional feature in RFC 4187)
* added support for protected result indication with AT_RESULT_IND for
EAP-SIM and EAP-AKA (phase1="result_ind=1")
* added driver_wext workaround for race condition between scanning and
association with drivers that take very long time to scan all
channels (e.g., madwifi with dual-band cards); wpa_supplicant is now
using a longer hardcoded timeout for the scan if the driver supports
notifications for scan completion (SIOCGIWSCAN event); this helps,
e.g., in cases where wpa_supplicant and madwifi driver ended up in
loop where the driver did not even try to associate
* stop EAPOL timer tick when no timers are in use in order to reduce
power consumption (no need to wake up the process once per second)
[Bug 237]
* added support for privilege separation (run only minimal part of
wpa_supplicant functionality as root and rest as unprivileged,
non-root process); see 'Privilege separation' in README for details;
this is disabled by default and can be enabled with CONFIG_PRIVSEP=y
in .config
* changed scan results data structure to include all information
elements to make it easier to support new IEs; old get_scan_result()
driver_ops is still supported for backwards compatibility (results
are converted internally to the new format), but all drivers should
start using the new get_scan_results2() to make them more likely to
work with new features
* Qt4 version of wpa_gui (wpa_gui-qt4 subdirectory) is now native Qt4
application, i.e., it does not require Qt3Support anymore; Windows
binary of wpa_gui.exe is now from this directory and only requires
QtCore4.dll and QtGui4.dll libraries
* updated Windows binary build to use Qt 4.3.3 and made Qt DLLs
available as a separate package to make wpa_gui installation easier:
http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip
* added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt);
only shared key/password authentication is supported in this version
2007-11-24 - v0.6.1
* added support for configuring password as NtPasswordHash
(16-byte MD4 hash of password) in hash:<32 hex digits> format
* added support for fallback from abbreviated TLS handshake to
full handshake when using EAP-FAST (e.g., due to an expired
PAC-Opaque)
* updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
draft (draft-ietf-emu-eap-gpsk-07.txt)
* added support for drivers that take care of RSN 4-way handshake
internally (WPA_DRIVER_FLAGS_4WAY_HANDSHAKE in get_capa flags and
WPA_ALG_PMK in set_key)
* added an experimental port for Mac OS X (CONFIG_DRIVER_OSX=y in
.config); this version supports only ap_scan=2 mode and allow the
driver to take care of the 4-way handshake
* fixed a buffer overflow in parsing TSF from scan results when using
driver_wext.c with a driver that includes the TSF (e.g., iwl4965)
[Bug 232]
* updated FT support to use the latest draft, IEEE 802.11r/D8.0
* fixed an integer overflow issue in the ASN.1 parser used by the
(experimental) internal TLS implementation to avoid a potential
buffer read overflow
* fixed a race condition with -W option (wait for a control interface
monitor before starting) that could have caused the first messages to
be lost
* added support for processing TNCC-TNCS-Messages to report
recommendation (allow/none/isolate) when using TNC [Bug 243]
2007-05-28 - v0.6.0
* added network configuration parameter 'frequency' for setting
initial channel for IBSS (adhoc) networks
* added experimental IEEE 802.11r/D6.0 support
* updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48
* updated EAP-PSK to use the IANA-allocated EAP type 47
* fixed EAP-PAX key derivation
* fixed EAP-PSK bit ordering of the Flags field
* fixed EAP-PEAP/TTLS/FAST to use the correct EAP identifier in
tunnelled identity request (previously, the identifier from the outer
method was used, not the tunnelled identifier which could be
different)
* added support for fragmentation of outer TLS packets during Phase 2
of EAP-PEAP/TTLS/FAST
* fixed EAP-TTLS AVP parser processing for too short AVP lengths
* added support for EAP-FAST authentication with inner methods that
generate MSK (e.g., EAP-MSCHAPv2 that was previously only supported
for PAC provisioning)
* added support for authenticated EAP-FAST provisioning
* added support for configuring maximum number of EAP-FAST PACs to
store in a PAC list (fast_max_pac_list_len=<max> in phase1 string)
* added support for storing EAP-FAST PACs in binary format
(fast_pac_format=binary in phase1 string)
* fixed dbus ctrl_iface to validate message interface before
dispatching to avoid a possible segfault [Bug 190]
* fixed PeerKey key derivation to use the correct PRF label
* updated Windows binary build to link against OpenSSL 0.9.8d and
added support for EAP-FAST
* updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
draft (draft-ietf-emu-eap-gpsk-04.txt)
* fixed EAP-AKA Notification processing to allow Notification to be
processed after AKA Challenge response has been sent
* updated to use IEEE 802.11w/D2.0 for management frame protection
(still experimental)
* fixed EAP-TTLS implementation not to crash on use of freed memory
if TLS library initialization fails
* added support for EAP-TNC (Trusted Network Connect)
(this version implements the EAP-TNC method and EAP-TTLS changes
needed to run two methods in sequence (IF-T) and the IF-IMC and
IF-TNCCS interfaces from TNCC)
2006-11-24 - v0.5.6
* added experimental, integrated TLSv1 client implementation with the
needed X.509/ASN.1/RSA/bignum processing (this can be enabled by
setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in
.config); this can be useful, e.g., if the target system does not
have a suitable TLS library and a minimal code size is required
(total size of this internal TLS/crypto code is bit under 50 kB on
x86 and the crypto code is shared by rest of the supplicant so some
of it was already required; TLSv1/X.509/ASN.1/RSA added about 25 kB)
* removed STAKey handshake since PeerKey handshake has replaced it in
IEEE 802.11ma and there are no known deployments of STAKey
* updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
draft (draft-ietf-emu-eap-gpsk-01.txt)
* added preliminary implementation of IEEE 802.11w/D1.0 (management
frame protection)
(Note: this requires driver support to work properly.)
(Note2: IEEE 802.11w is an unapproved draft and subject to change.)
* fixed Windows named pipes ctrl_iface to not stop listening for
commands if client program opens a named pipe and closes it
immediately without sending a command
* fixed USIM PIN status determination for the case that PIN is not
needed (this allows EAP-AKA to be used with USIM cards that do not
use PIN)
* added support for reading 3G USIM AID from EF_DIR to allow EAP-AKA to
be used with cards that do not support file selection based on
partial AID
* added support for matching the subjectAltName of the authentication
server certificate against multiple name components (e.g.,
altsubject_match="DNS:server.example.com;DNS:server2.example.com")
* fixed EAP-SIM/AKA key derivation for re-authentication case (only
affects IEEE 802.1X with dynamic WEP keys)
* changed ctrl_iface network configuration 'get' operations to not
return password/key material; if these fields are requested, "*"
will be returned if the password/key is set, but the value of the
parameter is not exposed
2006-08-27 - v0.5.5
* added support for building Windows version with UNICODE defined
(wide-char functions)
* driver_ndis: fixed static WEP configuration to avoid race condition
issues with some NDIS drivers between association and setting WEP
keys
* driver_ndis: added validation for IELength value in scan results to
avoid crashes when using buggy NDIS drivers [Bug 165]
* fixed Release|Win32 target in the Visual Studio project files
(previously, only Debug|Win32 target was set properly)
* changed control interface API call wpa_ctrl_pending() to allow it to
return -1 on error (e.g., connection lost); control interface clients
will need to make sure that they verify that the value is indeed >0
when determining whether there are pending messages
* added an alternative control interface backend for Windows targets:
Named Pipe (CONFIG_CTRL_IFACE=named_pipe); this is now the default
control interface mechanism for Windows builds (previously, UDP to
localhost was used)
* changed ctrl_interface configuration for UNIX domain sockets:
- deprecated ctrl_interface_group variable (it may be removed in
future versions)
- allow both directory and group be configured with ctrl_interface
in following format: DIR=/var/run/wpa_supplicant GROUP=wheel
- ctrl_interface=/var/run/wpa_supplicant is still supported for the
case when group is not changed
* added support for controlling more than one interface per process in
Windows version
* added a workaround for a case where the AP is using unknown address
(e.g., MAC address of the wired interface) as the source address for
EAPOL-Key frames; previously, that source address was used as the
destination for EAPOL-Key frames and in key derivation; now, BSSID is
used even if the source address does not match with it
(this resolves an interoperability issue with Thomson SpeedTouch 580)
* added a workaround for UDP-based control interface (which was used in
Windows builds before this release) to prevent packets with forged
addresses from being accepted as local control requests
* removed ndis_events.cpp and possibility of using external
ndis_events.exe; C version (ndis_events.c) is fully functional and
there is no desire to maintain two separate versions of this
implementation
* ndis_events: Changed NDIS event notification design to use WMI to
learn the adapter description through Win32_PnPEntity class; this
should fix some cases where the adapter name was not recognized
correctly (e.g., with some USB WLAN adapters, e.g., Ralink RT2500
USB) [Bug 113]
* fixed selection of the first network in ap_scan=2 mode; previously,
wpa_supplicant could get stuck in SCANNING state when only the first
network for enabled (e.g., after 'wpa_cli select_network 0')
* winsvc: added support for configuring ctrl_interface parameters in
registry (ctrl_interface string value in
HKLM\SOFTWARE\wpa_supplicant\interfaces\0000 key); this new value is
required to enable control interface (previously, this was hardcoded
to be enabled)
* allow wpa_gui subdirectory to be built with both Qt3 and Qt4
* converted wpa_gui-qt4 subdirectory to use Qt4 specific project format
2006-06-20 - v0.5.4
* fixed build with CONFIG_STAKEY=y [Bug 143]
* added support for doing MLME (IEEE 802.11 management frame
processing) in wpa_supplicant when using Devicescape IEEE 802.11
stack (wireless-dev.git tree)
* added a new network block configuration option, fragment_size, to
configure the maximum EAP fragment size
* driver_ndis: Disable WZC automatically for the selected interface to
avoid conflicts with two programs trying to control the radio; WZC
will be re-enabled (if it was enabled originally) when wpa_supplicant
is terminated
* added an experimental TLSv1 client implementation
(CONFIG_TLS=internal) that can be used instead of an external TLS
library, e.g., to reduce total size requirement on systems that do
not include any TLS library by default (this is not yet complete;
basic functionality is there, but certificate validation is not yet
included)
* added PeerKey handshake implementation for IEEE 802.11e
direct link setup (DLS) to replace STAKey handshake
* fixed WPA PSK update through ctrl_iface for the case where the old
PSK was derived from an ASCII passphrase and the new PSK is set as
a raw PSK (hex string)
* added new configuration option for identifying which network block
was used (id_str in wpa_supplicant.conf; included on
WPA_EVENT_CONNECT monitor event and as WPA_ID_STR environmental
variable in wpa_cli action scripts; in addition WPA_ID variable is
set to the current unique identifier that wpa_supplicant assigned
automatically for the network and that can be used with
GET_NETWORK/SET_NETWORK ctrl_iface commands)
* wpa_cli action script is now called only when the connect/disconnect
status changes or when associating with a different network
* fixed configuration parser not to remove CCMP from group cipher list
if WPA-None (adhoc) is used (pairwise=NONE in that case)
* fixed integrated NDIS events processing not to hang the process due
to a missed change in eloop_win.c API in v0.5.3 [Bug 155]
* added support for EAP Generalized Pre-Shared Key (EAP-GPSK,
draft-clancy-emu-eap-shared-secret-00.txt)
* added Microsoft Visual Studio 2005 solution and project files for
build wpa_supplicant for Windows (see vs2005 subdirectory)
* eloop_win: fixed unregistration of Windows events
* l2_packet_winpcap: fixed a deadlock in deinitializing l2_packet
at the end of RSN pre-authentication and added unregistration of
a Windows event to avoid getting eloop_win stuck with an invalid
handle
* driver_ndis: added support for selecting AP based on BSSID
* added new environmental variable for wpa_cli action scripts:
WPA_CTRL_DIR is the current control interface directory
* driver_ndis: added support for using NDISUIO instead of WinPcap for
OID set/query operations (CONFIG_USE_NDISUIO=y in .config); with new
l2_packet_ndis (CONFIG_L2_PACKET=ndis), this can be used to build
wpa_supplicant without requiring WinPcap; note that using NDISUIO
requires that WZC is disabled (net stop wzcsvc) since NDISUIO allows
only one application to open the device
* changed NDIS driver naming to only include device GUID, e.g.,
{7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D}, instead of including WinPcap
specific \Device\NPF_ prefix before the GUID; the prefix is still
allowed for backwards compatibility, but it is not required anymore
when specifying the interface
* driver_ndis: re-initialize driver interface is the adapter is removed
and re-inserted [Bug 159]
* driver_madwifi: fixed TKIP and CCMP sequence number configuration on
big endian hosts [Bug 146]
2006-04-27 - v0.5.3
* fixed EAP-GTC response to include correct user identity when run as
phase 2 method of EAP-FAST (i.e., EAP-FAST did not work in v0.5.2)
* driver_ndis: Fixed encryption mode configuration for unencrypted
networks (some NDIS drivers ignored this, but others, e.g., Broadcom,
refused to associate with open networks) [Bug 106]
* driver_ndis: use BSSID OID polling to detect when IBSS network is
formed even when ndis_events code is included since some NDIS drivers
do not generate media connect events in IBSS mode
* config_winreg: allow global ctrl_interface parameter to be configured
in Windows registry
* config_winreg: added support for saving configuration data into
Windows registry
* added support for controlling network device operational state
(dormant/up) for Linux 2.6.17 to improve DHCP processing (see
http://www.flamewarmaster.de/software/dhcpclient/ for a DHCP client
that can use this information)
* driver_wext: added support for WE-21 change to SSID configuration
* driver_wext: fixed privacy configuration for static WEP keys mode
[Bug 140]
* added an optional driver_ops callback for MLME-SETPROTECTION.request
primitive
* added support for EAP-SAKE (no EAP method number allocated yet, so
this is using the same experimental type 255 as EAP-PSK)
* added support for dynamically loading EAP methods (.so files) instead
of requiring them to be statically linked in; this is disabled by
default (see CONFIG_DYNAMIC_EAP_METHODS in defconfig for information
on how to use this)
2006-03-19 - v0.5.2
* do not try to use USIM APDUs when initializing PC/SC for SIM card
access for a network that has not enabled EAP-AKA
* fixed EAP phase 2 Nak for EAP-{PEAP,TTLS,FAST} (this was broken in
v0.5.1 due to the new support for expanded EAP types)
* added support for generating EAP Expanded Nak
* try to fetch scan results once before requesting new scan when
starting up in ap_scan=1 mode (this can speed up initial association
a lot with, e.g., madwifi-ng driver)
* added support for receiving EAPOL frames from a Linux bridge
interface (-bbr0 on command line)
* fixed EAPOL re-authentication for sessions that used PMKSA caching
* changed EAP method registration to use a dynamic list of methods
instead of a static list generated at build time
* fixed PMKSA cache deinitialization not to use freed memory when
removing PMKSA entries
* fixed a memory leak in EAP-TTLS re-authentication
* reject WPA/WPA2 message 3/4 if it does not include any valid
WPA/RSN IE
* driver_wext: added fallback to use SIOCSIWENCODE for setting auth_alg
if the driver does not support SIOCSIWAUTH
2006-01-29 - v0.5.1
* driver_test: added better support for multiple APs and STAs by using
a directory with sockets that include MAC address for each device in
the name (driver_param=test_dir=/tmp/test)
* added support for EAP expanded type (vendor specific EAP methods)
* added AP_SCAN command into ctrl_iface so that ap_scan configuration
option can be changed if needed
* wpa_cli/wpa_gui: skip non-socket files in control directory when
using UNIX domain sockets; this avoids selecting an incorrect
interface (e.g., a PID file could be in this directory, even though
use of this directory for something else than socket files is not
recommended)
* fixed TLS library deinitialization after RSN pre-authentication not
to disable TLS library for normal authentication
* driver_wext: Remove null-termination from SSID length if the driver
used it; some Linux drivers do this and they were causing problems in
wpa_supplicant not finding matching configuration block. This change
would break a case where the SSID actually ends in '\0', but that is
not likely to happen in real use.
* fixed PMKSA cache processing not to trigger deauthentication if the
current PMKSA cache entry is replaced with a valid new entry
* fixed PC/SC initialization for ap_scan != 1 modes (this fixes
EAP-SIM and EAP-AKA with real SIM/USIM card when using ap_scan=0 or
ap_scan=2)
2005-12-18 - v0.5.0 (beginning of 0.5.x development releases)
* added experimental STAKey handshake implementation for IEEE 802.11e
direct link setup (DLS); note: this is disabled by default in both
build and runtime configuration (can be enabled with CONFIG_STAKEY=y
and stakey=1)
* fixed EAP-SIM and EAP-AKA pseudonym and fast re-authentication to
decrypt AT_ENCR_DATA attributes correctly
* fixed EAP-AKA to allow resynchronization within the same session
* made code closer to ANSI C89 standard to make it easier to port to
other C libraries and compilers
* started moving operating system or C library specific functions into
wrapper functions defined in os.h and implemented in os_*.c to make
code more portable
* wpa_supplicant can now be built with Microsoft Visual C++
(e.g., with the freely available Toolkit 2003 version or Visual
C++ 2005 Express Edition and Platform SDK); see nmake.mak for an
example makefile for nmake
* added support for using Windows registry for command line parameters
(CONFIG_MAIN=main_winsvc) and configuration data
(CONFIG_BACKEND=winreg); see win_example.reg for an example registry
contents; this version can be run both as a Windows service and as a
normal application; 'wpasvc.exe app' to start as applicant,
'wpasvc.exe reg <full path to wpasvc.exe>' to register a service,
'net start wpasvc' to start the service, 'wpasvc.exe unreg' to
unregister a service
* made it possible to link ndis_events.exe functionality into
wpa_supplicant.exe by defining CONFIG_NDIS_EVENTS_INTEGRATED
* added better support for multiple control interface backends
(CONFIG_CTRL_IFACE option); currently, 'unix' and 'udp' are supported
* fixed PC/SC code to use correct length for GSM AUTH command buffer
and to not use pioRecvPci with SCardTransmit() calls; these were not
causing visible problems with pcsc-lite, but Windows Winscard.dll
refused the previously used parameters; this fixes EAP-SIM and
EAP-AKA authentication using SIM/USIM card under Windows
* added new event loop implementation for Windows using
WaitForMultipleObject() instead of select() in order to allow waiting
for non-socket objects; this can be selected with
CONFIG_ELOOP=eloop_win in .config
* added support for selecting l2_packet implementation in .config
(CONFIG_L2_PACKET; following options are available now: linux, pcap,
winpcap, freebsd, none)
* added new l2_packet implementation for WinPcap
(CONFIG_L2_PACKET=winpcap) that uses a separate receive thread to
reduce latency in EAPOL receive processing from about 100 ms to about
3 ms
* added support for EAP-FAST key derivation using other ciphers than
RC4-128-SHA for authentication and AES128-SHA for provisioning
* added support for configuring CA certificate as DER file and as a
configuration blob
* fixed private key configuration as configuration blob and added
support for using PKCS#12 as a blob
* tls_gnutls: added support for using PKCS#12 files; added support for
session resumption
* added support for loading trusted CA certificates from Windows
certificate store: ca_cert="cert_store://<name>", where <name> is
likely CA (Intermediate CA certificates) or ROOT (root certificates)
* added C version of ndis_events.cpp and made it possible to build this
with MinGW so that CONFIG_NDIS_EVENTS_INTEGRATED can be used more
easily on cross-compilation builds
* added wpasvc.exe into Windows binary release; this is an alternative
version of wpa_supplicant.exe with configuration backend using
Windows registry and with the entry point designed to run as a
Windows service
* integrated ndis_events.exe functionality into wpa_supplicant.exe and
wpasvc.exe and removed this additional tool from the Windows binary
release since it is not needed anymore
* load winscard.dll functions dynamically when building with MinGW
since MinGW does not yet include winscard library
2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases)
* l2_packet_pcap: fixed wired IEEE 802.1X authentication with libpcap
and WinPcap to receive frames sent to PAE group address
* disable EAP state machine when IEEE 802.1X authentication is not used
in order to get rid of bogus "EAP failed" messages
* fixed OpenSSL error reporting to go through all pending errors to
avoid confusing reports of old errors being reported at later point
during handshake
* fixed configuration file updating to not write empty variables
(e.g., proto or key_mgmt) that the file parser would not accept
* fixed ADD_NETWORK ctrl_iface command to use the same default values
for variables as empty network definitions read from config file
would get
* fixed EAP state machine to not discard EAP-Failure messages in many
cases (e.g., during TLS handshake)
* fixed a infinite loop in private key reading if the configured file
cannot be parsed successfully
* driver_madwifi: added support for madwifi-ng
* wpa_gui: do not display password/PSK field contents
* wpa_gui: added CA certificate configuration
* driver_ndis: fixed scan request in ap_scan=2 mode not to change SSID
* driver_ndis: include Beacon IEs in AssocInfo in order to notice if
the new AP is using different WPA/RSN IE
* use longer timeout for IEEE 802.11 association to avoid problems with
drivers that may take more than five second to associate
2005-10-27 - v0.4.6
* allow fallback to WPA, if mixed WPA+WPA2 networks have mismatch in
RSN IE, but WPA IE would match with wpa_supplicant configuration
* added support for named configuration blobs in order to avoid having
to use file system for external files (e.g., certificates);
variables can be set to "blob://<blob name>" instead of file path to
use a named blob; supported fields: pac_file, client_cert,
private_key
* fixed RSN pre-authentication (it was broken in the clean up of WPA
state machine interface in v0.4.5)
* driver_madwifi: set IEEE80211_KEY_GROUP flag for group keys to make
sure the driver configures broadcast decryption correctly
* added ca_path (and ca_path2) configuration variables that can be used
to configure OpenSSL CA path, e.g., /etc/ssl/certs, for using the
system-wide trusted CA list
* added support for starting wpa_supplicant without a configuration
file (-C argument must be used to set ctrl_interface parameter for
this case; in addition, -p argument can be used to provide
driver_param; these new arguments can also be used with a
configuration to override the values from the configuration)
* added global control interface that can be optionally used for adding
and removing network interfaces dynamically (-g command line argument
for both wpa_supplicant and wpa_cli) without having to restart
wpa_supplicant process
* wpa_gui:
- try to save configuration whenever something is modified
- added WEP key configuration
- added possibility to edit the current network configuration
* driver_ndis: fixed driver polling not to increase frequency on each
received EAPOL frame due to incorrectly cancelled timeout
* added simple configuration file examples (in examples subdirectory)
* fixed driver_wext.c to filter wireless events based on ifindex to
avoid interfaces receiving events from other interfaces
* delay sending initial EAPOL-Start couple of seconds to speed up
authentication for the most common case of Authenticator starting
EAP authentication immediately after association
2005-09-25 - v0.4.5
* added a workaround for clearing keys with ndiswrapper to allow
roaming from WPA enabled AP to plaintext one
* added docbook documentation (doc/docbook) that can be used to
generate, e.g., man pages
* l2_packet_linux: use socket type SOCK_DGRAM instead of SOCK_RAW for
PF_PACKET in order to prepare for network devices that do not use
- Ethernet headers (e.g., network stack with native IEEE 802.11 frames)
+ Ethernet headers (e.g., network stack that includes IEEE 802.11
+ header in the frames)
* use receipt of EAPOL-Key frame as a lower layer success indication
for EAP state machine to allow recovery from dropped EAP-Success
frame
* cleaned up internal EAPOL frame processing by not including link
layer (Ethernet) header during WPA and EAPOL/EAP processing; this
header is added only when transmitted the frame; this makes it easier
to use wpa_supplicant on link layers that use different header than
Ethernet
* updated EAP-PSK to use draft 9 by default since this can now be
tested with hostapd; removed support for draft 3, including
server_nai configuration option from network blocks
* driver_wired: add PAE address to the multicast address list in order
to be able to receive EAPOL frames with drivers that do not include
these multicast addresses by default
* driver_wext: add support for WE-19
* added support for multiple configuration backends (CONFIG_BACKEND
option); currently, only 'file' is supported (i.e., the format used
in wpa_supplicant.conf)
* added support for updating configuration ('wpa_cli save_config');
this is disabled by default and can be enabled with global
update_config=1 variable in wpa_supplicant.conf; this allows wpa_cli
and wpa_gui to store the configuration changes in a permanent store
* added GET_NETWORK ctrl_iface command
(e.g., 'wpa_cli get_network 0 ssid')
2005-08-21 - v0.4.4
* replaced OpenSSL patch for EAP-FAST support
(openssl-tls-extensions.patch) with a more generic and correct
patch (the new patch is not compatible with the previous one, so the
OpenSSL library will need to be patched with the new patch in order
to be able to build wpa_supplicant with EAP-FAST support)
* added support for using Windows certificate store (through CryptoAPI)
for client certificate and private key operations (EAP-TLS)
(see wpa_supplicant.conf for more information on how to configure
this with private_key)
* ported wpa_gui to Windows
* added Qt4 version of wpa_gui (wpa_gui-qt4 directory); this can be
built with the open source version of the Qt4 for Windows
* allow non-WPA modes (e.g., IEEE 802.1X with dynamic WEP) to be used
with drivers that do not support WPA
* ndis_events: fixed Windows 2000 support
* added support for enabling/disabling networks from the list of all
configured networks ('wpa_cli enable_network <network id>' and
'wpa_cli disable_network <network id>')
* added support for adding and removing network from the current
configuration ('wpa_cli add_network' and 'wpa_cli remove_network
<network id>'); added networks are disabled by default and they can
be enabled with enable_network command once the configuration is done
for the new network; note: configuration file is not yet updated, so
these new networks are lost when wpa_supplicant is restarted
* added support for setting network configuration parameters through
the control interface, for example:
wpa_cli set_network 0 ssid "\"my network\""
* fixed parsing of strings that include both " and # within double
quoted area (e.g., "start"#end")
* added EAP workaround for PEAP session resumption: allow outer,
i.e., not tunneled, EAP-Success to terminate session since; this can
be disabled with eap_workaround=0
(this was allowed for PEAPv1 before, but now it is also allowed for
PEAPv0 since at least one RADIUS authentication server seems to be
doing this for PEAPv0, too)
* wpa_gui: added preliminary support for adding new networks to the
wpa_supplicant configuration (double click on the scan results to
open network configuration)
2005-06-26 - v0.4.3
* removed interface for external EAPOL/EAP supplicant (e.g.,
Xsupplicant), (CONFIG_XSUPPLICANT_IFACE) since it is not required
anymore and is unlikely to be used by anyone
* driver_ndis: fixed WinPcap 3.0 support
* fixed build with CONFIG_DNET_PCAP=y on Linux
* l2_packet: moved different implementations into separate files
(l2_packet_*.c)
2005-06-12 - v0.4.2
* driver_ipw: updated driver structures to match with ipw2200-1.0.4
(note: ipw2100-1.1.0 is likely to require an update to work with
this)
* added support for using ap_scan=2 mode with multiple network blocks;
wpa_supplicant will go through the networks one by one until the
driver reports a successful association; this uses the same order for
networks as scan_ssid=1 scans, i.e., the priority field is ignored
and the network block order in the file is used instead
* fixed a potential issue in RSN pre-authentication ending up using
freed memory if pre-authentication times out
* added support for matching alternative subject name extensions of the
authentication server certificate; new configuration variables
altsubject_match and altsubject_match2
* driver_ndis: added support for IEEE 802.1X authentication with wired
NDIS drivers
* added support for querying private key password (EAP-TLS) through the
control interface (wpa_cli/wpa_gui) if one is not included in the
configuration file
* driver_broadcom: fixed couple of memory leaks in scan result
processing
* EAP-PAX is now registered as EAP type 46
* fixed EAP-PAX MAC calculation
* fixed EAP-PAX CK and ICK key derivation
* added support for using password with EAP-PAX (as an alternative to
entering key with eappsk); SHA-1 hash of the password will be used as
the key in this case
* added support for arbitrary driver interface parameters through the
configuration file with a new driver_param field; this adds a new
driver_ops function set_param()
* added possibility to override l2_packet module with driver interface
API (new send_eapol handler); this can be used to implement driver
specific TX/RX functions for EAPOL frames
* fixed ctrl_interface_group processing for the case where gid is
entered as a number, not group name
* driver_test: added support for testing hostapd with wpa_supplicant
by using test driver interface without any kernel drivers or network
cards
2005-05-22 - v0.4.1
* driver_madwifi: fixed WPA/WPA2 mode configuration to allow EAPOL
packets to be encrypted; this was apparently broken by the changed
ioctl order in v0.4.0
* driver_madwifi: added preliminary support for compiling against 'BSD'
branch of madwifi CVS tree
* added support for EAP-MSCHAPv2 password retries within the same EAP
authentication session
* added support for password changes with EAP-MSCHAPv2 (used when the
password has expired)
* added support for reading additional certificates from PKCS#12 files
and adding them to the certificate chain
* fixed association with IEEE 802.1X (no WPA) when dynamic WEP keys
were used
* fixed a possible double free in EAP-TTLS fast-reauthentication when
identity or password is entered through control interface
* display EAP Notification messages to user through control interface
with "CTRL-EVENT-EAP-NOTIFICATION" prefix
* added GUI version of wpa_cli, wpa_gui; this is not build
automatically with 'make'; use 'make wpa_gui' to build (this requires
Qt development tools)
* added 'disconnect' command to control interface for setting
wpa_supplicant in state where it will not associate before
'reassociate' command has been used
* added support for selecting a network from the list of all configured
networks ('wpa_cli select_network <network id>'; this disabled all
other networks; to re-enable, 'wpa_cli select_network any')
* added support for getting scan results through control interface
* added EAP workaround for PEAPv1 session resumption: allow outer,
i.e., not tunneled, EAP-Success to terminate session since; this can
be disabled with eap_workaround=0
2005-04-25 - v0.4.0 (beginning of 0.4.x development releases)
* added a new build time option, CONFIG_NO_STDOUT_DEBUG, that can be
used to reduce the size of the wpa_supplicant considerably if
debugging code is not needed
* fixed EAPOL-Key validation to drop packets with invalid Key Data
Length; such frames could have crashed wpa_supplicant due to buffer
overflow
* added support for wired authentication (IEEE 802.1X on wired
Ethernet); driver interface 'wired'
* obsoleted set_wpa() handler in the driver interface API (it can be
replaced by moving enable/disable functionality into init()/deinit())
(calls to set_wpa() are still present for backwards compatibility,
but they may be removed in the future)
* driver_madwifi: fixed association in plaintext mode
* modified the EAP workaround that accepts EAP-Success with incorrect
Identifier to be even less strict about verification in order to
interoperate with some authentication servers
* added support for sending TLS alerts
* added support for 'any' SSID wildcard; if ssid is not configured or
is set to an empty string, any SSID will be accepted for non-WPA AP
* added support for asking PIN (for SIM) from frontends (e.g.,
wpa_cli); if a PIN is needed, but not included in the configuration
file, a control interface request is sent and EAP processing is
delayed until the PIN is available
* added support for using external devices (e.g., a smartcard) for
private key operations in EAP-TLS (CONFIG_SMARTCARD=y in .config);
new wpa_supplicant.conf variables:
- global: opensc_engine_path, pkcs11_engine_path, pkcs11_module_path
- network: engine, engine_id, key_id
* added experimental support for EAP-PAX
* added monitor mode for wpa_cli (-a<path to a program to run>) that
allows external commands (e.g., shell scripts) to be run based on
wpa_supplicant events, e.g., when authentication has been completed
and data connection is ready; other related wpa_cli arguments:
-B (run in background), -P (write PID file); wpa_supplicant has a new
command line argument (-W) that can be used to make it wait until a
control interface command is received in order to avoid missing
events
* added support for opportunistic WPA2 PMKSA key caching (disabled by
default, can be enabled with proactive_key_caching=1)
* fixed RSN IE in 4-Way Handshake message 2/4 for the case where
Authenticator rejects PMKSA caching attempt and the driver is not
using assoc_info events
* added -P<pid file> argument for wpa_supplicant to write the current
process id into a file
2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases)
* added new phase1 option parameter, include_tls_length=1, to force
wpa_supplicant to add TLS Message Length field to all TLS messages
even if the packet is not fragmented; this may be needed with some
authentication servers
* fixed WPA/RSN IE verification in message 3 of 4-Way Handshake when
using drivers that take care of AP selection (e.g., when using
ap_scan=2)
* fixed reprocessing of pending request after ctrl_iface requests for
identity/password/otp
* fixed ctrl_iface requests for identity/password/otp in Phase 2 of
EAP-PEAP and EAP-TTLS
* all drivers using driver_wext: set interface up and select Managed
mode when starting wpa_supplicant; set interface down when exiting
* renamed driver_ipw2100.c to driver_ipw.c since it now supports both
ipw2100 and ipw2200; please note that this also changed the
configuration variable in .config to CONFIG_DRIVER_IPW
2005-01-24 - v0.3.6
* fixed a busy loop introduced in v0.3.5 for scan result processing
when no matching AP is found
2005-01-23 - v0.3.5
* added a workaround for an interoperability issue with a Cisco AP
when using WPA2-PSK
* fixed non-WPA IEEE 802.1X to use the same authentication timeout as
WPA with IEEE 802.1X (i.e., timeout 10 -> 70 sec to allow
retransmission of dropped frames)
* fixed issues with 64-bit CPUs and SHA1 cleanup in previous version
(e.g., segfault when processing EAPOL-Key frames)
* fixed EAP workaround and fast reauthentication configuration for
RSN pre-authentication; previously these were disabled and
pre-authentication would fail if the used authentication server
requires EAP workarounds
* added support for blacklisting APs that fail or timeout
authentication in ap_scan=1 mode so that all APs are tried in cases
where the ones with strongest signal level are failing authentication
* fixed CA certificate loading after a failed EAP-TLS/PEAP/TTLS
authentication attempt
* allow EAP-PEAP/TTLS fast reauthentication only if Phase 2 succeeded
in the previous authentication (previously, only Phase 1 success was
verified)
2005-01-09 - v0.3.4
* added preliminary support for IBSS (ad-hoc) mode configuration
(mode=1 in network block); this included a new key_mgmt mode
WPA-NONE, i.e., TKIP or CCMP with a fixed key (based on psk) and no
key management; see wpa_supplicant.conf for more details and an
example on how to configure this (note: this is currently implemented
only for driver_hostapd.c, but the changes should be trivial to add
in associate() handler for other drivers, too (assuming the driver
supports WPA-None)
* added preliminary port for native Windows (i.e., no cygwin) using
mingw
2005-01-02 - v0.3.3
* added optional support for GNU Readline and History Libraries for
wpa_cli (CONFIG_READLINE)
* cleaned up EAP state machine <-> method interface and number of
small problems with error case processing not terminating on
EAP-Failure but waiting for timeout
* added couple of workarounds for interoperability issues with a
Cisco AP when using WPA2
* added support for EAP-FAST (draft-cam-winget-eap-fast-00.txt);
Note: This requires a patch for openssl to add support for TLS
extensions and number of workarounds for operations without
certificates. Proof of concept type of experimental patch is
included in openssl-tls-extensions.patch.
2004-12-19 - v0.3.2
* fixed private key loading for cases where passphrase is not set
* fixed Windows/cygwin L2 packet handler freeing; previous version
could cause a segfault when RSN pre-authentication was completed
* added support for PMKSA caching with drivers that generate RSN IEs
(e.g., NDIS); currently, this is only implemented in driver_ndis.c,
but similar code can be easily added to driver_ndiswrapper.c once
ndiswrapper gets full support for RSN PMKSA caching
* improved recovery from PMKID mismatches by requesting full EAP
authentication in case of failed PMKSA caching attempt
* driver_ndis: added support for NDIS NdisMIncidateStatus() events
(this requires that ndis_events is ran while wpa_supplicant is
running)
* driver_ndis: use ADD_WEP/REMOVE_WEP when configuring WEP keys
* added support for driver interfaces to replace the interface name
based on driver/OS specific mapping, e.g., in case of driver_ndis,
this allows the beginning of the adapter description to be used as
the interface name
* added support for CR+LF (Windows-style) line ends in configuration
file
* driver_ndis: enable radio before starting scanning, disable radio
when exiting
* modified association event handler to set portEnabled = FALSE before
clearing port Valid in order to reset EAP state machine and avoid
problems with new authentication getting ignored because of state
machines ending up in AUTHENTICATED/SUCCESS state based on old
information
* added support for driver events to add PMKID candidates in order to
allow drivers to give priority to most likely roaming candidates
* driver_hostap: moved PrivacyInvoked configuration to associate()
function so that this will not be set for plaintext connections
* added KEY_MGMT_802_1X_NO_WPA as a new key_mgmt type so that driver
interface can distinguish plaintext and IEEE 802.1X (no WPA)
authentication
* fixed static WEP key configuration to use broadcast/default type for
all keys (previously, the default TX key was configured as pairwise/
unicast key)
* driver_ndis: added legacy WPA capability detection for non-WPA2
drivers
* added support for setting static WEP keys for IEEE 802.1X without
dynamic WEP keying (eapol_flags=0)
2004-12-12 - v0.3.1
* added support for reading PKCS#12 (PFX) files (as a replacement for
PEM/DER) to get certificate and private key (CONFIG_PKCS12)
* fixed compilation with CONFIG_PCSC=y
* added new ap_scan mode, ap_scan=2, for drivers that take care of
association, but need to be configured with security policy and SSID,
e.g., ndiswrapper and NDIS driver; this mode should allow such
drivers to work with hidden SSIDs and optimized roaming; when
ap_scan=2 is used, only the first network block in the configuration
file is used and this configuration should have explicit security
policy (i.e., only one option in the lists) for key_mgmt, pairwise,
group, proto variables
* added experimental port of wpa_supplicant for Windows
- driver_ndis.c driver interface (NDIS OIDs)
- currently, this requires cygwin and WinPcap
- small utility, win_if_list, can be used to get interface name
* control interface can now be removed at build time; add
CONFIG_CTRL_IFACE=y to .config to maintain old functionality
* optional Xsupplicant interface can now be removed at build time;
(CONFIG_XSUPPLICANT_IFACE=y in .config to bring it back)
* added auth_alg to driver interface associate() parameters to make it
easier for drivers to configure authentication algorithm as part of
the association
2004-12-05 - v0.3.0 (beginning of 0.3.x development releases)
* driver_broadcom: added new driver interface for Broadcom wl.o driver
(a generic driver for Broadcom IEEE 802.11a/g cards)
* wpa_cli: fixed parsing of -p <path> command line argument
* PEAPv1: fixed tunneled EAP-Success reply handling to reply with TLS
ACK, not tunneled EAP-Success (of which only the first byte was
actually send due to a bug in previous code); this seems to
interoperate with most RADIUS servers that implements PEAPv1
* PEAPv1: added support for terminating PEAP authentication on tunneled
EAP-Success message; this can be configured by adding
peap_outer_success=0 on phase1 parameters in wpa_supplicant.conf
(some RADIUS servers require this whereas others require a tunneled
reply
* PEAPv1: changed phase1 option peaplabel to use default to 0, i.e., to
the old label for key derivation; previously, the default was 1,
but it looks like most existing PEAPv1 implementations use the old
label which is thus more suitable default option
* added support for EAP-PSK (draft-bersani-eap-psk-03.txt)
* fixed parsing of wep_tx_keyidx
* added support for configuring list of allowed Phase 2 EAP types
(for both EAP-PEAP and EAP-TTLS) instead of only one type
* added support for configuring IEEE 802.11 authentication algorithm
(auth_alg; mainly for using Shared Key authentication with static
WEP keys)
* added support for EAP-AKA (with UMTS SIM)
* fixed couple of errors in PCSC handling that could have caused
random-looking errors for EAP-SIM
* added support for EAP-SIM pseudonyms and fast re-authentication
* added support for EAP-TLS/PEAP/TTLS fast re-authentication (TLS
session resumption)
* added support for EAP-SIM with two challenges
(phase1="sim_min_num_chal=3" can be used to require three challenges)
* added support for configuring DH/DSA parameters for an ephemeral DH
key exchange (EAP-TLS/PEAP/TTLS) using new configuration parameters
dh_file and dh_file2 (phase 2); this adds support for using DSA keys
and optional DH key exchange to achieve forward secracy with RSA keys
* added support for matching subject of the authentication server
certificate with a substring when using EAP-TLS/PEAP/TTLS; new
configuration variables subject_match and subject_match2
* changed SSID configuration in driver_wext.c (used by many driver
interfaces) to use ssid_len+1 as the length for SSID since some Linux
drivers expect this
* fixed couple of unaligned reads in scan result parsing to fix WPA
connection on some platforms (e.g., ARM)
* added driver interface for Intel ipw2100 driver
* added support for LEAP with WPA
* added support for larger scan results report (old limit was 4 kB of
data, i.e., about 35 or so APs) when using Linux wireless extensions
v17 or newer
* fixed a bug in PMKSA cache processing: skip sending of EAPOL-Start
only if there is a PMKSA cache entry for the current AP
* fixed error handling for case where reading of scan results fails:
must schedule a new scan or wpa_supplicant will remain waiting
forever
* changed debug output to remove shared password/key material by
default; all key information can be included with -K command line
argument to match the previous behavior
* added support for timestamping debug log messages (disabled by
default, can be enabled with -t command line argument)
* set pairwise/group cipher suite for non-WPA IEEE 802.1X to WEP-104
if keys are not configured to be used; this fixes IEEE 802.1X mode
with drivers that use this information to configure whether Privacy
bit can be in Beacon frames (e.g., ndiswrapper)
* avoid clearing driver keys if no keys have been configured since last
key clear request; this seems to improve reliability of group key
handshake for ndiswrapper & NDIS driver which seems to be suffering
of some kind of timing issue when the keys are cleared again after
association
* changed driver interface API:
- WPA_SUPPLICANT_DRIVER_VERSION define can be used to determine which
version is being used (now, this is set to 2; previously, it was
not defined)
- pass pointer to private data structure to all calls
- the new API is not backwards compatible; all in-tree driver
interfaces has been converted to the new API
* added support for controlling multiple interfaces (radios) per
wpa_supplicant process; each interface needs to be listed on the
command line (-c, -i, -D arguments) with -N as a separator
(-cwpa1.conf -iwlan0 -Dhostap -N -cwpa2.conf -iath0 -Dmadwifi)
* added a workaround for EAP servers that incorrectly use same Id for
sequential EAP packets
* changed libpcap/libdnet configuration to use .config variable,
CONFIG_DNET_PCAP, instead of requiring Makefile modification
* improved downgrade attack detection in IE verification of msg 3/4:
verify both WPA and RSN IEs, if present, not only the selected one;
reject the AP if an RSN IE is found in msg 3/4, but not in Beacon or
Probe Response frame, and RSN is enabled in wpa_supplicant
configuration
* fixed WPA msg 3/4 processing to allow Key Data field contain other
IEs than just one WPA IE
* added support for FreeBSD and driver interface for the BSD net80211
layer (CONFIG_DRIVER_BSD=y in .config); please note that some of the
required kernel mods have not yet been committed
* made EAP workarounds configurable; enabled by default, can be
disabled with network block option eap_workaround=0
2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases)
* resolved couple of interoperability issues with EAP-PEAPv1 and
Phase 2 (inner EAP) fragment reassembly
* driver_madwifi: fixed WEP key configuration for IEEE 802.1X when the
AP is using non-zero key index for the unicast key and key index zero
for the broadcast key
* driver_hostap: fixed IEEE 802.1X WEP key updates and
re-authentication by allowing unencrypted EAPOL frames when not using
WPA
* added a new driver interface, 'wext', which uses only standard,
driver independent functionality in Linux wireless extensions;
currently, this can be used only for non-WPA IEEE 802.1X mode, but
eventually, this is to be extended to support full WPA/WPA2 once
Linux wireless extensions get support for this
* added support for mode in which the driver is responsible for AP
scanning and selection; this is disabled by default and can be
enabled with global ap_scan=0 variable in wpa_supplicant.conf;
this mode can be used, e.g., with generic 'wext' driver interface to
use wpa_supplicant as IEEE 802.1X Supplicant with any Linux driver
supporting wireless extensions.
* driver_madwifi: fixed WPA2 configuration and scan_ssid=1 (e.g.,
operation with an AP that does not include SSID in the Beacon frames)
* added support for new EAP authentication methods:
EAP-TTLS/EAP-OTP, EAP-PEAPv0/OTP, EAP-PEAPv1/OTP, EAP-OTP
* added support for asking one-time-passwords from frontends (e.g.,
wpa_cli); this 'otp' command works otherwise like 'password' command,
but the password is used only once and the frontend will be asked for
a new password whenever a request from authenticator requires a
password; this can be used with both EAP-OTP and EAP-GTC
* changed wpa_cli to automatically re-establish connection so that it
does not need to be re-started when wpa_supplicant is terminated and
started again
* improved user data (identity/password/otp) requests through
frontends: process pending EAPOL packets after getting new
information so that full authentication does not need to be
restarted; in addition, send pending requests again whenever a new
frontend is attached
* changed control frontends to use a new directory for socket files to
make it easier for wpa_cli to automatically select between interfaces
and to provide access control for the control interface;
wpa_supplicant.conf: ctrl_interface is now a path
(/var/run/wpa_supplicant is the recommended path) and
ctrl_interface_group can be used to select which group gets access to
the control interface;
wpa_cli: by default, try to connect to the first interface available
in /var/run/wpa_supplicant; this path can be overridden with -p option
and an interface can be selected with -i option (i.e., in most common
cases, wpa_cli does not need to get any arguments)
* added support for LEAP
* added driver interface for Linux ndiswrapper
* added priority option for network blocks in the configuration file;
this allows networks to be grouped based on priority (the scan
results are searched for matches with network blocks in this order)
2004-06-20 - v0.2.3
* sort scan results to improve AP selection
* fixed control interface socket removal for some error cases
* improved scan requesting and authentication timeout
* small improvements/bug fixes for EAP-MSCHAPv2, EAP-PEAP, and
TLS processing
* PEAP version can now be forced with phase1="peapver=<ver>"
(mostly for testing; by default, the highest version supported by
both the Supplicant and Authentication Server is selected
automatically)
* added support for madwifi driver (Atheros ar521x)
* added a workaround for cases where AP sets Install Tx/Rx bit for
WPA Group Key messages when pairwise keys are used (without this,
the Group Key would be used for Tx and the AP would drop frames
from the station)
* added GSM SIM/USIM interface for GSM authentication algorithm for
EAP-SIM; this requires pcsc-lite
* added support for ATMEL AT76C5XXx driver
* fixed IEEE 802.1X WEP key derivation in the case where Authenticator
does not include key data in the EAPOL-Key frame (i.e., part of
EAP keying material is used as data encryption key)
* added support for using plaintext and static WEP networks
(key_mgmt=NONE)
2004-05-31 - v0.2.2
* added support for new EAP authentication methods:
EAP-TTLS/EAP-MD5-Challenge
EAP-TTLS/EAP-GTC
EAP-TTLS/EAP-MSCHAPv2
EAP-TTLS/EAP-TLS
EAP-TTLS/MSCHAPv2
EAP-TTLS/MSCHAP
EAP-TTLS/PAP
EAP-TTLS/CHAP
EAP-PEAP/TLS
EAP-PEAP/GTC
EAP-PEAP/MD5-Challenge
EAP-GTC
EAP-SIM (not yet complete; needs GSM/SIM authentication interface)
* added support for anonymous identity (to be used when identity is
sent in plaintext; real identity will be used within TLS protected
tunnel (e.g., with EAP-TTLS)
* added event messages from wpa_supplicant to frontends, e.g., wpa_cli
* added support for requesting identity and password information using
control interface; in other words, the password for EAP-PEAP or
EAP-TTLS does not need to be included in the configuration file since
a frontand (e.g., wpa_cli) can ask it from the user
* improved RSN pre-authentication to use a candidate list and process
all candidates from each scan; not only one per scan
* fixed RSN IE and WPA IE capabilities field parsing
* ignore Tx bit in GTK IE when Pairwise keys are used
* avoid making new scan requests during IEEE 802.1X negotiation
* use openssl/libcrypto for MD5 and SHA-1 when compiling wpa_supplicant
with TLS support (this replaces the included implementation with
library code to save about 8 kB since the library code is needed
anyway for TLS)
* fixed WPA-PSK only mode when compiled without IEEE 802.1X support
(i.e., without CONFIG_IEEE8021X_EAPOL=y in .config)
2004-05-06 - v0.2.1
* added support for internal IEEE 802.1X (actually, IEEE 802.1aa/D6.1)
Supplicant
- EAPOL state machines for Supplicant [IEEE 802.1aa/D6.1]
- EAP peer state machine [draft-ietf-eap-statemachine-02.pdf]
- EAP-MD5 (cannot be used with WPA-RADIUS)
[draft-ietf-eap-rfc2284bis-09.txt]
- EAP-TLS [RFC 2716]
- EAP-MSCHAPv2 (currently used only with EAP-PEAP)
- EAP-PEAP/MSCHAPv2 [draft-josefsson-pppext-eap-tls-eap-07.txt]
[draft-kamath-pppext-eap-mschapv2-00.txt]
(PEAP version 0, 1, and parts of 2; only 0 and 1 are enabled by
default; tested with FreeRADIUS, Microsoft IAS, and Funk Odyssey)
- new configuration file options: eap, identity, password, ca_cert,
client_cert, privatekey, private_key_passwd
- Xsupplicant is not required anymore, but it can be used by
disabling the internal IEEE 802.1X Supplicant with -e command line
option
- this code is not included in the default build; Makefile need to
be edited for this (uncomment lines for selected functionality)
- EAP-TLS and EAP-PEAP require openssl libraries
* use module prefix in debug messages (WPA, EAP, EAP-TLS, ..)
* added support for non-WPA IEEE 802.1X mode with dynamic WEP keys
(i.e., complete IEEE 802.1X/EAP authentication and use IEEE 802.1X
EAPOL-Key frames instead of WPA key handshakes)
* added support for IEEE 802.11i/RSN (WPA2)
- improved PTK Key Handshake
- PMKSA caching, pre-authentication
* fixed wpa_supplicant to ignore possible extra data after WPA
EAPOL-Key packets (this fixes 'Invalid EAPOL-Key MIC when using
TPTK' error from message 3 of 4-Way Handshake in case the AP
includes extra data after the EAPOL-Key)
* added interface for external programs (frontends) to control
wpa_supplicant
- CLI example (wpa_cli) with interactive mode and command line
mode
- replaced SIGUSR1 status/statistics with the new control interface
* made some feature compile time configurable
- .config file for make
- driver interfaces (hostap, hermes, ..)
- EAPOL/EAP functions
2004-02-15 - v0.2.0
* Initial version of wpa_supplicant
diff --git a/contrib/wpa/wpa_supplicant/Makefile b/contrib/wpa/wpa_supplicant/Makefile
index 271f2aab3118..ce1c8b2e3366 100644
--- a/contrib/wpa/wpa_supplicant/Makefile
+++ b/contrib/wpa/wpa_supplicant/Makefile
@@ -1,2073 +1,2071 @@
BINALL=wpa_supplicant wpa_cli
ifndef CONFIG_NO_WPA_PASSPHRASE
BINALL += wpa_passphrase
endif
ALL = $(BINALL)
ALL += systemd/wpa_supplicant.service
ALL += systemd/wpa_supplicant@.service
ALL += systemd/wpa_supplicant-nl80211@.service
ALL += systemd/wpa_supplicant-wired@.service
ALL += dbus/fi.w1.wpa_supplicant1.service
ifdef CONFIG_BUILD_WPA_CLIENT_SO
ALL += libwpa_client.so
endif
EXTRA_TARGETS=dynamic_eap_methods
CONFIG_FILE=.config
include ../src/build.rules
ifdef LIBS
# If LIBS is set with some global build system defaults, clone those for
# LIBS_c and LIBS_p to cover wpa_passphrase and wpa_cli as well.
ifndef LIBS_c
LIBS_c := $(LIBS)
endif
ifndef LIBS_p
LIBS_p := $(LIBS)
endif
endif
-export LIBDIR ?= /usr/local/lib/
-export INCDIR ?= /usr/local/include/
-export BINDIR ?= /usr/local/sbin/
+export LIBDIR ?= /usr/local/lib
+export INCDIR ?= /usr/local/include
+export BINDIR ?= /usr/local/sbin
PKG_CONFIG ?= pkg-config
CFLAGS += $(EXTRA_CFLAGS)
CFLAGS += -I$(abspath ../src)
CFLAGS += -I$(abspath ../src/utils)
ifndef CONFIG_NO_GITVER
# Add VERSION_STR postfix for builds from a git repository
ifeq ($(wildcard ../.git),../.git)
GITVER := $(shell git describe --dirty=+)
ifneq ($(GITVER),)
CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
endif
endif
endif
ifdef CONFIG_TESTING_OPTIONS
CFLAGS += -DCONFIG_TESTING_OPTIONS
CONFIG_WPS_TESTING=y
CONFIG_TDLS_TESTING=y
endif
mkconfig:
@if [ -f .config ]; then \
echo '.config exists - did not replace it'; \
exit 1; \
fi
echo CONFIG_DRIVER_HOSTAP=y >> .config
echo CONFIG_DRIVER_WEXT=y >> .config
$(DESTDIR)$(BINDIR)/%: %
install -D $(<) $(@)
install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL))
$(MAKE) -C ../src install
ifdef CONFIG_BUILD_WPA_CLIENT_SO
install -m 0644 -D libwpa_client.so $(DESTDIR)/$(LIBDIR)/libwpa_client.so
install -m 0644 -D ../src/common/wpa_ctrl.h $(DESTDIR)/$(INCDIR)/wpa_ctrl.h
endif
if ls eap_*.so >/dev/null 2>&1; then \
install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
; fi
ifdef CONFIG_FIPS
CONFIG_NO_RANDOM_POOL=
CONFIG_OPENSSL_CMAC=y
endif
OBJS = config.o
OBJS += notify.o
OBJS += bss.o
OBJS += eap_register.o
OBJS += ../src/utils/common.o
OBJS += ../src/utils/config.o
OBJS += ../src/utils/wpa_debug.o
OBJS += ../src/utils/wpabuf.o
OBJS += ../src/utils/bitfield.o
OBJS += ../src/utils/ip_addr.o
OBJS += ../src/utils/crc32.o
OBJS += op_classes.o
OBJS += rrm.o
OBJS += twt.o
OBJS += robust_av.o
OBJS_p = wpa_passphrase.o
OBJS_p += ../src/utils/common.o
OBJS_p += ../src/utils/wpa_debug.o
OBJS_p += ../src/utils/wpabuf.o
OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o
OBJS_c += ../src/utils/wpa_debug.o
OBJS_c += ../src/utils/common.o
OBJS_c += ../src/common/cli.o
OBJS += wmm_ac.o
ifndef CONFIG_OS
ifdef CONFIG_NATIVE_WINDOWS
CONFIG_OS=win32
else
CONFIG_OS=unix
endif
endif
ifeq ($(CONFIG_OS), internal)
CFLAGS += -DOS_NO_C_LIB_DEFINES
endif
OBJS += ../src/utils/os_$(CONFIG_OS).o
OBJS_p += ../src/utils/os_$(CONFIG_OS).o
OBJS_c += ../src/utils/os_$(CONFIG_OS).o
ifdef CONFIG_WPA_TRACE
CFLAGS += -DWPA_TRACE
OBJS += ../src/utils/trace.o
OBJS_p += ../src/utils/trace.o
OBJS_c += ../src/utils/trace.o
OBJS_priv += ../src/utils/trace.o
LIBCTRL += ../src/utils/trace.o
LIBCTRLSO += ../src/utils/trace.c
LDFLAGS += -rdynamic
CFLAGS += -funwind-tables
ifdef CONFIG_WPA_TRACE_BFD
CFLAGS += -DPACKAGE="wpa_supplicant" -DWPA_TRACE_BFD
LIBS += -lbfd -ldl -liberty -lz
LIBS_p += -lbfd -ldl -liberty -lz
LIBS_c += -lbfd -ldl -liberty -lz
endif
endif
ifndef CONFIG_ELOOP
CONFIG_ELOOP=eloop
endif
OBJS += ../src/utils/$(CONFIG_ELOOP).o
OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
ifndef CONFIG_OSX
ifeq ($(CONFIG_ELOOP), eloop)
# Using glibc < 2.17 requires -lrt for clock_gettime()
# OS X has an alternate implementation
LIBS += -lrt
LIBS_c += -lrt
LIBS_p += -lrt
endif
endif
ifdef CONFIG_ELOOP_POLL
CFLAGS += -DCONFIG_ELOOP_POLL
endif
ifdef CONFIG_ELOOP_EPOLL
CFLAGS += -DCONFIG_ELOOP_EPOLL
endif
ifdef CONFIG_ELOOP_KQUEUE
CFLAGS += -DCONFIG_ELOOP_KQUEUE
endif
ifdef CONFIG_EAPOL_TEST
CFLAGS += -Werror -DEAPOL_TEST
endif
ifdef CONFIG_CODE_COVERAGE
CFLAGS += -O0 -fprofile-arcs -ftest-coverage
LIBS += -lgcov
LIBS_c += -lgcov
LIBS_p += -lgcov
endif
ifdef CONFIG_HT_OVERRIDES
CFLAGS += -DCONFIG_HT_OVERRIDES
endif
ifdef CONFIG_VHT_OVERRIDES
CFLAGS += -DCONFIG_VHT_OVERRIDES
endif
ifdef CONFIG_HE_OVERRIDES
CFLAGS += -DCONFIG_HE_OVERRIDES
endif
ifndef CONFIG_BACKEND
CONFIG_BACKEND=file
endif
ifeq ($(CONFIG_BACKEND), file)
OBJS += config_file.o
ifndef CONFIG_NO_CONFIG_BLOBS
NEED_BASE64=y
endif
CFLAGS += -DCONFIG_BACKEND_FILE
endif
ifeq ($(CONFIG_BACKEND), winreg)
OBJS += config_winreg.o
endif
ifeq ($(CONFIG_BACKEND), none)
OBJS += config_none.o
endif
ifdef CONFIG_NO_CONFIG_WRITE
CFLAGS += -DCONFIG_NO_CONFIG_WRITE
endif
ifdef CONFIG_NO_CONFIG_BLOBS
CFLAGS += -DCONFIG_NO_CONFIG_BLOBS
endif
ifdef CONFIG_NO_SCAN_PROCESSING
CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
endif
ifdef CONFIG_SUITEB
CFLAGS += -DCONFIG_SUITEB
endif
ifdef CONFIG_SUITEB192
CFLAGS += -DCONFIG_SUITEB192
NEED_SHA384=y
endif
ifdef CONFIG_OCV
CFLAGS += -DCONFIG_OCV
OBJS += ../src/common/ocv.o
endif
ifdef CONFIG_IEEE80211R
CFLAGS += -DCONFIG_IEEE80211R
OBJS += ../src/rsn_supp/wpa_ft.o
endif
ifdef CONFIG_MESH
NEED_80211_COMMON=y
NEED_AES_SIV=y
CONFIG_SAE=y
CONFIG_AP=y
CFLAGS += -DCONFIG_MESH
OBJS += mesh.o
OBJS += mesh_mpm.o
OBJS += mesh_rsn.o
endif
ifdef CONFIG_SAE
CFLAGS += -DCONFIG_SAE
OBJS += ../src/common/sae.o
ifdef CONFIG_SAE_PK
CFLAGS += -DCONFIG_SAE_PK
OBJS += ../src/common/sae_pk.o
endif
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
NEED_DRAGONFLY=y
ifdef CONFIG_TESTING_OPTIONS
NEED_DH_GROUPS_ALL=y
endif
endif
ifdef CONFIG_DPP
CFLAGS += -DCONFIG_DPP
OBJS += ../src/common/dpp.o
OBJS += ../src/common/dpp_auth.o
OBJS += ../src/common/dpp_backup.o
OBJS += ../src/common/dpp_crypto.o
OBJS += ../src/common/dpp_pkex.o
OBJS += ../src/common/dpp_reconfig.o
OBJS += ../src/common/dpp_tcp.o
OBJS += dpp_supplicant.o
NEED_AES_SIV=y
NEED_HMAC_SHA256_KDF=y
NEED_HMAC_SHA384_KDF=y
NEED_HMAC_SHA512_KDF=y
NEED_SHA384=y
NEED_SHA512=y
NEED_ECC=y
NEED_JSON=y
NEED_GAS_SERVER=y
NEED_BASE64=y
NEED_ASN1=y
ifdef CONFIG_DPP2
CFLAGS += -DCONFIG_DPP2
endif
endif
ifdef CONFIG_OWE
CFLAGS += -DCONFIG_OWE
NEED_ECC=y
NEED_HMAC_SHA256_KDF=y
NEED_HMAC_SHA384_KDF=y
NEED_HMAC_SHA512_KDF=y
NEED_SHA384=y
NEED_SHA512=y
endif
ifdef CONFIG_FILS
CFLAGS += -DCONFIG_FILS
NEED_SHA384=y
NEED_AES_SIV=y
ifdef CONFIG_FILS_SK_PFS
CFLAGS += -DCONFIG_FILS_SK_PFS
NEED_ECC=y
endif
endif
ifdef CONFIG_MBO
CONFIG_WNM=y
endif
ifdef CONFIG_WNM
CFLAGS += -DCONFIG_WNM
OBJS += wnm_sta.o
endif
ifdef CONFIG_TDLS
CFLAGS += -DCONFIG_TDLS
OBJS += ../src/rsn_supp/tdls.o
endif
ifdef CONFIG_TDLS_TESTING
CFLAGS += -DCONFIG_TDLS_TESTING
endif
ifdef CONFIG_PMKSA_CACHE_EXTERNAL
CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
endif
ifndef CONFIG_NO_WPA
OBJS += ../src/rsn_supp/wpa.o
OBJS += ../src/rsn_supp/preauth.o
OBJS += ../src/rsn_supp/pmksa_cache.o
OBJS += ../src/rsn_supp/wpa_ie.o
OBJS += ../src/common/wpa_common.o
NEED_AES=y
NEED_SHA1=y
NEED_MD5=y
NEED_RC4=y
else
CFLAGS += -DCONFIG_NO_WPA
ifeq ($(CONFIG_TLS), internal)
NEED_SHA1=y
NEED_MD5=y
endif
endif
ifdef CONFIG_IBSS_RSN
NEED_RSN_AUTHENTICATOR=y
CFLAGS += -DCONFIG_IBSS_RSN
CFLAGS += -DCONFIG_NO_VLAN
OBJS += ibss_rsn.o
endif
ifdef CONFIG_MATCH_IFACE
CFLAGS += -DCONFIG_MATCH_IFACE
endif
ifdef CONFIG_P2P
OBJS += p2p_supplicant.o
OBJS += p2p_supplicant_sd.o
OBJS += ../src/p2p/p2p.o
OBJS += ../src/p2p/p2p_utils.o
OBJS += ../src/p2p/p2p_parse.o
OBJS += ../src/p2p/p2p_build.o
OBJS += ../src/p2p/p2p_go_neg.o
OBJS += ../src/p2p/p2p_sd.o
OBJS += ../src/p2p/p2p_pd.o
OBJS += ../src/p2p/p2p_invitation.o
OBJS += ../src/p2p/p2p_dev_disc.o
OBJS += ../src/p2p/p2p_group.o
OBJS += ../src/ap/p2p_hostapd.o
CFLAGS += -DCONFIG_P2P
NEED_GAS=y
NEED_OFFCHANNEL=y
CONFIG_WPS=y
CONFIG_AP=y
ifdef CONFIG_P2P_STRICT
CFLAGS += -DCONFIG_P2P_STRICT
endif
-endif
-
ifdef CONFIG_WIFI_DISPLAY
CFLAGS += -DCONFIG_WIFI_DISPLAY
OBJS += wifi_display.o
endif
+endif
ifdef CONFIG_PASN
CFLAGS += -DCONFIG_PASN
CFLAGS += -DCONFIG_PTKSA_CACHE
NEED_HMAC_SHA256_KDF=y
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
OBJS += ../src/common/ptksa_cache.o
OBJS += pasn_supplicant.o
endif
ifdef CONFIG_HS20
OBJS += hs20_supplicant.o
CFLAGS += -DCONFIG_HS20
CONFIG_INTERWORKING=y
endif
ifdef CONFIG_INTERWORKING
OBJS += interworking.o
CFLAGS += -DCONFIG_INTERWORKING
NEED_GAS=y
endif
ifdef CONFIG_NO_ROAMING
CFLAGS += -DCONFIG_NO_ROAMING
endif
include ../src/drivers/drivers.mak
ifdef CONFIG_AP
OBJS_d += $(DRV_BOTH_OBJS)
CFLAGS += $(DRV_BOTH_CFLAGS)
LDFLAGS += $(DRV_BOTH_LDFLAGS)
LIBS += $(DRV_BOTH_LIBS)
else
NEED_AP_MLME=
OBJS_d += $(DRV_WPA_OBJS)
CFLAGS += $(DRV_WPA_CFLAGS)
LDFLAGS += $(DRV_WPA_LDFLAGS)
LIBS += $(DRV_WPA_LIBS)
endif
ifndef CONFIG_L2_PACKET
CONFIG_L2_PACKET=linux
endif
OBJS_l2 += ../src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).o
ifeq ($(CONFIG_L2_PACKET), pcap)
ifdef CONFIG_WINPCAP
CFLAGS += -DCONFIG_WINPCAP
LIBS += -lwpcap -lpacket
LIBS_w += -lwpcap
else
LIBS += -ldnet -lpcap
endif
endif
ifeq ($(CONFIG_L2_PACKET), winpcap)
LIBS += -lwpcap -lpacket
LIBS_w += -lwpcap
endif
ifeq ($(CONFIG_L2_PACKET), freebsd)
LIBS += -lpcap
endif
ifdef CONFIG_ERP
CFLAGS += -DCONFIG_ERP
NEED_HMAC_SHA256_KDF=y
endif
ifdef CONFIG_EAP_TLS
# EAP-TLS
ifeq ($(CONFIG_EAP_TLS), dyn)
CFLAGS += -DEAP_TLS_DYNAMIC
EAPDYN += eap_tls.so
else
CFLAGS += -DEAP_TLS
OBJS += ../src/eap_peer/eap_tls.o
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_UNAUTH_TLS
# EAP-UNAUTH-TLS
CFLAGS += -DEAP_UNAUTH_TLS
ifndef CONFIG_EAP_TLS
OBJS += ../src/eap_peer/eap_tls.o
TLS_FUNCS=y
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_PEAP
# EAP-PEAP
SRC_EAP_PEAP = ../src/eap_peer/eap_peap.c ../src/eap_common/eap_peap_common.c
ifeq ($(CONFIG_EAP_PEAP), dyn)
CFLAGS += -DEAP_PEAP_DYNAMIC
EAPDYN += eap_peap.so
else
CFLAGS += -DEAP_PEAP
OBJS += $(patsubst %.c, %.o, $(SRC_EAP_PEAP))
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_TTLS
# EAP-TTLS
ifeq ($(CONFIG_EAP_TTLS), dyn)
CFLAGS += -DEAP_TTLS_DYNAMIC
EAPDYN += eap_ttls.so
else
CFLAGS += -DEAP_TTLS
OBJS += ../src/eap_peer/eap_ttls.o
endif
TLS_FUNCS=y
ifndef CONFIG_FIPS
MS_FUNCS=y
CHAP=y
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_MD5
# EAP-MD5
ifeq ($(CONFIG_EAP_MD5), dyn)
CFLAGS += -DEAP_MD5_DYNAMIC
EAPDYN += eap_md5.so
else
CFLAGS += -DEAP_MD5
OBJS += ../src/eap_peer/eap_md5.o
endif
CHAP=y
CONFIG_IEEE8021X_EAPOL=y
endif
# backwards compatibility for old spelling
ifdef CONFIG_MSCHAPV2
ifndef CONFIG_EAP_MSCHAPV2
CONFIG_EAP_MSCHAPV2=y
endif
endif
ifdef CONFIG_EAP_MSCHAPV2
# EAP-MSCHAPv2
SRC_EAP_MSCHAPV2 = ../src/eap_peer/eap_mschapv2.c ../src/eap_peer/mschapv2.c
ifeq ($(CONFIG_EAP_MSCHAPV2), dyn)
CFLAGS += -DEAP_MSCHAPv2_DYNAMIC
EAPDYN += eap_mschapv2.so
else
CFLAGS += -DEAP_MSCHAPv2
OBJS += $(patsubst %.c, %.o, $(SRC_EAP_MSCHAPV2))
endif
MS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_GTC
# EAP-GTC
ifeq ($(CONFIG_EAP_GTC), dyn)
CFLAGS += -DEAP_GTC_DYNAMIC
EAPDYN += eap_gtc.so
else
CFLAGS += -DEAP_GTC
OBJS += ../src/eap_peer/eap_gtc.o
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_OTP
# EAP-OTP
ifeq ($(CONFIG_EAP_OTP), dyn)
CFLAGS += -DEAP_OTP_DYNAMIC
EAPDYN += eap_otp.so
else
CFLAGS += -DEAP_OTP
OBJS += ../src/eap_peer/eap_otp.o
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_SIM
# EAP-SIM
ifeq ($(CONFIG_EAP_SIM), dyn)
CFLAGS += -DEAP_SIM_DYNAMIC
EAPDYN += eap_sim.so
else
CFLAGS += -DEAP_SIM
OBJS += ../src/eap_peer/eap_sim.o
endif
CONFIG_IEEE8021X_EAPOL=y
CONFIG_EAP_SIM_COMMON=y
NEED_AES_CBC=y
endif
ifdef CONFIG_EAP_LEAP
# EAP-LEAP
ifeq ($(CONFIG_EAP_LEAP), dyn)
CFLAGS += -DEAP_LEAP_DYNAMIC
EAPDYN += eap_leap.so
else
CFLAGS += -DEAP_LEAP
OBJS += ../src/eap_peer/eap_leap.o
endif
MS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_PSK
# EAP-PSK
SRC_EAP_PSK = ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c
ifeq ($(CONFIG_EAP_PSK), dyn)
CFLAGS += -DEAP_PSK_DYNAMIC
EAPDYN += eap_psk.so
else
CFLAGS += -DEAP_PSK
OBJS += $(patsubst %.c, %.o, $(SRC_EAP_PSK))
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_AES=y
NEED_AES_ENCBLOCK=y
NEED_AES_EAX=y
endif
ifdef CONFIG_EAP_AKA
# EAP-AKA
ifeq ($(CONFIG_EAP_AKA), dyn)
CFLAGS += -DEAP_AKA_DYNAMIC
EAPDYN += eap_aka.so
else
CFLAGS += -DEAP_AKA
OBJS += ../src/eap_peer/eap_aka.o
endif
CONFIG_IEEE8021X_EAPOL=y
CONFIG_EAP_SIM_COMMON=y
NEED_AES_CBC=y
endif
ifdef CONFIG_EAP_PROXY
CFLAGS += -DCONFIG_EAP_PROXY
OBJS += ../src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).o
include eap_proxy_$(CONFIG_EAP_PROXY).mak
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_AKA_PRIME
# EAP-AKA'
ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
CFLAGS += -DEAP_AKA_PRIME_DYNAMIC
else
CFLAGS += -DEAP_AKA_PRIME
endif
endif
ifdef CONFIG_EAP_SIM_COMMON
OBJS += ../src/eap_common/eap_sim_common.o
NEED_AES=y
NEED_FIPS186_2_PRF=y
endif
ifdef CONFIG_EAP_FAST
# EAP-FAST
SRC_EAP_FAST = ../src/eap_peer/eap_fast.c ../src/eap_peer/eap_fast_pac.c
SRC_EAP_FAST += ../src/eap_common/eap_fast_common.c
ifeq ($(CONFIG_EAP_FAST), dyn)
CFLAGS += -DEAP_FAST_DYNAMIC
EAPDYN += eap_fast.so
else
CFLAGS += -DEAP_FAST
OBJS += $(patsubst %.c, %.o, $(SRC_EAP_FAST))
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
NEED_T_PRF=y
endif
ifdef CONFIG_EAP_TEAP
# EAP-TEAP
SRC_EAP_TEAP = ../src/eap_peer/eap_teap.c ../src/eap_peer/eap_teap_pac.c
SRC_EAP_TEAP += ../src/eap_common/eap_teap_common.c
ifeq ($(CONFIG_EAP_TEAP), dyn)
CFLAGS += -DEAP_TEAP_DYNAMIC
EAPDYN += eap_teap.so
else
CFLAGS += -DEAP_TEAP
OBJS += $(patsubst %.c, %.o, $(SRC_EAP_TEAP))
endif
TLS_FUNCS=y
CONFIG_IEEE8021X_EAPOL=y
NEED_T_PRF=y
NEED_SHA384=y
NEED_TLS_PRF_SHA256=y
NEED_TLS_PRF_SHA384=y
endif
ifdef CONFIG_EAP_PAX
# EAP-PAX
SRC_EAP_PAX = ../src/eap_peer/eap_pax.c ../src/eap_common/eap_pax_common.c
ifeq ($(CONFIG_EAP_PAX), dyn)
CFLAGS += -DEAP_PAX_DYNAMIC
EAPDYN += eap_pax.so
else
CFLAGS += -DEAP_PAX
OBJS += $(patsubst %.c, %.o, $(SRC_EAP_PAX))
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_SAKE
# EAP-SAKE
SRC_EAP_SAKE = ../src/eap_peer/eap_sake.c ../src/eap_common/eap_sake_common.c
ifeq ($(CONFIG_EAP_SAKE), dyn)
CFLAGS += -DEAP_SAKE_DYNAMIC
EAPDYN += eap_sake.so
else
CFLAGS += -DEAP_SAKE
OBJS += $(patsubst %.c, %.o, $(SRC_EAP_SAKE))
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_GPSK
# EAP-GPSK
SRC_EAP_GPSK = ../src/eap_peer/eap_gpsk.c ../src/eap_common/eap_gpsk_common.c
ifeq ($(CONFIG_EAP_GPSK), dyn)
CFLAGS += -DEAP_GPSK_DYNAMIC
EAPDYN += eap_gpsk.so
else
CFLAGS += -DEAP_GPSK
OBJS += $(patsubst %.c, %.o, $(SRC_EAP_GPSK))
endif
CONFIG_IEEE8021X_EAPOL=y
ifdef CONFIG_EAP_GPSK_SHA256
CFLAGS += -DEAP_GPSK_SHA256
endif
endif
ifdef CONFIG_EAP_PWD
CFLAGS += -DEAP_PWD
ifeq ($(CONFIG_TLS), wolfssl)
CFLAGS += -DCONFIG_ECC
endif
OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o
CONFIG_IEEE8021X_EAPOL=y
NEED_ECC=y
NEED_DRAGONFLY=y
endif
ifdef CONFIG_EAP_EKE
# EAP-EKE
SRC_EAP_EKE = ../src/eap_peer/eap_eke.c ../src/eap_common/eap_eke_common.c
ifeq ($(CONFIG_EAP_EKE), dyn)
CFLAGS += -DEAP_EKE_DYNAMIC
EAPDYN += eap_eke.so
else
CFLAGS += -DEAP_EKE
OBJS += $(patsubst %.c, %.o, $(SRC_EAP_EKE))
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
NEED_DH_GROUPS_ALL=y
NEED_AES_CBC=y
endif
ifdef CONFIG_WPS
# EAP-WSC
CFLAGS += -DCONFIG_WPS -DEAP_WSC
OBJS += wps_supplicant.o
OBJS += ../src/utils/uuid.o
OBJS += ../src/eap_peer/eap_wsc.o ../src/eap_common/eap_wsc_common.o
OBJS += ../src/wps/wps.o
OBJS += ../src/wps/wps_common.o
OBJS += ../src/wps/wps_attr_parse.o
OBJS += ../src/wps/wps_attr_build.o
OBJS += ../src/wps/wps_attr_process.o
OBJS += ../src/wps/wps_dev_attr.o
OBJS += ../src/wps/wps_enrollee.o
OBJS += ../src/wps/wps_registrar.o
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
NEED_BASE64=y
NEED_AES_CBC=y
NEED_MODEXP=y
ifdef CONFIG_WPS_NFC
CFLAGS += -DCONFIG_WPS_NFC
OBJS += ../src/wps/ndef.o
NEED_WPS_OOB=y
endif
ifdef NEED_WPS_OOB
CFLAGS += -DCONFIG_WPS_OOB
endif
ifdef CONFIG_WPS_ER
CONFIG_WPS_UPNP=y
CFLAGS += -DCONFIG_WPS_ER
OBJS += ../src/wps/wps_er.o
OBJS += ../src/wps/wps_er_ssdp.o
endif
ifdef CONFIG_WPS_UPNP
CFLAGS += -DCONFIG_WPS_UPNP
OBJS += ../src/wps/wps_upnp.o
OBJS += ../src/wps/wps_upnp_ssdp.o
OBJS += ../src/wps/wps_upnp_web.o
OBJS += ../src/wps/wps_upnp_event.o
OBJS += ../src/wps/wps_upnp_ap.o
OBJS += ../src/wps/upnp_xml.o
OBJS += ../src/wps/httpread.o
OBJS += ../src/wps/http_client.o
OBJS += ../src/wps/http_server.o
endif
ifdef CONFIG_WPS_STRICT
CFLAGS += -DCONFIG_WPS_STRICT
OBJS += ../src/wps/wps_validate.o
endif
ifdef CONFIG_WPS_TESTING
CFLAGS += -DCONFIG_WPS_TESTING
endif
ifdef CONFIG_WPS_REG_DISABLE_OPEN
CFLAGS += -DCONFIG_WPS_REG_DISABLE_OPEN
endif
endif
ifdef CONFIG_EAP_IKEV2
# EAP-IKEv2
SRC_EAP_IKEV2 = ../src/eap_peer/eap_ikev2.c
SRC_EAP_IKEV2 += ../src/eap_peer/ikev2.c
SRC_EAP_IKEV2 += ../src/eap_common/eap_ikev2_common.c
SRC_EAP_IKEV2 += ../src/eap_common/ikev2_common.c
ifeq ($(CONFIG_EAP_IKEV2), dyn)
CFLAGS += -DEAP_IKEV2_DYNAMIC
EAPDYN += eap_ikev2.so
else
CFLAGS += -DEAP_IKEV2
OBJS += $(patsubst %.c, %.o, $(SRC_EAP_IKEV2))
endif
CONFIG_IEEE8021X_EAPOL=y
NEED_DH_GROUPS=y
NEED_DH_GROUPS_ALL=y
NEED_MODEXP=y
NEED_CIPHER=y
endif
ifdef CONFIG_EAP_VENDOR_TEST
ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn)
CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC
EAPDYN += eap_vendor_test.so
else
CFLAGS += -DEAP_VENDOR_TEST
OBJS += ../src/eap_peer/eap_vendor_test.o
endif
CONFIG_IEEE8021X_EAPOL=y
endif
ifdef CONFIG_EAP_TNC
# EAP-TNC
CFLAGS += -DEAP_TNC
OBJS += ../src/eap_peer/eap_tnc.o
OBJS += ../src/eap_peer/tncc.o
NEED_BASE64=y
ifndef CONFIG_NATIVE_WINDOWS
ifndef CONFIG_DRIVER_BSD
LIBS += -ldl
endif
endif
endif
ifdef CONFIG_MACSEC
CFLAGS += -DCONFIG_MACSEC
CONFIG_IEEE8021X_EAPOL=y
NEED_AES_ENCBLOCK=y
NEED_AES_UNWRAP=y
NEED_AES_WRAP=y
OBJS += wpas_kay.o
OBJS += ../src/pae/ieee802_1x_cp.o
OBJS += ../src/pae/ieee802_1x_kay.o
OBJS += ../src/pae/ieee802_1x_key.o
OBJS += ../src/pae/ieee802_1x_secy_ops.o
ifdef CONFIG_AP
OBJS += ../src/ap/wpa_auth_kay.o
endif
endif
ifdef CONFIG_IEEE8021X_EAPOL
# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
CFLAGS += -DIEEE8021X_EAPOL
OBJS += ../src/eapol_supp/eapol_supp_sm.o
OBJS += ../src/eap_peer/eap.o ../src/eap_peer/eap_methods.o
NEED_EAP_COMMON=y
ifdef CONFIG_DYNAMIC_EAP_METHODS
CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
LIBS += -ldl -rdynamic
endif
endif
ifdef CONFIG_AP
NEED_EAP_COMMON=y
NEED_RSN_AUTHENTICATOR=y
CFLAGS += -DCONFIG_AP
OBJS += ap.o
CFLAGS += -DCONFIG_NO_RADIUS
CFLAGS += -DCONFIG_NO_ACCOUNTING
CFLAGS += -DCONFIG_NO_VLAN
OBJS += ../src/ap/hostapd.o
OBJS += ../src/ap/wpa_auth_glue.o
OBJS += ../src/ap/utils.o
OBJS += ../src/ap/authsrv.o
OBJS += ../src/ap/ap_config.o
OBJS += ../src/ap/sta_info.o
OBJS += ../src/ap/tkip_countermeasures.o
OBJS += ../src/ap/ap_mlme.o
OBJS += ../src/ap/ieee802_1x.o
OBJS += ../src/eapol_auth/eapol_auth_sm.o
OBJS += ../src/ap/ieee802_11_auth.o
OBJS += ../src/ap/ieee802_11_shared.o
OBJS += ../src/ap/drv_callbacks.o
OBJS += ../src/ap/ap_drv_ops.o
OBJS += ../src/ap/beacon.o
OBJS += ../src/ap/bss_load.o
OBJS += ../src/ap/eap_user_db.o
OBJS += ../src/ap/neighbor_db.o
OBJS += ../src/ap/rrm.o
OBJS += ../src/ap/ieee802_11_ht.o
ifdef CONFIG_IEEE80211AC
OBJS += ../src/ap/ieee802_11_vht.o
endif
ifdef CONFIG_IEEE80211AX
OBJS += ../src/ap/ieee802_11_he.o
endif
ifdef CONFIG_WNM_AP
CFLAGS += -DCONFIG_WNM_AP
OBJS += ../src/ap/wnm_ap.o
endif
ifdef CONFIG_MBO
OBJS += ../src/ap/mbo_ap.o
endif
ifdef CONFIG_FILS
OBJS += ../src/ap/fils_hlp.o
endif
ifdef CONFIG_CTRL_IFACE
OBJS += ../src/ap/ctrl_iface_ap.o
endif
CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
OBJS += ../src/eap_server/eap_server.o
OBJS += ../src/eap_server/eap_server_identity.o
OBJS += ../src/eap_server/eap_server_methods.o
ifdef CONFIG_IEEE80211AC
CFLAGS += -DCONFIG_IEEE80211AC
endif
ifdef CONFIG_IEEE80211AX
CFLAGS += -DCONFIG_IEEE80211AX
endif
ifdef NEED_AP_MLME
OBJS += ../src/ap/wmm.o
OBJS += ../src/ap/ap_list.o
OBJS += ../src/ap/ieee802_11.o
OBJS += ../src/ap/hw_features.o
OBJS += ../src/ap/dfs.o
CFLAGS += -DNEED_AP_MLME
endif
ifdef CONFIG_WPS
CFLAGS += -DEAP_SERVER_WSC
OBJS += ../src/ap/wps_hostapd.o
OBJS += ../src/eap_server/eap_server_wsc.o
endif
ifdef CONFIG_DPP
OBJS += ../src/ap/dpp_hostapd.o
OBJS += ../src/ap/gas_query_ap.o
NEED_AP_GAS_SERV=y
endif
ifdef CONFIG_INTERWORKING
NEED_AP_GAS_SERV=y
endif
ifdef NEED_AP_GAS_SERV
OBJS += ../src/ap/gas_serv.o
endif
ifdef CONFIG_HS20
OBJS += ../src/ap/hs20.o
endif
endif
ifdef CONFIG_MBO
OBJS += mbo.o
CFLAGS += -DCONFIG_MBO
endif
ifdef NEED_RSN_AUTHENTICATOR
CFLAGS += -DCONFIG_NO_RADIUS
NEED_AES_WRAP=y
OBJS += ../src/ap/wpa_auth.o
OBJS += ../src/ap/wpa_auth_ie.o
OBJS += ../src/ap/pmksa_cache_auth.o
endif
ifdef CONFIG_ACS
CFLAGS += -DCONFIG_ACS
OBJS += ../src/ap/acs.o
LIBS += -lm
endif
ifdef CONFIG_PCSC
# PC/SC interface for smartcards (USIM, GSM SIM)
CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
OBJS += ../src/utils/pcsc_funcs.o
-# -lpthread may not be needed depending on how pcsc-lite was configured
ifdef CONFIG_NATIVE_WINDOWS
#Once MinGW gets support for WinScard, -lwinscard could be used instead of the
#dynamic symbol loading that is now used in pcsc_funcs.c
#LIBS += -lwinscard
else
ifdef CONFIG_OSX
LIBS += -framework PCSC
else
-LIBS += -lpcsclite -lpthread
+LIBS += $(shell $(PKG_CONFIG) --libs libpcsclite)
endif
endif
endif
ifdef CONFIG_SIM_SIMULATOR
CFLAGS += -DCONFIG_SIM_SIMULATOR
NEED_MILENAGE=y
endif
ifdef CONFIG_USIM_SIMULATOR
CFLAGS += -DCONFIG_USIM_SIMULATOR
NEED_MILENAGE=y
endif
ifdef NEED_MILENAGE
OBJS += ../src/crypto/milenage.o
NEED_AES_ENCBLOCK=y
endif
ifdef CONFIG_PKCS12
CFLAGS += -DPKCS12_FUNCS
endif
ifdef CONFIG_SMARTCARD
CFLAGS += -DCONFIG_SMARTCARD
endif
ifdef NEED_DRAGONFLY
OBJS += ../src/common/dragonfly.o
endif
ifdef MS_FUNCS
OBJS += ../src/crypto/ms_funcs.o
NEED_DES=y
NEED_MD4=y
endif
ifdef CHAP
OBJS += ../src/eap_common/chap.o
endif
ifdef TLS_FUNCS
NEED_DES=y
# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, EAP_FAST, and
# EAP_TEAP)
OBJS += ../src/eap_peer/eap_tls_common.o
ifndef CONFIG_FIPS
NEED_TLS_PRF=y
NEED_SHA1=y
NEED_MD5=y
endif
endif
ifndef CONFIG_TLS
CONFIG_TLS=openssl
endif
ifdef CONFIG_TLSV11
CFLAGS += -DCONFIG_TLSV11
endif
ifdef CONFIG_TLSV12
CFLAGS += -DCONFIG_TLSV12
endif
ifeq ($(CONFIG_TLS), wolfssl)
ifdef TLS_FUNCS
CFLAGS += -DWOLFSSL_DER_LOAD
OBJS += ../src/crypto/tls_wolfssl.o
endif
OBJS += ../src/crypto/crypto_wolfssl.o
OBJS_p += ../src/crypto/crypto_wolfssl.o
ifdef NEED_FIPS186_2_PRF
OBJS += ../src/crypto/fips_prf_wolfssl.o
endif
NEED_TLS_PRF_SHA256=y
LIBS += -lwolfssl -lm
LIBS_p += -lwolfssl -lm
endif
ifeq ($(CONFIG_TLS), openssl)
ifdef TLS_FUNCS
CFLAGS += -DEAP_TLS_OPENSSL
OBJS += ../src/crypto/tls_openssl.o
OBJS += ../src/crypto/tls_openssl_ocsp.o
LIBS += -lssl
endif
OBJS += ../src/crypto/crypto_openssl.o
OBJS_p += ../src/crypto/crypto_openssl.o
OBJS_priv += ../src/crypto/crypto_openssl.o
ifdef NEED_FIPS186_2_PRF
OBJS += ../src/crypto/fips_prf_openssl.o
endif
NEED_TLS_PRF_SHA256=y
LIBS += -lcrypto
LIBS_p += -lcrypto
ifdef CONFIG_TLS_ADD_DL
LIBS += -ldl
LIBS_p += -ldl
endif
ifndef CONFIG_TLS_DEFAULT_CIPHERS
CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW"
endif
CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\"
endif
ifeq ($(CONFIG_TLS), gnutls)
ifndef CONFIG_CRYPTO
# default to libgcrypt
CONFIG_CRYPTO=gnutls
endif
ifdef TLS_FUNCS
OBJS += ../src/crypto/tls_gnutls.o
LIBS += -lgnutls -lgpg-error
endif
OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o
ifdef NEED_FIPS186_2_PRF
OBJS += ../src/crypto/fips_prf_internal.o
SHA1OBJS += ../src/crypto/sha1-internal.o
endif
ifeq ($(CONFIG_CRYPTO), gnutls)
LIBS += -lgcrypt
LIBS_p += -lgcrypt
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
ifeq ($(CONFIG_CRYPTO), nettle)
LIBS += -lnettle -lgmp
LIBS_p += -lnettle -lgmp
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
endif
ifeq ($(CONFIG_TLS), internal)
ifndef CONFIG_CRYPTO
CONFIG_CRYPTO=internal
endif
ifdef TLS_FUNCS
OBJS += ../src/crypto/crypto_internal-rsa.o
OBJS += ../src/crypto/tls_internal.o
OBJS += ../src/tls/tlsv1_common.o
OBJS += ../src/tls/tlsv1_record.o
OBJS += ../src/tls/tlsv1_cred.o
OBJS += ../src/tls/tlsv1_client.o
OBJS += ../src/tls/tlsv1_client_write.o
OBJS += ../src/tls/tlsv1_client_read.o
OBJS += ../src/tls/tlsv1_client_ocsp.o
OBJS += ../src/tls/rsa.o
OBJS += ../src/tls/x509v3.o
OBJS += ../src/tls/pkcs1.o
OBJS += ../src/tls/pkcs5.o
OBJS += ../src/tls/pkcs8.o
NEED_ASN1=y
NEED_BASE64=y
NEED_TLS_PRF=y
ifdef CONFIG_TLSV12
NEED_TLS_PRF_SHA256=y
endif
NEED_MODEXP=y
NEED_CIPHER=y
CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
endif
ifdef NEED_CIPHER
NEED_DES=y
OBJS += ../src/crypto/crypto_internal-cipher.o
endif
ifdef NEED_MODEXP
OBJS += ../src/crypto/crypto_internal-modexp.o
OBJS += ../src/tls/bignum.o
endif
ifeq ($(CONFIG_CRYPTO), libtomcrypt)
OBJS += ../src/crypto/crypto_libtomcrypt.o
OBJS_p += ../src/crypto/crypto_libtomcrypt.o
LIBS += -ltomcrypt -ltfm
LIBS_p += -ltomcrypt -ltfm
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
ifeq ($(CONFIG_CRYPTO), internal)
OBJS += ../src/crypto/crypto_internal.o
OBJS_p += ../src/crypto/crypto_internal.o
NEED_AES_ENC=y
CFLAGS += -DCONFIG_CRYPTO_INTERNAL
ifdef CONFIG_INTERNAL_LIBTOMMATH
CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
CFLAGS += -DLTM_FAST
endif
else
LIBS += -ltommath
LIBS_p += -ltommath
endif
CONFIG_INTERNAL_AES=y
CONFIG_INTERNAL_DES=y
CONFIG_INTERNAL_SHA1=y
CONFIG_INTERNAL_MD4=y
CONFIG_INTERNAL_MD5=y
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_SHA384=y
CONFIG_INTERNAL_SHA512=y
CONFIG_INTERNAL_RC4=y
CONFIG_INTERNAL_DH_GROUP5=y
endif
ifeq ($(CONFIG_CRYPTO), cryptoapi)
OBJS += ../src/crypto/crypto_cryptoapi.o
OBJS_p += ../src/crypto/crypto_cryptoapi.o
CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_RC4=y
endif
endif
ifeq ($(CONFIG_TLS), linux)
OBJS += ../src/crypto/crypto_linux.o
OBJS_p += ../src/crypto/crypto_linux.o
ifdef TLS_FUNCS
OBJS += ../src/crypto/crypto_internal-rsa.o
OBJS += ../src/crypto/tls_internal.o
OBJS += ../src/tls/tlsv1_common.o
OBJS += ../src/tls/tlsv1_record.o
OBJS += ../src/tls/tlsv1_cred.o
OBJS += ../src/tls/tlsv1_client.o
OBJS += ../src/tls/tlsv1_client_write.o
OBJS += ../src/tls/tlsv1_client_read.o
OBJS += ../src/tls/tlsv1_client_ocsp.o
OBJS += ../src/tls/rsa.o
OBJS += ../src/tls/x509v3.o
OBJS += ../src/tls/pkcs1.o
OBJS += ../src/tls/pkcs5.o
OBJS += ../src/tls/pkcs8.o
NEED_ASN1=y
NEED_BASE64=y
NEED_TLS_PRF=y
ifdef CONFIG_TLSV12
NEED_TLS_PRF_SHA256=y
endif
NEED_MODEXP=y
NEED_CIPHER=y
CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
endif
ifdef NEED_MODEXP
OBJS += ../src/crypto/crypto_internal-modexp.o
OBJS += ../src/tls/bignum.o
CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
CFLAGS += -DLTM_FAST
endif
CONFIG_INTERNAL_DH_GROUP5=y
ifdef NEED_FIPS186_2_PRF
OBJS += ../src/crypto/fips_prf_internal.o
OBJS += ../src/crypto/sha1-internal.o
endif
endif
ifeq ($(CONFIG_TLS), none)
ifdef TLS_FUNCS
OBJS += ../src/crypto/tls_none.o
CFLAGS += -DEAP_TLS_NONE
CONFIG_INTERNAL_AES=y
CONFIG_INTERNAL_SHA1=y
CONFIG_INTERNAL_MD5=y
endif
OBJS += ../src/crypto/crypto_none.o
OBJS_p += ../src/crypto/crypto_none.o
CONFIG_INTERNAL_SHA256=y
CONFIG_INTERNAL_RC4=y
endif
ifdef TLS_FUNCS
ifdef CONFIG_SMARTCARD
ifndef CONFIG_NATIVE_WINDOWS
ifneq ($(CONFIG_L2_PACKET), freebsd)
LIBS += -ldl
endif
endif
endif
endif
ifndef TLS_FUNCS
OBJS += ../src/crypto/tls_none.o
ifeq ($(CONFIG_TLS), internal)
CONFIG_INTERNAL_AES=y
CONFIG_INTERNAL_SHA1=y
CONFIG_INTERNAL_MD5=y
CONFIG_INTERNAL_RC4=y
endif
endif
AESOBJS = # none so far (see below)
ifdef CONFIG_INTERNAL_AES
AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o
endif
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), wolfssl)
NEED_INTERNAL_AES_WRAP=y
endif
endif
ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
# Seems to be needed at least with BoringSSL
NEED_INTERNAL_AES_WRAP=y
CFLAGS += -DCONFIG_OPENSSL_INTERNAL_AES_WRAP
endif
ifdef CONFIG_FIPS
# Have to use internal AES key wrap routines to use OpenSSL EVP since the
# OpenSSL AES_wrap_key()/AES_unwrap_key() API is not available in FIPS mode.
NEED_INTERNAL_AES_WRAP=y
endif
ifdef NEED_INTERNAL_AES_WRAP
ifneq ($(CONFIG_TLS), linux)
AESOBJS += ../src/crypto/aes-unwrap.o
endif
endif
ifdef NEED_AES_EAX
AESOBJS += ../src/crypto/aes-eax.o
NEED_AES_CTR=y
endif
ifdef NEED_AES_SIV
AESOBJS += ../src/crypto/aes-siv.o
NEED_AES_CTR=y
endif
ifdef NEED_AES_CTR
AESOBJS += ../src/crypto/aes-ctr.o
endif
ifdef NEED_AES_ENCBLOCK
AESOBJS += ../src/crypto/aes-encblock.o
endif
NEED_AES_ENC=y
ifdef CONFIG_OPENSSL_CMAC
CFLAGS += -DCONFIG_OPENSSL_CMAC
else
ifneq ($(CONFIG_TLS), linux)
ifneq ($(CONFIG_TLS), wolfssl)
AESOBJS += ../src/crypto/aes-omac1.o
endif
endif
endif
ifdef NEED_AES_WRAP
NEED_AES_ENC=y
ifdef NEED_INTERNAL_AES_WRAP
AESOBJS += ../src/crypto/aes-wrap.o
endif
endif
ifdef NEED_AES_CBC
NEED_AES_ENC=y
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), linux)
ifneq ($(CONFIG_TLS), wolfssl)
AESOBJS += ../src/crypto/aes-cbc.o
endif
endif
endif
endif
ifdef NEED_AES_ENC
ifdef CONFIG_INTERNAL_AES
AESOBJS += ../src/crypto/aes-internal-enc.o
endif
endif
ifdef NEED_AES
OBJS += $(AESOBJS)
endif
ifdef NEED_SHA1
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), linux)
ifneq ($(CONFIG_TLS), gnutls)
ifneq ($(CONFIG_TLS), wolfssl)
SHA1OBJS += ../src/crypto/sha1.o
endif
endif
endif
endif
SHA1OBJS += ../src/crypto/sha1-prf.o
ifdef CONFIG_INTERNAL_SHA1
SHA1OBJS += ../src/crypto/sha1-internal.o
ifdef NEED_FIPS186_2_PRF
SHA1OBJS += ../src/crypto/fips_prf_internal.o
endif
endif
ifdef CONFIG_NO_WPA_PASSPHRASE
CFLAGS += -DCONFIG_NO_PBKDF2
else
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), wolfssl)
SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
endif
endif
endif
ifdef NEED_T_PRF
SHA1OBJS += ../src/crypto/sha1-tprf.o
endif
ifdef NEED_TLS_PRF
SHA1OBJS += ../src/crypto/sha1-tlsprf.o
endif
endif
ifndef CONFIG_FIPS
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), linux)
ifneq ($(CONFIG_TLS), gnutls)
ifneq ($(CONFIG_TLS), wolfssl)
MD5OBJS += ../src/crypto/md5.o
endif
endif
endif
endif
endif
ifdef NEED_MD5
ifdef CONFIG_INTERNAL_MD5
MD5OBJS += ../src/crypto/md5-internal.o
endif
OBJS += $(MD5OBJS)
OBJS_p += $(MD5OBJS)
OBJS_priv += $(MD5OBJS)
endif
ifdef NEED_MD4
ifdef CONFIG_INTERNAL_MD4
OBJS += ../src/crypto/md4-internal.o
endif
endif
DESOBJS = # none needed when not internal
ifdef NEED_DES
ifndef CONFIG_FIPS
CFLAGS += -DCONFIG_DES
endif
ifdef CONFIG_INTERNAL_DES
DESOBJS += ../src/crypto/des-internal.o
endif
endif
ifdef CONFIG_NO_RC4
CFLAGS += -DCONFIG_NO_RC4
endif
ifdef NEED_RC4
ifdef CONFIG_INTERNAL_RC4
ifndef CONFIG_NO_RC4
OBJS += ../src/crypto/rc4.o
endif
endif
endif
SHA256OBJS = # none by default
CFLAGS += -DCONFIG_SHA256
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), linux)
ifneq ($(CONFIG_TLS), gnutls)
ifneq ($(CONFIG_TLS), wolfssl)
SHA256OBJS += ../src/crypto/sha256.o
endif
endif
endif
endif
SHA256OBJS += ../src/crypto/sha256-prf.o
ifdef CONFIG_INTERNAL_SHA256
SHA256OBJS += ../src/crypto/sha256-internal.o
endif
ifdef CONFIG_INTERNAL_SHA384
CFLAGS += -DCONFIG_INTERNAL_SHA384
SHA256OBJS += ../src/crypto/sha384-internal.o
endif
ifdef CONFIG_INTERNAL_SHA512
CFLAGS += -DCONFIG_INTERNAL_SHA512
SHA256OBJS += ../src/crypto/sha512-internal.o
endif
ifdef NEED_TLS_PRF_SHA256
SHA256OBJS += ../src/crypto/sha256-tlsprf.o
endif
ifdef NEED_TLS_PRF_SHA384
SHA256OBJS += ../src/crypto/sha384-tlsprf.o
endif
ifdef NEED_HMAC_SHA256_KDF
CFLAGS += -DCONFIG_HMAC_SHA256_KDF
OBJS += ../src/crypto/sha256-kdf.o
endif
ifdef NEED_HMAC_SHA384_KDF
CFLAGS += -DCONFIG_HMAC_SHA384_KDF
OBJS += ../src/crypto/sha384-kdf.o
endif
ifdef NEED_HMAC_SHA512_KDF
CFLAGS += -DCONFIG_HMAC_SHA512_KDF
OBJS += ../src/crypto/sha512-kdf.o
endif
OBJS += $(SHA256OBJS)
ifdef NEED_SHA384
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), linux)
ifneq ($(CONFIG_TLS), gnutls)
ifneq ($(CONFIG_TLS), wolfssl)
OBJS += ../src/crypto/sha384.o
endif
endif
endif
endif
CFLAGS += -DCONFIG_SHA384
OBJS += ../src/crypto/sha384-prf.o
endif
ifdef NEED_SHA512
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), linux)
ifneq ($(CONFIG_TLS), gnutls)
ifneq ($(CONFIG_TLS), wolfssl)
OBJS += ../src/crypto/sha512.o
endif
endif
endif
endif
CFLAGS += -DCONFIG_SHA512
OBJS += ../src/crypto/sha512-prf.o
endif
ifdef NEED_ASN1
OBJS += ../src/tls/asn1.o
endif
ifdef NEED_DH_GROUPS
OBJS += ../src/crypto/dh_groups.o
endif
ifdef NEED_DH_GROUPS_ALL
CFLAGS += -DALL_DH_GROUPS
endif
ifdef CONFIG_INTERNAL_DH_GROUP5
ifdef NEED_DH_GROUPS
OBJS += ../src/crypto/dh_group5.o
endif
endif
ifdef NEED_ECC
CFLAGS += -DCONFIG_ECC
endif
ifdef CONFIG_NO_RANDOM_POOL
CFLAGS += -DCONFIG_NO_RANDOM_POOL
else
ifdef CONFIG_GETRANDOM
CFLAGS += -DCONFIG_GETRANDOM
endif
OBJS += ../src/crypto/random.o
endif
ifdef CONFIG_CTRL_IFACE
ifeq ($(CONFIG_CTRL_IFACE), y)
ifdef CONFIG_NATIVE_WINDOWS
CONFIG_CTRL_IFACE=named_pipe
else
CONFIG_CTRL_IFACE=unix
endif
endif
CFLAGS += -DCONFIG_CTRL_IFACE
ifeq ($(CONFIG_CTRL_IFACE), unix)
CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
OBJS += ../src/common/ctrl_iface_common.o
endif
ifeq ($(CONFIG_CTRL_IFACE), udp)
CFLAGS += -DCONFIG_CTRL_IFACE_UDP
endif
ifeq ($(CONFIG_CTRL_IFACE), udp6)
CONFIG_CTRL_IFACE=udp
CFLAGS += -DCONFIG_CTRL_IFACE_UDP
CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
endif
ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
endif
ifeq ($(CONFIG_CTRL_IFACE), udp-remote)
CONFIG_CTRL_IFACE=udp
CFLAGS += -DCONFIG_CTRL_IFACE_UDP
CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
endif
ifeq ($(CONFIG_CTRL_IFACE), udp6-remote)
CONFIG_CTRL_IFACE=udp
CFLAGS += -DCONFIG_CTRL_IFACE_UDP
CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
endif
OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o
endif
ifdef CONFIG_CTRL_IFACE_DBUS_NEW
CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
OBJS += dbus/dbus_dict_helpers.o
OBJS += dbus/dbus_new_helpers.o
OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o
OBJS += dbus/dbus_common.o
ifdef CONFIG_WPS
OBJS += dbus/dbus_new_handlers_wps.o
endif
ifdef CONFIG_P2P
OBJS += dbus/dbus_new_handlers_p2p.o
endif
ifndef DBUS_LIBS
DBUS_LIBS := $(shell $(PKG_CONFIG) --libs dbus-1)
endif
ifndef DBUS_INCLUDE
DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
endif
ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
OBJS += dbus/dbus_new_introspect.o
CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
endif
CFLAGS += $(DBUS_INCLUDE)
LIBS += $(DBUS_LIBS)
endif
ifdef CONFIG_READLINE
OBJS_c += ../src/utils/edit_readline.o
LIBS_c += -lreadline -lncurses
else
ifdef CONFIG_WPA_CLI_EDIT
OBJS_c += ../src/utils/edit.o
else
OBJS_c += ../src/utils/edit_simple.o
endif
endif
ifdef CONFIG_NATIVE_WINDOWS
CFLAGS += -DCONFIG_NATIVE_WINDOWS
LIBS += -lws2_32 -lgdi32 -lcrypt32
LIBS_c += -lws2_32
LIBS_p += -lws2_32 -lgdi32
ifeq ($(CONFIG_CRYPTO), cryptoapi)
LIBS_p += -lcrypt32
endif
endif
ifdef CONFIG_NO_STDOUT_DEBUG
CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
ifndef CONFIG_CTRL_IFACE
CFLAGS += -DCONFIG_NO_WPA_MSG
endif
endif
ifdef CONFIG_IPV6
# for eapol_test only
CFLAGS += -DCONFIG_IPV6
endif
ifdef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
CFLAGS += -DCONFIG_NO_LINUX_PACKET_SOCKET_WAR
endif
ifdef NEED_BASE64
OBJS += ../src/utils/base64.o
endif
ifdef NEED_SME
OBJS += sme.o
CFLAGS += -DCONFIG_SME
endif
OBJS += ../src/common/ieee802_11_common.o
OBJS += ../src/common/hw_features_common.o
ifdef NEED_EAP_COMMON
OBJS += ../src/eap_common/eap_common.o
endif
ifndef CONFIG_MAIN
CONFIG_MAIN=main
endif
ifdef CONFIG_DEBUG_SYSLOG
CFLAGS += -DCONFIG_DEBUG_SYSLOG
ifdef CONFIG_DEBUG_SYSLOG_FACILITY
CFLAGS += -DLOG_HOSTAPD="$(CONFIG_DEBUG_SYSLOG_FACILITY)"
endif
endif
ifdef CONFIG_DEBUG_LINUX_TRACING
CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
endif
ifdef CONFIG_DEBUG_FILE
CFLAGS += -DCONFIG_DEBUG_FILE
endif
ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT
endif
ifdef CONFIG_FIPS
CFLAGS += -DCONFIG_FIPS
ifneq ($(CONFIG_TLS), openssl)
ifneq ($(CONFIG_TLS), wolfssl)
$(error CONFIG_FIPS=y requires CONFIG_TLS=openssl)
endif
endif
endif
OBJS += $(SHA1OBJS) $(DESOBJS)
OBJS_p += $(SHA1OBJS)
OBJS_p += $(SHA256OBJS)
OBJS_priv += $(SHA1OBJS)
ifdef CONFIG_BGSCAN_SIMPLE
CFLAGS += -DCONFIG_BGSCAN_SIMPLE
OBJS += bgscan_simple.o
NEED_BGSCAN=y
endif
ifdef CONFIG_BGSCAN_LEARN
CFLAGS += -DCONFIG_BGSCAN_LEARN
OBJS += bgscan_learn.o
NEED_BGSCAN=y
endif
ifdef NEED_BGSCAN
CFLAGS += -DCONFIG_BGSCAN
OBJS += bgscan.o
endif
ifdef CONFIG_AUTOSCAN_EXPONENTIAL
CFLAGS += -DCONFIG_AUTOSCAN_EXPONENTIAL
OBJS += autoscan_exponential.o
NEED_AUTOSCAN=y
endif
ifdef CONFIG_AUTOSCAN_PERIODIC
CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
OBJS += autoscan_periodic.o
NEED_AUTOSCAN=y
endif
ifdef NEED_AUTOSCAN
CFLAGS += -DCONFIG_AUTOSCAN
OBJS += autoscan.o
endif
ifdef CONFIG_EXT_PASSWORD_TEST
OBJS += ../src/utils/ext_password_test.o
CFLAGS += -DCONFIG_EXT_PASSWORD_TEST
NEED_EXT_PASSWORD=y
endif
ifdef CONFIG_EXT_PASSWORD_FILE
OBJS += ../src/utils/ext_password_file.o
CFLAGS += -DCONFIG_EXT_PASSWORD_FILE
NEED_EXT_PASSWORD=y
endif
ifdef NEED_EXT_PASSWORD
OBJS += ../src/utils/ext_password.o
CFLAGS += -DCONFIG_EXT_PASSWORD
endif
ifdef NEED_GAS_SERVER
OBJS += ../src/common/gas_server.o
CFLAGS += -DCONFIG_GAS_SERVER
NEED_GAS=y
endif
ifdef NEED_GAS
OBJS += ../src/common/gas.o
OBJS += gas_query.o
CFLAGS += -DCONFIG_GAS
NEED_OFFCHANNEL=y
endif
ifdef NEED_OFFCHANNEL
OBJS += offchannel.o
CFLAGS += -DCONFIG_OFFCHANNEL
endif
ifdef NEED_JSON
OBJS += ../src/utils/json.o
CFLAGS += -DCONFIG_JSON
endif
ifdef CONFIG_MODULE_TESTS
CFLAGS += -DCONFIG_MODULE_TESTS
OBJS += wpas_module_tests.o
OBJS += ../src/utils/utils_module_tests.o
OBJS += ../src/common/common_module_tests.o
OBJS += ../src/crypto/crypto_module_tests.o
ifdef CONFIG_WPS
OBJS += ../src/wps/wps_module_tests.o
endif
endif
OBJS += ../src/drivers/driver_common.o
OBJS_priv += ../src/drivers/driver_common.o
OBJS += wpa_supplicant.o events.o bssid_ignore.o wpas_glue.o scan.o
OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
OBJS_t += ../src/radius/radius_client.o
OBJS_t += ../src/radius/radius.o
OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
OBJS_nfc := $(OBJS) $(OBJS_l2) nfc_pw_token.o
OBJS_nfc += $(OBJS_d) ../src/drivers/drivers.o
OBJS += $(CONFIG_MAIN).o
ifdef CONFIG_PRIVSEP
OBJS_priv += $(OBJS_d) ../src/drivers/drivers.o
OBJS_priv += $(OBJS_l2)
OBJS_priv += ../src/utils/os_$(CONFIG_OS).o
OBJS_priv += ../src/utils/$(CONFIG_ELOOP).o
OBJS_priv += ../src/utils/common.o
OBJS_priv += ../src/utils/wpa_debug.o
OBJS_priv += ../src/utils/wpabuf.o
OBJS_priv += wpa_priv.o
ifdef CONFIG_DRIVER_NL80211
OBJS_priv += ../src/common/ieee802_11_common.o
endif
OBJS += ../src/l2_packet/l2_packet_privsep.o
OBJS += ../src/drivers/driver_privsep.o
EXTRA_progs += wpa_priv
else
OBJS += $(OBJS_d) ../src/drivers/drivers.o
OBJS += $(OBJS_l2)
endif
ifdef CONFIG_NDIS_EVENTS_INTEGRATED
CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED
OBJS += ../src/drivers/ndis_events.o
EXTRALIBS += -loleaut32 -lole32 -luuid
ifdef PLATFORMSDKLIB
EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib
else
EXTRALIBS += WbemUuid.Lib
endif
endif
ifdef CONFIG_FST
CFLAGS += -DCONFIG_FST
ifdef CONFIG_FST_TEST
CFLAGS += -DCONFIG_FST_TEST
endif
FST_OBJS += ../src/fst/fst.o
FST_OBJS += ../src/fst/fst_session.o
FST_OBJS += ../src/fst/fst_iface.o
FST_OBJS += ../src/fst/fst_group.o
FST_OBJS += ../src/fst/fst_ctrl_aux.o
ifdef CONFIG_CTRL_IFACE
FST_OBJS += ../src/fst/fst_ctrl_iface.o
endif
OBJS += $(FST_OBJS)
OBJS_t += $(FST_OBJS)
OBJS_t2 += $(FST_OBJS)
OBJS_nfc += $(FST_OBJS)
endif
ifdef CONFIG_WEP
CFLAGS += -DCONFIG_WEP
endif
ifdef CONFIG_NO_TKIP
CFLAGS += -DCONFIG_NO_TKIP
endif
dynamic_eap_methods: $(EAPDYN)
_OBJS_VAR := OBJS_priv
include ../src/objs.mk
wpa_priv: $(BCHECK) $(OBJS_priv)
$(Q)$(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS)
@$(E) " LD " $@
_OBJS_VAR := OBJS
include ../src/objs.mk
wpa_supplicant: $(BCHECK) $(OBJS) $(EXTRA_progs)
$(Q)$(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
@$(E) " LD " $@
_OBJS_VAR := OBJS_t
include ../src/objs.mk
eapol_test: $(OBJS_t)
$(Q)$(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
@$(E) " LD " $@
_OBJS_VAR := OBJS_t2
include ../src/objs.mk
preauth_test: $(OBJS_t2)
$(Q)$(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
@$(E) " LD " $@
_OBJS_VAR := OBJS_p
include ../src/objs.mk
wpa_passphrase: $(OBJS_p)
$(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS)
@$(E) " LD " $@
_OBJS_VAR := OBJS_c
include ../src/objs.mk
wpa_cli: $(OBJS_c)
$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
@$(E) " LD " $@
LIBCTRL += ../src/common/wpa_ctrl.o
LIBCTRL += ../src/utils/os_$(CONFIG_OS).o
LIBCTRL += ../src/utils/common.o
LIBCTRL += ../src/utils/wpa_debug.o
LIBCTRLSO += ../src/common/wpa_ctrl.c
LIBCTRLSO += ../src/utils/os_$(CONFIG_OS).c
LIBCTRLSO += ../src/utils/common.c
LIBCTRLSO += ../src/utils/wpa_debug.c
_OBJS_VAR := LIBCTRL
include ../src/objs.mk
libwpa_client.a: $(LIBCTRL)
$(Q)rm -f $@
$(Q)$(AR) crs $@ $?
@$(E) " AR " $@
libwpa_client.so: $(LIBCTRLSO)
@$(E) " CC $@ ($^)"
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -fPIC $^
OBJS_wpatest := libwpa_test.o
_OBJS_VAR := OBJS_wpatest
include ../src/objs.mk
libwpa_test1: $(OBJS_wpatest) libwpa_client.a
$(Q)$(LDO) $(LDFLAGS) -o libwpa_test1 $(OBJS_wpatest) libwpa_client.a $(LIBS_c)
@$(E) " LD " $@
libwpa_test2: $(OBJS_wpatest) libwpa_client.so
$(Q)$(LDO) $(LDFLAGS) -o libwpa_test2 $(OBJS_wpatest) -L. -lwpa_client $(LIBS_c)
@$(E) " LD " $@
_OBJS_VAR := OBJS_nfc
include ../src/objs.mk
nfc_pw_token: $(OBJS_nfc)
$(Q)$(LDO) $(LDFLAGS) -o nfc_pw_token $(OBJS_nfc) $(LIBS)
@$(E) " LD " $@
win_if_list: win_if_list.c
$(Q)$(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w)
@$(E) " LD " $@
eap_psk.so: $(SRC_EAP_PSK)
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-Deap_peer_psk_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
eap_pax.so: $(SRC_EAP_PAX)
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-D$(@F:eap_%.so=eap_peer_%)_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
eap_peap.so: $(SRC_EAP_PEAP)
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-D$(@F:eap_%.so=eap_peer_%)_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
eap_sake.so: $(SRC_EAP_SAKE)
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-D$(@F:eap_%.so=eap_peer_%)_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
eap_ikev2.so: $(SRC_EAP_IKEV2)
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-D$(@F:eap_%.so=eap_peer_%)_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
eap_eke.so: $(SRC_EAP_EKE)
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-D$(@F:eap_%.so=eap_peer_%)_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
eap_mschapv2.so: $(SRC_EAP_MSCHAPV2)
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-D$(@F:eap_%.so=eap_peer_%)_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
eap_fast.so: $(SRC_EAP_FAST)
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-D$(@F:eap_%.so=eap_peer_%)_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
eap_teap.so: $(SRC_EAP_TEAP)
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-D$(@F:eap_%.so=eap_peer_%)_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
eap_gpsk.so: $(SRC_EAP_GPSK)
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
-D$(@F:eap_%.so=eap_peer_%)_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
%.so: ../src/eap_peer/%.c
$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
-D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
@$(E) " CC/LD " $@
%.service: %.service.in
$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
@$(E) " sed" $<
%@.service: %.service.arg.in
$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
@$(E) " sed" $<
wpa_supplicant.exe: wpa_supplicant
mv -f $< $@
wpa_cli.exe: wpa_cli
mv -f $< $@
wpa_passphrase.exe: wpa_passphrase
mv -f $< $@
win_if_list.exe: win_if_list
mv -f $< $@
eapol_test.exe: eapol_test
mv -f $< $@
WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe
windows-bin: $(WINALL)
$(STRIP) $(WINALL)
wpa_gui:
@echo "wpa_gui has been removed - see wpa_gui-qt4 for replacement"
wpa_gui-qt4/Makefile:
qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro
wpa_gui-qt4/lang/wpa_gui_de.qm: wpa_gui-qt4/lang/wpa_gui_de.ts
lrelease wpa_gui-qt4/wpa_gui.pro
wpa_gui-qt4: wpa_gui-qt4/Makefile wpa_gui-qt4/lang/wpa_gui_de.qm
$(MAKE) -C wpa_gui-qt4
FIPSDIR=/usr/local/ssl/fips-2.0
FIPSLD=$(FIPSDIR)/bin/fipsld
fips:
$(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)"
.PHONY: lcov-html
lcov-html: $(call BUILDOBJ,wpa_supplicant.gcda)
lcov -c -d $(BUILDDIR) > lcov.info
genhtml lcov.info --output-directory lcov-html
clean: common-clean
$(MAKE) -C ../src clean
$(MAKE) -C dbus clean
rm -f core *~ *.o *.d *.gcno *.gcda *.gcov
rm -f eap_*.so $(WINALL) eapol_test preauth_test
rm -f wpa_priv
rm -f nfc_pw_token
rm -f lcov.info
rm -rf lcov-html
rm -f libwpa_client.a
rm -f libwpa_client.so
rm -f libwpa_test1 libwpa_test2
diff --git a/contrib/wpa/wpa_supplicant/README b/contrib/wpa/wpa_supplicant/README
index 391912e9b6c5..05f15ff46bda 100644
--- a/contrib/wpa/wpa_supplicant/README
+++ b/contrib/wpa/wpa_supplicant/README
@@ -1,1079 +1,1163 @@
wpa_supplicant
==============
Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi> and contributors
All Rights Reserved.
This program is licensed under the BSD license (the one with
advertisement clause removed).
If you are submitting changes to the project, please see CONTRIBUTIONS
file for more instructions.
License
-------
This software may be distributed, used, and modified under the terms of
BSD license:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name(s) of the above-listed copyright holder(s) nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Features
--------
Supported WPA/IEEE 802.11i features:
- WPA-PSK ("WPA-Personal")
- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise")
Following authentication methods are supported with an integrate IEEE 802.1X
Supplicant:
* EAP-TLS
* EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)
* EAP-PEAP/TLS (both PEAPv0 and PEAPv1)
* EAP-PEAP/GTC (both PEAPv0 and PEAPv1)
* EAP-PEAP/OTP (both PEAPv0 and PEAPv1)
* EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)
* EAP-TTLS/EAP-MD5-Challenge
* EAP-TTLS/EAP-GTC
* EAP-TTLS/EAP-OTP
* EAP-TTLS/EAP-MSCHAPv2
* EAP-TTLS/EAP-TLS
* EAP-TTLS/MSCHAPv2
* EAP-TTLS/MSCHAP
* EAP-TTLS/PAP
* EAP-TTLS/CHAP
* EAP-SIM
* EAP-AKA
* EAP-AKA'
* EAP-PSK
* EAP-PAX
* EAP-SAKE
* EAP-IKEv2
* EAP-GPSK
* EAP-pwd
* LEAP (note: requires special support from the driver for IEEE 802.11
authentication)
(following methods are supported, but since they do not generate keying
material, they cannot be used with WPA or IEEE 802.1X WEP keying)
* EAP-MD5-Challenge
* EAP-MSCHAPv2
* EAP-GTC
* EAP-OTP
- key management for CCMP, TKIP, WEP104, WEP40
- RSN/WPA2 (IEEE 802.11i)
* pre-authentication
* PMKSA caching
Supported TLS/crypto libraries:
- OpenSSL (default)
- GnuTLS
Internal TLS/crypto implementation (optional):
- can be used in place of an external TLS/crypto library
- TLSv1
- X.509 certificate processing
- PKCS #1
- ASN.1
- RSA
- bignum
- minimal size (ca. 50 kB binary, parts of which are already needed for WPA;
TLSv1/X.509/ASN.1/RSA/bignum parts are about 25 kB on x86)
Requirements
------------
Current hardware/software requirements:
- Linux kernel 2.4.x or 2.6.x with Linux Wireless Extensions v15 or newer
- FreeBSD 6-CURRENT
- NetBSD-current
- Microsoft Windows with WinPcap (at least WinXP, may work with other versions)
- drivers:
Linux drivers that support cfg80211/nl80211. Even though there are
number of driver specific interface included in wpa_supplicant, please
note that Linux drivers are moving to use generic wireless configuration
interface driver_nl80211 (-Dnl80211 on wpa_supplicant command line)
should be the default option to start with before falling back to driver
specific interface.
Linux drivers that support WPA/WPA2 configuration with the generic
Linux wireless extensions (WE-18 or newer). Obsoleted by nl80211.
In theory, any driver that supports Linux wireless extensions can be
used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in
configuration file.
Wired Ethernet drivers (with ap_scan=0)
BSD net80211 layer (e.g., Atheros driver)
At the moment, this is for FreeBSD 6-CURRENT branch and NetBSD-current.
Windows NDIS
The current Windows port requires WinPcap (http://winpcap.polito.it/).
See README-Windows.txt for more information.
wpa_supplicant was designed to be portable for different drivers and
operating systems. Hopefully, support for more wlan cards and OSes will be
added in the future. See developer's documentation
(http://hostap.epitest.fi/wpa_supplicant/devel/) for more information about the
design of wpa_supplicant and porting to other drivers. One main goal
is to add full WPA/WPA2 support to Linux wireless extensions to allow
new drivers to be supported without having to implement new
driver-specific interface code in wpa_supplicant.
Optional libraries for layer2 packet processing:
- libpcap (tested with 0.7.2, most relatively recent versions assumed to work,
this is likely to be available with most distributions,
http://tcpdump.org/)
- libdnet (tested with v1.4, most versions assumed to work,
http://libdnet.sourceforge.net/)
These libraries are _not_ used in the default Linux build. Instead,
internal Linux specific implementation is used. libpcap/libdnet are
more portable and they can be used by adding CONFIG_L2_PACKET=pcap into
.config. They may also be selected automatically for other operating
systems. In case of Windows builds, WinPcap is used by default
(CONFIG_L2_PACKET=winpcap).
Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS:
- OpenSSL (tested with 1.0.1 and 1.0.2 versions; assumed to
work with most relatively recent versions; this is likely to be
available with most distributions, http://www.openssl.org/)
- GnuTLS
- internal TLSv1 implementation
One of these libraries is needed when EAP-TLS, EAP-PEAP, EAP-TTLS, or
EAP-FAST support is enabled. WPA-PSK mode does not require this or EAPOL/EAP
implementation. A configuration file, .config, for compilation is
needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5,
EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so
they should only be enabled if testing the EAPOL/EAP state
machines. However, there can be used as inner authentication
algorithms with EAP-PEAP and EAP-TTLS.
See Building and installing section below for more detailed
information about the wpa_supplicant build time configuration.
WPA
---
The original security mechanism of IEEE 802.11 standard was not
designed to be strong and has proven to be insufficient for most
networks that require some kind of security. Task group I (Security)
of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
to address the flaws of the base standard and has in practice
completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
802.11 standard was approved in June 2004 and published in July 2004.
Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the
IEEE 802.11i work (draft 3.0) to define a subset of the security
enhancements that can be implemented with existing wlan hardware. This
is called Wi-Fi Protected Access<TM> (WPA). This has now become a
mandatory component of interoperability testing and certification done
by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web
site (http://www.wi-fi.org/OpenSection/protected_access.asp).
IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm
for protecting wireless networks. WEP uses RC4 with 40-bit keys,
24-bit initialization vector (IV), and CRC32 to protect against packet
forgery. All these choices have proven to be insufficient: key space is
too small against current attacks, RC4 key scheduling is insufficient
(beginning of the pseudorandom stream should be skipped), IV space is
too small and IV reuse makes attacks easier, there is no replay
protection, and non-keyed authentication does not protect against bit
flipping packet data.
WPA is an intermediate solution for the security issues. It uses
Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a
compromise on strong security and possibility to use existing
hardware. It still uses RC4 for the encryption like WEP, but with
per-packet RC4 keys. In addition, it implements replay protection,
keyed packet authentication mechanism (Michael MIC).
Keys can be managed using two different mechanisms. WPA can either use
an external authentication server (e.g., RADIUS) and EAP just like
IEEE 802.1X is using or pre-shared keys without need for additional
servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal",
respectively. Both mechanisms will generate a master session key for
the Authenticator (AP) and Supplicant (client station).
WPA implements a new key handshake (4-Way Handshake and Group Key
Handshake) for generating and exchanging data encryption keys between
the Authenticator and Supplicant. This handshake is also used to
verify that both Authenticator and Supplicant know the master session
key. These handshakes are identical regardless of the selected key
management mechanism (only the method for generating master session
key changes).
IEEE 802.11i / WPA2
-------------------
The design for parts of IEEE 802.11i that were not included in WPA has
finished (May 2004) and this amendment to IEEE 802.11 was approved in
June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new
version of WPA called WPA2. This includes, e.g., support for more
robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC)
to replace TKIP and optimizations for handoff (reduced number of
messages in initial key handshake, pre-authentication, and PMKSA caching).
wpa_supplicant
--------------
wpa_supplicant is an implementation of the WPA Supplicant component,
i.e., the part that runs in the client stations. It implements WPA key
negotiation with a WPA Authenticator and EAP authentication with
Authentication Server. In addition, it controls the roaming and IEEE
802.11 authentication/association of the wlan driver.
wpa_supplicant is designed to be a "daemon" program that runs in the
background and acts as the backend component controlling the wireless
connection. wpa_supplicant supports separate frontend programs and an
example text-based frontend, wpa_cli, is included with wpa_supplicant.
Following steps are used when associating with an AP using WPA:
- wpa_supplicant requests the kernel driver to scan neighboring BSSes
- wpa_supplicant selects a BSS based on its configuration
- wpa_supplicant requests the kernel driver to associate with the chosen
BSS
- If WPA-EAP: integrated IEEE 802.1X Supplicant completes EAP
authentication with the authentication server (proxied by the
Authenticator in the AP)
- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant
- If WPA-PSK: wpa_supplicant uses PSK as the master session key
- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake
with the Authenticator (AP)
- wpa_supplicant configures encryption keys for unicast and broadcast
- normal data packets can be transmitted and received
Building and installing
-----------------------
In order to be able to build wpa_supplicant, you will first need to
select which parts of it will be included. This is done by creating a
build time configuration file, .config, in the wpa_supplicant root
directory. Configuration options are text lines using following
format: CONFIG_<option>=y. Lines starting with # are considered
comments and are ignored. See defconfig file for an example configuration
and a list of available options and additional notes.
The build time configuration can be used to select only the needed
features and limit the binary size and requirements for external
libraries. The main configuration parts are the selection of which
driver interfaces (e.g., nl80211, wext, ..) and which authentication
methods (e.g., EAP-TLS, EAP-PEAP, ..) are included.
Following build time configuration options are used to control IEEE
802.1X/EAPOL and EAP state machines and all EAP methods. Including
TLS, PEAP, or TTLS will require linking wpa_supplicant with OpenSSL
library for TLS implementation. Alternatively, GnuTLS or the internal
TLSv1 implementation can be used for TLS functionality.
CONFIG_IEEE8021X_EAPOL=y
CONFIG_EAP_MD5=y
CONFIG_EAP_MSCHAPV2=y
CONFIG_EAP_TLS=y
CONFIG_EAP_PEAP=y
CONFIG_EAP_TTLS=y
CONFIG_EAP_GTC=y
CONFIG_EAP_OTP=y
CONFIG_EAP_SIM=y
CONFIG_EAP_AKA=y
CONFIG_EAP_AKA_PRIME=y
CONFIG_EAP_PSK=y
CONFIG_EAP_SAKE=y
CONFIG_EAP_GPSK=y
CONFIG_EAP_PAX=y
CONFIG_EAP_LEAP=y
CONFIG_EAP_IKEV2=y
CONFIG_EAP_PWD=y
Following option can be used to include GSM SIM/USIM interface for GSM/UMTS
authentication algorithm (for EAP-SIM/EAP-AKA/EAP-AKA'). This requires pcsc-lite
(http://www.linuxnet.com/) for smart card access.
CONFIG_PCSC=y
Following options can be added to .config to select which driver
interfaces are included.
CONFIG_DRIVER_NL80211=y
CONFIG_DRIVER_WEXT=y
CONFIG_DRIVER_BSD=y
CONFIG_DRIVER_NDIS=y
Following example includes some more features and driver interfaces that
are included in the wpa_supplicant package:
CONFIG_DRIVER_NL80211=y
CONFIG_DRIVER_WEXT=y
CONFIG_DRIVER_BSD=y
CONFIG_DRIVER_NDIS=y
CONFIG_IEEE8021X_EAPOL=y
CONFIG_EAP_MD5=y
CONFIG_EAP_MSCHAPV2=y
CONFIG_EAP_TLS=y
CONFIG_EAP_PEAP=y
CONFIG_EAP_TTLS=y
CONFIG_EAP_GTC=y
CONFIG_EAP_OTP=y
CONFIG_EAP_SIM=y
CONFIG_EAP_AKA=y
CONFIG_EAP_PSK=y
CONFIG_EAP_SAKE=y
CONFIG_EAP_GPSK=y
CONFIG_EAP_PAX=y
CONFIG_EAP_LEAP=y
CONFIG_EAP_IKEV2=y
CONFIG_PCSC=y
EAP-PEAP and EAP-TTLS will automatically include configured EAP
methods (MD5, OTP, GTC, MSCHAPV2) for inner authentication selection.
After you have created a configuration file, you can build
wpa_supplicant and wpa_cli with 'make' command. You may then install
the binaries to a suitable system directory, e.g., /usr/local/bin.
Example commands:
# build wpa_supplicant and wpa_cli
make
# install binaries (this may need root privileges)
cp wpa_cli wpa_supplicant /usr/local/bin
You will need to make a configuration file, e.g.,
/etc/wpa_supplicant.conf, with network configuration for the networks
you are going to use. Configuration file section below includes
explanation of the configuration file format and includes various
examples. Once the configuration is ready, you can test whether the
configuration work by first running wpa_supplicant with following
command to start it on foreground with debugging enabled:
wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
Assuming everything goes fine, you can start using following command
to start wpa_supplicant on background without debugging:
wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
Please note that if you included more than one driver interface in the
build time configuration (.config), you may need to specify which
interface to use by including -D<driver name> option on the command
line. See following section for more details on command line options
for wpa_supplicant.
Command line options
--------------------
usage:
wpa_supplicant [-BddfhKLqqtuvW] [-P<pid file>] [-g<global ctrl>] \
[-G<group>] \
-i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] [-p<driver_param>] \
[-b<br_ifname> [-MN -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \
[-p<driver_param>] [-b<br_ifname>] [-m<P2P Device config file>] ...
options:
-b = optional bridge interface name
-B = run daemon in the background
-c = Configuration file
-C = ctrl_interface parameter (only used if -c is not)
-i = interface name
-d = increase debugging verbosity (-dd even more)
-D = driver name (can be multiple drivers: nl80211,wext)
-f = Log output to default log location (normally /tmp)
-g = global ctrl_interface
-G = global ctrl_interface group
-K = include keys (passwords, etc.) in debug output
-t = include timestamp in debug messages
-h = show this help text
-L = show license (BSD)
-p = driver parameters
-P = PID file
-q = decrease debugging verbosity (-qq even less)
-u = enable DBus control interface
-v = show version
-W = wait for a control interface monitor before starting
-M = start describing matching interface
-N = start describing new interface
-m = Configuration file for the P2P Device
drivers:
nl80211 = Linux nl80211/cfg80211
wext = Linux wireless extensions (generic)
wired = wpa_supplicant wired Ethernet driver
roboswitch = wpa_supplicant Broadcom switch driver
bsd = BSD 802.11 support (Atheros, etc.)
ndis = Windows NDIS driver
In most common cases, wpa_supplicant is started with
wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0
This makes the process fork into background.
The easiest way to debug problems, and to get debug log for bug
reports, is to start wpa_supplicant on foreground with debugging
enabled:
wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d
If the specific driver wrapper is not known beforehand, it is possible
to specify multiple comma separated driver wrappers on the command
line. wpa_supplicant will use the first driver wrapper that is able to
initialize the interface.
wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0
wpa_supplicant can control multiple interfaces (radios) either by
running one process for each interface separately or by running just
one process and list of options at command line. Each interface is
separated with -N argument. As an example, following command would
start wpa_supplicant for two interfaces:
wpa_supplicant \
-c wpa1.conf -i wlan0 -D nl80211 -N \
-c wpa2.conf -i wlan1 -D wext
If the interfaces on which wpa_supplicant is to run are not known or do
not exist, wpa_supplicant can match an interface when it arrives. Each
matched interface is separated with -M argument and the -i argument now
allows for pattern matching.
As an example, the following command would start wpa_supplicant for a
specific wired interface called lan0, any interface starting with wlan
and lastly any other interface. Each match has its own configuration
file, and for the wired interface a specific driver has also been given.
wpa_supplicant \
-M -c wpa_wired.conf -ilan0 -D wired \
-M -c wpa1.conf -iwlan* \
-M -c wpa2.conf
If the interface is added in a Linux bridge (e.g., br0), the bridge
interface needs to be configured to wpa_supplicant in addition to the
main interface:
wpa_supplicant -cw.conf -Dnl80211 -iwlan0 -bbr0
Configuration file
------------------
wpa_supplicant is configured using a text file that lists all accepted
networks and security policies, including pre-shared keys. See
example configuration file, wpa_supplicant.conf, for detailed
information about the configuration format and supported fields.
Changes to configuration file can be reloaded be sending SIGHUP signal
to wpa_supplicant ('killall -HUP wpa_supplicant'). Similarly,
reloading can be triggered with 'wpa_cli reconfigure' command.
Configuration file can include one or more network blocks, e.g., one
for each used SSID. wpa_supplicant will automatically select the best
network based on the order of network blocks in the configuration
file, network security level (WPA/WPA2 is preferred), and signal
strength.
Example configuration files for some common configurations:
1) WPA-Personal (PSK) as home network and WPA-Enterprise with EAP-TLS as work
network
# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=wheel
#
# home network; allow all valid ciphers
network={
ssid="home"
scan_ssid=1
key_mgmt=WPA-PSK
psk="very secret passphrase"
}
#
# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
network={
ssid="work"
scan_ssid=1
key_mgmt=WPA-EAP
pairwise=CCMP TKIP
group=CCMP TKIP
eap=TLS
identity="user@example.com"
ca_cert="/etc/cert/ca.pem"
client_cert="/etc/cert/user.pem"
private_key="/etc/cert/user.prv"
private_key_passwd="password"
}
2) WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel
(e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series)
ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=wheel
network={
ssid="example"
scan_ssid=1
key_mgmt=WPA-EAP
eap=PEAP
identity="user@example.com"
password="foobar"
ca_cert="/etc/cert/ca.pem"
phase1="peaplabel=0"
phase2="auth=MSCHAPV2"
}
3) EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=wheel
network={
ssid="example"
scan_ssid=1
key_mgmt=WPA-EAP
eap=TTLS
identity="user@example.com"
anonymous_identity="anonymous@example.com"
password="foobar"
ca_cert="/etc/cert/ca.pem"
phase2="auth=MD5"
}
4) IEEE 802.1X (i.e., no WPA) with dynamic WEP keys (require both unicast and
broadcast); use EAP-TLS for authentication
ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=wheel
network={
ssid="1x-test"
scan_ssid=1
key_mgmt=IEEE8021X
eap=TLS
identity="user@example.com"
ca_cert="/etc/cert/ca.pem"
client_cert="/etc/cert/user.pem"
private_key="/etc/cert/user.prv"
private_key_passwd="password"
eapol_flags=3
}
5) Catch all example that allows more or less all configuration modes. The
configuration options are used based on what security policy is used in the
selected SSID. This is mostly for testing and is not recommended for normal
use.
ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=wheel
network={
ssid="example"
scan_ssid=1
key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
pairwise=CCMP TKIP
group=CCMP TKIP WEP104 WEP40
psk="very secret passphrase"
eap=TTLS PEAP TLS
identity="user@example.com"
password="foobar"
ca_cert="/etc/cert/ca.pem"
client_cert="/etc/cert/user.pem"
private_key="/etc/cert/user.prv"
private_key_passwd="password"
phase1="peaplabel=0"
ca_cert2="/etc/cert/ca2.pem"
client_cert2="/etc/cer/user.pem"
private_key2="/etc/cer/user.prv"
private_key2_passwd="password"
}
6) Authentication for wired Ethernet. This can be used with 'wired' or
'roboswitch' interface (-Dwired or -Droboswitch on command line).
ctrl_interface=/var/run/wpa_supplicant
ctrl_interface_group=wheel
ap_scan=0
network={
key_mgmt=IEEE8021X
eap=MD5
identity="user"
password="password"
eapol_flags=0
}
Certificates
------------
Some EAP authentication methods require use of certificates. EAP-TLS
uses both server side and client certificates whereas EAP-PEAP and
EAP-TTLS only require the server side certificate. When client
certificate is used, a matching private key file has to also be
included in configuration. If the private key uses a passphrase, this
has to be configured in wpa_supplicant.conf ("private_key_passwd").
wpa_supplicant supports X.509 certificates in PEM and DER
formats. User certificate and private key can be included in the same
file.
If the user certificate and private key is received in PKCS#12/PFX
format, they need to be converted to suitable PEM/DER format for
wpa_supplicant. This can be done, e.g., with following commands:
# convert client certificate and private key to PEM format
openssl pkcs12 -in example.pfx -out user.pem -clcerts
# convert CA certificate (if included in PFX file) to PEM format
openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
wpa_cli
-------
wpa_cli is a text-based frontend program for interacting with
wpa_supplicant. It is used to query current status, change
configuration, trigger events, and request interactive user input.
wpa_cli can show the current authentication status, selected security
mode, dot11 and dot1x MIBs, etc. In addition, it can configure some
variables like EAPOL state machine parameters and trigger events like
reassociation and IEEE 802.1X logoff/logon. wpa_cli provides a user
interface to request authentication information, like username and
password, if these are not included in the configuration. This can be
used to implement, e.g., one-time-passwords or generic token card
authentication where the authentication is based on a
challenge-response that uses an external device for generating the
response.
The control interface of wpa_supplicant can be configured to allow
non-root user access (ctrl_interface_group in the configuration
file). This makes it possible to run wpa_cli with a normal user
account.
wpa_cli supports two modes: interactive and command line. Both modes
share the same command set and the main difference is in interactive
mode providing access to unsolicited messages (event messages,
username/password requests).
Interactive mode is started when wpa_cli is executed without including
the command as a command line parameter. Commands are then entered on
the wpa_cli prompt. In command line mode, the same commands are
entered as command line arguments for wpa_cli.
Interactive authentication parameters request
When wpa_supplicant need authentication parameters, like username and
password, which are not present in the configuration file, it sends a
request message to all attached frontend programs, e.g., wpa_cli in
interactive mode. wpa_cli shows these requests with
"CTRL-REQ-<type>-<id>:<text>" prefix. <type> is IDENTITY, PASSWORD, or
OTP (one-time-password). <id> is a unique identifier for the current
network. <text> is description of the request. In case of OTP request,
it includes the challenge from the authentication server.
The reply to these requests can be given with 'identity', 'password',
and 'otp' commands. <id> needs to be copied from the the matching
request. 'password' and 'otp' commands can be used regardless of
whether the request was for PASSWORD or OTP. The main difference
between these two commands is that values given with 'password' are
remembered as long as wpa_supplicant is running whereas values given
with 'otp' are used only once and then forgotten, i.e., wpa_supplicant
will ask frontend for a new value for every use. This can be used to
implement one-time-password lists and generic token card -based
authentication.
Example request for password and a matching reply:
CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
> password 1 mysecretpassword
Example request for generic token card challenge-response:
CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
> otp 2 9876
wpa_cli commands
status = get current WPA/EAPOL/EAP status
mib = get MIB variables (dot1x, dot11)
help = show this usage help
interface [ifname] = show interfaces/select interface
level <debug level> = change debug level
license = show full wpa_cli license
logoff = IEEE 802.1X EAPOL state machine logoff
logon = IEEE 802.1X EAPOL state machine logon
set = set variables (shows list of variables when run without arguments)
pmksa = show PMKSA cache
reassociate = force reassociation
reconfigure = force wpa_supplicant to re-read its configuration file
preauthenticate <BSSID> = force preauthentication
identity <network id> <identity> = configure identity for an SSID
password <network id> <password> = configure password for an SSID
pin <network id> <pin> = configure pin for an SSID
otp <network id> <password> = configure one-time-password for an SSID
passphrase <network id> <passphrase> = configure private key passphrase
for an SSID
bssid <network id> <BSSID> = set preferred BSSID for an SSID
list_networks = list configured networks
select_network <network id> = select a network (disable others)
enable_network <network id> = enable a network
disable_network <network id> = disable a network
add_network = add a network
remove_network <network id> = remove a network
set_network <network id> <variable> <value> = set network variables (shows
list of variables when run without arguments)
get_network <network id> <variable> = get network variables
save_config = save the current configuration
disconnect = disconnect and wait for reassociate command before connecting
scan = request new BSS scan
scan_results = get latest scan results
get_capability <eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilities
terminate = terminate wpa_supplicant
quit = exit wpa_cli
wpa_cli command line options
wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] [-a<action file>] \
[-P<pid file>] [-g<global ctrl>] [command..]
-h = help (show this usage text)
-v = shown version information
-a = run in daemon mode executing the action file based on events from
wpa_supplicant
-B = run a daemon in the background
default path: /var/run/wpa_supplicant
default interface: first interface found in socket path
Using wpa_cli to run external program on connect/disconnect
-----------------------------------------------------------
wpa_cli can used to run external programs whenever wpa_supplicant
connects or disconnects from a network. This can be used, e.g., to
update network configuration and/or trigget DHCP client to update IP
addresses, etc.
One wpa_cli process in "action" mode needs to be started for each
interface. For example, the following command starts wpa_cli for the
default interface (-i can be used to select the interface in case of
more than one interface being used at the same time):
wpa_cli -a/sbin/wpa_action.sh -B
The action file (-a option, /sbin/wpa_action.sh in this example) will
be executed whenever wpa_supplicant completes authentication (connect
event) or detects disconnection). The action script will be called
with two command line arguments: interface name and event (CONNECTED
or DISCONNECTED). If the action script needs to get more information
about the current network, it can use 'wpa_cli status' to query
wpa_supplicant for more information.
Following example can be used as a simple template for an action
script:
#!/bin/sh
IFNAME=$1
CMD=$2
if [ "$CMD" = "CONNECTED" ]; then
SSID=`wpa_cli -i$IFNAME status | grep ^ssid= | cut -f2- -d=`
# configure network, signal DHCP client, etc.
fi
if [ "$CMD" = "DISCONNECTED" ]; then
# remove network configuration, if needed
SSID=
fi
Integrating with pcmcia-cs/cardmgr scripts
------------------------------------------
wpa_supplicant needs to be running when using a wireless network with
WPA. It can be started either from system startup scripts or from
pcmcia-cs/cardmgr scripts (when using PC Cards). WPA handshake must be
completed before data frames can be exchanged, so wpa_supplicant
should be started before DHCP client.
For example, following small changes to pcmcia-cs scripts can be used
to enable WPA support:
Add MODE="Managed" and WPA="y" to the network scheme in
/etc/pcmcia/wireless.opts.
Add the following block to the end of 'start' action handler in
/etc/pcmcia/wireless:
if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
/usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf \
-i$DEVICE
fi
Add the following block to the end of 'stop' action handler (may need
to be separated from other actions) in /etc/pcmcia/wireless:
if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
killall wpa_supplicant
fi
This will make cardmgr start wpa_supplicant when the card is plugged
in.
Dynamic interface add and operation without configuration files
---------------------------------------------------------------
wpa_supplicant can be started without any configuration files or
network interfaces. When used in this way, a global (i.e., per
wpa_supplicant process) control interface is used to add and remove
network interfaces. Each network interface can then be configured
through a per-network interface control interface. For example,
following commands show how to start wpa_supplicant without any
network interfaces and then add a network interface and configure a
network (SSID):
# Start wpa_supplicant in the background
wpa_supplicant -g/var/run/wpa_supplicant-global -B
# Add a new interface (wlan0, no configuration file, driver=nl80211, and
# enable control interface)
wpa_cli -g/var/run/wpa_supplicant-global interface_add wlan0 \
"" nl80211 /var/run/wpa_supplicant
# Configure a network using the newly added network interface:
wpa_cli -iwlan0 add_network
wpa_cli -iwlan0 set_network 0 ssid '"test"'
wpa_cli -iwlan0 set_network 0 key_mgmt WPA-PSK
wpa_cli -iwlan0 set_network 0 psk '"12345678"'
wpa_cli -iwlan0 set_network 0 pairwise TKIP
wpa_cli -iwlan0 set_network 0 group TKIP
wpa_cli -iwlan0 set_network 0 proto WPA
wpa_cli -iwlan0 enable_network 0
# At this point, the new network interface should start trying to associate
# with the WPA-PSK network using SSID test.
# Remove network interface
wpa_cli -g/var/run/wpa_supplicant-global interface_remove wlan0
Privilege separation
--------------------
To minimize the size of code that needs to be run with root privileges
(e.g., to control wireless interface operation), wpa_supplicant
supports optional privilege separation. If enabled, this separates the
privileged operations into a separate process (wpa_priv) while leaving
rest of the code (e.g., EAP authentication and WPA handshakes) into an
unprivileged process (wpa_supplicant) that can be run as non-root
user. Privilege separation restricts the effects of potential software
errors by containing the majority of the code in an unprivileged
process to avoid full system compromise.
Privilege separation is not enabled by default and it can be enabled
by adding CONFIG_PRIVSEP=y to the build configuration (.config). When
enabled, the privileged operations (driver wrapper and l2_packet) are
linked into a separate daemon program, wpa_priv. The unprivileged
program, wpa_supplicant, will be built with a special driver/l2_packet
wrappers that communicate with the privileged wpa_priv process to
perform the needed operations. wpa_priv can control what privileged
are allowed.
wpa_priv needs to be run with network admin privileges (usually, root
user). It opens a UNIX domain socket for each interface that is
included on the command line; any other interface will be off limits
for wpa_supplicant in this kind of configuration. After this,
wpa_supplicant can be run as a non-root user (e.g., all standard users
on a laptop or as a special non-privileged user account created just
for this purpose to limit access to user files even further).
Example configuration:
- create user group for users that are allowed to use wpa_supplicant
('wpapriv' in this example) and assign users that should be able to
use wpa_supplicant into that group
- create /var/run/wpa_priv directory for UNIX domain sockets and control
user access by setting it accessible only for the wpapriv group:
mkdir /var/run/wpa_priv
chown root:wpapriv /var/run/wpa_priv
chmod 0750 /var/run/wpa_priv
- start wpa_priv as root (e.g., from system startup scripts) with the
enabled interfaces configured on the command line:
wpa_priv -B -P /var/run/wpa_priv.pid nl80211:wlan0
- run wpa_supplicant as non-root with a user that is in wpapriv group:
wpa_supplicant -i ath0 -c wpa_supplicant.conf
wpa_priv does not use the network interface before wpa_supplicant is
started, so it is fine to include network interfaces that are not
available at the time wpa_priv is started. As an alternative, wpa_priv
can be started when an interface is added (hotplug/udev/etc. scripts).
wpa_priv can control multiple interface with one process, but it is
also possible to run multiple wpa_priv processes at the same time, if
desired.
It should be noted that the interface used between wpa_supplicant and
wpa_priv does not include all the capabilities of the wpa_supplicant
driver interface and at times, this interface lacks update especially
for recent addition. Consequently, use of wpa_priv does come with the
price of somewhat reduced available functionality. The next section
describing how wpa_supplicant can be used with reduced privileges
without having to handle the complexity of separate wpa_priv. While that
approve does not provide separation for network admin capabilities, it
does allow other root privileges to be dropped without the drawbacks of
the wpa_priv process.
Linux capabilities instead of privileged process
------------------------------------------------
wpa_supplicant performs operations that need special permissions, e.g.,
to control the network connection. Traditionally this has been achieved
by running wpa_supplicant as a privileged process with effective user id
0 (root). Linux capabilities can be used to provide restricted set of
capabilities to match the functions needed by wpa_supplicant. The
minimum set of capabilities needed for the operations is CAP_NET_ADMIN
and CAP_NET_RAW.
setcap(8) can be used to set file capabilities. For example:
sudo setcap cap_net_raw,cap_net_admin+ep wpa_supplicant
Please note that this would give anyone being able to run that
wpa_supplicant binary access to the additional capabilities. This can
further be limited by file owner/group and mode bits. For example:
sudo chown wpas wpa_supplicant
sudo chmod 0100 wpa_supplicant
This combination of setcap, chown, and chmod commands would allow wpas
user to execute wpa_supplicant with additional network admin/raw
capabilities.
Common way style of creating a control interface socket in
/var/run/wpa_supplicant could not be done by this user, but this
directory could be created before starting the wpa_supplicant and set to
suitable mode to allow wpa_supplicant to create sockets
there. Alternatively, other directory or abstract socket namespace could
be used for the control interface.
External requests for radio control
-----------------------------------
External programs can request wpa_supplicant to not start offchannel
operations during other tasks that may need exclusive control of the
radio. The RADIO_WORK control interface command can be used for this.
"RADIO_WORK add <name> [freq=<MHz>] [timeout=<seconds>]" command can be
used to reserve a slot for radio access. If freq is specified, other
radio work items on the same channel may be completed in
parallel. Otherwise, all other radio work items are blocked during
execution. Timeout is set to 10 seconds by default to avoid blocking
wpa_supplicant operations for excessive time. If a longer (or shorter)
safety timeout is needed, that can be specified with the optional
timeout parameter. This command returns an identifier for the radio work
item.
Once the radio work item has been started, "EXT-RADIO-WORK-START <id>"
event message is indicated that the external processing can start. Once
the operation has been completed, "RADIO_WORK done <id>" is used to
indicate that to wpa_supplicant. This allows other radio works to be
performed. If this command is forgotten (e.g., due to the external
program terminating), wpa_supplicant will time out the radio work item
and send "EXT-RADIO-WORK-TIMEOUT <id>" event to indicate that this has
happened. "RADIO_WORK done <id>" can also be used to cancel items that
have not yet been started.
For example, in wpa_cli interactive mode:
> radio_work add test
1
<3>EXT-RADIO-WORK-START 1
> radio_work show
ext:test@wlan0:0:1:2.487797
> radio_work done 1
OK
> radio_work show
> radio_work done 3
OK
> radio_work show
ext:test freq=2412 timeout=30@wlan0:2412:1:28.583483
<3>EXT-RADIO-WORK-TIMEOUT 2
> radio_work add test2 freq=2412 timeout=60
5
<3>EXT-RADIO-WORK-START 5
> radio_work add test3
6
> radio_work add test4
7
> radio_work show
ext:test2 freq=2412 timeout=60@wlan0:2412:1:9.751844
ext:test3@wlan0:0:0:5.071812
ext:test4@wlan0:0:0:3.143870
> radio_work done 6
OK
> radio_work show
ext:test2 freq=2412 timeout=60@wlan0:2412:1:16.287869
ext:test4@wlan0:0:0:9.679895
> radio_work done 5
OK
<3>EXT-RADIO-WORK-START 7
<3>EXT-RADIO-WORK-TIMEOUT 7
+
+
+DSCP policy procedures
+----------------------
+
+DSCP policy procedures defined in WFA QoS Management-R2 program
+facilitates AP devices to configure DSCP settings for specific uplink
+data streams.
+
+An AP may transmit a DSCP Policy Request frame containing zero or more
+QoS Management IEs to an associated STA which supports DSCP policy
+procedures. Each QoS Management element in a DSCP Policy Request frame
+represents one DSCP policy, and shall include one DSCP Policy attribute
+including a DSCP Policy ID, Request type, and a DSCP value.
+
+wpa_supplicant sends control interface event messages consisting details
+of DSCP policies requested by the AP through a DSCP Policy Request frame
+to external programs. The format of the control interface event messages
+is as shown below:
+
+- Control interface event message format to indicate DSCP request start
+
+ <3>CTRL-EVENT-DSCP-POLICY request_start [clear_all] [more]
+
+ clear_all - AP requested to clear all DSCP policies configured earlier
+ more - AP may request to configure more DSCP policies with new DSCP
+ request
+
+- Control interface event message format to add new policy
+
+ <3>CTRL-EVENT-DSCP-POLICY add <policy_id> <dscp_value> <ip_version=0|4|6>
+ [protocol] [source ip] [destination_ip]/[domain name] [source port]
+ [[<start_port> <end_port>]/destination port]
+
+ ip_version = 0: Both IPv4 and IPv6
+ = 4: IPv4
+ = 6: IPv6
+ protocol: Internet Protocol Numbers as per IETF RFCs
+ = 6: TCP
+ = 17: UDP
+ = 50: ESP
+
+- Control interface event message format to remove a particular policy,
+ identified by the policy_id attribute.
+
+ <3>CTRL-EVENT-DSCP-POLICY remove <policy_id>
+
+- DSCP policy may get rejected due to invalid policy parameters. Ccontrol
+ interface event message format for rejected policy.
+
+ <3>CTRL-EVENT-DSCP-POLICY reject <policy_id>
+
+- Control interface event message format to indicate end of DSCP request.
+
+ <3>CTRL-EVENT-DSCP-POLICY request_end
+
+- External applications shall clear active DSCP policies upon receiving
+ "CTRL-EVENT-DISCONNECTED" or "CTRL-EVENT-DSCP-POLICY clear_all" events.
+
+- Control interface event message format to indicate wpa_supplicant started
+ a timer to wait until the unsolicited DSCP request from the AP.
+
+ <3>CTRL-EVENT-DSCP-POLICY request_wait start
+
+- Control interface event message format to indicate timeout to receive the
+ unsolicited DSCP request. This event is expected only when an unsolicited
+ DSCP request is not received from the AP before timeout.
+
+ <3>CTRL-EVENT-DSCP-POLICY request_wait end
+
+DSCP Response:
+A QoS Management STA that enables DSCP Policy capability shall respond
+with DSCP response on receipt of a successful DSCP request from its
+associated AP. wpa_supplicant sends DSCP policy response based on the
+control interface command received from the user is as below:
+
+DSCP_RESP <[reset]>/<[solicited] [policy_id=1 status=0...]> [more]
+
+DSCP Query:
+DSCP Policy Query enables a STA to query its associated AP for DSCP
+policies applicable to the STA. Currently, this includes support to send
+a wildcard DSCP query or a DSCP query with a single domain name
+attribute. The command format for the DSCP query command is as follows:
+DSCP_QUERY <wildcard>/<domain_name=<string>>
diff --git a/contrib/wpa/wpa_supplicant/ap.c b/contrib/wpa/wpa_supplicant/ap.c
index cdf0ed5c7b5f..6a0a69e68ee6 100644
--- a/contrib/wpa/wpa_supplicant/ap.c
+++ b/contrib/wpa/wpa_supplicant/ap.c
@@ -1,1841 +1,1945 @@
/*
* WPA Supplicant - Basic AP mode support routines
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2009, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/uuid.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "crypto/dh_group5.h"
#include "ap/hostapd.h"
#include "ap/ap_config.h"
#include "ap/ap_drv_ops.h"
#ifdef NEED_AP_MLME
#include "ap/ieee802_11.h"
#endif /* NEED_AP_MLME */
#include "ap/beacon.h"
#include "ap/ieee802_1x.h"
#include "ap/wps_hostapd.h"
#include "ap/ctrl_iface_ap.h"
#include "ap/dfs.h"
#include "wps/wps.h"
#include "common/ieee802_11_defs.h"
#include "config_ssid.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "p2p_supplicant.h"
#include "ap.h"
#include "ap/sta_info.h"
#include "notify.h"
#ifdef CONFIG_WPS
static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
static bool is_chanwidth160_supported(struct hostapd_hw_modes *mode,
struct hostapd_config *conf)
{
#ifdef CONFIG_IEEE80211AX
if (conf->ieee80211ax) {
struct he_capabilities *he_cap;
he_cap = &mode->he_capab[IEEE80211_MODE_AP];
if (he_cap->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
(HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G))
return true;
}
#endif /* CONFIG_IEEE80211AX */
if (mode->vht_capab & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
return true;
return false;
}
#endif /* CONFIG_P2P */
static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct hostapd_config *conf,
struct hostapd_hw_modes *mode)
{
#ifdef CONFIG_P2P
u8 center_chan = 0;
u8 channel = conf->channel;
#endif /* CONFIG_P2P */
u8 freq_seg_idx;
if (!conf->secondary_channel)
goto no_vht;
/* Use the maximum oper channel width if it's given. */
if (ssid->max_oper_chwidth)
hostapd_set_oper_chwidth(conf, ssid->max_oper_chwidth);
+ if (hostapd_get_oper_chwidth(conf))
+ ieee80211_freq_to_channel_ext(ssid->frequency, 0,
+ hostapd_get_oper_chwidth(conf),
+ &conf->op_class,
+ &conf->channel);
if (hostapd_get_oper_chwidth(conf) == CHANWIDTH_80P80MHZ) {
ieee80211_freq_to_chan(ssid->vht_center_freq2,
&freq_seg_idx);
hostapd_set_oper_centr_freq_seg1_idx(conf, freq_seg_idx);
}
if (!ssid->p2p_group) {
if (!ssid->vht_center_freq1)
goto no_vht;
ieee80211_freq_to_chan(ssid->vht_center_freq1,
&freq_seg_idx);
hostapd_set_oper_centr_freq_seg0_idx(conf, freq_seg_idx);
wpa_printf(MSG_DEBUG,
"VHT seg0 index %d and seg1 index %d for AP",
hostapd_get_oper_centr_freq_seg0_idx(conf),
hostapd_get_oper_centr_freq_seg1_idx(conf));
return;
}
#ifdef CONFIG_P2P
switch (hostapd_get_oper_chwidth(conf)) {
case CHANWIDTH_80MHZ:
case CHANWIDTH_80P80MHZ:
center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel,
conf->op_class);
wpa_printf(MSG_DEBUG,
"VHT center channel %u for 80 or 80+80 MHz bandwidth",
center_chan);
break;
case CHANWIDTH_160MHZ:
center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel,
conf->op_class);
wpa_printf(MSG_DEBUG,
"VHT center channel %u for 160 MHz bandwidth",
center_chan);
break;
default:
/*
* conf->vht_oper_chwidth might not be set for non-P2P GO cases,
* try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is
* not supported.
*/
hostapd_set_oper_chwidth(conf, CHANWIDTH_160MHZ);
ieee80211_freq_to_channel_ext(ssid->frequency, 0,
conf->vht_oper_chwidth,
&conf->op_class,
&conf->channel);
center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel,
conf->op_class);
if (center_chan && is_chanwidth160_supported(mode, conf)) {
wpa_printf(MSG_DEBUG,
"VHT center channel %u for auto-selected 160 MHz bandwidth",
center_chan);
} else {
hostapd_set_oper_chwidth(conf, CHANWIDTH_80MHZ);
ieee80211_freq_to_channel_ext(ssid->frequency, 0,
conf->vht_oper_chwidth,
&conf->op_class,
&conf->channel);
center_chan = wpas_p2p_get_vht80_center(wpa_s, mode,
channel,
conf->op_class);
wpa_printf(MSG_DEBUG,
"VHT center channel %u for auto-selected 80 MHz bandwidth",
center_chan);
}
break;
}
if (!center_chan)
goto no_vht;
hostapd_set_oper_centr_freq_seg0_idx(conf, center_chan);
wpa_printf(MSG_DEBUG, "VHT seg0 index %d for P2P GO",
hostapd_get_oper_centr_freq_seg0_idx(conf));
return;
#endif /* CONFIG_P2P */
no_vht:
wpa_printf(MSG_DEBUG,
"No VHT higher bandwidth support for the selected channel %d",
conf->channel);
hostapd_set_oper_centr_freq_seg0_idx(
conf, conf->channel + conf->secondary_channel * 2);
hostapd_set_oper_chwidth(conf, CHANWIDTH_USE_HT);
}
static struct hostapd_hw_modes *
wpa_supplicant_find_hw_mode(struct wpa_supplicant *wpa_s,
enum hostapd_hw_mode hw_mode)
{
struct hostapd_hw_modes *mode = NULL;
int i;
for (i = 0; i < wpa_s->hw.num_modes; i++) {
if (wpa_s->hw.modes[i].mode == hw_mode) {
mode = &wpa_s->hw.modes[i];
break;
}
}
return mode;
}
+#ifdef CONFIG_P2P
+
+static int get_max_oper_chwidth_6ghz(int chwidth)
+{
+ switch (chwidth) {
+ case CHANWIDTH_USE_HT:
+ return 20;
+ case CHANWIDTH_40MHZ_6GHZ:
+ return 40;
+ case CHANWIDTH_80MHZ:
+ return 80;
+ case CHANWIDTH_80P80MHZ:
+ case CHANWIDTH_160MHZ:
+ return 160;
+ default:
+ return 0;
+ }
+}
+
+
+static void wpas_conf_ap_he_6ghz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ struct wpa_ssid *ssid,
+ struct hostapd_config *conf)
+{
+ bool is_chanwidth_40_80, is_chanwidth_160;
+ int he_chanwidth;
+
+ he_chanwidth =
+ mode->he_capab[wpas_mode_to_ieee80211_mode(
+ ssid->mode)].phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+ is_chanwidth_40_80 = he_chanwidth &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+ is_chanwidth_160 = he_chanwidth &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+
+ wpa_printf(MSG_DEBUG,
+ "Enable HE support (p2p_group=%d he_chwidth_cap=%d)",
+ ssid->p2p_group, he_chanwidth);
+
+ if (mode->he_capab[wpas_mode_to_ieee80211_mode(
+ ssid->mode)].he_supported &&
+ ssid->he)
+ conf->ieee80211ax = 1;
+
+ if (is_chanwidth_40_80 && ssid->p2p_group &&
+ get_max_oper_chwidth_6ghz(ssid->max_oper_chwidth) >= 40) {
+ conf->secondary_channel =
+ wpas_p2p_get_sec_channel_offset_40mhz(
+ wpa_s, mode, conf->channel);
+ wpa_printf(MSG_DEBUG,
+ "Secondary channel offset %d for P2P group",
+ conf->secondary_channel);
+ if (ssid->max_oper_chwidth == CHANWIDTH_40MHZ_6GHZ)
+ ssid->max_oper_chwidth = CHANWIDTH_USE_HT;
+ }
+
+ if ((is_chanwidth_40_80 || is_chanwidth_160) && ssid->p2p_group &&
+ get_max_oper_chwidth_6ghz(ssid->max_oper_chwidth) >= 80)
+ wpas_conf_ap_vht(wpa_s, ssid, conf, mode);
+}
+
+#endif /* CONFIG_P2P */
+
+
int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct hostapd_config *conf)
{
conf->hw_mode = ieee80211_freq_to_channel_ext(ssid->frequency, 0,
- ssid->max_oper_chwidth,
+ CHANWIDTH_USE_HT,
&conf->op_class,
&conf->channel);
- /* ssid->max_oper_chwidth is not valid in all cases, so fall back to the
- * less specific mechanism, if needed, at least for now */
- if (conf->hw_mode == NUM_HOSTAPD_MODES)
- conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
- &conf->channel);
if (conf->hw_mode == NUM_HOSTAPD_MODES) {
wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
ssid->frequency);
return -1;
}
/*
* Enable HT20 if the driver supports it, by setting conf->ieee80211n
* and a mask of allowed capabilities within conf->ht_capab.
* Using default config settings for: conf->ht_op_mode_fixed,
* conf->secondary_channel, conf->require_ht
*/
if (wpa_s->hw.modes) {
struct hostapd_hw_modes *mode = NULL;
int no_ht = 0;
wpa_printf(MSG_DEBUG,
"Determining HT/VHT options based on driver capabilities (freq=%u chan=%u)",
ssid->frequency, conf->channel);
- mode = wpa_supplicant_find_hw_mode(wpa_s, conf->hw_mode);
+ mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+ conf->hw_mode, is_6ghz_freq(ssid->frequency));
/* May drop to IEEE 802.11b if the driver does not support IEEE
* 802.11g */
if (!mode && conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
wpa_printf(MSG_INFO,
"Try downgrade to IEEE 802.11b as 802.11g is not supported by the current hardware");
mode = wpa_supplicant_find_hw_mode(wpa_s,
conf->hw_mode);
}
if (!mode) {
wpa_printf(MSG_ERROR,
"No match between requested and supported hw modes found");
return -1;
}
#ifdef CONFIG_HT_OVERRIDES
if (ssid->disable_ht)
ssid->ht = 0;
#endif /* CONFIG_HT_OVERRIDES */
if (!ssid->ht) {
wpa_printf(MSG_DEBUG,
"HT not enabled in network profile");
conf->ieee80211n = 0;
conf->ht_capab = 0;
no_ht = 1;
}
- if (!no_ht && mode && mode->ht_capab) {
+ if (mode && is_6ghz_freq(ssid->frequency) &&
+ conf->hw_mode == HOSTAPD_MODE_IEEE80211A) {
+#ifdef CONFIG_P2P
+ wpas_conf_ap_he_6ghz(wpa_s, mode, ssid, conf);
+#endif /* CONFIG_P2P */
+ } else if (!no_ht && mode && mode->ht_capab) {
wpa_printf(MSG_DEBUG,
"Enable HT support (p2p_group=%d 11a=%d ht40_hw_capab=%d ssid->ht40=%d)",
ssid->p2p_group,
conf->hw_mode == HOSTAPD_MODE_IEEE80211A,
!!(mode->ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET),
ssid->ht40);
conf->ieee80211n = 1;
if (ssid->ht40 &&
(mode->ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
conf->secondary_channel = ssid->ht40;
else
conf->secondary_channel = 0;
#ifdef CONFIG_P2P
if (ssid->p2p_group &&
conf->hw_mode == HOSTAPD_MODE_IEEE80211A &&
(mode->ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
ssid->ht40) {
conf->secondary_channel =
- wpas_p2p_get_ht40_mode(wpa_s, mode,
- conf->channel);
+ wpas_p2p_get_sec_channel_offset_40mhz(
+ wpa_s, mode, conf->channel);
wpa_printf(MSG_DEBUG,
"HT secondary channel offset %d for P2P group",
conf->secondary_channel);
} else if (ssid->p2p_group && conf->secondary_channel &&
conf->hw_mode != HOSTAPD_MODE_IEEE80211A) {
/* This ended up trying to configure invalid
* 2.4 GHz channels (e.g., HT40+ on channel 11)
* in some cases, so clear the secondary channel
* configuration now to avoid such cases that
* would lead to group formation failures. */
wpa_printf(MSG_DEBUG,
"Disable HT secondary channel for P2P group on 2.4 GHz");
conf->secondary_channel = 0;
}
#endif /* CONFIG_P2P */
if (!ssid->p2p_group &&
(mode->ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
conf->secondary_channel = ssid->ht40;
wpa_printf(MSG_DEBUG,
"HT secondary channel offset %d for AP",
conf->secondary_channel);
}
if (conf->secondary_channel)
conf->ht_capab |=
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
/*
* white-list capabilities that won't cause issues
* to connecting stations, while leaving the current
* capabilities intact (currently disabled SMPS).
*/
conf->ht_capab |= mode->ht_capab &
(HT_CAP_INFO_GREEN_FIELD |
HT_CAP_INFO_SHORT_GI20MHZ |
HT_CAP_INFO_SHORT_GI40MHZ |
HT_CAP_INFO_RX_STBC_MASK |
HT_CAP_INFO_TX_STBC |
HT_CAP_INFO_MAX_AMSDU_SIZE);
/* check this before VHT, because setting oper chan
* width and friends is the same call for HE and VHT
* and checks if conf->ieee8021ax == 1 */
if (mode->he_capab[wpas_mode_to_ieee80211_mode(
ssid->mode)].he_supported &&
ssid->he)
conf->ieee80211ax = 1;
if (mode->vht_capab && ssid->vht) {
conf->ieee80211ac = 1;
conf->vht_capab |= mode->vht_capab;
wpas_conf_ap_vht(wpa_s, ssid, conf, mode);
}
}
}
if (conf->secondary_channel) {
struct wpa_supplicant *iface;
for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
{
if (iface == wpa_s ||
iface->wpa_state < WPA_AUTHENTICATING ||
(int) iface->assoc_freq != ssid->frequency)
continue;
/*
* Do not allow 40 MHz co-ex PRI/SEC switch to force us
* to change our PRI channel since we have an existing,
* concurrent connection on that channel and doing
* multi-channel concurrency is likely to cause more
* harm than using different PRI/SEC selection in
* environment with multiple BSSes on these two channels
* with mixed 20 MHz or PRI channel selection.
*/
conf->no_pri_sec_switch = 1;
}
}
return 0;
}
static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct hostapd_config *conf)
{
struct hostapd_bss_config *bss = conf->bss[0];
conf->driver = wpa_s->driver;
os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface));
if (wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf))
return -1;
if (ssid->pbss > 1) {
wpa_printf(MSG_ERROR, "Invalid pbss value(%d) for AP mode",
ssid->pbss);
return -1;
}
bss->pbss = ssid->pbss;
#ifdef CONFIG_ACS
if (ssid->acs) {
/* Setting channel to 0 in order to enable ACS */
conf->channel = 0;
wpa_printf(MSG_DEBUG, "Use automatic channel selection");
}
#endif /* CONFIG_ACS */
if (ieee80211_is_dfs(ssid->frequency, wpa_s->hw.modes,
wpa_s->hw.num_modes) && wpa_s->conf->country[0]) {
conf->ieee80211h = 1;
conf->ieee80211d = 1;
conf->country[0] = wpa_s->conf->country[0];
conf->country[1] = wpa_s->conf->country[1];
conf->country[2] = ' ';
}
#ifdef CONFIG_P2P
if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G &&
(ssid->mode == WPAS_MODE_P2P_GO ||
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)) {
/* Remove 802.11b rates from supported and basic rate sets */
int *list = os_malloc(4 * sizeof(int));
if (list) {
list[0] = 60;
list[1] = 120;
list[2] = 240;
list[3] = -1;
}
conf->basic_rates = list;
list = os_malloc(9 * sizeof(int));
if (list) {
list[0] = 60;
list[1] = 90;
list[2] = 120;
list[3] = 180;
list[4] = 240;
list[5] = 360;
list[6] = 480;
list[7] = 540;
list[8] = -1;
}
conf->supported_rates = list;
}
#ifdef CONFIG_IEEE80211AX
if (ssid->mode == WPAS_MODE_P2P_GO ||
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
conf->ieee80211ax = ssid->he;
#endif /* CONFIG_IEEE80211AX */
bss->isolate = !wpa_s->conf->p2p_intra_bss;
bss->extended_key_id = wpa_s->conf->extended_key_id;
bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk;
bss->wpa_deny_ptk0_rekey = ssid->wpa_deny_ptk0_rekey;
if (ssid->p2p_group) {
os_memcpy(bss->ip_addr_go, wpa_s->p2pdev->conf->ip_addr_go, 4);
os_memcpy(bss->ip_addr_mask, wpa_s->p2pdev->conf->ip_addr_mask,
4);
os_memcpy(bss->ip_addr_start,
wpa_s->p2pdev->conf->ip_addr_start, 4);
os_memcpy(bss->ip_addr_end, wpa_s->p2pdev->conf->ip_addr_end,
4);
}
#endif /* CONFIG_P2P */
if (ssid->ssid_len == 0) {
wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
return -1;
}
os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len);
bss->ssid.ssid_len = ssid->ssid_len;
bss->ssid.ssid_set = 1;
bss->ignore_broadcast_ssid = ssid->ignore_broadcast_ssid;
if (ssid->auth_alg)
bss->auth_algs = ssid->auth_alg;
if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt))
bss->wpa = ssid->proto;
if (ssid->key_mgmt == DEFAULT_KEY_MGMT)
bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
else
bss->wpa_key_mgmt = ssid->key_mgmt;
bss->wpa_pairwise = ssid->pairwise_cipher;
if (wpa_key_mgmt_sae(bss->wpa_key_mgmt) && ssid->passphrase) {
bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
} else if (ssid->psk_set) {
bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk));
bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
if (bss->ssid.wpa_psk == NULL)
return -1;
os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN);
bss->ssid.wpa_psk->group = 1;
bss->ssid.wpa_psk_set = 1;
} else if (ssid->passphrase) {
bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
#ifdef CONFIG_WEP
} else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] ||
ssid->wep_key_len[2] || ssid->wep_key_len[3]) {
struct hostapd_wep_keys *wep = &bss->ssid.wep;
int i;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i] == 0)
continue;
wep->key[i] = os_memdup(ssid->wep_key[i],
ssid->wep_key_len[i]);
if (wep->key[i] == NULL)
return -1;
wep->len[i] = ssid->wep_key_len[i];
}
wep->idx = ssid->wep_tx_keyidx;
wep->keys_set = 1;
#endif /* CONFIG_WEP */
}
#ifdef CONFIG_SAE
if (ssid->sae_password) {
struct sae_password_entry *pw;
pw = os_zalloc(sizeof(*pw));
if (!pw)
return -1;
os_memset(pw->peer_addr, 0xff, ETH_ALEN);
pw->password = os_strdup(ssid->sae_password);
if (!pw->password) {
os_free(pw);
return -1;
}
if (ssid->sae_password_id) {
pw->identifier = os_strdup(ssid->sae_password_id);
if (!pw->identifier) {
str_clear_free(pw->password);
os_free(pw);
return -1;
}
}
pw->next = bss->sae_passwords;
bss->sae_passwords = pw;
}
- bss->sae_pwe = wpa_s->conf->sae_pwe;
+ if (ssid->sae_pwe != DEFAULT_SAE_PWE)
+ bss->sae_pwe = ssid->sae_pwe;
+ else
+ bss->sae_pwe = wpa_s->conf->sae_pwe;
#endif /* CONFIG_SAE */
if (wpa_s->conf->go_interworking) {
wpa_printf(MSG_DEBUG,
"P2P: Enable Interworking with access_network_type: %d",
wpa_s->conf->go_access_network_type);
bss->interworking = wpa_s->conf->go_interworking;
bss->access_network_type = wpa_s->conf->go_access_network_type;
bss->internet = wpa_s->conf->go_internet;
if (wpa_s->conf->go_venue_group) {
wpa_printf(MSG_DEBUG,
"P2P: Venue group: %d Venue type: %d",
wpa_s->conf->go_venue_group,
wpa_s->conf->go_venue_type);
bss->venue_group = wpa_s->conf->go_venue_group;
bss->venue_type = wpa_s->conf->go_venue_type;
bss->venue_info_set = 1;
}
}
if (ssid->ap_max_inactivity)
bss->ap_max_inactivity = ssid->ap_max_inactivity;
if (ssid->dtim_period)
bss->dtim_period = ssid->dtim_period;
else if (wpa_s->conf->dtim_period)
bss->dtim_period = wpa_s->conf->dtim_period;
if (ssid->beacon_int)
conf->beacon_int = ssid->beacon_int;
else if (wpa_s->conf->beacon_int)
conf->beacon_int = wpa_s->conf->beacon_int;
#ifdef CONFIG_P2P
if (ssid->mode == WPAS_MODE_P2P_GO ||
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) {
wpa_printf(MSG_INFO,
"CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it",
wpa_s->conf->p2p_go_ctwindow,
conf->beacon_int);
conf->p2p_go_ctwindow = 0;
} else {
conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow;
}
}
#endif /* CONFIG_P2P */
if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
bss->rsn_pairwise = bss->wpa_pairwise;
bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
bss->rsn_pairwise);
if (bss->wpa && bss->ieee802_1x) {
bss->ssid.security_policy = SECURITY_WPA;
} else if (bss->wpa) {
bss->ssid.security_policy = SECURITY_WPA_PSK;
#ifdef CONFIG_WEP
} else if (bss->ieee802_1x) {
int cipher = WPA_CIPHER_NONE;
bss->ssid.security_policy = SECURITY_IEEE_802_1X;
bss->ssid.wep.default_len = bss->default_wep_key_len;
if (bss->default_wep_key_len)
cipher = bss->default_wep_key_len >= 13 ?
WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
bss->wpa_group = cipher;
bss->wpa_pairwise = cipher;
bss->rsn_pairwise = cipher;
} else if (bss->ssid.wep.keys_set) {
int cipher = WPA_CIPHER_WEP40;
if (bss->ssid.wep.len[0] >= 13)
cipher = WPA_CIPHER_WEP104;
bss->ssid.security_policy = SECURITY_STATIC_WEP;
bss->wpa_group = cipher;
bss->wpa_pairwise = cipher;
bss->rsn_pairwise = cipher;
#endif /* CONFIG_WEP */
} else {
bss->ssid.security_policy = SECURITY_PLAINTEXT;
bss->wpa_group = WPA_CIPHER_NONE;
bss->wpa_pairwise = WPA_CIPHER_NONE;
bss->rsn_pairwise = WPA_CIPHER_NONE;
}
if (bss->wpa_group_rekey < 86400 && (bss->wpa & 2) &&
(bss->wpa_group == WPA_CIPHER_CCMP ||
bss->wpa_group == WPA_CIPHER_GCMP ||
bss->wpa_group == WPA_CIPHER_CCMP_256 ||
bss->wpa_group == WPA_CIPHER_GCMP_256)) {
/*
* Strong ciphers do not need frequent rekeying, so increase
* the default GTK rekeying period to 24 hours.
*/
bss->wpa_group_rekey = 86400;
}
if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT)
bss->ieee80211w = ssid->ieee80211w;
#ifdef CONFIG_OCV
bss->ocv = ssid->ocv;
#endif /* CONFIG_OCV */
#ifdef CONFIG_WPS
/*
* Enable WPS by default for open and WPA/WPA2-Personal network, but
* require user interaction to actually use it. Only the internal
* Registrar is supported.
*/
if (bss->ssid.security_policy != SECURITY_WPA_PSK &&
bss->ssid.security_policy != SECURITY_PLAINTEXT)
goto no_wps;
if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
(!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ||
!(bss->wpa & 2)))
goto no_wps; /* WPS2 does not allow WPA/TKIP-only
* configuration */
if (ssid->wps_disabled)
goto no_wps;
bss->eap_server = 1;
if (!ssid->ignore_broadcast_ssid)
bss->wps_state = 2;
bss->ap_setup_locked = 2;
if (wpa_s->conf->config_methods)
bss->config_methods = os_strdup(wpa_s->conf->config_methods);
os_memcpy(bss->device_type, wpa_s->conf->device_type,
WPS_DEV_TYPE_LEN);
if (wpa_s->conf->device_name) {
bss->device_name = os_strdup(wpa_s->conf->device_name);
bss->friendly_name = os_strdup(wpa_s->conf->device_name);
}
if (wpa_s->conf->manufacturer)
bss->manufacturer = os_strdup(wpa_s->conf->manufacturer);
if (wpa_s->conf->model_name)
bss->model_name = os_strdup(wpa_s->conf->model_name);
if (wpa_s->conf->model_number)
bss->model_number = os_strdup(wpa_s->conf->model_number);
if (wpa_s->conf->serial_number)
bss->serial_number = os_strdup(wpa_s->conf->serial_number);
if (is_nil_uuid(wpa_s->conf->uuid))
os_memcpy(bss->uuid, wpa_s->wps->uuid, WPS_UUID_LEN);
else
os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
os_memcpy(bss->os_version, wpa_s->conf->os_version, 4);
bss->pbc_in_m1 = wpa_s->conf->pbc_in_m1;
if (ssid->eap.fragment_size != DEFAULT_FRAGMENT_SIZE)
bss->fragment_size = ssid->eap.fragment_size;
no_wps:
#endif /* CONFIG_WPS */
if (wpa_s->max_stations &&
wpa_s->max_stations < wpa_s->conf->max_num_sta)
bss->max_num_sta = wpa_s->max_stations;
else
bss->max_num_sta = wpa_s->conf->max_num_sta;
if (!bss->isolate)
bss->isolate = wpa_s->conf->ap_isolate;
bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack;
if (wpa_s->conf->ap_vendor_elements) {
bss->vendor_elements =
wpabuf_dup(wpa_s->conf->ap_vendor_elements);
}
+ if (wpa_s->conf->ap_assocresp_elements) {
+ bss->assocresp_elements =
+ wpabuf_dup(wpa_s->conf->ap_assocresp_elements);
+ }
bss->ftm_responder = wpa_s->conf->ftm_responder;
bss->ftm_initiator = wpa_s->conf->ftm_initiator;
bss->transition_disable = ssid->transition_disable;
return 0;
}
static void ap_public_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
{
#ifdef CONFIG_P2P
struct wpa_supplicant *wpa_s = ctx;
const struct ieee80211_mgmt *mgmt;
mgmt = (const struct ieee80211_mgmt *) buf;
if (len < IEEE80211_HDRLEN + 1)
return;
if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
return;
wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
mgmt->u.action.category,
buf + IEEE80211_HDRLEN + 1,
len - IEEE80211_HDRLEN - 1, freq);
#endif /* CONFIG_P2P */
}
static void ap_wps_event_cb(void *ctx, enum wps_event event,
union wps_event_data *data)
{
#ifdef CONFIG_P2P
struct wpa_supplicant *wpa_s = ctx;
if (event == WPS_EV_FAIL) {
struct wps_event_fail *fail = &data->fail;
if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s &&
wpa_s == wpa_s->global->p2p_group_formation) {
/*
* src/ap/wps_hostapd.c has already sent this on the
* main interface, so only send on the parent interface
* here if needed.
*/
wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL
"msg=%d config_error=%d",
fail->msg, fail->config_error);
}
wpas_p2p_wps_failed(wpa_s, fail);
}
#endif /* CONFIG_P2P */
}
static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr,
int authorized, const u8 *p2p_dev_addr)
{
wpas_notify_sta_authorized(ctx, mac_addr, authorized, p2p_dev_addr);
}
#ifdef CONFIG_P2P
static void ap_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
const u8 *psk, size_t psk_len)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL)
return;
wpas_p2p_new_psk_cb(wpa_s, mac_addr, p2p_dev_addr, psk, psk_len);
}
#endif /* CONFIG_P2P */
static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
{
#ifdef CONFIG_P2P
struct wpa_supplicant *wpa_s = ctx;
const struct ieee80211_mgmt *mgmt;
mgmt = (const struct ieee80211_mgmt *) buf;
if (len < IEEE80211_HDRLEN + 1)
return -1;
wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
mgmt->u.action.category,
buf + IEEE80211_HDRLEN + 1,
len - IEEE80211_HDRLEN - 1, freq);
#endif /* CONFIG_P2P */
return 0;
}
static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da,
const u8 *bssid, const u8 *ie, size_t ie_len,
int ssi_signal)
{
struct wpa_supplicant *wpa_s = ctx;
unsigned int freq = 0;
if (wpa_s->ap_iface)
freq = wpa_s->ap_iface->freq;
return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len,
freq, ssi_signal);
}
static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
const u8 *uuid_e)
{
struct wpa_supplicant *wpa_s = ctx;
wpas_p2p_wps_success(wpa_s, mac_addr, 1);
}
static void wpas_ap_configured_cb(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_printf(MSG_DEBUG, "AP interface setup completed - state %s",
hostapd_state_text(wpa_s->ap_iface->state));
if (wpa_s->ap_iface->state == HAPD_IFACE_DISABLED) {
wpa_supplicant_ap_deinit(wpa_s);
return;
}
#ifdef CONFIG_ACS
if (wpa_s->current_ssid && wpa_s->current_ssid->acs) {
wpa_s->assoc_freq = wpa_s->ap_iface->freq;
wpa_s->current_ssid->frequency = wpa_s->ap_iface->freq;
}
#endif /* CONFIG_ACS */
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
if (wpa_s->ap_configured_cb)
wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx,
wpa_s->ap_configured_cb_data);
}
int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct wpa_driver_associate_params params;
struct hostapd_iface *hapd_iface;
struct hostapd_config *conf;
size_t i;
if (ssid->ssid == NULL || ssid->ssid_len == 0) {
wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
return -1;
}
wpa_supplicant_ap_deinit(wpa_s);
wpa_printf(MSG_DEBUG, "Setting up AP (SSID='%s')",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
os_memset(&params, 0, sizeof(params));
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
switch (ssid->mode) {
case WPAS_MODE_AP:
case WPAS_MODE_P2P_GO:
case WPAS_MODE_P2P_GROUP_FORMATION:
params.mode = IEEE80211_MODE_AP;
break;
default:
return -1;
}
if (ssid->frequency == 0)
ssid->frequency = 2462; /* default channel 11 */
params.freq.freq = ssid->frequency;
if ((ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO) &&
ssid->enable_edmg) {
u8 primary_channel;
if (ieee80211_freq_to_chan(ssid->frequency, &primary_channel) ==
NUM_HOSTAPD_MODES) {
wpa_printf(MSG_WARNING,
"EDMG: Failed to get the primary channel");
return -1;
}
hostapd_encode_edmg_chan(ssid->enable_edmg, ssid->edmg_channel,
primary_channel, &params.freq.edmg);
}
params.wpa_proto = ssid->proto;
if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
else if (ssid->key_mgmt & WPA_KEY_MGMT_SAE)
wpa_s->key_mgmt = WPA_KEY_MGMT_SAE;
else
wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
params.key_mgmt_suite = wpa_s->key_mgmt;
wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher,
1);
if (wpa_s->pairwise_cipher < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
"cipher.");
return -1;
}
params.pairwise_suite = wpa_s->pairwise_cipher;
params.group_suite = params.pairwise_suite;
#ifdef CONFIG_P2P
if (ssid->mode == WPAS_MODE_P2P_GO ||
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
params.p2p = 1;
#endif /* CONFIG_P2P */
if (wpa_s->p2pdev->set_ap_uapsd)
params.uapsd = wpa_s->p2pdev->ap_uapsd;
else if (params.p2p && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
params.uapsd = 1; /* mandatory for P2P GO */
else
params.uapsd = -1;
if (ieee80211_is_dfs(params.freq.freq, wpa_s->hw.modes,
wpa_s->hw.num_modes))
params.freq.freq = 0; /* set channel after CAC */
if (params.p2p)
wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_GO);
else
wpa_drv_get_ext_capa(wpa_s, WPA_IF_AP_BSS);
if (wpa_drv_associate(wpa_s, &params) < 0) {
wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
return -1;
}
wpa_s->ap_iface = hapd_iface = hostapd_alloc_iface();
if (hapd_iface == NULL)
return -1;
hapd_iface->owner = wpa_s;
hapd_iface->drv_flags = wpa_s->drv_flags;
hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads;
hapd_iface->extended_capa = wpa_s->extended_capa;
hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask;
hapd_iface->extended_capa_len = wpa_s->extended_capa_len;
wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
if (conf == NULL) {
wpa_supplicant_ap_deinit(wpa_s);
return -1;
}
os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
wpa_s->conf->wmm_ac_params,
sizeof(wpa_s->conf->wmm_ac_params));
os_memcpy(wpa_s->ap_iface->conf->tx_queue, wpa_s->conf->tx_queue,
sizeof(wpa_s->conf->tx_queue));
if (params.uapsd > 0) {
conf->bss[0]->wmm_enabled = 1;
conf->bss[0]->wmm_uapsd = 1;
}
if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) {
wpa_printf(MSG_ERROR, "Failed to create AP configuration");
wpa_supplicant_ap_deinit(wpa_s);
return -1;
}
#ifdef CONFIG_P2P
if (ssid->mode == WPAS_MODE_P2P_GO)
conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER;
else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER |
P2P_GROUP_FORMATION;
#endif /* CONFIG_P2P */
hapd_iface->num_bss = conf->num_bss;
hapd_iface->bss = os_calloc(conf->num_bss,
sizeof(struct hostapd_data *));
if (hapd_iface->bss == NULL) {
wpa_supplicant_ap_deinit(wpa_s);
return -1;
}
for (i = 0; i < conf->num_bss; i++) {
hapd_iface->bss[i] =
hostapd_alloc_bss_data(hapd_iface, conf,
conf->bss[i]);
if (hapd_iface->bss[i] == NULL) {
wpa_supplicant_ap_deinit(wpa_s);
return -1;
}
hapd_iface->bss[i]->msg_ctx = wpa_s;
hapd_iface->bss[i]->msg_ctx_parent = wpa_s->p2pdev;
hapd_iface->bss[i]->public_action_cb = ap_public_action_rx;
hapd_iface->bss[i]->public_action_cb_ctx = wpa_s;
hapd_iface->bss[i]->vendor_action_cb = ap_vendor_action_rx;
hapd_iface->bss[i]->vendor_action_cb_ctx = wpa_s;
hostapd_register_probereq_cb(hapd_iface->bss[i],
ap_probe_req_rx, wpa_s);
hapd_iface->bss[i]->wps_reg_success_cb = ap_wps_reg_success_cb;
hapd_iface->bss[i]->wps_reg_success_cb_ctx = wpa_s;
hapd_iface->bss[i]->wps_event_cb = ap_wps_event_cb;
hapd_iface->bss[i]->wps_event_cb_ctx = wpa_s;
hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb;
hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s;
#ifdef CONFIG_P2P
hapd_iface->bss[i]->new_psk_cb = ap_new_psk_cb;
hapd_iface->bss[i]->new_psk_cb_ctx = wpa_s;
hapd_iface->bss[i]->p2p = wpa_s->global->p2p;
hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(wpa_s,
ssid);
#endif /* CONFIG_P2P */
hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb;
hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s;
#ifdef CONFIG_TESTING_OPTIONS
hapd_iface->bss[i]->ext_eapol_frame_io =
wpa_s->ext_eapol_frame_io;
#endif /* CONFIG_TESTING_OPTIONS */
}
os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN);
hapd_iface->bss[0]->driver = wpa_s->driver;
hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv;
wpa_s->current_ssid = ssid;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
wpa_s->assoc_freq = ssid->frequency;
wpa_s->ap_iface->conf->enable_edmg = ssid->enable_edmg;
wpa_s->ap_iface->conf->edmg_channel = ssid->edmg_channel;
#if defined(CONFIG_P2P) && defined(CONFIG_ACS)
if (wpa_s->p2p_go_do_acs) {
wpa_s->ap_iface->conf->channel = 0;
wpa_s->ap_iface->conf->hw_mode = wpa_s->p2p_go_acs_band;
ssid->acs = 1;
}
#endif /* CONFIG_P2P && CONFIG_ACS */
if (hostapd_setup_interface(wpa_s->ap_iface)) {
wpa_printf(MSG_ERROR, "Failed to initialize AP interface");
wpa_supplicant_ap_deinit(wpa_s);
return -1;
}
return 0;
}
void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_WPS
eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
#endif /* CONFIG_WPS */
if (wpa_s->ap_iface == NULL)
return;
wpa_s->current_ssid = NULL;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_s->assoc_freq = 0;
wpas_p2p_ap_deinit(wpa_s);
wpa_s->ap_iface->driver_ap_teardown =
!!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
hostapd_interface_deinit(wpa_s->ap_iface);
hostapd_interface_free(wpa_s->ap_iface);
wpa_s->ap_iface = NULL;
wpa_drv_deinit_ap(wpa_s);
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
" reason=%d locally_generated=1",
MAC2STR(wpa_s->own_addr), WLAN_REASON_DEAUTH_LEAVING);
}
void ap_tx_status(void *ctx, const u8 *addr,
const u8 *buf, size_t len, int ack)
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
hostapd_tx_status(wpa_s->ap_iface->bss[0], addr, buf, len, ack);
#endif /* NEED_AP_MLME */
}
void ap_eapol_tx_status(void *ctx, const u8 *dst,
const u8 *data, size_t len, int ack)
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
if (!wpa_s->ap_iface)
return;
hostapd_tx_status(wpa_s->ap_iface->bss[0], dst, data, len, ack);
#endif /* NEED_AP_MLME */
}
void ap_client_poll_ok(void *ctx, const u8 *addr)
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->ap_iface)
hostapd_client_poll_ok(wpa_s->ap_iface->bss[0], addr);
#endif /* NEED_AP_MLME */
}
void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds)
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], addr, wds);
#endif /* NEED_AP_MLME */
}
void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt)
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
struct hostapd_frame_info fi;
os_memset(&fi, 0, sizeof(fi));
fi.datarate = rx_mgmt->datarate;
fi.ssi_signal = rx_mgmt->ssi_signal;
ieee802_11_mgmt(wpa_s->ap_iface->bss[0], rx_mgmt->frame,
rx_mgmt->frame_len, &fi);
#endif /* NEED_AP_MLME */
}
void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok)
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
ieee802_11_mgmt_cb(wpa_s->ap_iface->bss[0], buf, len, stype, ok);
#endif /* NEED_AP_MLME */
}
void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
const u8 *src_addr, const u8 *buf, size_t len)
{
ieee802_1x_receive(wpa_s->ap_iface->bss[0], src_addr, buf, len);
}
#ifdef CONFIG_WPS
int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
const u8 *p2p_dev_addr)
{
if (!wpa_s->ap_iface)
return -1;
return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0],
p2p_dev_addr);
}
int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s)
{
struct wps_registrar *reg;
int reg_sel = 0, wps_sta = 0;
if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]->wps)
return -1;
reg = wpa_s->ap_iface->bss[0]->wps->registrar;
reg_sel = wps_registrar_wps_cancel(reg);
wps_sta = ap_for_each_sta(wpa_s->ap_iface->bss[0],
ap_sta_wps_cancel, NULL);
if (!reg_sel && !wps_sta) {
wpa_printf(MSG_DEBUG, "No WPS operation in progress at this "
"time");
return -1;
}
/*
* There are 2 cases to return wps cancel as success:
* 1. When wps cancel was initiated but no connection has been
* established with client yet.
* 2. Client is in the middle of exchanging WPS messages.
*/
return 0;
}
int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin, char *buf, size_t buflen,
int timeout)
{
int ret, ret_len = 0;
if (!wpa_s->ap_iface)
return -1;
if (pin == NULL) {
unsigned int rpin;
if (wps_generate_pin(&rpin) < 0)
return -1;
ret_len = os_snprintf(buf, buflen, "%08d", rpin);
if (os_snprintf_error(buflen, ret_len))
return -1;
pin = buf;
} else if (buf) {
ret_len = os_snprintf(buf, buflen, "%s", pin);
if (os_snprintf_error(buflen, ret_len))
return -1;
}
ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin,
timeout);
if (ret)
return -1;
return ret_len;
}
static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
{
struct wpa_supplicant *wpa_s = eloop_data;
wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
wpas_wps_ap_pin_disable(wpa_s);
}
static void wpas_wps_ap_pin_enable(struct wpa_supplicant *wpa_s, int timeout)
{
struct hostapd_data *hapd;
if (wpa_s->ap_iface == NULL)
return;
hapd = wpa_s->ap_iface->bss[0];
wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
hapd->ap_pin_failures = 0;
eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
if (timeout > 0)
eloop_register_timeout(timeout, 0,
wpas_wps_ap_pin_timeout, wpa_s, NULL);
}
void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s)
{
struct hostapd_data *hapd;
if (wpa_s->ap_iface == NULL)
return;
wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
hapd = wpa_s->ap_iface->bss[0];
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = NULL;
eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL);
}
const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout)
{
struct hostapd_data *hapd;
unsigned int pin;
char pin_txt[9];
if (wpa_s->ap_iface == NULL)
return NULL;
hapd = wpa_s->ap_iface->bss[0];
if (wps_generate_pin(&pin) < 0)
return NULL;
os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin);
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = os_strdup(pin_txt);
if (hapd->conf->ap_pin == NULL)
return NULL;
wpas_wps_ap_pin_enable(wpa_s, timeout);
return hapd->conf->ap_pin;
}
const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s)
{
struct hostapd_data *hapd;
if (wpa_s->ap_iface == NULL)
return NULL;
hapd = wpa_s->ap_iface->bss[0];
return hapd->conf->ap_pin;
}
int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
int timeout)
{
struct hostapd_data *hapd;
char pin_txt[9];
int ret;
if (wpa_s->ap_iface == NULL)
return -1;
hapd = wpa_s->ap_iface->bss[0];
ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin);
if (os_snprintf_error(sizeof(pin_txt), ret))
return -1;
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = os_strdup(pin_txt);
if (hapd->conf->ap_pin == NULL)
return -1;
wpas_wps_ap_pin_enable(wpa_s, timeout);
return 0;
}
void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s)
{
struct hostapd_data *hapd;
if (wpa_s->ap_iface == NULL)
return;
hapd = wpa_s->ap_iface->bss[0];
/*
* Registrar failed to prove its knowledge of the AP PIN. Disable AP
* PIN if this happens multiple times to slow down brute force attacks.
*/
hapd->ap_pin_failures++;
wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
hapd->ap_pin_failures);
if (hapd->ap_pin_failures < 3)
return;
wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN");
hapd->ap_pin_failures = 0;
os_free(hapd->conf->ap_pin);
hapd->conf->ap_pin = NULL;
}
#ifdef CONFIG_WPS_NFC
struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
int ndef)
{
struct hostapd_data *hapd;
if (wpa_s->ap_iface == NULL)
return NULL;
hapd = wpa_s->ap_iface->bss[0];
return hostapd_wps_nfc_config_token(hapd, ndef);
}
struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
int ndef)
{
struct hostapd_data *hapd;
if (wpa_s->ap_iface == NULL)
return NULL;
hapd = wpa_s->ap_iface->bss[0];
return hostapd_wps_nfc_hs_cr(hapd, ndef);
}
int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
const struct wpabuf *req,
const struct wpabuf *sel)
{
struct hostapd_data *hapd;
if (wpa_s->ap_iface == NULL)
return -1;
hapd = wpa_s->ap_iface->bss[0];
return hostapd_wps_nfc_report_handover(hapd, req, sel);
}
#endif /* CONFIG_WPS_NFC */
#endif /* CONFIG_WPS */
#ifdef CONFIG_CTRL_IFACE
int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
struct hostapd_data *hapd;
if (wpa_s->ap_iface)
hapd = wpa_s->ap_iface->bss[0];
else if (wpa_s->ifmsh)
hapd = wpa_s->ifmsh->bss[0];
else
return -1;
return hostapd_ctrl_iface_sta_first(hapd, buf, buflen);
}
int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
char *buf, size_t buflen)
{
struct hostapd_data *hapd;
if (wpa_s->ap_iface)
hapd = wpa_s->ap_iface->bss[0];
else if (wpa_s->ifmsh)
hapd = wpa_s->ifmsh->bss[0];
else
return -1;
return hostapd_ctrl_iface_sta(hapd, txtaddr, buf, buflen);
}
int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
char *buf, size_t buflen)
{
struct hostapd_data *hapd;
if (wpa_s->ap_iface)
hapd = wpa_s->ap_iface->bss[0];
else if (wpa_s->ifmsh)
hapd = wpa_s->ifmsh->bss[0];
else
return -1;
return hostapd_ctrl_iface_sta_next(hapd, txtaddr, buf, buflen);
}
int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
const char *txtaddr)
{
if (wpa_s->ap_iface == NULL)
return -1;
return hostapd_ctrl_iface_disassociate(wpa_s->ap_iface->bss[0],
txtaddr);
}
int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
const char *txtaddr)
{
if (wpa_s->ap_iface == NULL)
return -1;
return hostapd_ctrl_iface_deauthenticate(wpa_s->ap_iface->bss[0],
txtaddr);
}
int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
size_t buflen, int verbose)
{
char *pos = buf, *end = buf + buflen;
int ret;
struct hostapd_bss_config *conf;
if (wpa_s->ap_iface == NULL)
return -1;
conf = wpa_s->ap_iface->bss[0]->conf;
if (conf->wpa == 0)
return 0;
ret = os_snprintf(pos, end - pos,
"pairwise_cipher=%s\n"
"group_cipher=%s\n"
"key_mgmt=%s\n",
wpa_cipher_txt(conf->rsn_pairwise),
wpa_cipher_txt(conf->wpa_group),
wpa_key_mgmt_txt(conf->wpa_key_mgmt,
conf->wpa));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
return pos - buf;
}
#endif /* CONFIG_CTRL_IFACE */
int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
{
struct hostapd_iface *iface = wpa_s->ap_iface;
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct hostapd_data *hapd;
if (ssid == NULL || wpa_s->ap_iface == NULL ||
ssid->mode == WPAS_MODE_INFRA ||
ssid->mode == WPAS_MODE_IBSS)
return -1;
#ifdef CONFIG_P2P
if (ssid->mode == WPAS_MODE_P2P_GO)
iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER;
else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER |
P2P_GROUP_FORMATION;
#endif /* CONFIG_P2P */
hapd = iface->bss[0];
if (hapd->drv_priv == NULL)
return -1;
ieee802_11_set_beacons(iface);
hostapd_set_ap_wps_ie(hapd);
return 0;
}
int ap_switch_channel(struct wpa_supplicant *wpa_s,
struct csa_settings *settings)
{
#ifdef NEED_AP_MLME
struct hostapd_iface *iface = NULL;
if (wpa_s->ap_iface)
iface = wpa_s->ap_iface;
else if (wpa_s->ifmsh)
iface = wpa_s->ifmsh;
if (!iface || !iface->bss[0])
return -1;
return hostapd_switch_channel(iface->bss[0], settings);
#else /* NEED_AP_MLME */
return -1;
#endif /* NEED_AP_MLME */
}
#ifdef CONFIG_CTRL_IFACE
int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
{
struct csa_settings settings;
int ret = hostapd_parse_csa_settings(pos, &settings);
if (ret)
return ret;
return ap_switch_channel(wpa_s, &settings);
}
#endif /* CONFIG_CTRL_IFACE */
void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
int offset, int width, int cf1, int cf2, int finished)
{
struct hostapd_iface *iface = wpa_s->ap_iface;
if (!iface)
iface = wpa_s->ifmsh;
if (!iface)
return;
wpa_s->assoc_freq = freq;
if (wpa_s->current_ssid)
wpa_s->current_ssid->frequency = freq;
hostapd_event_ch_switch(iface->bss[0], freq, ht,
offset, width, cf1, cf2, finished);
}
int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
const u8 *addr)
{
struct hostapd_data *hapd;
struct hostapd_bss_config *conf;
if (!wpa_s->ap_iface)
return -1;
if (addr)
wpa_printf(MSG_DEBUG, "AP: Set MAC address filter: " MACSTR,
MAC2STR(addr));
else
wpa_printf(MSG_DEBUG, "AP: Clear MAC address filter");
hapd = wpa_s->ap_iface->bss[0];
conf = hapd->conf;
os_free(conf->accept_mac);
conf->accept_mac = NULL;
conf->num_accept_mac = 0;
os_free(conf->deny_mac);
conf->deny_mac = NULL;
conf->num_deny_mac = 0;
if (addr == NULL) {
conf->macaddr_acl = ACCEPT_UNLESS_DENIED;
return 0;
}
conf->macaddr_acl = DENY_UNLESS_ACCEPTED;
conf->accept_mac = os_zalloc(sizeof(struct mac_acl_entry));
if (conf->accept_mac == NULL)
return -1;
os_memcpy(conf->accept_mac[0].addr, addr, ETH_ALEN);
conf->num_accept_mac = 1;
return 0;
}
#ifdef CONFIG_WPS_NFC
int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
const struct wpabuf *pw, const u8 *pubkey_hash)
{
struct hostapd_data *hapd;
struct wps_context *wps;
if (!wpa_s->ap_iface)
return -1;
hapd = wpa_s->ap_iface->bss[0];
wps = hapd->wps;
if (wpa_s->p2pdev->conf->wps_nfc_dh_pubkey == NULL ||
wpa_s->p2pdev->conf->wps_nfc_dh_privkey == NULL) {
wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known");
return -1;
}
dh5_free(wps->dh_ctx);
wpabuf_free(wps->dh_pubkey);
wpabuf_free(wps->dh_privkey);
wps->dh_privkey = wpabuf_dup(
wpa_s->p2pdev->conf->wps_nfc_dh_privkey);
wps->dh_pubkey = wpabuf_dup(
wpa_s->p2pdev->conf->wps_nfc_dh_pubkey);
if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
wps->dh_ctx = NULL;
wpabuf_free(wps->dh_pubkey);
wps->dh_pubkey = NULL;
wpabuf_free(wps->dh_privkey);
wps->dh_privkey = NULL;
return -1;
}
wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
if (wps->dh_ctx == NULL)
return -1;
return wps_registrar_add_nfc_pw_token(hapd->wps->registrar, pubkey_hash,
pw_id,
pw ? wpabuf_head(pw) : NULL,
pw ? wpabuf_len(pw) : 0, 1);
}
#endif /* CONFIG_WPS_NFC */
#ifdef CONFIG_CTRL_IFACE
int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s)
{
struct hostapd_data *hapd;
if (!wpa_s->ap_iface)
return -1;
hapd = wpa_s->ap_iface->bss[0];
return hostapd_ctrl_iface_stop_ap(hapd);
}
int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf,
size_t len)
{
size_t reply_len = 0, i;
char ap_delimiter[] = "---- AP ----\n";
char mesh_delimiter[] = "---- mesh ----\n";
size_t dlen;
if (wpa_s->ap_iface) {
dlen = os_strlen(ap_delimiter);
if (dlen > len - reply_len)
return reply_len;
os_memcpy(&buf[reply_len], ap_delimiter, dlen);
reply_len += dlen;
for (i = 0; i < wpa_s->ap_iface->num_bss; i++) {
reply_len += hostapd_ctrl_iface_pmksa_list(
wpa_s->ap_iface->bss[i],
&buf[reply_len], len - reply_len);
}
}
if (wpa_s->ifmsh) {
dlen = os_strlen(mesh_delimiter);
if (dlen > len - reply_len)
return reply_len;
os_memcpy(&buf[reply_len], mesh_delimiter, dlen);
reply_len += dlen;
reply_len += hostapd_ctrl_iface_pmksa_list(
wpa_s->ifmsh->bss[0], &buf[reply_len],
len - reply_len);
}
return reply_len;
}
void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s)
{
size_t i;
if (wpa_s->ap_iface) {
for (i = 0; i < wpa_s->ap_iface->num_bss; i++)
hostapd_ctrl_iface_pmksa_flush(wpa_s->ap_iface->bss[i]);
}
if (wpa_s->ifmsh)
hostapd_ctrl_iface_pmksa_flush(wpa_s->ifmsh->bss[0]);
}
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
#ifdef CONFIG_MESH
int wpas_ap_pmksa_cache_list_mesh(struct wpa_supplicant *wpa_s, const u8 *addr,
char *buf, size_t len)
{
return hostapd_ctrl_iface_pmksa_list_mesh(wpa_s->ifmsh->bss[0], addr,
&buf[0], len);
}
int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd)
{
struct external_pmksa_cache *entry;
void *pmksa_cache;
pmksa_cache = hostapd_ctrl_iface_pmksa_create_entry(wpa_s->own_addr,
cmd);
if (!pmksa_cache)
return -1;
entry = os_zalloc(sizeof(struct external_pmksa_cache));
if (!entry)
return -1;
entry->pmksa_cache = pmksa_cache;
dl_list_add(&wpa_s->mesh_external_pmksa_cache, &entry->list);
return 0;
}
#endif /* CONFIG_MESH */
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+int wpas_ap_update_beacon(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_data *hapd;
+
+ if (!wpa_s->ap_iface)
+ return -1;
+ hapd = wpa_s->ap_iface->bss[0];
+
+ wpabuf_free(hapd->conf->assocresp_elements);
+ hapd->conf->assocresp_elements = NULL;
+ if (wpa_s->conf->ap_assocresp_elements) {
+ hapd->conf->assocresp_elements =
+ wpabuf_dup(wpa_s->conf->ap_assocresp_elements);
+ }
+
+ wpabuf_free(hapd->conf->vendor_elements);
+ hapd->conf->vendor_elements = NULL;
+ if (wpa_s->conf->ap_vendor_elements) {
+ hapd->conf->vendor_elements =
+ wpabuf_dup(wpa_s->conf->ap_vendor_elements);
+ }
+
+ return ieee802_11_set_beacon(hapd);
+}
+
#endif /* CONFIG_CTRL_IFACE */
#ifdef NEED_AP_MLME
void wpas_ap_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
struct dfs_event *radar)
{
struct hostapd_iface *iface = wpa_s->ap_iface;
if (!iface)
iface = wpa_s->ifmsh;
if (!iface || !iface->bss[0])
return;
wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
hostapd_dfs_radar_detected(iface, radar->freq,
radar->ht_enabled, radar->chan_offset,
radar->chan_width,
radar->cf1, radar->cf2);
}
void wpas_ap_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
struct dfs_event *radar)
{
struct hostapd_iface *iface = wpa_s->ap_iface;
if (!iface)
iface = wpa_s->ifmsh;
if (!iface || !iface->bss[0])
return;
wpa_printf(MSG_DEBUG, "DFS CAC started on %d MHz", radar->freq);
hostapd_dfs_start_cac(iface, radar->freq,
radar->ht_enabled, radar->chan_offset,
radar->chan_width, radar->cf1, radar->cf2);
}
void wpas_ap_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
struct dfs_event *radar)
{
struct hostapd_iface *iface = wpa_s->ap_iface;
if (!iface)
iface = wpa_s->ifmsh;
if (!iface || !iface->bss[0])
return;
wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
hostapd_dfs_complete_cac(iface, 1, radar->freq,
radar->ht_enabled, radar->chan_offset,
radar->chan_width, radar->cf1, radar->cf2);
}
void wpas_ap_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
struct dfs_event *radar)
{
struct hostapd_iface *iface = wpa_s->ap_iface;
if (!iface)
iface = wpa_s->ifmsh;
if (!iface || !iface->bss[0])
return;
wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
hostapd_dfs_complete_cac(iface, 0, radar->freq,
radar->ht_enabled, radar->chan_offset,
radar->chan_width, radar->cf1, radar->cf2);
}
void wpas_ap_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
struct dfs_event *radar)
{
struct hostapd_iface *iface = wpa_s->ap_iface;
if (!iface)
iface = wpa_s->ifmsh;
if (!iface || !iface->bss[0])
return;
wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
hostapd_dfs_nop_finished(iface, radar->freq,
radar->ht_enabled, radar->chan_offset,
radar->chan_width, radar->cf1, radar->cf2);
}
#endif /* NEED_AP_MLME */
void ap_periodic(struct wpa_supplicant *wpa_s)
{
if (wpa_s->ap_iface)
hostapd_periodic_iface(wpa_s->ap_iface);
}
diff --git a/contrib/wpa/wpa_supplicant/ap.h b/contrib/wpa/wpa_supplicant/ap.h
index 6c6e94cdf6a2..7bc1b781e3ac 100644
--- a/contrib/wpa/wpa_supplicant/ap.h
+++ b/contrib/wpa/wpa_supplicant/ap.h
@@ -1,105 +1,106 @@
/*
* WPA Supplicant - Basic AP mode support routines
* Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
* Copyright (c) 2009, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef AP_H
#define AP_H
int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s);
void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
const u8 *src_addr, const u8 *buf, size_t len);
int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
const u8 *p2p_dev_addr);
int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
const char *pin, char *buf, size_t buflen,
int timeout);
int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s);
void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s);
const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout);
const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s);
int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
int timeout);
int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen);
int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
char *buf, size_t buflen);
int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
char *buf, size_t buflen);
int ap_ctrl_iface_sta_deauthenticate(struct wpa_supplicant *wpa_s,
const char *txtaddr);
int ap_ctrl_iface_sta_disassociate(struct wpa_supplicant *wpa_s,
const char *txtaddr);
int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
size_t buflen, int verbose);
void ap_tx_status(void *ctx, const u8 *addr,
const u8 *buf, size_t len, int ack);
void ap_eapol_tx_status(void *ctx, const u8 *dst,
const u8 *data, size_t len, int ack);
void ap_client_poll_ok(void *ctx, const u8 *addr);
void ap_rx_from_unknown_sta(void *ctx, const u8 *addr, int wds);
void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt);
void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok);
int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s);
int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
const u8 *addr);
void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s);
int ap_switch_channel(struct wpa_supplicant *wpa_s,
struct csa_settings *settings);
int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr);
void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
int offset, int width, int cf1, int cf2, int finished);
struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
int ndef);
#ifdef CONFIG_AP
struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
int ndef);
#else /* CONFIG_AP */
static inline struct wpabuf *
wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
int ndef)
{
return NULL;
}
#endif /* CONFIG_AP */
int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
const struct wpabuf *req,
const struct wpabuf *sel);
int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
const struct wpabuf *pw, const u8 *pubkey_hash);
struct hostapd_config;
int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct hostapd_config *conf);
int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s);
int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf,
size_t len);
void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s);
int wpas_ap_pmksa_cache_list_mesh(struct wpa_supplicant *wpa_s, const u8 *addr,
char *buf, size_t len);
int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd);
+int wpas_ap_update_beacon(struct wpa_supplicant *wpa_s);
void wpas_ap_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
struct dfs_event *radar);
void wpas_ap_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
struct dfs_event *radar);
void wpas_ap_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
struct dfs_event *radar);
void wpas_ap_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
struct dfs_event *radar);
void wpas_ap_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
struct dfs_event *radar);
void ap_periodic(struct wpa_supplicant *wpa_s);
#endif /* AP_H */
diff --git a/contrib/wpa/wpa_supplicant/config.c b/contrib/wpa/wpa_supplicant/config.c
index e8e9fd432618..c5177d915524 100644
--- a/contrib/wpa/wpa_supplicant/config.c
+++ b/contrib/wpa/wpa_supplicant/config.c
@@ -1,5376 +1,5392 @@
/*
* WPA Supplicant / Configuration parser and common functions
* Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "utils/uuid.h"
#include "utils/ip_addr.h"
#include "common/ieee802_1x_defs.h"
#include "common/sae.h"
#include "crypto/sha1.h"
#include "rsn_supp/wpa.h"
#include "eap_peer/eap.h"
#include "p2p/p2p.h"
#include "fst/fst.h"
#include "config.h"
#if !defined(CONFIG_CTRL_IFACE) && defined(CONFIG_NO_CONFIG_WRITE)
#define NO_CONFIG_WRITE
#endif
/*
* Structure for network configuration parsing. This data is used to implement
* a generic parser for each network block variable. The table of configuration
* variables is defined below in this file (ssid_fields[]).
*/
struct parse_data {
/* Configuration variable name */
char *name;
/* Parser function for this variable. The parser functions return 0 or 1
* to indicate success. Value 0 indicates that the parameter value may
* have changed while value 1 means that the value did not change.
* Error cases (failure to parse the string) are indicated by returning
* -1. */
int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid,
int line, const char *value);
#ifndef NO_CONFIG_WRITE
/* Writer function (i.e., to get the variable in text format from
* internal presentation). */
char * (*writer)(const struct parse_data *data, struct wpa_ssid *ssid);
#endif /* NO_CONFIG_WRITE */
/* Variable specific parameters for the parser. */
void *param1, *param2, *param3, *param4;
/* 0 = this variable can be included in debug output and ctrl_iface
* 1 = this variable contains key/private data and it must not be
* included in debug output unless explicitly requested. In
* addition, this variable will not be readable through the
* ctrl_iface.
*/
int key_data;
};
static int wpa_config_parse_str(const struct parse_data *data,
struct wpa_ssid *ssid,
int line, const char *value)
{
size_t res_len, *dst_len, prev_len;
char **dst, *tmp;
if (os_strcmp(value, "NULL") == 0) {
wpa_printf(MSG_DEBUG, "Unset configuration string '%s'",
data->name);
tmp = NULL;
res_len = 0;
goto set;
}
tmp = wpa_config_parse_string(value, &res_len);
if (tmp == NULL) {
wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.",
line, data->name,
data->key_data ? "[KEY DATA REMOVED]" : value);
return -1;
}
if (data->key_data) {
wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
(u8 *) tmp, res_len);
} else {
wpa_hexdump_ascii(MSG_MSGDUMP, data->name,
(u8 *) tmp, res_len);
}
if (data->param3 && res_len < (size_t) data->param3) {
wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
"min_len=%ld)", line, data->name,
(unsigned long) res_len, (long) data->param3);
os_free(tmp);
return -1;
}
if (data->param4 && res_len > (size_t) data->param4) {
wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
"max_len=%ld)", line, data->name,
(unsigned long) res_len, (long) data->param4);
os_free(tmp);
return -1;
}
set:
dst = (char **) (((u8 *) ssid) + (long) data->param1);
dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2);
if (data->param2)
prev_len = *dst_len;
else if (*dst)
prev_len = os_strlen(*dst);
else
prev_len = 0;
if ((*dst == NULL && tmp == NULL) ||
(*dst && tmp && prev_len == res_len &&
os_memcmp(*dst, tmp, res_len) == 0)) {
/* No change to the previously configured value */
os_free(tmp);
return 1;
}
os_free(*dst);
*dst = tmp;
if (data->param2)
*dst_len = res_len;
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_string_ascii(const u8 *value, size_t len)
{
char *buf;
buf = os_malloc(len + 3);
if (buf == NULL)
return NULL;
buf[0] = '"';
os_memcpy(buf + 1, value, len);
buf[len + 1] = '"';
buf[len + 2] = '\0';
return buf;
}
static char * wpa_config_write_string_hex(const u8 *value, size_t len)
{
char *buf;
buf = os_zalloc(2 * len + 1);
if (buf == NULL)
return NULL;
wpa_snprintf_hex(buf, 2 * len + 1, value, len);
return buf;
}
static char * wpa_config_write_string(const u8 *value, size_t len)
{
if (value == NULL)
return NULL;
if (is_hex(value, len))
return wpa_config_write_string_hex(value, len);
else
return wpa_config_write_string_ascii(value, len);
}
static char * wpa_config_write_str(const struct parse_data *data,
struct wpa_ssid *ssid)
{
size_t len;
char **src;
src = (char **) (((u8 *) ssid) + (long) data->param1);
if (*src == NULL)
return NULL;
if (data->param2)
len = *((size_t *) (((u8 *) ssid) + (long) data->param2));
else
len = os_strlen(*src);
return wpa_config_write_string((const u8 *) *src, len);
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_int(const struct parse_data *data,
struct wpa_ssid *ssid,
int line, const char *value)
{
int val, *dst;
char *end;
dst = (int *) (((u8 *) ssid) + (long) data->param1);
val = strtol(value, &end, 0);
if (*end) {
wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
line, value);
return -1;
}
if (*dst == val)
return 1;
*dst = val;
wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
if (data->param3 && *dst < (long) data->param3) {
wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
"min_value=%ld)", line, data->name, *dst,
(long) data->param3);
*dst = (long) data->param3;
return -1;
}
if (data->param4 && *dst > (long) data->param4) {
wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
"max_value=%ld)", line, data->name, *dst,
(long) data->param4);
*dst = (long) data->param4;
return -1;
}
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_int(const struct parse_data *data,
struct wpa_ssid *ssid)
{
int *src, res;
char *value;
src = (int *) (((u8 *) ssid) + (long) data->param1);
value = os_malloc(20);
if (value == NULL)
return NULL;
res = os_snprintf(value, 20, "%d", *src);
if (os_snprintf_error(20, res)) {
os_free(value);
return NULL;
}
value[20 - 1] = '\0';
return value;
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_addr_list(const struct parse_data *data,
int line, const char *value,
u8 **list, size_t *num, char *name,
u8 abort_on_error, u8 masked)
{
const char *pos;
u8 *buf, *n, addr[2 * ETH_ALEN];
size_t count;
buf = NULL;
count = 0;
pos = value;
while (pos && *pos) {
while (*pos == ' ')
pos++;
if (hwaddr_masked_aton(pos, addr, &addr[ETH_ALEN], masked)) {
if (abort_on_error || count == 0) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid %s address '%s'",
line, name, value);
os_free(buf);
return -1;
}
/* continue anyway since this could have been from a
* truncated configuration file line */
wpa_printf(MSG_INFO,
"Line %d: Ignore likely truncated %s address '%s'",
line, name, pos);
} else {
n = os_realloc_array(buf, count + 1, 2 * ETH_ALEN);
if (n == NULL) {
os_free(buf);
return -1;
}
buf = n;
os_memmove(buf + 2 * ETH_ALEN, buf,
count * 2 * ETH_ALEN);
os_memcpy(buf, addr, 2 * ETH_ALEN);
count++;
wpa_printf(MSG_MSGDUMP,
"%s: addr=" MACSTR " mask=" MACSTR,
name, MAC2STR(addr),
MAC2STR(&addr[ETH_ALEN]));
}
pos = os_strchr(pos, ' ');
}
os_free(*list);
*list = buf;
*num = count;
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_addr_list(const struct parse_data *data,
const u8 *list, size_t num, char *name)
{
char *value, *end, *pos;
int res;
size_t i;
if (list == NULL || num == 0)
return NULL;
value = os_malloc(2 * 20 * num);
if (value == NULL)
return NULL;
pos = value;
end = value + 2 * 20 * num;
for (i = num; i > 0; i--) {
const u8 *a = list + (i - 1) * 2 * ETH_ALEN;
const u8 *m = a + ETH_ALEN;
if (i < num)
*pos++ = ' ';
res = hwaddr_mask_txt(pos, end - pos, a, m);
if (res < 0) {
os_free(value);
return NULL;
}
pos += res;
}
return value;
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_bssid(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
os_strcmp(value, "any") == 0) {
ssid->bssid_set = 0;
wpa_printf(MSG_MSGDUMP, "BSSID any");
return 0;
}
if (hwaddr_aton(value, ssid->bssid)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.",
line, value);
return -1;
}
ssid->bssid_set = 1;
wpa_hexdump(MSG_MSGDUMP, "BSSID", ssid->bssid, ETH_ALEN);
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_bssid(const struct parse_data *data,
struct wpa_ssid *ssid)
{
char *value;
int res;
if (!ssid->bssid_set)
return NULL;
value = os_malloc(20);
if (value == NULL)
return NULL;
res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid));
if (os_snprintf_error(20, res)) {
os_free(value);
return NULL;
}
value[20 - 1] = '\0';
return value;
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_bssid_hint(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
os_strcmp(value, "any") == 0) {
ssid->bssid_hint_set = 0;
wpa_printf(MSG_MSGDUMP, "BSSID hint any");
return 0;
}
if (hwaddr_aton(value, ssid->bssid_hint)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID hint '%s'.",
line, value);
return -1;
}
ssid->bssid_hint_set = 1;
wpa_hexdump(MSG_MSGDUMP, "BSSID hint", ssid->bssid_hint, ETH_ALEN);
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_bssid_hint(const struct parse_data *data,
struct wpa_ssid *ssid)
{
char *value;
int res;
if (!ssid->bssid_hint_set)
return NULL;
value = os_malloc(20);
if (!value)
return NULL;
res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid_hint));
if (os_snprintf_error(20, res)) {
os_free(value);
return NULL;
}
return value;
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_bssid_ignore(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
return wpa_config_parse_addr_list(data, line, value,
&ssid->bssid_ignore,
&ssid->num_bssid_ignore,
"bssid_ignore", 1, 1);
}
/* deprecated alias for bssid_ignore for backwards compatibility */
static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
return wpa_config_parse_addr_list(data, line, value,
&ssid->bssid_ignore,
&ssid->num_bssid_ignore,
"bssid_ignore", 1, 1);
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_bssid_ignore(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_addr_list(data, ssid->bssid_ignore,
ssid->num_bssid_ignore,
"bssid_ignore");
}
/* deprecated alias for bssid_ignore for backwards compatibility */
static char * wpa_config_write_bssid_blacklist(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_addr_list(data, ssid->bssid_ignore,
ssid->num_bssid_ignore,
"bssid_ignore");
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_bssid_accept(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
return wpa_config_parse_addr_list(data, line, value,
&ssid->bssid_accept,
&ssid->num_bssid_accept,
"bssid_accept", 1, 1);
}
/* deprecated alias for bssid_accept for backwards compatibility */
static int wpa_config_parse_bssid_whitelist(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
return wpa_config_parse_addr_list(data, line, value,
&ssid->bssid_accept,
&ssid->num_bssid_accept,
"bssid_accept", 1, 1);
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_bssid_accept(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_addr_list(data, ssid->bssid_accept,
ssid->num_bssid_accept,
"bssid_accept");
}
/* deprecated alias for bssid_accept for backwards compatibility */
static char * wpa_config_write_bssid_whitelist(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_addr_list(data, ssid->bssid_accept,
ssid->num_bssid_accept,
"bssid_accept");
}
#endif /* NO_CONFIG_WRITE */
#ifndef NO_CONFIG_WRITE
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_psk(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
#ifdef CONFIG_EXT_PASSWORD
if (os_strncmp(value, "ext:", 4) == 0) {
str_clear_free(ssid->passphrase);
ssid->passphrase = NULL;
ssid->psk_set = 0;
os_free(ssid->ext_psk);
ssid->ext_psk = os_strdup(value + 4);
if (ssid->ext_psk == NULL)
return -1;
wpa_printf(MSG_DEBUG, "PSK: External password '%s'",
ssid->ext_psk);
return 0;
}
#endif /* CONFIG_EXT_PASSWORD */
if (*value == '"') {
#ifndef CONFIG_NO_PBKDF2
const char *pos;
size_t len;
value++;
pos = os_strrchr(value, '"');
if (pos)
len = pos - value;
else
len = os_strlen(value);
if (len < 8 || len > 63) {
wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase "
"length %lu (expected: 8..63) '%s'.",
line, (unsigned long) len, value);
return -1;
}
wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
(u8 *) value, len);
if (has_ctrl_char((u8 *) value, len)) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid passphrase character",
line);
return -1;
}
if (ssid->passphrase && os_strlen(ssid->passphrase) == len &&
os_memcmp(ssid->passphrase, value, len) == 0) {
/* No change to the previously configured value */
return 1;
}
ssid->psk_set = 0;
str_clear_free(ssid->passphrase);
ssid->passphrase = dup_binstr(value, len);
if (ssid->passphrase == NULL)
return -1;
return 0;
#else /* CONFIG_NO_PBKDF2 */
wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not "
"supported.", line);
return -1;
#endif /* CONFIG_NO_PBKDF2 */
}
if (hexstr2bin(value, ssid->psk, PMK_LEN) ||
value[PMK_LEN * 2] != '\0') {
wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
line, value);
return -1;
}
str_clear_free(ssid->passphrase);
ssid->passphrase = NULL;
ssid->psk_set = 1;
wpa_hexdump_key(MSG_MSGDUMP, "PSK", ssid->psk, PMK_LEN);
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_psk(const struct parse_data *data,
struct wpa_ssid *ssid)
{
#ifdef CONFIG_EXT_PASSWORD
if (ssid->ext_psk) {
size_t len = 4 + os_strlen(ssid->ext_psk) + 1;
char *buf = os_malloc(len);
int res;
if (buf == NULL)
return NULL;
res = os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
if (os_snprintf_error(len, res)) {
os_free(buf);
buf = NULL;
}
return buf;
}
#endif /* CONFIG_EXT_PASSWORD */
if (ssid->passphrase)
return wpa_config_write_string_ascii(
(const u8 *) ssid->passphrase,
os_strlen(ssid->passphrase));
if (ssid->psk_set)
return wpa_config_write_string_hex(ssid->psk, PMK_LEN);
return NULL;
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_proto(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
int val = 0, last, errors = 0;
char *start, *end, *buf;
buf = os_strdup(value);
if (buf == NULL)
return -1;
start = buf;
while (*start != '\0') {
while (*start == ' ' || *start == '\t')
start++;
if (*start == '\0')
break;
end = start;
while (*end != ' ' && *end != '\t' && *end != '\0')
end++;
last = *end == '\0';
*end = '\0';
if (os_strcmp(start, "WPA") == 0)
val |= WPA_PROTO_WPA;
else if (os_strcmp(start, "RSN") == 0 ||
os_strcmp(start, "WPA2") == 0)
val |= WPA_PROTO_RSN;
else if (os_strcmp(start, "OSEN") == 0)
val |= WPA_PROTO_OSEN;
else {
wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
line, start);
errors++;
}
if (last)
break;
start = end + 1;
}
os_free(buf);
if (val == 0) {
wpa_printf(MSG_ERROR,
"Line %d: no proto values configured.", line);
errors++;
}
if (!errors && ssid->proto == val)
return 1;
wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
ssid->proto = val;
return errors ? -1 : 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_proto(const struct parse_data *data,
struct wpa_ssid *ssid)
{
int ret;
char *buf, *pos, *end;
pos = buf = os_zalloc(20);
if (buf == NULL)
return NULL;
end = buf + 20;
if (ssid->proto & WPA_PROTO_WPA) {
ret = os_snprintf(pos, end - pos, "%sWPA",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return buf;
pos += ret;
}
if (ssid->proto & WPA_PROTO_RSN) {
ret = os_snprintf(pos, end - pos, "%sRSN",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return buf;
pos += ret;
}
if (ssid->proto & WPA_PROTO_OSEN) {
ret = os_snprintf(pos, end - pos, "%sOSEN",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret))
return buf;
pos += ret;
}
if (pos == buf) {
os_free(buf);
buf = NULL;
}
return buf;
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_key_mgmt(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
int val = 0, last, errors = 0;
char *start, *end, *buf;
buf = os_strdup(value);
if (buf == NULL)
return -1;
start = buf;
while (*start != '\0') {
while (*start == ' ' || *start == '\t')
start++;
if (*start == '\0')
break;
end = start;
while (*end != ' ' && *end != '\t' && *end != '\0')
end++;
last = *end == '\0';
*end = '\0';
if (os_strcmp(start, "WPA-PSK") == 0)
val |= WPA_KEY_MGMT_PSK;
else if (os_strcmp(start, "WPA-EAP") == 0)
val |= WPA_KEY_MGMT_IEEE8021X;
else if (os_strcmp(start, "IEEE8021X") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA;
else if (os_strcmp(start, "NONE") == 0)
val |= WPA_KEY_MGMT_NONE;
else if (os_strcmp(start, "WPA-NONE") == 0)
val |= WPA_KEY_MGMT_WPA_NONE;
#ifdef CONFIG_IEEE80211R
else if (os_strcmp(start, "FT-PSK") == 0)
val |= WPA_KEY_MGMT_FT_PSK;
else if (os_strcmp(start, "FT-EAP") == 0)
val |= WPA_KEY_MGMT_FT_IEEE8021X;
#ifdef CONFIG_SHA384
else if (os_strcmp(start, "FT-EAP-SHA384") == 0)
val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
val |= WPA_KEY_MGMT_PSK_SHA256;
else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
#ifdef CONFIG_WPS
else if (os_strcmp(start, "WPS") == 0)
val |= WPA_KEY_MGMT_WPS;
#endif /* CONFIG_WPS */
#ifdef CONFIG_SAE
else if (os_strcmp(start, "SAE") == 0)
val |= WPA_KEY_MGMT_SAE;
else if (os_strcmp(start, "FT-SAE") == 0)
val |= WPA_KEY_MGMT_FT_SAE;
#endif /* CONFIG_SAE */
#ifdef CONFIG_HS20
else if (os_strcmp(start, "OSEN") == 0)
val |= WPA_KEY_MGMT_OSEN;
#endif /* CONFIG_HS20 */
#ifdef CONFIG_SUITEB
else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_SUITEB192
else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
#endif /* CONFIG_SUITEB192 */
#ifdef CONFIG_FILS
else if (os_strcmp(start, "FILS-SHA256") == 0)
val |= WPA_KEY_MGMT_FILS_SHA256;
else if (os_strcmp(start, "FILS-SHA384") == 0)
val |= WPA_KEY_MGMT_FILS_SHA384;
#ifdef CONFIG_IEEE80211R
else if (os_strcmp(start, "FT-FILS-SHA256") == 0)
val |= WPA_KEY_MGMT_FT_FILS_SHA256;
else if (os_strcmp(start, "FT-FILS-SHA384") == 0)
val |= WPA_KEY_MGMT_FT_FILS_SHA384;
#endif /* CONFIG_IEEE80211R */
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
else if (os_strcmp(start, "OWE") == 0)
val |= WPA_KEY_MGMT_OWE;
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP
else if (os_strcmp(start, "DPP") == 0)
val |= WPA_KEY_MGMT_DPP;
#endif /* CONFIG_DPP */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
errors++;
}
if (last)
break;
start = end + 1;
}
os_free(buf);
if (val == 0) {
wpa_printf(MSG_ERROR,
"Line %d: no key_mgmt values configured.", line);
errors++;
}
if (!errors && ssid->key_mgmt == val)
return 1;
wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
ssid->key_mgmt = val;
return errors ? -1 : 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_key_mgmt(const struct parse_data *data,
struct wpa_ssid *ssid)
{
char *buf, *pos, *end;
int ret;
pos = buf = os_zalloc(100);
if (buf == NULL)
return NULL;
end = buf + 100;
if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
ret = os_snprintf(pos, end - pos, "%sWPA-EAP",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
ret = os_snprintf(pos, end - pos, "%sIEEE8021X",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) {
ret = os_snprintf(pos, end - pos, "%sNONE",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
ret = os_snprintf(pos, end - pos, "%sWPA-NONE",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#ifdef CONFIG_IEEE80211R
if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) {
ret = os_snprintf(pos, end - pos, "%sFT-PSK",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
ret = os_snprintf(pos, end - pos, "%sFT-EAP",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#ifdef CONFIG_SHA384
if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
ret = os_snprintf(pos, end - pos, "%sFT-EAP-SHA384",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#ifdef CONFIG_WPS
if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
ret = os_snprintf(pos, end - pos, "%sWPS",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_SAE
if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
ret = os_snprintf(pos, end - pos, "%sSAE",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE) {
ret = os_snprintf(pos, end - pos, "%sFT-SAE",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_HS20
if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) {
ret = os_snprintf(pos, end - pos, "%sOSEN",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#endif /* CONFIG_HS20 */
#ifdef CONFIG_SUITEB
if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_SUITEB192
if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B-192",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#endif /* CONFIG_SUITEB192 */
#ifdef CONFIG_FILS
if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#ifdef CONFIG_IEEE80211R
if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#endif /* CONFIG_IEEE80211R */
#endif /* CONFIG_FILS */
#ifdef CONFIG_DPP
if (ssid->key_mgmt & WPA_KEY_MGMT_DPP) {
ret = os_snprintf(pos, end - pos, "%sDPP",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#endif /* CONFIG_DPP */
#ifdef CONFIG_OWE
if (ssid->key_mgmt & WPA_KEY_MGMT_OWE) {
ret = os_snprintf(pos, end - pos, "%sOWE",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
#endif /* CONFIG_OWE */
if (pos == buf) {
os_free(buf);
buf = NULL;
}
return buf;
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_cipher(int line, const char *value)
{
#ifdef CONFIG_NO_WPA
return -1;
#else /* CONFIG_NO_WPA */
int val = wpa_parse_cipher(value);
if (val < 0) {
wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
line, value);
return -1;
}
if (val == 0) {
wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.",
line);
return -1;
}
return val;
#endif /* CONFIG_NO_WPA */
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_cipher(int cipher)
{
#ifdef CONFIG_NO_WPA
return NULL;
#else /* CONFIG_NO_WPA */
char *buf = os_zalloc(50);
if (buf == NULL)
return NULL;
if (wpa_write_ciphers(buf, buf + 50, cipher, " ") < 0) {
os_free(buf);
return NULL;
}
return buf;
#endif /* CONFIG_NO_WPA */
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_pairwise(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
int val;
val = wpa_config_parse_cipher(line, value);
if (val == -1)
return -1;
if (val & ~WPA_ALLOWED_PAIRWISE_CIPHERS) {
wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
"(0x%x).", line, val);
return -1;
}
if (ssid->pairwise_cipher == val)
return 1;
wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
ssid->pairwise_cipher = val;
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_pairwise(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_cipher(ssid->pairwise_cipher);
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_group(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
int val;
val = wpa_config_parse_cipher(line, value);
if (val == -1)
return -1;
/*
* Backwards compatibility - filter out WEP ciphers that were previously
* allowed.
*/
val &= ~(WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40);
if (val & ~WPA_ALLOWED_GROUP_CIPHERS) {
wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
"(0x%x).", line, val);
return -1;
}
if (ssid->group_cipher == val)
return 1;
wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
ssid->group_cipher = val;
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_group(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_cipher(ssid->group_cipher);
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_group_mgmt(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
int val;
val = wpa_config_parse_cipher(line, value);
if (val == -1)
return -1;
if (val & ~WPA_ALLOWED_GROUP_MGMT_CIPHERS) {
wpa_printf(MSG_ERROR,
"Line %d: not allowed group management cipher (0x%x).",
line, val);
return -1;
}
if (ssid->group_mgmt_cipher == val)
return 1;
wpa_printf(MSG_MSGDUMP, "group_mgmt: 0x%x", val);
ssid->group_mgmt_cipher = val;
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_group_mgmt(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_cipher(ssid->group_mgmt_cipher);
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_auth_alg(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
int val = 0, last, errors = 0;
char *start, *end, *buf;
buf = os_strdup(value);
if (buf == NULL)
return -1;
start = buf;
while (*start != '\0') {
while (*start == ' ' || *start == '\t')
start++;
if (*start == '\0')
break;
end = start;
while (*end != ' ' && *end != '\t' && *end != '\0')
end++;
last = *end == '\0';
*end = '\0';
if (os_strcmp(start, "OPEN") == 0)
val |= WPA_AUTH_ALG_OPEN;
else if (os_strcmp(start, "SHARED") == 0)
val |= WPA_AUTH_ALG_SHARED;
else if (os_strcmp(start, "LEAP") == 0)
val |= WPA_AUTH_ALG_LEAP;
else {
wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'",
line, start);
errors++;
}
if (last)
break;
start = end + 1;
}
os_free(buf);
if (val == 0) {
wpa_printf(MSG_ERROR,
"Line %d: no auth_alg values configured.", line);
errors++;
}
if (!errors && ssid->auth_alg == val)
return 1;
wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
ssid->auth_alg = val;
return errors ? -1 : 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_auth_alg(const struct parse_data *data,
struct wpa_ssid *ssid)
{
char *buf, *pos, *end;
int ret;
pos = buf = os_zalloc(30);
if (buf == NULL)
return NULL;
end = buf + 30;
if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) {
ret = os_snprintf(pos, end - pos, "%sOPEN",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) {
ret = os_snprintf(pos, end - pos, "%sSHARED",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) {
ret = os_snprintf(pos, end - pos, "%sLEAP",
pos == buf ? "" : " ");
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
if (pos == buf) {
os_free(buf);
buf = NULL;
}
return buf;
}
#endif /* NO_CONFIG_WRITE */
static int * wpa_config_parse_int_array(const char *value)
{
int *freqs;
size_t used, len;
const char *pos;
used = 0;
len = 10;
freqs = os_calloc(len + 1, sizeof(int));
if (freqs == NULL)
return NULL;
pos = value;
while (pos) {
while (*pos == ' ')
pos++;
if (used == len) {
int *n;
size_t i;
n = os_realloc_array(freqs, len * 2 + 1, sizeof(int));
if (n == NULL) {
os_free(freqs);
return NULL;
}
for (i = len; i <= len * 2; i++)
n[i] = 0;
freqs = n;
len *= 2;
}
freqs[used] = atoi(pos);
if (freqs[used] == 0)
break;
used++;
pos = os_strchr(pos + 1, ' ');
}
return freqs;
}
static int wpa_config_parse_scan_freq(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
int *freqs;
freqs = wpa_config_parse_int_array(value);
if (freqs == NULL)
return -1;
if (freqs[0] == 0) {
os_free(freqs);
freqs = NULL;
}
os_free(ssid->scan_freq);
ssid->scan_freq = freqs;
return 0;
}
static int wpa_config_parse_freq_list(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
int *freqs;
freqs = wpa_config_parse_int_array(value);
if (freqs == NULL)
return -1;
if (freqs[0] == 0) {
os_free(freqs);
freqs = NULL;
}
os_free(ssid->freq_list);
ssid->freq_list = freqs;
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_freqs(const struct parse_data *data,
const int *freqs)
{
char *buf, *pos, *end;
int i, ret;
size_t count;
if (freqs == NULL)
return NULL;
count = 0;
for (i = 0; freqs[i]; i++)
count++;
pos = buf = os_zalloc(10 * count + 1);
if (buf == NULL)
return NULL;
end = buf + 10 * count + 1;
for (i = 0; freqs[i]; i++) {
ret = os_snprintf(pos, end - pos, "%s%u",
i == 0 ? "" : " ", freqs[i]);
if (os_snprintf_error(end - pos, ret)) {
end[-1] = '\0';
return buf;
}
pos += ret;
}
return buf;
}
static char * wpa_config_write_scan_freq(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_freqs(data, ssid->scan_freq);
}
static char * wpa_config_write_freq_list(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_freqs(data, ssid->freq_list);
}
#endif /* NO_CONFIG_WRITE */
#ifdef IEEE8021X_EAPOL
static int wpa_config_parse_eap(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
int last, errors = 0;
char *start, *end, *buf;
struct eap_method_type *methods = NULL, *tmp;
size_t num_methods = 0;
buf = os_strdup(value);
if (buf == NULL)
return -1;
start = buf;
while (*start != '\0') {
while (*start == ' ' || *start == '\t')
start++;
if (*start == '\0')
break;
end = start;
while (*end != ' ' && *end != '\t' && *end != '\0')
end++;
last = *end == '\0';
*end = '\0';
tmp = methods;
methods = os_realloc_array(methods, num_methods + 1,
sizeof(*methods));
if (methods == NULL) {
os_free(tmp);
os_free(buf);
return -1;
}
methods[num_methods].method = eap_peer_get_type(
start, &methods[num_methods].vendor);
if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
methods[num_methods].method == EAP_TYPE_NONE) {
wpa_printf(MSG_ERROR, "Line %d: unknown EAP method "
"'%s'", line, start);
wpa_printf(MSG_ERROR, "You may need to add support for"
" this EAP method during wpa_supplicant\n"
"build time configuration.\n"
"See README for more information.");
errors++;
} else if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
methods[num_methods].method == EAP_TYPE_LEAP)
ssid->leap++;
else
ssid->non_leap++;
num_methods++;
if (last)
break;
start = end + 1;
}
os_free(buf);
tmp = methods;
methods = os_realloc_array(methods, num_methods + 1, sizeof(*methods));
if (methods == NULL) {
os_free(tmp);
return -1;
}
methods[num_methods].vendor = EAP_VENDOR_IETF;
methods[num_methods].method = EAP_TYPE_NONE;
num_methods++;
if (!errors && ssid->eap.eap_methods) {
struct eap_method_type *prev_m;
size_t i, j, prev_methods, match = 0;
prev_m = ssid->eap.eap_methods;
for (i = 0; prev_m[i].vendor != EAP_VENDOR_IETF ||
prev_m[i].method != EAP_TYPE_NONE; i++) {
/* Count the methods */
}
prev_methods = i + 1;
for (i = 0; prev_methods == num_methods && i < prev_methods;
i++) {
for (j = 0; j < num_methods; j++) {
if (prev_m[i].vendor == methods[j].vendor &&
prev_m[i].method == methods[j].method) {
match++;
break;
}
}
}
if (match == num_methods) {
os_free(methods);
return 1;
}
}
wpa_hexdump(MSG_MSGDUMP, "eap methods",
(u8 *) methods, num_methods * sizeof(*methods));
os_free(ssid->eap.eap_methods);
ssid->eap.eap_methods = methods;
return errors ? -1 : 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_eap(const struct parse_data *data,
struct wpa_ssid *ssid)
{
int i, ret;
char *buf, *pos, *end;
const struct eap_method_type *eap_methods = ssid->eap.eap_methods;
const char *name;
if (eap_methods == NULL)
return NULL;
pos = buf = os_zalloc(100);
if (buf == NULL)
return NULL;
end = buf + 100;
for (i = 0; eap_methods[i].vendor != EAP_VENDOR_IETF ||
eap_methods[i].method != EAP_TYPE_NONE; i++) {
name = eap_get_name(eap_methods[i].vendor,
eap_methods[i].method);
if (name) {
ret = os_snprintf(pos, end - pos, "%s%s",
pos == buf ? "" : " ", name);
if (os_snprintf_error(end - pos, ret))
break;
pos += ret;
}
}
end[-1] = '\0';
return buf;
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_password(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
u8 *hash;
if (os_strcmp(value, "NULL") == 0) {
if (!ssid->eap.password)
return 1; /* Already unset */
wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
bin_clear_free(ssid->eap.password, ssid->eap.password_len);
ssid->eap.password = NULL;
ssid->eap.password_len = 0;
return 0;
}
#ifdef CONFIG_EXT_PASSWORD
if (os_strncmp(value, "ext:", 4) == 0) {
char *name = os_strdup(value + 4);
if (!name)
return -1;
bin_clear_free(ssid->eap.password, ssid->eap.password_len);
ssid->eap.password = (u8 *) name;
ssid->eap.password_len = os_strlen(name);
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_PASSWORD;
return 0;
}
#endif /* CONFIG_EXT_PASSWORD */
if (os_strncmp(value, "hash:", 5) != 0) {
char *tmp;
size_t res_len;
tmp = wpa_config_parse_string(value, &res_len);
if (!tmp) {
wpa_printf(MSG_ERROR,
"Line %d: failed to parse password.", line);
return -1;
}
wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
(u8 *) tmp, res_len);
bin_clear_free(ssid->eap.password, ssid->eap.password_len);
ssid->eap.password = (u8 *) tmp;
ssid->eap.password_len = res_len;
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
return 0;
}
/* NtPasswordHash: hash:<32 hex digits> */
if (os_strlen(value + 5) != 2 * 16) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid password hash length (expected 32 hex digits)",
line);
return -1;
}
hash = os_malloc(16);
if (!hash)
return -1;
if (hexstr2bin(value + 5, hash, 16)) {
os_free(hash);
wpa_printf(MSG_ERROR, "Line %d: Invalid password hash", line);
return -1;
}
wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
if (ssid->eap.password && ssid->eap.password_len == 16 &&
os_memcmp(ssid->eap.password, hash, 16) == 0 &&
(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
bin_clear_free(hash, 16);
return 1;
}
bin_clear_free(ssid->eap.password, ssid->eap.password_len);
ssid->eap.password = hash;
ssid->eap.password_len = 16;
ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
return 0;
}
static int wpa_config_parse_machine_password(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
u8 *hash;
if (os_strcmp(value, "NULL") == 0) {
if (!ssid->eap.machine_password)
return 1; /* Already unset */
wpa_printf(MSG_DEBUG,
"Unset configuration string 'machine_password'");
bin_clear_free(ssid->eap.machine_password,
ssid->eap.machine_password_len);
ssid->eap.machine_password = NULL;
ssid->eap.machine_password_len = 0;
return 0;
}
#ifdef CONFIG_EXT_PASSWORD
if (os_strncmp(value, "ext:", 4) == 0) {
char *name = os_strdup(value + 4);
if (!name)
return -1;
bin_clear_free(ssid->eap.machine_password,
ssid->eap.machine_password_len);
ssid->eap.machine_password = (u8 *) name;
ssid->eap.machine_password_len = os_strlen(name);
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH;
ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD;
return 0;
}
#endif /* CONFIG_EXT_PASSWORD */
if (os_strncmp(value, "hash:", 5) != 0) {
char *tmp;
size_t res_len;
tmp = wpa_config_parse_string(value, &res_len);
if (!tmp) {
wpa_printf(MSG_ERROR,
"Line %d: failed to parse machine_password.",
line);
return -1;
}
wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
(u8 *) tmp, res_len);
bin_clear_free(ssid->eap.machine_password,
ssid->eap.machine_password_len);
ssid->eap.machine_password = (u8 *) tmp;
ssid->eap.machine_password_len = res_len;
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH;
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD;
return 0;
}
/* NtPasswordHash: hash:<32 hex digits> */
if (os_strlen(value + 5) != 2 * 16) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid machine_password hash length (expected 32 hex digits)",
line);
return -1;
}
hash = os_malloc(16);
if (!hash)
return -1;
if (hexstr2bin(value + 5, hash, 16)) {
os_free(hash);
wpa_printf(MSG_ERROR, "Line %d: Invalid machine_password hash",
line);
return -1;
}
wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
if (ssid->eap.machine_password &&
ssid->eap.machine_password_len == 16 &&
os_memcmp(ssid->eap.machine_password, hash, 16) == 0 &&
(ssid->eap.flags & EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH)) {
bin_clear_free(hash, 16);
return 1;
}
bin_clear_free(ssid->eap.machine_password,
ssid->eap.machine_password_len);
ssid->eap.machine_password = hash;
ssid->eap.machine_password_len = 16;
ssid->eap.flags |= EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH;
ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD;
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_password(const struct parse_data *data,
struct wpa_ssid *ssid)
{
char *buf;
if (!ssid->eap.password)
return NULL;
#ifdef CONFIG_EXT_PASSWORD
if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
buf = os_zalloc(4 + ssid->eap.password_len + 1);
if (!buf)
return NULL;
os_memcpy(buf, "ext:", 4);
os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len);
return buf;
}
#endif /* CONFIG_EXT_PASSWORD */
if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
return wpa_config_write_string(
ssid->eap.password, ssid->eap.password_len);
}
buf = os_malloc(5 + 32 + 1);
if (!buf)
return NULL;
os_memcpy(buf, "hash:", 5);
wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.password, 16);
return buf;
}
static char * wpa_config_write_machine_password(const struct parse_data *data,
struct wpa_ssid *ssid)
{
char *buf;
if (!ssid->eap.machine_password)
return NULL;
#ifdef CONFIG_EXT_PASSWORD
if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD) {
buf = os_zalloc(4 + ssid->eap.machine_password_len + 1);
if (!buf)
return NULL;
os_memcpy(buf, "ext:", 4);
os_memcpy(buf + 4, ssid->eap.machine_password,
ssid->eap.machine_password_len);
return buf;
}
#endif /* CONFIG_EXT_PASSWORD */
if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH)) {
return wpa_config_write_string(
ssid->eap.machine_password,
ssid->eap.machine_password_len);
}
buf = os_malloc(5 + 32 + 1);
if (!buf)
return NULL;
os_memcpy(buf, "hash:", 5);
wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.machine_password, 16);
return buf;
}
#endif /* NO_CONFIG_WRITE */
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_WEP
static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line,
const char *value, int idx)
{
char *buf, title[20];
int res;
buf = wpa_config_parse_string(value, len);
if (buf == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.",
line, idx, value);
return -1;
}
if (*len > MAX_WEP_KEY_LEN) {
wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.",
line, idx, value);
os_free(buf);
return -1;
}
if (*len && *len != 5 && *len != 13 && *len != 16) {
wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key length %u - "
"this network block will be ignored",
line, (unsigned int) *len);
}
os_memcpy(key, buf, *len);
str_clear_free(buf);
res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
if (!os_snprintf_error(sizeof(title), res))
wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
return 0;
}
static int wpa_config_parse_wep_key0(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
return wpa_config_parse_wep_key(ssid->wep_key[0],
&ssid->wep_key_len[0], line,
value, 0);
}
static int wpa_config_parse_wep_key1(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
return wpa_config_parse_wep_key(ssid->wep_key[1],
&ssid->wep_key_len[1], line,
value, 1);
}
static int wpa_config_parse_wep_key2(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
return wpa_config_parse_wep_key(ssid->wep_key[2],
&ssid->wep_key_len[2], line,
value, 2);
}
static int wpa_config_parse_wep_key3(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
return wpa_config_parse_wep_key(ssid->wep_key[3],
&ssid->wep_key_len[3], line,
value, 3);
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_wep_key(struct wpa_ssid *ssid, int idx)
{
if (ssid->wep_key_len[idx] == 0)
return NULL;
return wpa_config_write_string(ssid->wep_key[idx],
ssid->wep_key_len[idx]);
}
static char * wpa_config_write_wep_key0(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_wep_key(ssid, 0);
}
static char * wpa_config_write_wep_key1(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_wep_key(ssid, 1);
}
static char * wpa_config_write_wep_key2(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_wep_key(ssid, 2);
}
static char * wpa_config_write_wep_key3(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_wep_key(ssid, 3);
}
#endif /* NO_CONFIG_WRITE */
#endif /* CONFIG_WEP */
#ifdef CONFIG_P2P
static int wpa_config_parse_go_p2p_dev_addr(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
os_strcmp(value, "any") == 0) {
os_memset(ssid->go_p2p_dev_addr, 0, ETH_ALEN);
wpa_printf(MSG_MSGDUMP, "GO P2P Device Address any");
return 0;
}
if (hwaddr_aton(value, ssid->go_p2p_dev_addr)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid GO P2P Device Address '%s'.",
line, value);
return -1;
}
ssid->bssid_set = 1;
wpa_printf(MSG_MSGDUMP, "GO P2P Device Address " MACSTR,
MAC2STR(ssid->go_p2p_dev_addr));
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_go_p2p_dev_addr(const struct parse_data *data,
struct wpa_ssid *ssid)
{
char *value;
int res;
if (is_zero_ether_addr(ssid->go_p2p_dev_addr))
return NULL;
value = os_malloc(20);
if (value == NULL)
return NULL;
res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr));
if (os_snprintf_error(20, res)) {
os_free(value);
return NULL;
}
value[20 - 1] = '\0';
return value;
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
return wpa_config_parse_addr_list(data, line, value,
&ssid->p2p_client_list,
&ssid->num_p2p_clients,
"p2p_client_list", 0, 0);
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_addr_list(data, ssid->p2p_client_list,
ssid->num_p2p_clients,
"p2p_client_list");
}
#endif /* NO_CONFIG_WRITE */
static int wpa_config_parse_psk_list(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
struct psk_list_entry *p;
const char *pos;
p = os_zalloc(sizeof(*p));
if (p == NULL)
return -1;
pos = value;
if (os_strncmp(pos, "P2P-", 4) == 0) {
p->p2p = 1;
pos += 4;
}
if (hwaddr_aton(pos, p->addr)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list address '%s'",
line, pos);
os_free(p);
return -1;
}
pos += 17;
if (*pos != '-') {
wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list '%s'",
line, pos);
os_free(p);
return -1;
}
pos++;
if (hexstr2bin(pos, p->psk, PMK_LEN) || pos[PMK_LEN * 2] != '\0') {
wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list PSK '%s'",
line, pos);
os_free(p);
return -1;
}
dl_list_add(&ssid->psk_list, &p->list);
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_psk_list(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return NULL;
}
#endif /* NO_CONFIG_WRITE */
#endif /* CONFIG_P2P */
#ifdef CONFIG_MESH
static int wpa_config_parse_mesh_basic_rates(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
int *rates = wpa_config_parse_int_array(value);
if (rates == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Invalid mesh_basic_rates '%s'",
line, value);
return -1;
}
if (rates[0] == 0) {
os_free(rates);
rates = NULL;
}
os_free(ssid->mesh_basic_rates);
ssid->mesh_basic_rates = rates;
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return wpa_config_write_freqs(data, ssid->mesh_basic_rates);
}
#endif /* NO_CONFIG_WRITE */
#endif /* CONFIG_MESH */
#ifdef CONFIG_MACSEC
static int wpa_config_parse_mka_cak(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
size_t len;
len = os_strlen(value);
if (len > 2 * MACSEC_CAK_MAX_LEN ||
(len != 2 * 16 && len != 2 * 32) ||
hexstr2bin(value, ssid->mka_cak, len / 2)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.",
line, value);
return -1;
}
ssid->mka_cak_len = len / 2;
ssid->mka_psk_set |= MKA_PSK_SET_CAK;
wpa_hexdump_key(MSG_MSGDUMP, "MKA-CAK", ssid->mka_cak,
ssid->mka_cak_len);
return 0;
}
static int wpa_config_parse_mka_ckn(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
size_t len;
len = os_strlen(value);
if (len > 2 * MACSEC_CKN_MAX_LEN || /* too long */
len < 2 || /* too short */
len % 2 != 0 /* not an integral number of bytes */) {
wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
line, value);
return -1;
}
ssid->mka_ckn_len = len / 2;
if (hexstr2bin(value, ssid->mka_ckn, ssid->mka_ckn_len)) {
wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
line, value);
return -1;
}
ssid->mka_psk_set |= MKA_PSK_SET_CKN;
wpa_hexdump_key(MSG_MSGDUMP, "MKA-CKN", ssid->mka_ckn,
ssid->mka_ckn_len);
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_mka_cak(const struct parse_data *data,
struct wpa_ssid *ssid)
{
if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK))
return NULL;
return wpa_config_write_string_hex(ssid->mka_cak, ssid->mka_cak_len);
}
static char * wpa_config_write_mka_ckn(const struct parse_data *data,
struct wpa_ssid *ssid)
{
if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN))
return NULL;
return wpa_config_write_string_hex(ssid->mka_ckn, ssid->mka_ckn_len);
}
#endif /* NO_CONFIG_WRITE */
#endif /* CONFIG_MACSEC */
#ifdef CONFIG_OCV
static int wpa_config_parse_ocv(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
char *end;
ssid->ocv = strtol(value, &end, 0);
if (*end || ssid->ocv < 0 || ssid->ocv > 1) {
wpa_printf(MSG_ERROR, "Line %d: Invalid ocv value '%s'.",
line, value);
return -1;
}
if (ssid->ocv && ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION)
ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_ocv(const struct parse_data *data,
struct wpa_ssid *ssid)
{
char *value = os_malloc(20);
if (!value)
return NULL;
os_snprintf(value, 20, "%d", ssid->ocv);
value[20 - 1] = '\0';
return value;
}
#endif /* NO_CONFIG_WRITE */
#endif /* CONFIG_OCV */
static int wpa_config_parse_peerkey(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{
wpa_printf(MSG_INFO, "NOTE: Obsolete peerkey parameter ignored");
return 0;
}
#ifndef NO_CONFIG_WRITE
static char * wpa_config_write_peerkey(const struct parse_data *data,
struct wpa_ssid *ssid)
{
return NULL;
}
#endif /* NO_CONFIG_WRITE */
/* Helper macros for network block parser */
#ifdef OFFSET
#undef OFFSET
#endif /* OFFSET */
/* OFFSET: Get offset of a variable within the wpa_ssid structure */
#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v)
/* STR: Define a string variable for an ASCII string; f = field name */
#ifdef NO_CONFIG_WRITE
#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
#define _STRe(f, m) #f, wpa_config_parse_str, OFFSET(eap.m)
#else /* NO_CONFIG_WRITE */
#define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f)
#define _STRe(f, m) #f, wpa_config_parse_str, wpa_config_write_str, \
OFFSET(eap.m)
#endif /* NO_CONFIG_WRITE */
#define STR(f) _STR(f), NULL, NULL, NULL, 0
#define STRe(f, m) _STRe(f, m), NULL, NULL, NULL, 0
#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1
#define STR_KEYe(f, m) _STRe(f, m), NULL, NULL, NULL, 1
/* STR_LEN: Define a string variable with a separate variable for storing the
* data length. Unlike STR(), this can be used to store arbitrary binary data
* (i.e., even nul termination character). */
#define _STR_LEN(f) _STR(f), OFFSET(f ## _len)
#define _STR_LENe(f, m) _STRe(f, m), OFFSET(eap.m ## _len)
#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0
#define STR_LENe(f, m) _STR_LENe(f, m), NULL, NULL, 0
#define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1
/* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length
* explicitly specified. */
#define _STR_RANGE(f, min, max) _STR_LEN(f), (void *) (min), (void *) (max)
#define STR_RANGE(f, min, max) _STR_RANGE(f, min, max), 0
#define STR_RANGE_KEY(f, min, max) _STR_RANGE(f, min, max), 1
#ifdef NO_CONFIG_WRITE
#define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0
#define _INTe(f, m) #f, wpa_config_parse_int, OFFSET(eap.m), (void *) 0
#else /* NO_CONFIG_WRITE */
#define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \
OFFSET(f), (void *) 0
#define _INTe(f, m) #f, wpa_config_parse_int, wpa_config_write_int, \
OFFSET(eap.m), (void *) 0
#endif /* NO_CONFIG_WRITE */
/* INT: Define an integer variable */
#define INT(f) _INT(f), NULL, NULL, 0
#define INTe(f, m) _INTe(f, m), NULL, NULL, 0
/* INT_RANGE: Define an integer variable with allowed value range */
#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
/* FUNC: Define a configuration variable that uses a custom function for
* parsing and writing the value. */
#ifdef NO_CONFIG_WRITE
#define _FUNC(f) #f, wpa_config_parse_ ## f, NULL, NULL, NULL, NULL
#else /* NO_CONFIG_WRITE */
#define _FUNC(f) #f, wpa_config_parse_ ## f, wpa_config_write_ ## f, \
NULL, NULL, NULL, NULL
#endif /* NO_CONFIG_WRITE */
#define FUNC(f) _FUNC(f), 0
#define FUNC_KEY(f) _FUNC(f), 1
/*
* Table of network configuration variables. This table is used to parse each
* network configuration variable, e.g., each line in wpa_supplicant.conf file
* that is inside a network block.
*
* This table is generated using the helper macros defined above and with
* generous help from the C pre-processor. The field name is stored as a string
* into .name and for STR and INT types, the offset of the target buffer within
* struct wpa_ssid is stored in .param1. .param2 (if not NULL) is similar
* offset to the field containing the length of the configuration variable.
* .param3 and .param4 can be used to mark the allowed range (length for STR
* and value for INT).
*
* For each configuration line in wpa_supplicant.conf, the parser goes through
* this table and select the entry that matches with the field name. The parser
* function (.parser) is then called to parse the actual value of the field.
*
* This kind of mechanism makes it easy to add new configuration parameters,
* since only one line needs to be added into this table and into the
* struct wpa_ssid definition if the new variable is either a string or
* integer. More complex types will need to use their own parser and writer
* functions.
*/
static const struct parse_data ssid_fields[] = {
{ STR_RANGE(ssid, 0, SSID_MAX_LEN) },
{ INT_RANGE(scan_ssid, 0, 1) },
{ FUNC(bssid) },
{ FUNC(bssid_hint) },
{ FUNC(bssid_ignore) },
{ FUNC(bssid_accept) },
{ FUNC(bssid_blacklist) }, /* deprecated alias for bssid_ignore */
{ FUNC(bssid_whitelist) }, /* deprecated alias for bssid_accept */
{ FUNC_KEY(psk) },
{ INT(mem_only_psk) },
{ STR_KEY(sae_password) },
{ STR(sae_password_id) },
{ FUNC(proto) },
{ FUNC(key_mgmt) },
{ INT(bg_scan_period) },
{ FUNC(pairwise) },
{ FUNC(group) },
{ FUNC(group_mgmt) },
{ FUNC(auth_alg) },
{ FUNC(scan_freq) },
{ FUNC(freq_list) },
{ INT_RANGE(ht, 0, 1) },
{ INT_RANGE(vht, 0, 1) },
{ INT_RANGE(ht40, -1, 1) },
{ INT_RANGE(max_oper_chwidth, CHANWIDTH_USE_HT,
CHANWIDTH_80P80MHZ) },
{ INT(vht_center_freq1) },
{ INT(vht_center_freq2) },
#ifdef IEEE8021X_EAPOL
{ FUNC(eap) },
{ STR_LENe(identity, identity) },
{ STR_LENe(anonymous_identity, anonymous_identity) },
{ STR_LENe(imsi_identity, imsi_identity) },
{ STR_LENe(machine_identity, machine_identity) },
{ FUNC_KEY(password) },
{ FUNC_KEY(machine_password) },
{ STRe(ca_cert, cert.ca_cert) },
{ STRe(ca_path, cert.ca_path) },
{ STRe(client_cert, cert.client_cert) },
{ STRe(private_key, cert.private_key) },
{ STR_KEYe(private_key_passwd, cert.private_key_passwd) },
{ STRe(dh_file, cert.dh_file) },
{ STRe(subject_match, cert.subject_match) },
{ STRe(check_cert_subject, cert.check_cert_subject) },
{ STRe(altsubject_match, cert.altsubject_match) },
{ STRe(domain_suffix_match, cert.domain_suffix_match) },
{ STRe(domain_match, cert.domain_match) },
{ STRe(ca_cert2, phase2_cert.ca_cert) },
{ STRe(ca_path2, phase2_cert.ca_path) },
{ STRe(client_cert2, phase2_cert.client_cert) },
{ STRe(private_key2, phase2_cert.private_key) },
{ STR_KEYe(private_key2_passwd, phase2_cert.private_key_passwd) },
{ STRe(dh_file2, phase2_cert.dh_file) },
{ STRe(subject_match2, phase2_cert.subject_match) },
{ STRe(check_cert_subject2, phase2_cert.check_cert_subject) },
{ STRe(altsubject_match2, phase2_cert.altsubject_match) },
{ STRe(domain_suffix_match2, phase2_cert.domain_suffix_match) },
{ STRe(domain_match2, phase2_cert.domain_match) },
{ STRe(phase1, phase1) },
{ STRe(phase2, phase2) },
{ STRe(machine_phase2, machine_phase2) },
{ STRe(pcsc, pcsc) },
{ STR_KEYe(pin, cert.pin) },
{ STRe(engine_id, cert.engine_id) },
{ STRe(key_id, cert.key_id) },
{ STRe(cert_id, cert.cert_id) },
{ STRe(ca_cert_id, cert.ca_cert_id) },
{ STR_KEYe(pin2, phase2_cert.pin) },
{ STRe(engine_id2, phase2_cert.engine_id) },
{ STRe(key_id2, phase2_cert.key_id) },
{ STRe(cert_id2, phase2_cert.cert_id) },
{ STRe(ca_cert_id2, phase2_cert.ca_cert_id) },
{ INTe(engine, cert.engine) },
{ INTe(engine2, phase2_cert.engine) },
{ STRe(machine_ca_cert, machine_cert.ca_cert) },
{ STRe(machine_ca_path, machine_cert.ca_path) },
{ STRe(machine_client_cert, machine_cert.client_cert) },
{ STRe(machine_private_key, machine_cert.private_key) },
{ STR_KEYe(machine_private_key_passwd,
machine_cert.private_key_passwd) },
{ STRe(machine_dh_file, machine_cert.dh_file) },
{ STRe(machine_subject_match, machine_cert.subject_match) },
{ STRe(machine_check_cert_subject, machine_cert.check_cert_subject) },
{ STRe(machine_altsubject_match, machine_cert.altsubject_match) },
{ STRe(machine_domain_suffix_match,
machine_cert.domain_suffix_match) },
{ STRe(machine_domain_match, machine_cert.domain_match) },
{ STR_KEYe(machine_pin, machine_cert.pin) },
{ STRe(machine_engine_id, machine_cert.engine_id) },
{ STRe(machine_key_id, machine_cert.key_id) },
{ STRe(machine_cert_id, machine_cert.cert_id) },
{ STRe(machine_ca_cert_id, machine_cert.ca_cert_id) },
{ INTe(machine_engine, machine_cert.engine) },
{ INTe(machine_ocsp, machine_cert.ocsp) },
{ INT(eapol_flags) },
{ INTe(sim_num, sim_num) },
{ STRe(openssl_ciphers, openssl_ciphers) },
{ INTe(erp, erp) },
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_WEP
{ FUNC_KEY(wep_key0) },
{ FUNC_KEY(wep_key1) },
{ FUNC_KEY(wep_key2) },
{ FUNC_KEY(wep_key3) },
{ INT(wep_tx_keyidx) },
#endif /* CONFIG_WEP */
{ INT(priority) },
#ifdef IEEE8021X_EAPOL
{ INT(eap_workaround) },
{ STRe(pac_file, pac_file) },
{ INTe(fragment_size, fragment_size) },
{ INTe(ocsp, cert.ocsp) },
{ INTe(ocsp2, phase2_cert.ocsp) },
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_MESH
{ INT_RANGE(mode, 0, 5) },
{ INT_RANGE(no_auto_peer, 0, 1) },
{ INT_RANGE(mesh_rssi_threshold, -255, 1) },
#else /* CONFIG_MESH */
{ INT_RANGE(mode, 0, 4) },
#endif /* CONFIG_MESH */
{ INT_RANGE(proactive_key_caching, 0, 1) },
{ INT_RANGE(disabled, 0, 2) },
{ STR(id_str) },
{ INT_RANGE(ieee80211w, 0, 2) },
#ifdef CONFIG_OCV
{ FUNC(ocv) },
#endif /* CONFIG_OCV */
{ FUNC(peerkey) /* obsolete - removed */ },
{ INT_RANGE(mixed_cell, 0, 1) },
{ INT_RANGE(frequency, 0, 70200) },
{ INT_RANGE(fixed_freq, 0, 1) },
{ INT_RANGE(enable_edmg, 0, 1) },
{ INT_RANGE(edmg_channel, 9, 13) },
#ifdef CONFIG_ACS
{ INT_RANGE(acs, 0, 1) },
#endif /* CONFIG_ACS */
#ifdef CONFIG_MESH
{ FUNC(mesh_basic_rates) },
{ INT(dot11MeshMaxRetries) },
{ INT(dot11MeshRetryTimeout) },
{ INT(dot11MeshConfirmTimeout) },
{ INT(dot11MeshHoldingTimeout) },
#endif /* CONFIG_MESH */
{ INT(wpa_ptk_rekey) },
{ INT_RANGE(wpa_deny_ptk0_rekey, 0, 2) },
{ INT(group_rekey) },
{ STR(bgscan) },
{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
#ifdef CONFIG_P2P
{ FUNC(go_p2p_dev_addr) },
{ FUNC(p2p_client_list) },
{ FUNC(psk_list) },
#endif /* CONFIG_P2P */
#ifdef CONFIG_HT_OVERRIDES
{ INT_RANGE(disable_ht, 0, 1) },
{ INT_RANGE(disable_ht40, -1, 1) },
{ INT_RANGE(disable_sgi, 0, 1) },
{ INT_RANGE(disable_ldpc, 0, 1) },
{ INT_RANGE(ht40_intolerant, 0, 1) },
{ INT_RANGE(tx_stbc, -1, 1) },
{ INT_RANGE(rx_stbc, -1, 3) },
{ INT_RANGE(disable_max_amsdu, -1, 1) },
{ INT_RANGE(ampdu_factor, -1, 3) },
{ INT_RANGE(ampdu_density, -1, 7) },
{ STR(ht_mcs) },
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
{ INT_RANGE(disable_vht, 0, 1) },
{ INT(vht_capa) },
{ INT(vht_capa_mask) },
{ INT_RANGE(vht_rx_mcs_nss_1, -1, 3) },
{ INT_RANGE(vht_rx_mcs_nss_2, -1, 3) },
{ INT_RANGE(vht_rx_mcs_nss_3, -1, 3) },
{ INT_RANGE(vht_rx_mcs_nss_4, -1, 3) },
{ INT_RANGE(vht_rx_mcs_nss_5, -1, 3) },
{ INT_RANGE(vht_rx_mcs_nss_6, -1, 3) },
{ INT_RANGE(vht_rx_mcs_nss_7, -1, 3) },
{ INT_RANGE(vht_rx_mcs_nss_8, -1, 3) },
{ INT_RANGE(vht_tx_mcs_nss_1, -1, 3) },
{ INT_RANGE(vht_tx_mcs_nss_2, -1, 3) },
{ INT_RANGE(vht_tx_mcs_nss_3, -1, 3) },
{ INT_RANGE(vht_tx_mcs_nss_4, -1, 3) },
{ INT_RANGE(vht_tx_mcs_nss_5, -1, 3) },
{ INT_RANGE(vht_tx_mcs_nss_6, -1, 3) },
{ INT_RANGE(vht_tx_mcs_nss_7, -1, 3) },
{ INT_RANGE(vht_tx_mcs_nss_8, -1, 3) },
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_HE_OVERRIDES
{ INT_RANGE(disable_he, 0, 1)},
#endif /* CONFIG_HE_OVERRIDES */
{ INT(ap_max_inactivity) },
{ INT(dtim_period) },
{ INT(beacon_int) },
#ifdef CONFIG_MACSEC
{ INT_RANGE(macsec_policy, 0, 1) },
{ INT_RANGE(macsec_integ_only, 0, 1) },
{ INT_RANGE(macsec_replay_protect, 0, 1) },
{ INT(macsec_replay_window) },
{ INT_RANGE(macsec_port, 1, 65534) },
{ INT_RANGE(mka_priority, 0, 255) },
{ FUNC_KEY(mka_cak) },
{ FUNC_KEY(mka_ckn) },
#endif /* CONFIG_MACSEC */
#ifdef CONFIG_HS20
{ INT(update_identifier) },
{ STR_RANGE(roaming_consortium_selection, 0, MAX_ROAMING_CONS_OI_LEN) },
#endif /* CONFIG_HS20 */
{ INT_RANGE(mac_addr, 0, 2) },
{ INT_RANGE(pbss, 0, 2) },
{ INT_RANGE(wps_disabled, 0, 1) },
{ INT_RANGE(fils_dh_group, 0, 65535) },
#ifdef CONFIG_DPP
{ STR(dpp_connector) },
{ STR_LEN(dpp_netaccesskey) },
{ INT(dpp_netaccesskey_expiry) },
{ STR_LEN(dpp_csign) },
{ STR_LEN(dpp_pp_key) },
{ INT_RANGE(dpp_pfs, 0, 2) },
#endif /* CONFIG_DPP */
{ INT_RANGE(owe_group, 0, 65535) },
{ INT_RANGE(owe_only, 0, 1) },
{ INT_RANGE(owe_ptk_workaround, 0, 1) },
{ INT_RANGE(multi_ap_backhaul_sta, 0, 1) },
{ INT_RANGE(ft_eap_pmksa_caching, 0, 1) },
{ INT_RANGE(beacon_prot, 0, 1) },
{ INT_RANGE(transition_disable, 0, 255) },
{ INT_RANGE(sae_pk, 0, 2) },
};
#undef OFFSET
#undef _STR
#undef STR
#undef STR_KEY
#undef _STR_LEN
#undef STR_LEN
#undef STR_LEN_KEY
#undef _STR_RANGE
#undef STR_RANGE
#undef STR_RANGE_KEY
#undef _INT
#undef INT
#undef INT_RANGE
#undef _FUNC
#undef FUNC
#undef FUNC_KEY
#define NUM_SSID_FIELDS ARRAY_SIZE(ssid_fields)
/**
* wpa_config_add_prio_network - Add a network to priority lists
* @config: Configuration data from wpa_config_read()
* @ssid: Pointer to the network configuration to be added to the list
* Returns: 0 on success, -1 on failure
*
* This function is used to add a network block to the priority list of
* networks. This must be called for each network when reading in the full
* configuration. In addition, this can be used indirectly when updating
* priorities by calling wpa_config_update_prio_list().
*/
int wpa_config_add_prio_network(struct wpa_config *config,
struct wpa_ssid *ssid)
{
size_t prio;
struct wpa_ssid *prev, **nlist;
/*
* Add to an existing priority list if one is available for the
* configured priority level for this network.
*/
for (prio = 0; prio < config->num_prio; prio++) {
prev = config->pssid[prio];
if (prev->priority == ssid->priority) {
while (prev->pnext)
prev = prev->pnext;
prev->pnext = ssid;
return 0;
}
}
/* First network for this priority - add a new priority list */
nlist = os_realloc_array(config->pssid, config->num_prio + 1,
sizeof(struct wpa_ssid *));
if (nlist == NULL)
return -1;
for (prio = 0; prio < config->num_prio; prio++) {
if (nlist[prio]->priority < ssid->priority) {
os_memmove(&nlist[prio + 1], &nlist[prio],
(config->num_prio - prio) *
sizeof(struct wpa_ssid *));
break;
}
}
nlist[prio] = ssid;
config->num_prio++;
config->pssid = nlist;
return 0;
}
/**
* wpa_config_update_prio_list - Update network priority list
* @config: Configuration data from wpa_config_read()
* Returns: 0 on success, -1 on failure
*
* This function is called to update the priority list of networks in the
* configuration when a network is being added or removed. This is also called
* if a priority for a network is changed.
*/
int wpa_config_update_prio_list(struct wpa_config *config)
{
struct wpa_ssid *ssid;
int ret = 0;
os_free(config->pssid);
config->pssid = NULL;
config->num_prio = 0;
ssid = config->ssid;
while (ssid) {
ssid->pnext = NULL;
if (wpa_config_add_prio_network(config, ssid) < 0)
ret = -1;
ssid = ssid->next;
}
return ret;
}
#ifdef IEEE8021X_EAPOL
static void eap_peer_config_free_cert(struct eap_peer_cert_config *cert)
{
os_free(cert->ca_cert);
os_free(cert->ca_path);
os_free(cert->client_cert);
os_free(cert->private_key);
str_clear_free(cert->private_key_passwd);
os_free(cert->dh_file);
os_free(cert->subject_match);
os_free(cert->check_cert_subject);
os_free(cert->altsubject_match);
os_free(cert->domain_suffix_match);
os_free(cert->domain_match);
str_clear_free(cert->pin);
os_free(cert->engine_id);
os_free(cert->key_id);
os_free(cert->cert_id);
os_free(cert->ca_cert_id);
}
static void eap_peer_config_free(struct eap_peer_config *eap)
{
os_free(eap->eap_methods);
bin_clear_free(eap->identity, eap->identity_len);
os_free(eap->anonymous_identity);
os_free(eap->imsi_identity);
os_free(eap->machine_identity);
bin_clear_free(eap->password, eap->password_len);
bin_clear_free(eap->machine_password, eap->machine_password_len);
eap_peer_config_free_cert(&eap->cert);
eap_peer_config_free_cert(&eap->phase2_cert);
eap_peer_config_free_cert(&eap->machine_cert);
os_free(eap->phase1);
os_free(eap->phase2);
os_free(eap->machine_phase2);
os_free(eap->pcsc);
os_free(eap->otp);
os_free(eap->pending_req_otp);
os_free(eap->pac_file);
bin_clear_free(eap->new_password, eap->new_password_len);
str_clear_free(eap->external_sim_resp);
os_free(eap->openssl_ciphers);
}
#endif /* IEEE8021X_EAPOL */
/**
* wpa_config_free_ssid - Free network/ssid configuration data
* @ssid: Configuration data for the network
*
* This function frees all resources allocated for the network configuration
* data.
*/
void wpa_config_free_ssid(struct wpa_ssid *ssid)
{
struct psk_list_entry *psk;
os_free(ssid->ssid);
str_clear_free(ssid->passphrase);
os_free(ssid->ext_psk);
str_clear_free(ssid->sae_password);
os_free(ssid->sae_password_id);
#ifdef IEEE8021X_EAPOL
eap_peer_config_free(&ssid->eap);
#endif /* IEEE8021X_EAPOL */
os_free(ssid->id_str);
os_free(ssid->scan_freq);
os_free(ssid->freq_list);
os_free(ssid->bgscan);
os_free(ssid->p2p_client_list);
os_free(ssid->bssid_ignore);
os_free(ssid->bssid_accept);
#ifdef CONFIG_HT_OVERRIDES
os_free(ssid->ht_mcs);
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_MESH
os_free(ssid->mesh_basic_rates);
#endif /* CONFIG_MESH */
#ifdef CONFIG_HS20
os_free(ssid->roaming_consortium_selection);
#endif /* CONFIG_HS20 */
os_free(ssid->dpp_connector);
bin_clear_free(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len);
os_free(ssid->dpp_csign);
os_free(ssid->dpp_pp_key);
while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
list))) {
dl_list_del(&psk->list);
bin_clear_free(psk, sizeof(*psk));
}
#ifdef CONFIG_SAE
sae_deinit_pt(ssid->pt);
#endif /* CONFIG_SAE */
bin_clear_free(ssid, sizeof(*ssid));
}
void wpa_config_free_cred(struct wpa_cred *cred)
{
size_t i;
os_free(cred->realm);
str_clear_free(cred->username);
str_clear_free(cred->password);
os_free(cred->ca_cert);
os_free(cred->client_cert);
os_free(cred->private_key);
str_clear_free(cred->private_key_passwd);
os_free(cred->imsi);
str_clear_free(cred->milenage);
for (i = 0; i < cred->num_domain; i++)
os_free(cred->domain[i]);
os_free(cred->domain);
os_free(cred->domain_suffix_match);
os_free(cred->eap_method);
os_free(cred->phase1);
os_free(cred->phase2);
os_free(cred->excluded_ssid);
os_free(cred->roaming_partner);
os_free(cred->provisioning_sp);
for (i = 0; i < cred->num_req_conn_capab; i++)
os_free(cred->req_conn_capab_port[i]);
os_free(cred->req_conn_capab_port);
os_free(cred->req_conn_capab_proto);
os_free(cred);
}
void wpa_config_flush_blobs(struct wpa_config *config)
{
#ifndef CONFIG_NO_CONFIG_BLOBS
struct wpa_config_blob *blob, *prev;
blob = config->blobs;
config->blobs = NULL;
while (blob) {
prev = blob;
blob = blob->next;
wpa_config_free_blob(prev);
}
#endif /* CONFIG_NO_CONFIG_BLOBS */
}
/**
* wpa_config_free - Free configuration data
* @config: Configuration data from wpa_config_read()
*
* This function frees all resources allocated for the configuration data by
* wpa_config_read().
*/
void wpa_config_free(struct wpa_config *config)
{
struct wpa_ssid *ssid, *prev = NULL;
struct wpa_cred *cred, *cprev;
int i;
ssid = config->ssid;
while (ssid) {
prev = ssid;
ssid = ssid->next;
wpa_config_free_ssid(prev);
}
cred = config->cred;
while (cred) {
cprev = cred;
cred = cred->next;
wpa_config_free_cred(cprev);
}
wpa_config_flush_blobs(config);
wpabuf_free(config->wps_vendor_ext_m1);
for (i = 0; i < MAX_WPS_VENDOR_EXT; i++)
wpabuf_free(config->wps_vendor_ext[i]);
os_free(config->ctrl_interface);
os_free(config->ctrl_interface_group);
os_free(config->opensc_engine_path);
os_free(config->pkcs11_engine_path);
os_free(config->pkcs11_module_path);
os_free(config->openssl_ciphers);
os_free(config->pcsc_reader);
str_clear_free(config->pcsc_pin);
os_free(config->driver_param);
os_free(config->device_name);
os_free(config->manufacturer);
os_free(config->model_name);
os_free(config->model_number);
os_free(config->serial_number);
os_free(config->config_methods);
os_free(config->p2p_ssid_postfix);
os_free(config->pssid);
os_free(config->p2p_pref_chan);
os_free(config->p2p_no_go_freq.range);
os_free(config->autoscan);
os_free(config->freq_list);
os_free(config->initial_freq_list);
wpabuf_free(config->wps_nfc_dh_pubkey);
wpabuf_free(config->wps_nfc_dh_privkey);
wpabuf_free(config->wps_nfc_dev_pw);
os_free(config->ext_password_backend);
os_free(config->sae_groups);
wpabuf_free(config->ap_vendor_elements);
+ wpabuf_free(config->ap_assocresp_elements);
os_free(config->osu_dir);
os_free(config->bgscan);
os_free(config->wowlan_triggers);
os_free(config->fst_group_id);
os_free(config->sched_scan_plans);
#ifdef CONFIG_MBO
os_free(config->non_pref_chan);
#endif /* CONFIG_MBO */
os_free(config->dpp_name);
os_free(config->dpp_mud_url);
os_free(config);
}
/**
* wpa_config_foreach_network - Iterate over each configured network
* @config: Configuration data from wpa_config_read()
* @func: Callback function to process each network
* @arg: Opaque argument to pass to callback function
*
* Iterate over the set of configured networks calling the specified
* function for each item. We guard against callbacks removing the
* supplied network.
*/
void wpa_config_foreach_network(struct wpa_config *config,
void (*func)(void *, struct wpa_ssid *),
void *arg)
{
struct wpa_ssid *ssid, *next;
ssid = config->ssid;
while (ssid) {
next = ssid->next;
func(arg, ssid);
ssid = next;
}
}
/**
* wpa_config_get_network - Get configured network based on id
* @config: Configuration data from wpa_config_read()
* @id: Unique network id to search for
* Returns: Network configuration or %NULL if not found
*/
struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id)
{
struct wpa_ssid *ssid;
ssid = config->ssid;
while (ssid) {
if (id == ssid->id)
break;
ssid = ssid->next;
}
return ssid;
}
/**
* wpa_config_add_network - Add a new network with empty configuration
* @config: Configuration data from wpa_config_read()
* Returns: The new network configuration or %NULL if operation failed
*/
struct wpa_ssid * wpa_config_add_network(struct wpa_config *config)
{
int id;
struct wpa_ssid *ssid, *last = NULL;
id = -1;
ssid = config->ssid;
while (ssid) {
if (ssid->id > id)
id = ssid->id;
last = ssid;
ssid = ssid->next;
}
id++;
ssid = os_zalloc(sizeof(*ssid));
if (ssid == NULL)
return NULL;
ssid->id = id;
dl_list_init(&ssid->psk_list);
if (last)
last->next = ssid;
else
config->ssid = ssid;
wpa_config_update_prio_list(config);
return ssid;
}
/**
* wpa_config_remove_network - Remove a configured network based on id
* @config: Configuration data from wpa_config_read()
* @id: Unique network id to search for
* Returns: 0 on success, or -1 if the network was not found
*/
int wpa_config_remove_network(struct wpa_config *config, int id)
{
struct wpa_ssid *ssid, *prev = NULL;
ssid = config->ssid;
while (ssid) {
if (id == ssid->id)
break;
prev = ssid;
ssid = ssid->next;
}
if (ssid == NULL)
return -1;
if (prev)
prev->next = ssid->next;
else
config->ssid = ssid->next;
wpa_config_update_prio_list(config);
wpa_config_free_ssid(ssid);
return 0;
}
/**
* wpa_config_set_network_defaults - Set network default values
* @ssid: Pointer to network configuration data
*/
void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
{
ssid->proto = DEFAULT_PROTO;
ssid->pairwise_cipher = DEFAULT_PAIRWISE;
ssid->group_cipher = DEFAULT_GROUP;
ssid->key_mgmt = DEFAULT_KEY_MGMT;
ssid->wpa_deny_ptk0_rekey = PTK0_REKEY_ALLOW_ALWAYS;
ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
ssid->ht = 1;
ssid->vht = 1;
ssid->he = 1;
#ifdef IEEE8021X_EAPOL
ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM;
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_MESH
ssid->dot11MeshMaxRetries = DEFAULT_MESH_MAX_RETRIES;
ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT;
ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT;
ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT;
ssid->mesh_rssi_threshold = DEFAULT_MESH_RSSI_THRESHOLD;
#endif /* CONFIG_MESH */
#ifdef CONFIG_HT_OVERRIDES
ssid->disable_ht = DEFAULT_DISABLE_HT;
ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
ssid->disable_sgi = DEFAULT_DISABLE_SGI;
ssid->disable_ldpc = DEFAULT_DISABLE_LDPC;
ssid->tx_stbc = DEFAULT_TX_STBC;
ssid->rx_stbc = DEFAULT_RX_STBC;
ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
ssid->vht_rx_mcs_nss_1 = -1;
ssid->vht_rx_mcs_nss_2 = -1;
ssid->vht_rx_mcs_nss_3 = -1;
ssid->vht_rx_mcs_nss_4 = -1;
ssid->vht_rx_mcs_nss_5 = -1;
ssid->vht_rx_mcs_nss_6 = -1;
ssid->vht_rx_mcs_nss_7 = -1;
ssid->vht_rx_mcs_nss_8 = -1;
ssid->vht_tx_mcs_nss_1 = -1;
ssid->vht_tx_mcs_nss_2 = -1;
ssid->vht_tx_mcs_nss_3 = -1;
ssid->vht_tx_mcs_nss_4 = -1;
ssid->vht_tx_mcs_nss_5 = -1;
ssid->vht_tx_mcs_nss_6 = -1;
ssid->vht_tx_mcs_nss_7 = -1;
ssid->vht_tx_mcs_nss_8 = -1;
#endif /* CONFIG_VHT_OVERRIDES */
ssid->proactive_key_caching = -1;
ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
+ ssid->sae_pwe = DEFAULT_SAE_PWE;
#ifdef CONFIG_MACSEC
ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
#endif /* CONFIG_MACSEC */
ssid->mac_addr = -1;
ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH;
}
/**
* wpa_config_set - Set a variable in network configuration
* @ssid: Pointer to network configuration data
* @var: Variable name, e.g., "ssid"
* @value: Variable value
* @line: Line number in configuration file or 0 if not used
* Returns: 0 on success with possible change in the value, 1 on success with
* no change to previously configured value, or -1 on failure
*
* This function can be used to set network configuration variables based on
* both the configuration file and management interface input. The value
* parameter must be in the same format as the text-based configuration file is
* using. For example, strings are using double quotation marks.
*/
int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
int line)
{
size_t i;
int ret = 0;
if (ssid == NULL || var == NULL || value == NULL)
return -1;
for (i = 0; i < NUM_SSID_FIELDS; i++) {
const struct parse_data *field = &ssid_fields[i];
if (os_strcmp(var, field->name) != 0)
continue;
ret = field->parser(field, ssid, line, value);
if (ret < 0) {
if (line) {
wpa_printf(MSG_ERROR, "Line %d: failed to "
"parse %s '%s'.", line, var, value);
}
ret = -1;
}
#ifdef CONFIG_SAE
if (os_strcmp(var, "ssid") == 0 ||
os_strcmp(var, "psk") == 0 ||
os_strcmp(var, "sae_password") == 0 ||
os_strcmp(var, "sae_password_id") == 0) {
sae_deinit_pt(ssid->pt);
ssid->pt = NULL;
}
#endif /* CONFIG_SAE */
break;
}
if (i == NUM_SSID_FIELDS) {
if (line) {
wpa_printf(MSG_ERROR, "Line %d: unknown network field "
"'%s'.", line, var);
}
ret = -1;
}
ssid->was_recently_reconfigured = true;
return ret;
}
int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
const char *value)
{
size_t len;
char *buf;
int ret;
len = os_strlen(value);
buf = os_malloc(len + 3);
if (buf == NULL)
return -1;
buf[0] = '"';
os_memcpy(buf + 1, value, len);
buf[len + 1] = '"';
buf[len + 2] = '\0';
ret = wpa_config_set(ssid, var, buf, 0);
os_free(buf);
return ret;
}
/**
* wpa_config_get_all - Get all options from network configuration
* @ssid: Pointer to network configuration data
* @get_keys: Determines if keys/passwords will be included in returned list
* (if they may be exported)
* Returns: %NULL terminated list of all set keys and their values in the form
* of [key1, val1, key2, val2, ... , NULL]
*
* This function can be used to get list of all configured network properties.
* The caller is responsible for freeing the returned list and all its
* elements.
*/
char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
{
#ifdef NO_CONFIG_WRITE
return NULL;
#else /* NO_CONFIG_WRITE */
const struct parse_data *field;
char *key, *value;
size_t i;
char **props;
int fields_num;
get_keys = get_keys && ssid->export_keys;
props = os_calloc(2 * NUM_SSID_FIELDS + 1, sizeof(char *));
if (!props)
return NULL;
fields_num = 0;
for (i = 0; i < NUM_SSID_FIELDS; i++) {
field = &ssid_fields[i];
if (field->key_data && !get_keys)
continue;
value = field->writer(field, ssid);
if (value == NULL)
continue;
if (os_strlen(value) == 0) {
os_free(value);
continue;
}
key = os_strdup(field->name);
if (key == NULL) {
os_free(value);
goto err;
}
props[fields_num * 2] = key;
props[fields_num * 2 + 1] = value;
fields_num++;
}
return props;
err:
for (i = 0; props[i]; i++)
os_free(props[i]);
os_free(props);
return NULL;
#endif /* NO_CONFIG_WRITE */
}
#ifndef NO_CONFIG_WRITE
/**
* wpa_config_get - Get a variable in network configuration
* @ssid: Pointer to network configuration data
* @var: Variable name, e.g., "ssid"
* Returns: Value of the variable or %NULL on failure
*
* This function can be used to get network configuration variables. The
* returned value is a copy of the configuration variable in text format, i.e,.
* the same format that the text-based configuration file and wpa_config_set()
* are using for the value. The caller is responsible for freeing the returned
* value.
*/
char * wpa_config_get(struct wpa_ssid *ssid, const char *var)
{
size_t i;
if (ssid == NULL || var == NULL)
return NULL;
for (i = 0; i < NUM_SSID_FIELDS; i++) {
const struct parse_data *field = &ssid_fields[i];
if (os_strcmp(var, field->name) == 0) {
char *ret = field->writer(field, ssid);
if (ret && has_newline(ret)) {
wpa_printf(MSG_ERROR,
"Found newline in value for %s; not returning it",
var);
os_free(ret);
ret = NULL;
}
return ret;
}
}
return NULL;
}
/**
* wpa_config_get_no_key - Get a variable in network configuration (no keys)
* @ssid: Pointer to network configuration data
* @var: Variable name, e.g., "ssid"
* Returns: Value of the variable or %NULL on failure
*
* This function can be used to get network configuration variable like
* wpa_config_get(). The only difference is that this functions does not expose
* key/password material from the configuration. In case a key/password field
* is requested, the returned value is an empty string or %NULL if the variable
* is not set or "*" if the variable is set (regardless of its value). The
* returned value is a copy of the configuration variable in text format, i.e,.
* the same format that the text-based configuration file and wpa_config_set()
* are using for the value. The caller is responsible for freeing the returned
* value.
*/
char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var)
{
size_t i;
if (ssid == NULL || var == NULL)
return NULL;
for (i = 0; i < NUM_SSID_FIELDS; i++) {
const struct parse_data *field = &ssid_fields[i];
if (os_strcmp(var, field->name) == 0) {
char *res = field->writer(field, ssid);
if (field->key_data) {
if (res && res[0]) {
wpa_printf(MSG_DEBUG, "Do not allow "
"key_data field to be "
"exposed");
str_clear_free(res);
return os_strdup("*");
}
os_free(res);
return NULL;
}
return res;
}
}
return NULL;
}
#endif /* NO_CONFIG_WRITE */
/**
* wpa_config_update_psk - Update WPA PSK based on passphrase and SSID
* @ssid: Pointer to network configuration data
*
* This function must be called to update WPA PSK when either SSID or the
* passphrase has changed for the network configuration.
*/
void wpa_config_update_psk(struct wpa_ssid *ssid)
{
#ifndef CONFIG_NO_PBKDF2
pbkdf2_sha1(ssid->passphrase, ssid->ssid, ssid->ssid_len, 4096,
ssid->psk, PMK_LEN);
wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
ssid->psk, PMK_LEN);
ssid->psk_set = 1;
#endif /* CONFIG_NO_PBKDF2 */
}
static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred,
const char *value)
{
u8 *proto;
int **port;
int *ports, *nports;
const char *pos;
unsigned int num_ports;
proto = os_realloc_array(cred->req_conn_capab_proto,
cred->num_req_conn_capab + 1, sizeof(u8));
if (proto == NULL)
return -1;
cred->req_conn_capab_proto = proto;
port = os_realloc_array(cred->req_conn_capab_port,
cred->num_req_conn_capab + 1, sizeof(int *));
if (port == NULL)
return -1;
cred->req_conn_capab_port = port;
proto[cred->num_req_conn_capab] = atoi(value);
pos = os_strchr(value, ':');
if (pos == NULL) {
port[cred->num_req_conn_capab] = NULL;
cred->num_req_conn_capab++;
return 0;
}
pos++;
ports = NULL;
num_ports = 0;
while (*pos) {
nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
if (nports == NULL) {
os_free(ports);
return -1;
}
ports = nports;
ports[num_ports++] = atoi(pos);
pos = os_strchr(pos, ',');
if (pos == NULL)
break;
pos++;
}
nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
if (nports == NULL) {
os_free(ports);
return -1;
}
ports = nports;
ports[num_ports] = -1;
port[cred->num_req_conn_capab] = ports;
cred->num_req_conn_capab++;
return 0;
}
static int wpa_config_set_cred_roaming_consortiums(struct wpa_cred *cred,
const char *value)
{
u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
size_t roaming_consortiums_len[MAX_ROAMING_CONS];
unsigned int num_roaming_consortiums = 0;
const char *pos, *end;
size_t len;
os_memset(roaming_consortiums, 0, sizeof(roaming_consortiums));
os_memset(roaming_consortiums_len, 0, sizeof(roaming_consortiums_len));
for (pos = value;;) {
end = os_strchr(pos, ',');
len = end ? (size_t) (end - pos) : os_strlen(pos);
if (!end && len == 0)
break;
if (len == 0 || (len & 1) != 0 ||
len / 2 > MAX_ROAMING_CONS_OI_LEN ||
hexstr2bin(pos,
roaming_consortiums[num_roaming_consortiums],
len / 2) < 0) {
wpa_printf(MSG_INFO,
"Invalid roaming_consortiums entry: %s",
pos);
return -1;
}
roaming_consortiums_len[num_roaming_consortiums] = len / 2;
num_roaming_consortiums++;
if (!end)
break;
if (num_roaming_consortiums >= MAX_ROAMING_CONS) {
wpa_printf(MSG_INFO,
"Too many roaming_consortiums OIs");
return -1;
}
pos = end + 1;
}
os_memcpy(cred->roaming_consortiums, roaming_consortiums,
sizeof(roaming_consortiums));
os_memcpy(cred->roaming_consortiums_len, roaming_consortiums_len,
sizeof(roaming_consortiums_len));
cred->num_roaming_consortiums = num_roaming_consortiums;
return 0;
}
int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
const char *value, int line)
{
char *val;
size_t len;
int res;
if (os_strcmp(var, "temporary") == 0) {
cred->temporary = atoi(value);
return 0;
}
if (os_strcmp(var, "priority") == 0) {
cred->priority = atoi(value);
return 0;
}
if (os_strcmp(var, "sp_priority") == 0) {
int prio = atoi(value);
if (prio < 0 || prio > 255)
return -1;
cred->sp_priority = prio;
return 0;
}
if (os_strcmp(var, "pcsc") == 0) {
cred->pcsc = atoi(value);
return 0;
}
if (os_strcmp(var, "eap") == 0) {
struct eap_method_type method;
method.method = eap_peer_get_type(value, &method.vendor);
if (method.vendor == EAP_VENDOR_IETF &&
method.method == EAP_TYPE_NONE) {
wpa_printf(MSG_ERROR, "Line %d: unknown EAP type '%s' "
"for a credential", line, value);
return -1;
}
os_free(cred->eap_method);
cred->eap_method = os_malloc(sizeof(*cred->eap_method));
if (cred->eap_method == NULL)
return -1;
os_memcpy(cred->eap_method, &method, sizeof(method));
return 0;
}
if (os_strcmp(var, "password") == 0 &&
os_strncmp(value, "ext:", 4) == 0) {
if (has_newline(value))
return -1;
str_clear_free(cred->password);
cred->password = os_strdup(value);
cred->ext_password = 1;
return 0;
}
if (os_strcmp(var, "update_identifier") == 0) {
cred->update_identifier = atoi(value);
return 0;
}
if (os_strcmp(var, "min_dl_bandwidth_home") == 0) {
cred->min_dl_bandwidth_home = atoi(value);
return 0;
}
if (os_strcmp(var, "min_ul_bandwidth_home") == 0) {
cred->min_ul_bandwidth_home = atoi(value);
return 0;
}
if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0) {
cred->min_dl_bandwidth_roaming = atoi(value);
return 0;
}
if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0) {
cred->min_ul_bandwidth_roaming = atoi(value);
return 0;
}
if (os_strcmp(var, "max_bss_load") == 0) {
cred->max_bss_load = atoi(value);
return 0;
}
if (os_strcmp(var, "req_conn_capab") == 0)
return wpa_config_set_cred_req_conn_capab(cred, value);
if (os_strcmp(var, "ocsp") == 0) {
cred->ocsp = atoi(value);
return 0;
}
if (os_strcmp(var, "sim_num") == 0) {
cred->sim_num = atoi(value);
return 0;
}
val = wpa_config_parse_string(value, &len);
if (val == NULL ||
(os_strcmp(var, "excluded_ssid") != 0 &&
os_strcmp(var, "roaming_consortium") != 0 &&
os_strcmp(var, "required_roaming_consortium") != 0 &&
has_newline(val))) {
wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
"value '%s'.", line, var, value);
os_free(val);
return -1;
}
if (os_strcmp(var, "realm") == 0) {
os_free(cred->realm);
cred->realm = val;
return 0;
}
if (os_strcmp(var, "username") == 0) {
str_clear_free(cred->username);
cred->username = val;
return 0;
}
if (os_strcmp(var, "password") == 0) {
str_clear_free(cred->password);
cred->password = val;
cred->ext_password = 0;
return 0;
}
if (os_strcmp(var, "ca_cert") == 0) {
os_free(cred->ca_cert);
cred->ca_cert = val;
return 0;
}
if (os_strcmp(var, "client_cert") == 0) {
os_free(cred->client_cert);
cred->client_cert = val;
return 0;
}
if (os_strcmp(var, "private_key") == 0) {
os_free(cred->private_key);
cred->private_key = val;
return 0;
}
if (os_strcmp(var, "private_key_passwd") == 0) {
str_clear_free(cred->private_key_passwd);
cred->private_key_passwd = val;
return 0;
}
if (os_strcmp(var, "imsi") == 0) {
os_free(cred->imsi);
cred->imsi = val;
return 0;
}
if (os_strcmp(var, "milenage") == 0) {
str_clear_free(cred->milenage);
cred->milenage = val;
return 0;
}
if (os_strcmp(var, "domain_suffix_match") == 0) {
os_free(cred->domain_suffix_match);
cred->domain_suffix_match = val;
return 0;
}
if (os_strcmp(var, "domain") == 0) {
char **new_domain;
new_domain = os_realloc_array(cred->domain,
cred->num_domain + 1,
sizeof(char *));
if (new_domain == NULL) {
os_free(val);
return -1;
}
new_domain[cred->num_domain++] = val;
cred->domain = new_domain;
return 0;
}
if (os_strcmp(var, "phase1") == 0) {
os_free(cred->phase1);
cred->phase1 = val;
return 0;
}
if (os_strcmp(var, "phase2") == 0) {
os_free(cred->phase2);
cred->phase2 = val;
return 0;
}
if (os_strcmp(var, "roaming_consortium") == 0) {
if (len < 3 || len > sizeof(cred->roaming_consortium)) {
wpa_printf(MSG_ERROR, "Line %d: invalid "
"roaming_consortium length %d (3..15 "
"expected)", line, (int) len);
os_free(val);
return -1;
}
os_memcpy(cred->roaming_consortium, val, len);
cred->roaming_consortium_len = len;
os_free(val);
return 0;
}
if (os_strcmp(var, "required_roaming_consortium") == 0) {
if (len < 3 || len > sizeof(cred->required_roaming_consortium))
{
wpa_printf(MSG_ERROR, "Line %d: invalid "
"required_roaming_consortium length %d "
"(3..15 expected)", line, (int) len);
os_free(val);
return -1;
}
os_memcpy(cred->required_roaming_consortium, val, len);
cred->required_roaming_consortium_len = len;
os_free(val);
return 0;
}
if (os_strcmp(var, "roaming_consortiums") == 0) {
res = wpa_config_set_cred_roaming_consortiums(cred, val);
if (res < 0)
wpa_printf(MSG_ERROR,
"Line %d: invalid roaming_consortiums",
line);
os_free(val);
return res;
}
if (os_strcmp(var, "excluded_ssid") == 0) {
struct excluded_ssid *e;
if (len > SSID_MAX_LEN) {
wpa_printf(MSG_ERROR, "Line %d: invalid "
"excluded_ssid length %d", line, (int) len);
os_free(val);
return -1;
}
e = os_realloc_array(cred->excluded_ssid,
cred->num_excluded_ssid + 1,
sizeof(struct excluded_ssid));
if (e == NULL) {
os_free(val);
return -1;
}
cred->excluded_ssid = e;
e = &cred->excluded_ssid[cred->num_excluded_ssid++];
os_memcpy(e->ssid, val, len);
e->ssid_len = len;
os_free(val);
return 0;
}
if (os_strcmp(var, "roaming_partner") == 0) {
struct roaming_partner *p;
char *pos;
p = os_realloc_array(cred->roaming_partner,
cred->num_roaming_partner + 1,
sizeof(struct roaming_partner));
if (p == NULL) {
os_free(val);
return -1;
}
cred->roaming_partner = p;
p = &cred->roaming_partner[cred->num_roaming_partner];
pos = os_strchr(val, ',');
if (pos == NULL) {
os_free(val);
return -1;
}
*pos++ = '\0';
if (pos - val - 1 >= (int) sizeof(p->fqdn)) {
os_free(val);
return -1;
}
os_memcpy(p->fqdn, val, pos - val);
p->exact_match = atoi(pos);
pos = os_strchr(pos, ',');
if (pos == NULL) {
os_free(val);
return -1;
}
*pos++ = '\0';
p->priority = atoi(pos);
pos = os_strchr(pos, ',');
if (pos == NULL) {
os_free(val);
return -1;
}
*pos++ = '\0';
if (os_strlen(pos) >= sizeof(p->country)) {
os_free(val);
return -1;
}
os_memcpy(p->country, pos, os_strlen(pos) + 1);
cred->num_roaming_partner++;
os_free(val);
return 0;
}
if (os_strcmp(var, "provisioning_sp") == 0) {
os_free(cred->provisioning_sp);
cred->provisioning_sp = val;
return 0;
}
if (line) {
wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
line, var);
}
os_free(val);
return -1;
}
static char * alloc_int_str(int val)
{
const unsigned int bufsize = 20;
char *buf;
int res;
buf = os_malloc(bufsize);
if (buf == NULL)
return NULL;
res = os_snprintf(buf, bufsize, "%d", val);
if (os_snprintf_error(bufsize, res)) {
os_free(buf);
buf = NULL;
}
return buf;
}
static char * alloc_strdup(const char *str)
{
if (str == NULL)
return NULL;
return os_strdup(str);
}
char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var)
{
if (os_strcmp(var, "temporary") == 0)
return alloc_int_str(cred->temporary);
if (os_strcmp(var, "priority") == 0)
return alloc_int_str(cred->priority);
if (os_strcmp(var, "sp_priority") == 0)
return alloc_int_str(cred->sp_priority);
if (os_strcmp(var, "pcsc") == 0)
return alloc_int_str(cred->pcsc);
if (os_strcmp(var, "eap") == 0) {
if (!cred->eap_method)
return NULL;
return alloc_strdup(eap_get_name(cred->eap_method[0].vendor,
cred->eap_method[0].method));
}
if (os_strcmp(var, "update_identifier") == 0)
return alloc_int_str(cred->update_identifier);
if (os_strcmp(var, "min_dl_bandwidth_home") == 0)
return alloc_int_str(cred->min_dl_bandwidth_home);
if (os_strcmp(var, "min_ul_bandwidth_home") == 0)
return alloc_int_str(cred->min_ul_bandwidth_home);
if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0)
return alloc_int_str(cred->min_dl_bandwidth_roaming);
if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0)
return alloc_int_str(cred->min_ul_bandwidth_roaming);
if (os_strcmp(var, "max_bss_load") == 0)
return alloc_int_str(cred->max_bss_load);
if (os_strcmp(var, "req_conn_capab") == 0) {
unsigned int i;
char *buf, *end, *pos;
int ret;
if (!cred->num_req_conn_capab)
return NULL;
buf = os_malloc(4000);
if (buf == NULL)
return NULL;
pos = buf;
end = pos + 4000;
for (i = 0; i < cred->num_req_conn_capab; i++) {
int *ports;
ret = os_snprintf(pos, end - pos, "%s%u",
i > 0 ? "\n" : "",
cred->req_conn_capab_proto[i]);
if (os_snprintf_error(end - pos, ret))
return buf;
pos += ret;
ports = cred->req_conn_capab_port[i];
if (ports) {
int j;
for (j = 0; ports[j] != -1; j++) {
ret = os_snprintf(pos, end - pos,
"%s%d",
j > 0 ? "," : ":",
ports[j]);
if (os_snprintf_error(end - pos, ret))
return buf;
pos += ret;
}
}
}
return buf;
}
if (os_strcmp(var, "ocsp") == 0)
return alloc_int_str(cred->ocsp);
if (os_strcmp(var, "realm") == 0)
return alloc_strdup(cred->realm);
if (os_strcmp(var, "username") == 0)
return alloc_strdup(cred->username);
if (os_strcmp(var, "password") == 0) {
if (!cred->password)
return NULL;
return alloc_strdup("*");
}
if (os_strcmp(var, "ca_cert") == 0)
return alloc_strdup(cred->ca_cert);
if (os_strcmp(var, "client_cert") == 0)
return alloc_strdup(cred->client_cert);
if (os_strcmp(var, "private_key") == 0)
return alloc_strdup(cred->private_key);
if (os_strcmp(var, "private_key_passwd") == 0) {
if (!cred->private_key_passwd)
return NULL;
return alloc_strdup("*");
}
if (os_strcmp(var, "imsi") == 0)
return alloc_strdup(cred->imsi);
if (os_strcmp(var, "milenage") == 0) {
if (!(cred->milenage))
return NULL;
return alloc_strdup("*");
}
if (os_strcmp(var, "domain_suffix_match") == 0)
return alloc_strdup(cred->domain_suffix_match);
if (os_strcmp(var, "domain") == 0) {
unsigned int i;
char *buf, *end, *pos;
int ret;
if (!cred->num_domain)
return NULL;
buf = os_malloc(4000);
if (buf == NULL)
return NULL;
pos = buf;
end = pos + 4000;
for (i = 0; i < cred->num_domain; i++) {
ret = os_snprintf(pos, end - pos, "%s%s",
i > 0 ? "\n" : "", cred->domain[i]);
if (os_snprintf_error(end - pos, ret))
return buf;
pos += ret;
}
return buf;
}
if (os_strcmp(var, "phase1") == 0)
return alloc_strdup(cred->phase1);
if (os_strcmp(var, "phase2") == 0)
return alloc_strdup(cred->phase2);
if (os_strcmp(var, "roaming_consortium") == 0) {
size_t buflen;
char *buf;
if (!cred->roaming_consortium_len)
return NULL;
buflen = cred->roaming_consortium_len * 2 + 1;
buf = os_malloc(buflen);
if (buf == NULL)
return NULL;
wpa_snprintf_hex(buf, buflen, cred->roaming_consortium,
cred->roaming_consortium_len);
return buf;
}
if (os_strcmp(var, "required_roaming_consortium") == 0) {
size_t buflen;
char *buf;
if (!cred->required_roaming_consortium_len)
return NULL;
buflen = cred->required_roaming_consortium_len * 2 + 1;
buf = os_malloc(buflen);
if (buf == NULL)
return NULL;
wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium,
cred->required_roaming_consortium_len);
return buf;
}
if (os_strcmp(var, "roaming_consortiums") == 0) {
size_t buflen;
char *buf, *pos;
size_t i;
if (!cred->num_roaming_consortiums)
return NULL;
buflen = cred->num_roaming_consortiums *
MAX_ROAMING_CONS_OI_LEN * 2 + 1;
buf = os_malloc(buflen);
if (!buf)
return NULL;
pos = buf;
for (i = 0; i < cred->num_roaming_consortiums; i++) {
if (i > 0)
*pos++ = ',';
pos += wpa_snprintf_hex(
pos, buf + buflen - pos,
cred->roaming_consortiums[i],
cred->roaming_consortiums_len[i]);
}
*pos = '\0';
return buf;
}
if (os_strcmp(var, "excluded_ssid") == 0) {
unsigned int i;
char *buf, *end, *pos;
if (!cred->num_excluded_ssid)
return NULL;
buf = os_malloc(4000);
if (buf == NULL)
return NULL;
pos = buf;
end = pos + 4000;
for (i = 0; i < cred->num_excluded_ssid; i++) {
struct excluded_ssid *e;
int ret;
e = &cred->excluded_ssid[i];
ret = os_snprintf(pos, end - pos, "%s%s",
i > 0 ? "\n" : "",
wpa_ssid_txt(e->ssid, e->ssid_len));
if (os_snprintf_error(end - pos, ret))
return buf;
pos += ret;
}
return buf;
}
if (os_strcmp(var, "roaming_partner") == 0) {
unsigned int i;
char *buf, *end, *pos;
if (!cred->num_roaming_partner)
return NULL;
buf = os_malloc(4000);
if (buf == NULL)
return NULL;
pos = buf;
end = pos + 4000;
for (i = 0; i < cred->num_roaming_partner; i++) {
struct roaming_partner *p;
int ret;
p = &cred->roaming_partner[i];
ret = os_snprintf(pos, end - pos, "%s%s,%d,%u,%s",
i > 0 ? "\n" : "",
p->fqdn, p->exact_match, p->priority,
p->country);
if (os_snprintf_error(end - pos, ret))
return buf;
pos += ret;
}
return buf;
}
if (os_strcmp(var, "provisioning_sp") == 0)
return alloc_strdup(cred->provisioning_sp);
return NULL;
}
struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id)
{
struct wpa_cred *cred;
cred = config->cred;
while (cred) {
if (id == cred->id)
break;
cred = cred->next;
}
return cred;
}
struct wpa_cred * wpa_config_add_cred(struct wpa_config *config)
{
int id;
struct wpa_cred *cred, *last = NULL;
id = -1;
cred = config->cred;
while (cred) {
if (cred->id > id)
id = cred->id;
last = cred;
cred = cred->next;
}
id++;
cred = os_zalloc(sizeof(*cred));
if (cred == NULL)
return NULL;
cred->id = id;
cred->sim_num = DEFAULT_USER_SELECTED_SIM;
if (last)
last->next = cred;
else
config->cred = cred;
return cred;
}
int wpa_config_remove_cred(struct wpa_config *config, int id)
{
struct wpa_cred *cred, *prev = NULL;
cred = config->cred;
while (cred) {
if (id == cred->id)
break;
prev = cred;
cred = cred->next;
}
if (cred == NULL)
return -1;
if (prev)
prev->next = cred->next;
else
config->cred = cred->next;
wpa_config_free_cred(cred);
return 0;
}
#ifndef CONFIG_NO_CONFIG_BLOBS
/**
* wpa_config_get_blob - Get a named configuration blob
* @config: Configuration data from wpa_config_read()
* @name: Name of the blob
* Returns: Pointer to blob data or %NULL if not found
*/
const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
const char *name)
{
struct wpa_config_blob *blob = config->blobs;
while (blob) {
if (os_strcmp(blob->name, name) == 0)
return blob;
blob = blob->next;
}
return NULL;
}
/**
* wpa_config_set_blob - Set or add a named configuration blob
* @config: Configuration data from wpa_config_read()
* @blob: New value for the blob
*
* Adds a new configuration blob or replaces the current value of an existing
* blob.
*/
void wpa_config_set_blob(struct wpa_config *config,
struct wpa_config_blob *blob)
{
wpa_config_remove_blob(config, blob->name);
blob->next = config->blobs;
config->blobs = blob;
}
/**
* wpa_config_free_blob - Free blob data
* @blob: Pointer to blob to be freed
*/
void wpa_config_free_blob(struct wpa_config_blob *blob)
{
if (blob) {
os_free(blob->name);
bin_clear_free(blob->data, blob->len);
os_free(blob);
}
}
/**
* wpa_config_remove_blob - Remove a named configuration blob
* @config: Configuration data from wpa_config_read()
* @name: Name of the blob to remove
* Returns: 0 if blob was removed or -1 if blob was not found
*/
int wpa_config_remove_blob(struct wpa_config *config, const char *name)
{
struct wpa_config_blob *pos = config->blobs, *prev = NULL;
while (pos) {
if (os_strcmp(pos->name, name) == 0) {
if (prev)
prev->next = pos->next;
else
config->blobs = pos->next;
wpa_config_free_blob(pos);
return 0;
}
prev = pos;
pos = pos->next;
}
return -1;
}
#endif /* CONFIG_NO_CONFIG_BLOBS */
/**
* wpa_config_alloc_empty - Allocate an empty configuration
* @ctrl_interface: Control interface parameters, e.g., path to UNIX domain
* socket
* @driver_param: Driver parameters
* Returns: Pointer to allocated configuration data or %NULL on failure
*/
struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
const char *driver_param)
{
#define ecw2cw(ecw) ((1 << (ecw)) - 1)
struct wpa_config *config;
const int aCWmin = 4, aCWmax = 10;
const struct hostapd_wmm_ac_params ac_bk =
{ aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
const struct hostapd_wmm_ac_params ac_be =
{ aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
{ aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
{ aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
const struct hostapd_tx_queue_params txq_bk =
{ 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
const struct hostapd_tx_queue_params txq_be =
{ 3, ecw2cw(aCWmin), 4 * (ecw2cw(aCWmin) + 1) - 1, 0 };
const struct hostapd_tx_queue_params txq_vi =
{ 1, (ecw2cw(aCWmin) + 1) / 2 - 1, ecw2cw(aCWmin), 30 };
const struct hostapd_tx_queue_params txq_vo =
{ 1, (ecw2cw(aCWmin) + 1) / 4 - 1,
(ecw2cw(aCWmin) + 1) / 2 - 1, 15 };
#undef ecw2cw
config = os_zalloc(sizeof(*config));
if (config == NULL)
return NULL;
config->eapol_version = DEFAULT_EAPOL_VERSION;
config->ap_scan = DEFAULT_AP_SCAN;
config->user_mpm = DEFAULT_USER_MPM;
config->max_peer_links = DEFAULT_MAX_PEER_LINKS;
config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY;
config->dot11RSNASAERetransPeriod =
DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD;
config->fast_reauth = DEFAULT_FAST_REAUTH;
config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
config->p2p_go_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE;
config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW;
config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
config->max_num_sta = DEFAULT_MAX_NUM_STA;
config->ap_isolate = DEFAULT_AP_ISOLATE;
config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ;
config->scan_res_valid_for_connect = DEFAULT_SCAN_RES_VALID_FOR_CONNECT;
config->wmm_ac_params[0] = ac_be;
config->wmm_ac_params[1] = ac_bk;
config->wmm_ac_params[2] = ac_vi;
config->wmm_ac_params[3] = ac_vo;
config->tx_queue[0] = txq_vo;
config->tx_queue[1] = txq_vi;
config->tx_queue[2] = txq_be;
config->tx_queue[3] = txq_bk;
config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
config->cert_in_cb = DEFAULT_CERT_IN_CB;
config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION;
config->extended_key_id = DEFAULT_EXTENDED_KEY_ID;
#ifdef CONFIG_MBO
config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
config->disassoc_imminent_rssi_threshold =
DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
config->oce = DEFAULT_OCE_SUPPORT;
#endif /* CONFIG_MBO */
if (ctrl_interface)
config->ctrl_interface = os_strdup(ctrl_interface);
if (driver_param)
config->driver_param = os_strdup(driver_param);
config->gas_rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
return config;
}
#ifndef CONFIG_NO_STDOUT_DEBUG
/**
* wpa_config_debug_dump_networks - Debug dump of configured networks
* @config: Configuration data from wpa_config_read()
*/
void wpa_config_debug_dump_networks(struct wpa_config *config)
{
size_t prio;
struct wpa_ssid *ssid;
for (prio = 0; prio < config->num_prio; prio++) {
ssid = config->pssid[prio];
wpa_printf(MSG_DEBUG, "Priority group %d",
ssid->priority);
while (ssid) {
wpa_printf(MSG_DEBUG, " id=%d ssid='%s'",
ssid->id,
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
ssid = ssid->pnext;
}
}
}
#endif /* CONFIG_NO_STDOUT_DEBUG */
/**
* Structure for global configuration parsing. This data is used to implement a
* generic parser for the global interface configuration. The table of variables
* is defined below in this file (global_fields[]).
*/
struct global_parse_data {
/* Configuration variable name */
char *name;
/* Parser function for this variable. The parser functions return 0 or 1
* to indicate success. Value 0 indicates that the parameter value may
* have changed while value 1 means that the value did not change.
* Error cases (failure to parse the string) are indicated by returning
* -1. */
int (*parser)(const struct global_parse_data *data,
struct wpa_config *config, int line, const char *value);
/* Getter function to print the variable in text format to buf. */
int (*get)(const char *name, struct wpa_config *config, long offset,
char *buf, size_t buflen, int pretty_print);
/* Variable specific parameters for the parser. */
void *param1, *param2, *param3;
/* Indicates which configuration variable has changed. */
unsigned int changed_flag;
};
static int wpa_global_config_parse_int(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
{
int val, *dst;
char *end;
bool same;
dst = (int *) (((u8 *) config) + (long) data->param1);
val = strtol(pos, &end, 0);
if (*end) {
wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
line, pos);
return -1;
}
same = *dst == val;
*dst = val;
wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
if (data->param2 && *dst < (long) data->param2) {
wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
"min_value=%ld)", line, data->name, *dst,
(long) data->param2);
*dst = (long) data->param2;
return -1;
}
if (data->param3 && *dst > (long) data->param3) {
wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
"max_value=%ld)", line, data->name, *dst,
(long) data->param3);
*dst = (long) data->param3;
return -1;
}
return same;
}
static int wpa_global_config_parse_str(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
{
size_t len, prev_len;
char **dst, *tmp;
len = os_strlen(pos);
if (data->param2 && len < (size_t) data->param2) {
wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
"min_len=%ld)", line, data->name,
(unsigned long) len, (long) data->param2);
return -1;
}
if (data->param3 && len > (size_t) data->param3) {
wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
"max_len=%ld)", line, data->name,
(unsigned long) len, (long) data->param3);
return -1;
}
if (has_newline(pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid %s value with newline",
line, data->name);
return -1;
}
dst = (char **) (((u8 *) config) + (long) data->param1);
if (*dst)
prev_len = os_strlen(*dst);
else
prev_len = 0;
/* No change to the previously configured value */
if (*dst && prev_len == len && os_memcmp(*dst, pos, len) == 0)
return 1;
tmp = os_strdup(pos);
if (tmp == NULL)
return -1;
os_free(*dst);
*dst = tmp;
wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
return 0;
}
static int wpa_config_process_bgscan(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
{
size_t len;
char *tmp;
int res;
tmp = wpa_config_parse_string(pos, &len);
if (tmp == NULL) {
wpa_printf(MSG_ERROR, "Line %d: failed to parse %s",
line, data->name);
return -1;
}
res = wpa_global_config_parse_str(data, config, line, tmp);
os_free(tmp);
return res;
}
static int wpa_global_config_parse_bin(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
{
struct wpabuf **dst, *tmp;
tmp = wpabuf_parse_bin(pos);
if (!tmp)
return -1;
dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
if (wpabuf_cmp(*dst, tmp) == 0) {
wpabuf_free(tmp);
return 1;
}
wpabuf_free(*dst);
*dst = tmp;
wpa_printf(MSG_DEBUG, "%s", data->name);
return 0;
}
static int wpa_config_process_freq_list(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *value)
{
int *freqs;
freqs = wpa_config_parse_int_array(value);
if (freqs == NULL)
return -1;
if (freqs[0] == 0) {
os_free(freqs);
freqs = NULL;
}
os_free(config->freq_list);
config->freq_list = freqs;
return 0;
}
static int
wpa_config_process_initial_freq_list(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *value)
{
int *freqs;
freqs = wpa_config_parse_int_array(value);
if (!freqs)
return -1;
if (freqs[0] == 0) {
os_free(freqs);
freqs = NULL;
}
os_free(config->initial_freq_list);
config->initial_freq_list = freqs;
return 0;
}
#ifdef CONFIG_P2P
static int wpa_global_config_parse_ipv4(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
{
u32 *dst;
struct hostapd_ip_addr addr;
if (hostapd_parse_ip_addr(pos, &addr) < 0)
return -1;
if (addr.af != AF_INET)
return -1;
dst = (u32 *) (((u8 *) config) + (long) data->param1);
if (os_memcmp(dst, &addr.u.v4.s_addr, 4) == 0)
return 1;
os_memcpy(dst, &addr.u.v4.s_addr, 4);
wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name,
WPA_GET_BE32((u8 *) dst));
return 0;
}
#endif /* CONFIG_P2P */
static int wpa_config_process_country(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
{
if (!pos[0] || !pos[1]) {
wpa_printf(MSG_DEBUG, "Invalid country set");
return -1;
}
if (pos[0] == config->country[0] && pos[1] == config->country[1])
return 1;
config->country[0] = pos[0];
config->country[1] = pos[1];
wpa_printf(MSG_DEBUG, "country='%c%c'",
config->country[0], config->country[1]);
return 0;
}
static int wpa_config_process_load_dynamic_eap(
const struct global_parse_data *data, struct wpa_config *config,
int line, const char *so)
{
int ret;
wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
ret = eap_peer_method_load(so);
if (ret == -2) {
wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
"reloading.");
} else if (ret) {
wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
"method '%s'.", line, so);
return -1;
}
return 0;
}
#ifdef CONFIG_WPS
static int wpa_config_process_uuid(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
{
char buf[40];
if (uuid_str2bin(pos, config->uuid)) {
wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
return -1;
}
uuid_bin2str(config->uuid, buf, sizeof(buf));
wpa_printf(MSG_DEBUG, "uuid=%s", buf);
return 0;
}
static int wpa_config_process_device_type(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
return wps_dev_type_str2bin(pos, config->device_type);
}
static int wpa_config_process_os_version(const struct global_parse_data *data,
struct wpa_config *config, int line,
const char *pos)
{
if (hexstr2bin(pos, config->os_version, 4)) {
wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
return -1;
}
wpa_printf(MSG_DEBUG, "os_version=%08x",
WPA_GET_BE32(config->os_version));
return 0;
}
static int wpa_config_process_wps_vendor_ext_m1(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
struct wpabuf *tmp;
int len = os_strlen(pos) / 2;
u8 *p;
if (!len) {
wpa_printf(MSG_ERROR, "Line %d: "
"invalid wps_vendor_ext_m1", line);
return -1;
}
tmp = wpabuf_alloc(len);
if (tmp) {
p = wpabuf_put(tmp, len);
if (hexstr2bin(pos, p, len)) {
wpa_printf(MSG_ERROR, "Line %d: "
"invalid wps_vendor_ext_m1", line);
wpabuf_free(tmp);
return -1;
}
wpabuf_free(config->wps_vendor_ext_m1);
config->wps_vendor_ext_m1 = tmp;
} else {
wpa_printf(MSG_ERROR, "Can not allocate "
"memory for wps_vendor_ext_m1");
return -1;
}
return 0;
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
static int wpa_config_process_sec_device_type(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
int idx;
if (config->num_sec_device_types >= MAX_SEC_DEVICE_TYPES) {
wpa_printf(MSG_ERROR, "Line %d: too many sec_device_type "
"items", line);
return -1;
}
idx = config->num_sec_device_types;
if (wps_dev_type_str2bin(pos, config->sec_device_type[idx]))
return -1;
config->num_sec_device_types++;
return 0;
}
static int wpa_config_process_p2p_pref_chan(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
struct p2p_channel *pref = NULL, *n;
size_t num = 0;
const char *pos2;
u8 op_class, chan;
/* format: class:chan,class:chan,... */
while (*pos) {
op_class = atoi(pos);
pos2 = os_strchr(pos, ':');
if (pos2 == NULL)
goto fail;
pos2++;
chan = atoi(pos2);
n = os_realloc_array(pref, num + 1,
sizeof(struct p2p_channel));
if (n == NULL)
goto fail;
pref = n;
pref[num].op_class = op_class;
pref[num].chan = chan;
num++;
pos = os_strchr(pos2, ',');
if (pos == NULL)
break;
pos++;
}
os_free(config->p2p_pref_chan);
config->p2p_pref_chan = pref;
config->num_p2p_pref_chan = num;
wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs",
(u8 *) config->p2p_pref_chan,
config->num_p2p_pref_chan * sizeof(struct p2p_channel));
return 0;
fail:
os_free(pref);
wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
return -1;
}
static int wpa_config_process_p2p_no_go_freq(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
int ret;
ret = freq_range_list_parse(&config->p2p_no_go_freq, pos);
if (ret < 0) {
wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line);
return -1;
}
wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items",
config->p2p_no_go_freq.num);
return 0;
}
static int wpa_config_process_p2p_device_persistent_mac_addr(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
if (hwaddr_aton2(pos, config->p2p_device_persistent_mac_addr) < 0) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid p2p_device_persistent_mac_addr '%s'",
line, pos);
return -1;
}
return 0;
}
#endif /* CONFIG_P2P */
static int wpa_config_process_hessid(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
if (hwaddr_aton2(pos, config->hessid) < 0) {
wpa_printf(MSG_ERROR, "Line %d: Invalid hessid '%s'",
line, pos);
return -1;
}
return 0;
}
static int wpa_config_process_sae_groups(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
int *groups = wpa_config_parse_int_array(pos);
if (groups == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Invalid sae_groups '%s'",
line, pos);
return -1;
}
os_free(config->sae_groups);
config->sae_groups = groups;
return 0;
}
static int wpa_config_process_ap_vendor_elements(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
struct wpabuf *tmp;
- int len = os_strlen(pos) / 2;
- u8 *p;
- if (!len) {
+ if (!*pos) {
+ wpabuf_free(config->ap_vendor_elements);
+ config->ap_vendor_elements = NULL;
+ return 0;
+ }
+
+ tmp = wpabuf_parse_bin(pos);
+ if (!tmp) {
wpa_printf(MSG_ERROR, "Line %d: invalid ap_vendor_elements",
line);
return -1;
}
+ wpabuf_free(config->ap_vendor_elements);
+ config->ap_vendor_elements = tmp;
- tmp = wpabuf_alloc(len);
- if (tmp) {
- p = wpabuf_put(tmp, len);
+ return 0;
+}
- if (hexstr2bin(pos, p, len)) {
- wpa_printf(MSG_ERROR, "Line %d: invalid "
- "ap_vendor_elements", line);
- wpabuf_free(tmp);
- return -1;
- }
- wpabuf_free(config->ap_vendor_elements);
- config->ap_vendor_elements = tmp;
- } else {
- wpa_printf(MSG_ERROR, "Cannot allocate memory for "
- "ap_vendor_elements");
+static int wpa_config_process_ap_assocresp_elements(
+ const struct global_parse_data *data,
+ struct wpa_config *config, int line, const char *pos)
+{
+ struct wpabuf *tmp;
+
+ if (!*pos) {
+ wpabuf_free(config->ap_assocresp_elements);
+ config->ap_assocresp_elements = NULL;
+ return 0;
+ }
+
+ tmp = wpabuf_parse_bin(pos);
+ if (!tmp) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid ap_assocresp_elements",
+ line);
return -1;
}
+ wpabuf_free(config->ap_assocresp_elements);
+ config->ap_assocresp_elements = tmp;
return 0;
}
#ifdef CONFIG_CTRL_IFACE
static int wpa_config_process_no_ctrl_interface(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
wpa_printf(MSG_DEBUG, "no_ctrl_interface -> ctrl_interface=NULL");
os_free(config->ctrl_interface);
config->ctrl_interface = NULL;
return 0;
}
#endif /* CONFIG_CTRL_IFACE */
static int wpa_config_get_int(const char *name, struct wpa_config *config,
long offset, char *buf, size_t buflen,
int pretty_print)
{
int *val = (int *) (((u8 *) config) + (long) offset);
if (pretty_print)
return os_snprintf(buf, buflen, "%s=%d\n", name, *val);
return os_snprintf(buf, buflen, "%d", *val);
}
static int wpa_config_get_str(const char *name, struct wpa_config *config,
long offset, char *buf, size_t buflen,
int pretty_print)
{
char **val = (char **) (((u8 *) config) + (long) offset);
int res;
if (pretty_print)
res = os_snprintf(buf, buflen, "%s=%s\n", name,
*val ? *val : "null");
else if (!*val)
return -1;
else
res = os_snprintf(buf, buflen, "%s", *val);
if (os_snprintf_error(buflen, res))
res = -1;
return res;
}
#ifdef CONFIG_P2P
static int wpa_config_get_ipv4(const char *name, struct wpa_config *config,
long offset, char *buf, size_t buflen,
int pretty_print)
{
void *val = ((u8 *) config) + (long) offset;
int res;
char addr[INET_ADDRSTRLEN];
if (!val || !inet_ntop(AF_INET, val, addr, sizeof(addr)))
return -1;
if (pretty_print)
res = os_snprintf(buf, buflen, "%s=%s\n", name, addr);
else
res = os_snprintf(buf, buflen, "%s", addr);
if (os_snprintf_error(buflen, res))
res = -1;
return res;
}
#endif /* CONFIG_P2P */
#ifdef OFFSET
#undef OFFSET
#endif /* OFFSET */
/* OFFSET: Get offset of a variable within the wpa_config structure */
#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
#define FUNC(f) #f, wpa_config_process_ ## f, NULL, OFFSET(f), NULL, NULL
#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL
#define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_int, OFFSET(f)
#define INT(f) _INT(f), NULL, NULL
#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
#define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f)
#define STR(f) _STR(f), NULL, NULL
#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
#define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL
#define IPV4(f) #f, wpa_global_config_parse_ipv4, wpa_config_get_ipv4, \
OFFSET(f), NULL, NULL
static const struct global_parse_data global_fields[] = {
#ifdef CONFIG_CTRL_IFACE
{ STR(ctrl_interface), 0 },
{ FUNC_NO_VAR(no_ctrl_interface), 0 },
{ STR(ctrl_interface_group), 0 } /* deprecated */,
#endif /* CONFIG_CTRL_IFACE */
#ifdef CONFIG_MACSEC
{ INT_RANGE(eapol_version, 1, 3), 0 },
#else /* CONFIG_MACSEC */
{ INT_RANGE(eapol_version, 1, 2), 0 },
#endif /* CONFIG_MACSEC */
{ INT(ap_scan), 0 },
{ FUNC(bgscan), CFG_CHANGED_BGSCAN },
#ifdef CONFIG_MESH
{ INT(user_mpm), 0 },
{ INT_RANGE(max_peer_links, 0, 255), 0 },
{ INT(mesh_max_inactivity), 0 },
{ INT(dot11RSNASAERetransPeriod), 0 },
#endif /* CONFIG_MESH */
{ INT(disable_scan_offload), 0 },
{ INT(fast_reauth), 0 },
{ STR(opensc_engine_path), 0 },
{ STR(pkcs11_engine_path), 0 },
{ STR(pkcs11_module_path), 0 },
{ STR(openssl_ciphers), 0 },
{ STR(pcsc_reader), 0 },
{ STR(pcsc_pin), 0 },
{ INT(external_sim), 0 },
{ STR(driver_param), 0 },
{ INT(dot11RSNAConfigPMKLifetime), 0 },
{ INT(dot11RSNAConfigPMKReauthThreshold), 0 },
{ INT(dot11RSNAConfigSATimeout), 0 },
#ifndef CONFIG_NO_CONFIG_WRITE
{ INT(update_config), 0 },
#endif /* CONFIG_NO_CONFIG_WRITE */
{ FUNC_NO_VAR(load_dynamic_eap), 0 },
#ifdef CONFIG_WPS
{ FUNC(uuid), CFG_CHANGED_UUID },
{ INT_RANGE(auto_uuid, 0, 1), 0 },
{ STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN),
CFG_CHANGED_DEVICE_NAME },
{ STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
{ STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING },
{ STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING },
{ STR_RANGE(serial_number, 0, 32), CFG_CHANGED_WPS_STRING },
{ FUNC(device_type), CFG_CHANGED_DEVICE_TYPE },
{ FUNC(os_version), CFG_CHANGED_OS_VERSION },
{ STR(config_methods), CFG_CHANGED_CONFIG_METHODS },
{ INT_RANGE(wps_cred_processing, 0, 2), 0 },
{ INT_RANGE(wps_cred_add_sae, 0, 1), 0 },
{ FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION },
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
{ FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
{ INT(p2p_listen_reg_class), CFG_CHANGED_P2P_LISTEN_CHANNEL },
{ INT(p2p_listen_channel), CFG_CHANGED_P2P_LISTEN_CHANNEL },
{ INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL },
{ INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL },
{ INT_RANGE(p2p_go_intent, 0, 15), 0 },
{ STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
{ INT_RANGE(persistent_reconnect, 0, 1), 0 },
{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
{ INT(p2p_group_idle), 0 },
{ INT_RANGE(p2p_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 0 },
{ INT_RANGE(p2p_passphrase_len, 8, 63),
CFG_CHANGED_P2P_PASSPHRASE_LEN },
{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
{ FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN },
{ INT_RANGE(p2p_add_cli_chan, 0, 1), 0 },
{ INT_RANGE(p2p_optimize_listen_chan, 0, 1), 0 },
{ INT(p2p_go_ht40), 0 },
{ INT(p2p_go_vht), 0 },
{ INT(p2p_go_he), 0 },
{ INT(p2p_go_edmg), 0 },
{ INT(p2p_disabled), 0 },
{ INT_RANGE(p2p_go_ctwindow, 0, 127), 0 },
{ INT(p2p_no_group_iface), 0 },
{ INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 },
{ IPV4(ip_addr_go), 0 },
{ IPV4(ip_addr_mask), 0 },
{ IPV4(ip_addr_start), 0 },
{ IPV4(ip_addr_end), 0 },
{ INT_RANGE(p2p_cli_probe, 0, 1), 0 },
{ INT(p2p_device_random_mac_addr), 0 },
{ FUNC(p2p_device_persistent_mac_addr), 0 },
{ INT(p2p_interface_random_mac_addr), 0 },
{ INT(p2p_6ghz_disable), 0 },
#endif /* CONFIG_P2P */
{ FUNC(country), CFG_CHANGED_COUNTRY },
{ INT(bss_max_count), 0 },
{ INT(bss_expiration_age), 0 },
{ INT(bss_expiration_scan_count), 0 },
{ INT_RANGE(filter_ssids, 0, 1), 0 },
{ INT_RANGE(filter_rssi, -100, 0), 0 },
{ INT(max_num_sta), 0 },
{ INT_RANGE(ap_isolate, 0, 1), 0 },
{ INT_RANGE(disassoc_low_ack, 0, 1), 0 },
#ifdef CONFIG_HS20
{ INT_RANGE(hs20, 0, 1), 0 },
#endif /* CONFIG_HS20 */
{ INT_RANGE(interworking, 0, 1), 0 },
{ FUNC(hessid), 0 },
{ INT_RANGE(access_network_type, 0, 15), 0 },
{ INT_RANGE(go_interworking, 0, 1), 0 },
{ INT_RANGE(go_access_network_type, 0, 15), 0 },
{ INT_RANGE(go_internet, 0, 1), 0 },
{ INT_RANGE(go_venue_group, 0, 255), 0 },
{ INT_RANGE(go_venue_type, 0, 255), 0 },
{ INT_RANGE(pbc_in_m1, 0, 1), 0 },
{ STR(autoscan), 0 },
{ INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff),
CFG_CHANGED_NFC_PASSWORD_TOKEN },
{ BIN(wps_nfc_dh_pubkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
{ BIN(wps_nfc_dh_privkey), CFG_CHANGED_NFC_PASSWORD_TOKEN },
{ BIN(wps_nfc_dev_pw), CFG_CHANGED_NFC_PASSWORD_TOKEN },
{ STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND },
{ INT(p2p_go_max_inactivity), 0 },
{ INT_RANGE(auto_interworking, 0, 1), 0 },
{ INT(okc), 0 },
{ INT(pmf), 0 },
{ FUNC(sae_groups), 0 },
{ INT_RANGE(sae_pwe, 0, 3), 0 },
{ INT_RANGE(sae_pmkid_in_assoc, 0, 1), 0 },
{ INT(dtim_period), 0 },
{ INT(beacon_int), 0 },
+ { FUNC(ap_assocresp_elements), 0 },
{ FUNC(ap_vendor_elements), 0 },
{ INT_RANGE(ignore_old_scan_res, 0, 1), 0 },
{ FUNC(freq_list), 0 },
{ FUNC(initial_freq_list), 0},
{ INT(scan_cur_freq), 0 },
{ INT(scan_res_valid_for_connect), 0},
{ INT(sched_scan_interval), 0 },
{ INT(sched_scan_start_delay), 0 },
{ INT(tdls_external_control), 0},
{ STR(osu_dir), 0 },
{ STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS },
{ INT(p2p_search_delay), 0},
{ INT(mac_addr), 0 },
{ INT(rand_addr_lifetime), 0 },
{ INT(preassoc_mac_addr), 0 },
{ INT(key_mgmt_offload), 0},
{ INT(passive_scan), 0 },
{ INT(reassoc_same_bss_optim), 0 },
{ INT(wps_priority), 0},
#ifdef CONFIG_FST
{ STR_RANGE(fst_group_id, 1, FST_MAX_GROUP_ID_LEN), 0 },
{ INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 },
{ INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
#endif /* CONFIG_FST */
{ INT_RANGE(cert_in_cb, 0, 1), 0 },
{ INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
{ STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS },
#ifdef CONFIG_MBO
{ STR(non_pref_chan), 0 },
{ INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
{ INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
{ INT_RANGE(oce, 0, 3), 0 },
#endif /* CONFIG_MBO */
{ INT(gas_address3), 0 },
{ INT_RANGE(ftm_responder, 0, 1), 0 },
{ INT_RANGE(ftm_initiator, 0, 1), 0 },
{ INT(gas_rand_addr_lifetime), 0 },
{ INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
#ifdef CONFIG_DPP
{ INT_RANGE(dpp_config_processing, 0, 2), 0 },
{ STR(dpp_name), 0 },
{ STR(dpp_mud_url), 0 },
#endif /* CONFIG_DPP */
{ INT_RANGE(coloc_intf_reporting, 0, 1), 0 },
#ifdef CONFIG_WNM
{ INT_RANGE(disable_btm, 0, 1), CFG_CHANGED_DISABLE_BTM },
{ INT_RANGE(extended_key_id, 0, 1), 0 },
#endif /* CONFIG_WNM */
{ INT_RANGE(wowlan_disconnect_on_deinit, 0, 1), 0},
#ifdef CONFIG_PASN
#ifdef CONFIG_TESTING_OPTIONS
{ INT_RANGE(force_kdk_derivation, 0, 1), 0 },
{ INT_RANGE(pasn_corrupt_mic, 0, 1), 0 },
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_PASN */
};
#undef FUNC
#undef _INT
#undef INT
#undef INT_RANGE
#undef _STR
#undef STR
#undef STR_RANGE
#undef BIN
#undef IPV4
#define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields)
int wpa_config_dump_values(struct wpa_config *config, char *buf, size_t buflen)
{
int result = 0;
size_t i;
for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
const struct global_parse_data *field = &global_fields[i];
int tmp;
if (!field->get)
continue;
tmp = field->get(field->name, config, (long) field->param1,
buf, buflen, 1);
if (tmp < 0)
return -1;
buf += tmp;
buflen -= tmp;
result += tmp;
}
return result;
}
int wpa_config_get_value(const char *name, struct wpa_config *config,
char *buf, size_t buflen)
{
size_t i;
for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
const struct global_parse_data *field = &global_fields[i];
if (os_strcmp(name, field->name) != 0)
continue;
if (!field->get)
break;
return field->get(name, config, (long) field->param1,
buf, buflen, 0);
}
return -1;
}
int wpa_config_get_num_global_field_names(void)
{
return NUM_GLOBAL_FIELDS;
}
const char * wpa_config_get_global_field_name(unsigned int i, int *no_var)
{
if (i >= NUM_GLOBAL_FIELDS)
return NULL;
if (no_var)
*no_var = !global_fields[i].param1;
return global_fields[i].name;
}
/**
* wpa_config_process_global - Set a variable in global configuration
* @config: Pointer to global configuration data
* @pos: Name and value in the format "{name}={value}"
* @line: Line number in configuration file or 0 if not used
* Returns: 0 on success with a possible change in value, 1 on success with no
* change to previously configured value, or -1 on failure
*
* This function can be used to set global configuration variables based on
* both the configuration file and management interface input. The value
* parameter must be in the same format as the text-based configuration file is
* using. For example, strings are using double quotation marks.
*/
int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
{
size_t i;
int ret = 0;
for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
const struct global_parse_data *field = &global_fields[i];
size_t flen = os_strlen(field->name);
if (os_strncmp(pos, field->name, flen) != 0 ||
pos[flen] != '=')
continue;
ret = field->parser(field, config, line, pos + flen + 1);
if (ret < 0) {
wpa_printf(MSG_ERROR, "Line %d: failed to "
"parse '%s'.", line, pos);
ret = -1;
}
if (ret == 1)
break;
if (field->changed_flag == CFG_CHANGED_NFC_PASSWORD_TOKEN)
config->wps_nfc_pw_from_config = 1;
config->changed_parameters |= field->changed_flag;
break;
}
if (i == NUM_GLOBAL_FIELDS) {
#ifdef CONFIG_AP
if (os_strncmp(pos, "tx_queue_", 9) == 0) {
char *tmp = os_strchr(pos, '=');
if (!tmp) {
if (line < 0)
wpa_printf(MSG_ERROR,
"Line %d: invalid line %s",
line, pos);
return -1;
}
*tmp++ = '\0';
if (hostapd_config_tx_queue(config->tx_queue, pos,
tmp)) {
wpa_printf(MSG_ERROR,
"Line %d: invalid TX queue item",
line);
return -1;
}
}
if (os_strncmp(pos, "wmm_ac_", 7) == 0) {
char *tmp = os_strchr(pos, '=');
if (tmp == NULL) {
if (line < 0)
return -1;
wpa_printf(MSG_ERROR, "Line %d: invalid line "
"'%s'", line, pos);
return -1;
}
*tmp++ = '\0';
if (hostapd_config_wmm_ac(config->wmm_ac_params, pos,
tmp)) {
wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
"AC item", line);
return -1;
}
return ret;
}
#endif /* CONFIG_AP */
if (line < 0)
return -1;
wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
line, pos);
ret = -1;
}
return ret;
}
diff --git a/contrib/wpa/wpa_supplicant/config.h b/contrib/wpa/wpa_supplicant/config.h
index 68679c6e380a..0320d9eebb57 100644
--- a/contrib/wpa/wpa_supplicant/config.h
+++ b/contrib/wpa/wpa_supplicant/config.h
@@ -1,1752 +1,1763 @@
/*
* WPA Supplicant / Configuration file structures
* Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef CONFIG_H
#define CONFIG_H
#define DEFAULT_EAPOL_VERSION 1
#ifdef CONFIG_NO_SCAN_PROCESSING
#define DEFAULT_AP_SCAN 2
#else /* CONFIG_NO_SCAN_PROCESSING */
#define DEFAULT_AP_SCAN 1
#endif /* CONFIG_NO_SCAN_PROCESSING */
#define DEFAULT_USER_MPM 1
#define DEFAULT_MAX_PEER_LINKS 99
#define DEFAULT_MESH_MAX_INACTIVITY 300
/*
* The default dot11RSNASAERetransPeriod is defined as 40 ms in the standard,
* but use 1000 ms in practice to avoid issues on low power CPUs.
*/
#define DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD 1000
#define DEFAULT_FAST_REAUTH 1
#define DEFAULT_P2P_GO_INTENT 7
#define DEFAULT_P2P_INTRA_BSS 1
#define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60)
#define DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN 0
#define DEFAULT_BSS_MAX_COUNT 200
#define DEFAULT_BSS_EXPIRATION_AGE 180
#define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2
#define DEFAULT_MAX_NUM_STA 128
#define DEFAULT_AP_ISOLATE 0
#define DEFAULT_ACCESS_NETWORK_TYPE 15
#define DEFAULT_SCAN_CUR_FREQ 0
#define DEFAULT_P2P_SEARCH_DELAY 500
#define DEFAULT_RAND_ADDR_LIFETIME 60
#define DEFAULT_KEY_MGMT_OFFLOAD 1
#define DEFAULT_CERT_IN_CB 1
#define DEFAULT_P2P_GO_CTWINDOW 0
#define DEFAULT_WPA_RSC_RELAXATION 1
#define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED
#define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75
#define DEFAULT_OCE_SUPPORT OCE_STA
#define DEFAULT_EXTENDED_KEY_ID 0
#define DEFAULT_SCAN_RES_VALID_FOR_CONNECT 5
#include "config_ssid.h"
#include "wps/wps.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#define MAX_ROAMING_CONS 36
#define MAX_ROAMING_CONS_OI_LEN 15
struct wpa_cred {
/**
* next - Next credential in the list
*
* This pointer can be used to iterate over all credentials. The head
* of this list is stored in the cred field of struct wpa_config.
*/
struct wpa_cred *next;
/**
* id - Unique id for the credential
*
* This identifier is used as a unique identifier for each credential
* block when using the control interface. Each credential is allocated
* an id when it is being created, either when reading the
* configuration file or when a new credential is added through the
* control interface.
*/
int id;
/**
* temporary - Whether this credential is temporary and not to be saved
*/
int temporary;
/**
* priority - Priority group
*
* By default, all networks and credentials get the same priority group
* (0). This field can be used to give higher priority for credentials
* (and similarly in struct wpa_ssid for network blocks) to change the
* Interworking automatic networking selection behavior. The matching
* network (based on either an enabled network block or a credential)
* with the highest priority value will be selected.
*/
int priority;
/**
* pcsc - Use PC/SC and SIM/USIM card
*/
int pcsc;
/**
* realm - Home Realm for Interworking
*/
char *realm;
/**
* username - Username for Interworking network selection
*/
char *username;
/**
* password - Password for Interworking network selection
*/
char *password;
/**
* ext_password - Whether password is a name for external storage
*/
int ext_password;
/**
* ca_cert - CA certificate for Interworking network selection
*/
char *ca_cert;
/**
* client_cert - File path to client certificate file (PEM/DER)
*
* This field is used with Interworking networking selection for a case
* where client certificate/private key is used for authentication
* (EAP-TLS). Full path to the file should be used since working
* directory may change when wpa_supplicant is run in the background.
*
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
char *client_cert;
/**
* private_key - File path to client private key file (PEM/DER/PFX)
*
* When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
* commented out. Both the private key and certificate will be read
* from the PKCS#12 file in this case. Full path to the file should be
* used since working directory may change when wpa_supplicant is run
* in the background.
*
* Windows certificate store can be used by leaving client_cert out and
* configuring private_key in one of the following formats:
*
* cert://substring_to_match
*
* hash://certificate_thumbprint_in_hex
*
* For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
*
* Note that when running wpa_supplicant as an application, the user
* certificate store (My user account) is used, whereas computer store
* (Computer account) is used when running wpasvc as a service.
*
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
char *private_key;
/**
* private_key_passwd - Password for private key file
*/
char *private_key_passwd;
/**
* imsi - IMSI in <MCC> | <MNC> | '-' | <MSIN> format
*/
char *imsi;
/**
* milenage - Milenage parameters for SIM/USIM simulator in
* <Ki>:<OPc>:<SQN> format
*/
char *milenage;
/**
* domain_suffix_match - Constraint for server domain name
*
* If set, this FQDN is used as a suffix match requirement for the AAA
* server certificate in SubjectAltName dNSName element(s). If a
* matching dNSName is found, this constraint is met. If no dNSName
* values are present, this constraint is matched against SubjectName CN
* using same suffix match comparison. Suffix match here means that the
* host/domain name is compared one label at a time starting from the
* top-level domain and all the labels in @domain_suffix_match shall be
* included in the certificate. The certificate may include additional
* sub-level labels in addition to the required labels.
*
* For example, domain_suffix_match=example.com would match
* test.example.com but would not match test-example.com.
*/
char *domain_suffix_match;
/**
* domain - Home service provider FQDN(s)
*
* This is used to compare against the Domain Name List to figure out
* whether the AP is operated by the Home SP. Multiple domain entries
* can be used to configure alternative FQDNs that will be considered
* home networks.
*/
char **domain;
/**
* num_domain - Number of FQDNs in the domain array
*/
size_t num_domain;
/**
* roaming_consortium - Roaming Consortium OI
*
* If roaming_consortium_len is non-zero, this field contains the
* Roaming Consortium OI that can be used to determine which access
* points support authentication with this credential. This is an
* alternative to the use of the realm parameter. When using Roaming
* Consortium to match the network, the EAP parameters need to be
* pre-configured with the credential since the NAI Realm information
* may not be available or fetched.
*/
u8 roaming_consortium[15];
/**
* roaming_consortium_len - Length of roaming_consortium
*/
size_t roaming_consortium_len;
/**
* required_roaming_consortium - Required Roaming Consortium OI
*
* If required_roaming_consortium_len is non-zero, this field contains
* the Roaming Consortium OI that is required to be advertised by the AP
* for the credential to be considered matching.
*/
u8 required_roaming_consortium[15];
/**
* required_roaming_consortium_len - Length of required_roaming_consortium
*/
size_t required_roaming_consortium_len;
/**
* roaming_consortiums - Roaming Consortium OI(s) memberships
*
* This field contains one or more OIs identifying the roaming
* consortiums of which the provider is a member. The list is sorted
* from the most preferred one to the least preferred one. A match
* between the Roaming Consortium OIs advertised by an AP and the OIs
* in this list indicates that successful authentication is possible.
* (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/RoamingConsortiumOI)
*/
u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
/**
* roaming_consortiums_len - Length on roaming_consortiums[i]
*/
size_t roaming_consortiums_len[MAX_ROAMING_CONS];
/**
* num_roaming_consortiums - Number of entries in roaming_consortiums
*/
unsigned int num_roaming_consortiums;
/**
* eap_method - EAP method to use
*
* Pre-configured EAP method to use with this credential or %NULL to
* indicate no EAP method is selected, i.e., the method will be
* selected automatically based on ANQP information.
*/
struct eap_method_type *eap_method;
/**
* phase1 - Phase 1 (outer authentication) parameters
*
* Pre-configured EAP parameters or %NULL.
*/
char *phase1;
/**
* phase2 - Phase 2 (inner authentication) parameters
*
* Pre-configured EAP parameters or %NULL.
*/
char *phase2;
struct excluded_ssid {
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
} *excluded_ssid;
size_t num_excluded_ssid;
struct roaming_partner {
char fqdn[128];
int exact_match;
u8 priority;
char country[3];
} *roaming_partner;
size_t num_roaming_partner;
int update_identifier;
/**
* provisioning_sp - FQDN of the SP that provisioned the credential
*/
char *provisioning_sp;
/**
* sp_priority - Credential priority within a provisioning SP
*
* This is the priority of the credential among all credentials
* provisionined by the same SP (i.e., for entries that have identical
* provisioning_sp value). The range of this priority is 0-255 with 0
* being the highest and 255 the lower priority.
*/
int sp_priority;
unsigned int min_dl_bandwidth_home;
unsigned int min_ul_bandwidth_home;
unsigned int min_dl_bandwidth_roaming;
unsigned int min_ul_bandwidth_roaming;
/**
* max_bss_load - Maximum BSS Load Channel Utilization (1..255)
* This value is used as the maximum channel utilization for network
* selection purposes for home networks. If the AP does not advertise
* BSS Load or if the limit would prevent any connection, this
* constraint will be ignored.
*/
unsigned int max_bss_load;
size_t num_req_conn_capab;
u8 *req_conn_capab_proto;
int **req_conn_capab_port;
/**
* ocsp - Whether to use/require OCSP to check server certificate
*
* 0 = do not use OCSP stapling (TLS certificate status extension)
* 1 = try to use OCSP stapling, but not require response
* 2 = require valid OCSP stapling response
*/
int ocsp;
/**
* sim_num - User selected SIM identifier
*
* This variable is used for identifying which SIM is used if the system
* has more than one.
*/
int sim_num;
};
#define CFG_CHANGED_DEVICE_NAME BIT(0)
#define CFG_CHANGED_CONFIG_METHODS BIT(1)
#define CFG_CHANGED_DEVICE_TYPE BIT(2)
#define CFG_CHANGED_OS_VERSION BIT(3)
#define CFG_CHANGED_UUID BIT(4)
#define CFG_CHANGED_COUNTRY BIT(5)
#define CFG_CHANGED_SEC_DEVICE_TYPE BIT(6)
#define CFG_CHANGED_P2P_SSID_POSTFIX BIT(7)
#define CFG_CHANGED_WPS_STRING BIT(8)
#define CFG_CHANGED_P2P_INTRA_BSS BIT(9)
#define CFG_CHANGED_VENDOR_EXTENSION BIT(10)
#define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11)
#define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12)
#define CFG_CHANGED_P2P_PREF_CHAN BIT(13)
#define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
#define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
#define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
#define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
#define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18)
#define CFG_CHANGED_DISABLE_BTM BIT(19)
#define CFG_CHANGED_BGSCAN BIT(20)
/**
* struct wpa_config - wpa_supplicant configuration data
*
* This data structure is presents the per-interface (radio) configuration
* data. In many cases, there is only one struct wpa_config instance, but if
* more than one network interface is being controlled, one instance is used
* for each.
*/
struct wpa_config {
/**
* ssid - Head of the global network list
*
* This is the head for the list of all the configured networks.
*/
struct wpa_ssid *ssid;
/**
* pssid - Per-priority network lists (in priority order)
*/
struct wpa_ssid **pssid;
/**
* num_prio - Number of different priorities used in the pssid lists
*
* This indicates how many per-priority network lists are included in
* pssid.
*/
size_t num_prio;
/**
* cred - Head of the credential list
*
* This is the head for the list of all the configured credentials.
*/
struct wpa_cred *cred;
/**
* eapol_version - IEEE 802.1X/EAPOL version number
*
* wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which
* defines EAPOL version 2. However, there are many APs that do not
* handle the new version number correctly (they seem to drop the
* frames completely). In order to make wpa_supplicant interoperate
* with these APs, the version number is set to 1 by default. This
* configuration value can be used to set it to the new version (2).
*/
int eapol_version;
/**
* ap_scan - AP scanning/selection
*
* By default, wpa_supplicant requests driver to perform AP
* scanning and then uses the scan results to select a
* suitable AP. Another alternative is to allow the driver to
* take care of AP scanning and selection and use
* wpa_supplicant just to process EAPOL frames based on IEEE
* 802.11 association information from the driver.
*
* 1: wpa_supplicant initiates scanning and AP selection (default).
*
* 0: Driver takes care of scanning, AP selection, and IEEE 802.11
* association parameters (e.g., WPA IE generation); this mode can
* also be used with non-WPA drivers when using IEEE 802.1X mode;
* do not try to associate with APs (i.e., external program needs
* to control association). This mode must also be used when using
* wired Ethernet drivers.
*
* 2: like 0, but associate with APs using security policy and SSID
* (but not BSSID); this can be used, e.g., with ndiswrapper and NDIS
* drivers to enable operation with hidden SSIDs and optimized roaming;
* in this mode, the network blocks in the configuration are tried
* one by one until the driver reports successful association; each
* network block should have explicit security policy (i.e., only one
* option in the lists) for key_mgmt, pairwise, group, proto variables.
*
* Note: ap_scan=2 should not be used with the nl80211 driver interface
* (the current Linux interface). ap_scan=1 is optimized work working
* with nl80211. For finding networks using hidden SSID, scan_ssid=1 in
* the network block can be used with nl80211.
*/
int ap_scan;
/**
* bgscan - Background scan and roaming parameters or %NULL if none
*
* This is an optional set of parameters for background scanning and
* roaming within a network (ESS). For more detailed information see
* ssid block documentation.
*
* The variable defines default bgscan behavior for all BSS station
* networks except for those which have their own bgscan configuration.
*/
char *bgscan;
/**
* disable_scan_offload - Disable automatic offloading of scan requests
*
* By default, %wpa_supplicant tries to offload scanning if the driver
* indicates support for this (sched_scan). This configuration
* parameter can be used to disable this offloading mechanism.
*/
int disable_scan_offload;
/**
* ctrl_interface - Parameters for the control interface
*
* If this is specified, %wpa_supplicant will open a control interface
* that is available for external programs to manage %wpa_supplicant.
* The meaning of this string depends on which control interface
* mechanism is used. For all cases, the existence of this parameter
* in configuration is used to determine whether the control interface
* is enabled.
*
* For UNIX domain sockets (default on Linux and BSD): This is a
* directory that will be created for UNIX domain sockets for listening
* to requests from external programs (CLI/GUI, etc.) for status
* information and configuration. The socket file will be named based
* on the interface name, so multiple %wpa_supplicant processes can be
* run at the same time if more than one interface is used.
* /var/run/wpa_supplicant is the recommended directory for sockets and
* by default, wpa_cli will use it when trying to connect with
* %wpa_supplicant.
*
* Access control for the control interface can be configured
* by setting the directory to allow only members of a group
* to use sockets. This way, it is possible to run
* %wpa_supplicant as root (since it needs to change network
* configuration and open raw sockets) and still allow GUI/CLI
* components to be run as non-root users. However, since the
* control interface can be used to change the network
* configuration, this access needs to be protected in many
* cases. By default, %wpa_supplicant is configured to use gid
* 0 (root). If you want to allow non-root users to use the
* control interface, add a new group and change this value to
* match with that group. Add users that should have control
* interface access to this group.
*
* When configuring both the directory and group, use following format:
* DIR=/var/run/wpa_supplicant GROUP=wheel
* DIR=/var/run/wpa_supplicant GROUP=0
* (group can be either group name or gid)
*
* For UDP connections (default on Windows): The value will be ignored.
* This variable is just used to select that the control interface is
* to be created. The value can be set to, e.g., udp
* (ctrl_interface=udp).
*
* For Windows Named Pipe: This value can be used to set the security
* descriptor for controlling access to the control interface. Security
* descriptor can be set using Security Descriptor String Format (see
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/security_descriptor_string_format.asp).
* The descriptor string needs to be prefixed with SDDL=. For example,
* ctrl_interface=SDDL=D: would set an empty DACL (which will reject
* all connections).
*/
char *ctrl_interface;
/**
* ctrl_interface_group - Control interface group (DEPRECATED)
*
* This variable is only used for backwards compatibility. Group for
* UNIX domain sockets should now be specified using GROUP=group in
* ctrl_interface variable.
*/
char *ctrl_interface_group;
/**
* fast_reauth - EAP fast re-authentication (session resumption)
*
* By default, fast re-authentication is enabled for all EAP methods
* that support it. This variable can be used to disable fast
* re-authentication (by setting fast_reauth=0). Normally, there is no
* need to disable fast re-authentication.
*/
int fast_reauth;
/**
* opensc_engine_path - Path to the OpenSSL engine for opensc
*
* This is an OpenSSL specific configuration option for loading OpenSC
* engine (engine_opensc.so); if %NULL, this engine is not loaded.
*/
char *opensc_engine_path;
/**
* pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
*
* This is an OpenSSL specific configuration option for loading PKCS#11
* engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
*/
char *pkcs11_engine_path;
/**
* pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
*
* This is an OpenSSL specific configuration option for configuring
* path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this
* module is not loaded.
*/
char *pkcs11_module_path;
/**
* openssl_ciphers - OpenSSL cipher string
*
* This is an OpenSSL specific configuration option for configuring the
* default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
* default.
*/
char *openssl_ciphers;
/**
* pcsc_reader - PC/SC reader name prefix
*
* If not %NULL, PC/SC reader with a name that matches this prefix is
* initialized for SIM/USIM access. Empty string can be used to match
* the first available reader.
*/
char *pcsc_reader;
/**
* pcsc_pin - PIN for USIM, GSM SIM, and smartcards
*
* This field is used to configure PIN for SIM/USIM for EAP-SIM and
* EAP-AKA. If left out, this will be asked through control interface.
*/
char *pcsc_pin;
/**
* external_sim - Use external processing for SIM/USIM operations
*/
int external_sim;
/**
* driver_param - Driver interface parameters
*
* This text string is passed to the selected driver interface with the
* optional struct wpa_driver_ops::set_param() handler. This can be
* used to configure driver specific options without having to add new
* driver interface functionality.
*/
char *driver_param;
/**
* dot11RSNAConfigPMKLifetime - Maximum lifetime of a PMK
*
* dot11 MIB variable for the maximum lifetime of a PMK in the PMK
* cache (unit: seconds).
*/
unsigned int dot11RSNAConfigPMKLifetime;
/**
* dot11RSNAConfigPMKReauthThreshold - PMK re-authentication threshold
*
* dot11 MIB variable for the percentage of the PMK lifetime
* that should expire before an IEEE 802.1X reauthentication occurs.
*/
unsigned int dot11RSNAConfigPMKReauthThreshold;
/**
* dot11RSNAConfigSATimeout - Security association timeout
*
* dot11 MIB variable for the maximum time a security association
* shall take to set up (unit: seconds).
*/
unsigned int dot11RSNAConfigSATimeout;
/**
* update_config - Is wpa_supplicant allowed to update configuration
*
* This variable control whether wpa_supplicant is allow to re-write
* its configuration with wpa_config_write(). If this is zero,
* configuration data is only changed in memory and the external data
* is not overridden. If this is non-zero, wpa_supplicant will update
* the configuration data (e.g., a file) whenever configuration is
* changed. This update may replace the old configuration which can
* remove comments from it in case of a text file configuration.
*/
int update_config;
/**
* blobs - Configuration blobs
*/
struct wpa_config_blob *blobs;
/**
* uuid - Universally Unique IDentifier (UUID; see RFC 4122) for WPS
*/
u8 uuid[16];
/**
* auto_uuid - Automatic UUID behavior
* 0 = generate static value based on the local MAC address (default)
* 1 = generate a random UUID every time wpa_supplicant starts
*/
int auto_uuid;
/**
* device_name - Device Name (WPS)
* User-friendly description of device; up to 32 octets encoded in
* UTF-8
*/
char *device_name;
/**
* manufacturer - Manufacturer (WPS)
* The manufacturer of the device (up to 64 ASCII characters)
*/
char *manufacturer;
/**
* model_name - Model Name (WPS)
* Model of the device (up to 32 ASCII characters)
*/
char *model_name;
/**
* model_number - Model Number (WPS)
* Additional device description (up to 32 ASCII characters)
*/
char *model_number;
/**
* serial_number - Serial Number (WPS)
* Serial number of the device (up to 32 characters)
*/
char *serial_number;
/**
* device_type - Primary Device Type (WPS)
*/
u8 device_type[WPS_DEV_TYPE_LEN];
/**
* config_methods - Config Methods
*
* This is a space-separated list of supported WPS configuration
* methods. For example, "label virtual_display virtual_push_button
* keypad".
* Available methods: usba ethernet label display ext_nfc_token
* int_nfc_token nfc_interface push_button keypad
* virtual_display physical_display
* virtual_push_button physical_push_button.
*/
char *config_methods;
/**
* os_version - OS Version (WPS)
* 4-octet operating system version number
*/
u8 os_version[4];
/**
* country - Country code
*
* This is the ISO/IEC alpha2 country code for which we are operating
* in
*/
char country[2];
/**
* wps_cred_processing - Credential processing
*
* 0 = process received credentials internally
* 1 = do not process received credentials; just pass them over
* ctrl_iface to external program(s)
* 2 = process received credentials internally and pass them over
* ctrl_iface to external program(s)
*/
int wps_cred_processing;
/**
* wps_cred_add_sae - Whether to enable SAE automatically for WPS
*
* 0 = only add the explicitly listed WPA2-PSK configuration
* 1 = add both the WPA2-PSK and SAE configuration and enable PMF so
* that the station gets configured in WPA3-Personal transition mode
* (supports both WPA2-Personal (PSK) and WPA3-Personal (SAE) APs).
*/
int wps_cred_add_sae;
#define MAX_SEC_DEVICE_TYPES 5
/**
* sec_device_types - Secondary Device Types (P2P)
*/
u8 sec_device_type[MAX_SEC_DEVICE_TYPES][WPS_DEV_TYPE_LEN];
int num_sec_device_types;
int p2p_listen_reg_class;
int p2p_listen_channel;
int p2p_oper_reg_class;
int p2p_oper_channel;
int p2p_go_intent;
char *p2p_ssid_postfix;
int persistent_reconnect;
int p2p_intra_bss;
unsigned int num_p2p_pref_chan;
struct p2p_channel *p2p_pref_chan;
struct wpa_freq_range_list p2p_no_go_freq;
int p2p_add_cli_chan;
int p2p_ignore_shared_freq;
int p2p_optimize_listen_chan;
int p2p_6ghz_disable;
struct wpabuf *wps_vendor_ext_m1;
#define MAX_WPS_VENDOR_EXT 10
/**
* wps_vendor_ext - Vendor extension attributes in WPS
*/
struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXT];
/**
* p2p_group_idle - Maximum idle time in seconds for P2P group
*
* This value controls how long a P2P group is maintained after there
* is no other members in the group. As a GO, this means no associated
* stations in the group. As a P2P client, this means no GO seen in
* scan results. The maximum idle time is specified in seconds with 0
* indicating no time limit, i.e., the P2P group remains in active
* state indefinitely until explicitly removed. As a P2P client, the
* maximum idle time of P2P_MAX_CLIENT_IDLE seconds is enforced, i.e.,
* this parameter is mainly meant for GO use and for P2P client, it can
* only be used to reduce the default timeout to smaller value. A
* special value -1 can be used to configure immediate removal of the
* group for P2P client role on any disconnection after the data
* connection has been established.
*/
int p2p_group_idle;
/**
* p2p_go_freq_change_policy - The GO frequency change policy
*
* This controls the behavior of the GO when there is a change in the
* map of the currently used frequencies in case more than one channel
* is supported.
*
* @P2P_GO_FREQ_MOVE_SCM: Prefer working in a single channel mode if
* possible. In case the GO is the only interface using its frequency
* and there are other station interfaces on other frequencies, the GO
* will migrate to one of these frequencies.
*
* @P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS: Same as P2P_GO_FREQ_MOVE_SCM,
* but a transition is possible only in case one of the other used
* frequencies is one of the frequencies in the intersection of the
* frequency list of the local device and the peer device.
*
* @P2P_GO_FREQ_MOVE_STAY: Prefer to stay on the current frequency.
*
* @P2P_GO_FREQ_MOVE_SCM_ECSA: Same as
* P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS but a transition is possible only
* if all the group members advertise eCSA support.
*/
enum {
P2P_GO_FREQ_MOVE_SCM = 0,
P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS = 1,
P2P_GO_FREQ_MOVE_STAY = 2,
P2P_GO_FREQ_MOVE_SCM_ECSA = 3,
P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_SCM_ECSA,
} p2p_go_freq_change_policy;
#define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY
/**
* p2p_passphrase_len - Passphrase length (8..63) for P2P GO
*
* This parameter controls the length of the random passphrase that is
* generated at the GO.
*/
unsigned int p2p_passphrase_len;
/**
* bss_max_count - Maximum number of BSS entries to keep in memory
*/
unsigned int bss_max_count;
/**
* bss_expiration_age - BSS entry age after which it can be expired
*
* This value controls the time in seconds after which a BSS entry
* gets removed if it has not been updated or is not in use.
*/
unsigned int bss_expiration_age;
/**
* bss_expiration_scan_count - Expire BSS after number of scans
*
* If the BSS entry has not been seen in this many scans, it will be
* removed. A value of 1 means that entry is removed after the first
* scan in which the BSSID is not seen. Larger values can be used
* to avoid BSS entries disappearing if they are not visible in
* every scan (e.g., low signal quality or interference).
*/
unsigned int bss_expiration_scan_count;
/**
* filter_ssids - SSID-based scan result filtering
*
* 0 = do not filter scan results
* 1 = only include configured SSIDs in scan results/BSS table
*/
int filter_ssids;
/**
* filter_rssi - RSSI-based scan result filtering
*
* 0 = do not filter scan results
* -n = filter scan results below -n dBm
*/
int filter_rssi;
/**
* max_num_sta - Maximum number of STAs in an AP/P2P GO
*/
unsigned int max_num_sta;
/**
* ap_isolate - Whether to use client isolation feature
*
* Client isolation can be used to prevent low-level bridging of
* frames between associated stations in the BSS. By default,
* this bridging is allowed (ap_isolate=0); except in P2P GO case,
* where p2p_intra_bss parameter is used to determine whether to allow
* intra-BSS forwarding (ap_isolate = !p2p_intra_bss).
*
* 0 = do not enable AP isolation
* 1 = enable AP isolation
*/
int ap_isolate;
/**
* freq_list - Array of allowed scan frequencies or %NULL for all
*
* This is an optional zero-terminated array of frequencies in
* megahertz (MHz) to allow for narrowing scanning range.
*/
int *freq_list;
/**
* initial_freq_list - like freq_list but for initial scan
*
* This is an optional zero-terminated array of frequencies in
* megahertz (MHz) to allow for narrowing scanning range when
* the application is started.
*
* This can be used to speed up initial connection time if the
* channel is known ahead of time, without limiting the scanned
* frequencies during normal use.
*/
int *initial_freq_list;
/**
* scan_cur_freq - Whether to scan only the current channel
*
* If true, attempt to scan only the current channel if any other
* VIFs on this radio are already associated on a particular channel.
*/
int scan_cur_freq;
/**
* scan_res_valid_for_connect - Seconds scans are valid for association
*
* This configures the number of seconds old scan results are considered
* valid for association. When scan results are older than this value
* a new scan is triggered prior to the association.
*/
int scan_res_valid_for_connect;
/**
* changed_parameters - Bitmap of changed parameters since last update
*/
unsigned int changed_parameters;
/**
* disassoc_low_ack - Disassociate stations with massive packet loss
*/
int disassoc_low_ack;
/**
* interworking - Whether Interworking (IEEE 802.11u) is enabled
*/
int interworking;
/**
* access_network_type - Access Network Type
*
* When Interworking is enabled, scans will be limited to APs that
* advertise the specified Access Network Type (0..15; with 15
* indicating wildcard match).
*/
int access_network_type;
/**
* go_interworking - Whether Interworking for P2P GO is enabled
*/
int go_interworking;
/**
* go_access_network_type - P2P GO Access Network Type
*
* This indicates which access network type to advertise if Interworking
* is enabled for P2P GO.
*/
int go_access_network_type;
/**
* go_internet - Interworking: Internet connectivity (0 or 1)
*/
int go_internet;
/**
* go_venue_group - Interworking: Venue group
*/
int go_venue_group;
/**
* go_venue_type: Interworking: Venue type
*/
int go_venue_type;
/**
* hessid - Homogeneous ESS identifier
*
* If this is set (any octet is non-zero), scans will be used to
* request response only from BSSes belonging to the specified
* Homogeneous ESS. This is used only if interworking is enabled.
*/
u8 hessid[ETH_ALEN];
/**
* hs20 - Hotspot 2.0
*/
int hs20;
/**
* pbc_in_m1 - AP mode WPS probing workaround for PBC with Windows 7
*
* Windows 7 uses incorrect way of figuring out AP's WPS capabilities
* by acting as a Registrar and using M1 from the AP. The config
* methods attribute in that message is supposed to indicate only the
* configuration method supported by the AP in Enrollee role, i.e., to
* add an external Registrar. For that case, PBC shall not be used and
* as such, the PushButton config method is removed from M1 by default.
* If pbc_in_m1=1 is included in the configuration file, the PushButton
* config method is left in M1 (if included in config_methods
* parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from
* a label in the AP).
*/
int pbc_in_m1;
/**
* autoscan - Automatic scan parameters or %NULL if none
*
* This is an optional set of parameters for automatic scanning
* within an interface in following format:
* <autoscan module name>:<module parameters>
*/
char *autoscan;
/**
* wps_nfc_pw_from_config - NFC Device Password was read from config
*
* This parameter can be determined whether the NFC Device Password was
* included in the configuration (1) or generated dynamically (0). Only
* the former case is re-written back to the configuration file.
*/
int wps_nfc_pw_from_config;
/**
* wps_nfc_dev_pw_id - NFC Device Password ID for password token
*/
int wps_nfc_dev_pw_id;
/**
* wps_nfc_dh_pubkey - NFC DH Public Key for password token
*/
struct wpabuf *wps_nfc_dh_pubkey;
/**
* wps_nfc_dh_privkey - NFC DH Private Key for password token
*/
struct wpabuf *wps_nfc_dh_privkey;
/**
* wps_nfc_dev_pw - NFC Device Password for password token
*/
struct wpabuf *wps_nfc_dev_pw;
/**
* ext_password_backend - External password backend or %NULL if none
*
* format: <backend name>[:<optional backend parameters>]
*/
char *ext_password_backend;
/*
* p2p_go_max_inactivity - Timeout in seconds to detect STA inactivity
*
* This timeout value is used in P2P GO mode to clean up
* inactive stations.
* By default: 300 seconds.
*/
int p2p_go_max_inactivity;
struct hostapd_wmm_ac_params wmm_ac_params[4];
struct hostapd_tx_queue_params tx_queue[4];
/**
* auto_interworking - Whether to use network selection automatically
*
* 0 = do not automatically go through Interworking network selection
* (i.e., require explicit interworking_select command for this)
* 1 = perform Interworking network selection if one or more
* credentials have been configured and scan did not find a
* matching network block
*/
int auto_interworking;
/**
* p2p_go_ht40 - Default mode for HT40 enable when operating as GO.
*
* This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
* Note that regulatory constraints and driver capabilities are
* consulted anyway, so setting it to 1 can't do real harm.
* By default: 0 (disabled)
*/
int p2p_go_ht40;
/**
* p2p_go_vht - Default mode for VHT enable when operating as GO
*
* This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
* Note that regulatory constraints and driver capabilities are
* consulted anyway, so setting it to 1 can't do real harm.
* By default: 0 (disabled)
*/
int p2p_go_vht;
/**
* p2p_go_edmg - Default mode for EDMG enable when operating as GO
*
* This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
* Note that regulatory constraints and driver capabilities are
* consulted anyway, so setting it to 1 can't do real harm.
* By default: 0 (disabled)
*/
int p2p_go_edmg;
/**
* p2p_go_he - Default mode for 11ax HE enable when operating as GO
*
* This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
* Note that regulatory constraints and driver capabilities are
* consulted anyway, so setting it to 1 can't do real harm.
* By default: 0 (disabled)
*/
int p2p_go_he;
/**
* p2p_go_ctwindow - CTWindow to use when operating as GO
*
* By default: 0 (no CTWindow). Values 0-127 can be used to indicate
* the length of the CTWindow in TUs.
*/
int p2p_go_ctwindow;
/**
* p2p_disabled - Whether P2P operations are disabled for this interface
*/
int p2p_disabled;
/**
* p2p_no_group_iface - Whether group interfaces can be used
*
* By default, wpa_supplicant will create a separate interface for P2P
* group operations if the driver supports this. This functionality can
* be disabled by setting this parameter to 1. In that case, the same
* interface that was used for the P2P management operations is used
* also for the group operation.
*/
int p2p_no_group_iface;
/**
* p2p_cli_probe - Enable/disable P2P CLI probe request handling
*
* If this parameter is set to 1, a connected P2P Client will receive
* and handle Probe Request frames. Setting this parameter to 0
* disables this option. Default value: 0.
*
* Note: Setting this property at run time takes effect on the following
* interface state transition to/from the WPA_COMPLETED state.
*/
int p2p_cli_probe;
/**
* okc - Whether to enable opportunistic key caching by default
*
* By default, OKC is disabled unless enabled by the per-network
* proactive_key_caching=1 parameter. okc=1 can be used to change this
* default behavior.
*/
int okc;
/**
* pmf - Whether to enable/require PMF by default
*
* By default, PMF is disabled unless enabled by the per-network
* ieee80211w=1 or ieee80211w=2 parameter. pmf=1/2 can be used to change
* this default behavior for RSN network (this is not applicable for
* non-RSN cases).
*/
enum mfp_options pmf;
/**
* sae_groups - Preference list of enabled groups for SAE
*
* By default (if this parameter is not set), the mandatory group 19
* (ECC group defined over a 256-bit prime order field) is preferred,
* but other groups are also enabled. If this parameter is set, the
* groups will be tried in the indicated order.
*/
int *sae_groups;
/**
* sae_pwe - SAE mechanism for PWE derivation
* 0 = hunting-and-pecking loop only
* 1 = hash-to-element only
* 2 = both hunting-and-pecking loop and hash-to-element enabled
*/
int sae_pwe;
/**
* sae_pmkid_in_assoc - Whether to include PMKID in SAE Assoc Req
*/
int sae_pmkid_in_assoc;
/**
* dtim_period - Default DTIM period in Beacon intervals
*
* This parameter can be used to set the default value for network
* blocks that do not specify dtim_period.
*/
int dtim_period;
/**
* beacon_int - Default Beacon interval in TU
*
* This parameter can be used to set the default value for network
* blocks that do not specify beacon_int.
*/
int beacon_int;
/**
* ap_vendor_elements: Vendor specific elements for Beacon/ProbeResp
*
* This parameter can be used to define additional vendor specific
* elements for Beacon and Probe Response frames in AP/P2P GO mode. The
* format for these element(s) is a hexdump of the raw information
* elements (id+len+payload for one or more elements).
*/
struct wpabuf *ap_vendor_elements;
+ /**
+ * ap_assocresp_elements: Vendor specific elements for (Re)Association
+ * Response frames
+ *
+ * This parameter can be used to define additional vendor specific
+ * elements for (Re)Association Response frames in AP/P2P GO mode. The
+ * format for these element(s) is a hexdump of the raw information
+ * elements (id+len+payload for one or more elements).
+ */
+ struct wpabuf *ap_assocresp_elements;
+
/**
* ignore_old_scan_res - Ignore scan results older than request
*
* The driver may have a cache of scan results that makes it return
* information that is older than our scan trigger. This parameter can
* be used to configure such old information to be ignored instead of
* allowing it to update the internal BSS table.
*/
int ignore_old_scan_res;
/**
* sched_scan_interval - schedule scan interval
*/
unsigned int sched_scan_interval;
/**
* sched_scan_start_delay - Schedule scan start delay before first scan
*
* Delay (in seconds) before scheduling first scan plan cycle. The
* driver may ignore this parameter and start immediately (or at any
* other time), if this feature is not supported.
*/
unsigned int sched_scan_start_delay;
/**
* tdls_external_control - External control for TDLS setup requests
*
* Enable TDLS mode where external programs are given the control
* to specify the TDLS link to get established to the driver. The
* driver requests the TDLS setup to the supplicant only for the
* specified TDLS peers.
*/
int tdls_external_control;
u8 ip_addr_go[4];
u8 ip_addr_mask[4];
u8 ip_addr_start[4];
u8 ip_addr_end[4];
/**
* osu_dir - OSU provider information directory
*
* If set, allow FETCH_OSU control interface command to be used to fetch
* OSU provider information into all APs and store the results in this
* directory.
*/
char *osu_dir;
/**
* wowlan_triggers - Wake-on-WLAN triggers
*
* If set, these wowlan triggers will be configured.
*/
char *wowlan_triggers;
/**
* p2p_search_delay - Extra delay between concurrent search iterations
*
* Add extra delay (in milliseconds) between search iterations when
* there is a concurrent operation to make p2p_find friendlier to
* concurrent operations by avoiding it from taking 100% of radio
* resources.
*/
unsigned int p2p_search_delay;
/**
* mac_addr - MAC address policy default
*
* 0 = use permanent MAC address
* 1 = use random MAC address for each ESS connection
* 2 = like 1, but maintain OUI (with local admin bit set)
*
* By default, permanent MAC address is used unless policy is changed by
* the per-network mac_addr parameter. Global mac_addr=1 can be used to
* change this default behavior.
*/
int mac_addr;
/**
* rand_addr_lifetime - Lifetime of random MAC address in seconds
*/
unsigned int rand_addr_lifetime;
/**
* preassoc_mac_addr - Pre-association MAC address policy
*
* 0 = use permanent MAC address
* 1 = use random MAC address
* 2 = like 1, but maintain OUI (with local admin bit set)
*/
int preassoc_mac_addr;
/**
* key_mgmt_offload - Use key management offload
*
* Key management offload should be used if the device supports it.
* Key management offload is the capability of a device operating as
* a station to do the exchange necessary to establish temporal keys
* during initial RSN connection, after roaming, or during a PTK
* rekeying operation.
*/
int key_mgmt_offload;
/**
* user_mpm - MPM residency
*
* 0: MPM lives in driver.
* 1: wpa_supplicant handles peering and station allocation.
*
* If AMPE or SAE is enabled, the MPM is always in userspace.
*/
int user_mpm;
/**
* max_peer_links - Maximum number of peer links
*
* Maximum number of mesh peering currently maintained by the STA.
*/
int max_peer_links;
/**
* cert_in_cb - Whether to include a peer certificate dump in events
*
* This controls whether peer certificates for authentication server and
* its certificate chain are included in EAP peer certificate events.
*/
int cert_in_cb;
/**
* mesh_max_inactivity - Timeout in seconds to detect STA inactivity
*
* This timeout value is used in mesh STA to clean up inactive stations.
* By default: 300 seconds.
*/
int mesh_max_inactivity;
/**
* dot11RSNASAERetransPeriod - Timeout to retransmit SAE Auth frame
*
* This timeout value is used in mesh STA to retransmit
* SAE Authentication frame.
* By default: 1000 milliseconds.
*/
int dot11RSNASAERetransPeriod;
/**
* passive_scan - Whether to force passive scan for network connection
*
* This parameter can be used to force only passive scanning to be used
* for network connection cases. It should be noted that this will slow
* down scan operations and reduce likelihood of finding the AP. In
* addition, some use cases will override this due to functional
* requirements, e.g., for finding an AP that uses hidden SSID
* (scan_ssid=1) or P2P device discovery.
*/
int passive_scan;
/**
* reassoc_same_bss_optim - Whether to optimize reassoc-to-same-BSS
*/
int reassoc_same_bss_optim;
/**
* wps_priority - Priority for the networks added through WPS
*
* This priority value will be set to each network profile that is added
* by executing the WPS protocol.
*/
int wps_priority;
/**
* fst_group_id - FST group ID
*/
char *fst_group_id;
/**
* fst_priority - priority of the interface within the FST group
*/
int fst_priority;
/**
* fst_llt - default FST LLT (Link-Lost Timeout) to be used for the
* interface.
*/
int fst_llt;
/**
* wpa_rsc_relaxation - RSC relaxation on GTK installation
*
* Values:
* 0 - use the EAPOL-Key RSC value on GTK installation
* 1 - use the null RSC if a bogus RSC value is detected in message 3
* of 4-Way Handshake or message 1 of Group Key Handshake.
*/
int wpa_rsc_relaxation;
/**
* sched_scan_plans - Scan plans for scheduled scan
*
* Each scan plan specifies the interval between scans and the number of
* iterations. The last scan plan only specifies the scan interval and
* will be run infinitely.
*
* format: <interval:iterations> <interval2:iterations2> ... <interval>
*/
char *sched_scan_plans;
#ifdef CONFIG_MBO
/**
* non_pref_chan - Non-preferred channels list, separated by spaces.
*
* format: op_class:chan:preference:reason<:detail>
* Detail is optional.
*/
char *non_pref_chan;
/**
* mbo_cell_capa - Cellular capabilities for MBO
*/
enum mbo_cellular_capa mbo_cell_capa;
/**
* disassoc_imminent_rssi_threshold - RSSI threshold of candidate AP
* when disassociation imminent is set.
*/
int disassoc_imminent_rssi_threshold;
/**
* oce - Enable OCE in STA and/or STA-CFON mode
* - Set BIT(0) to enable OCE in non-AP STA mode
* - Set BIT(1) to enable OCE in STA-CFON mode
*/
unsigned int oce;
#endif /* CONFIG_MBO */
/**
* gas_address3 - GAS Address3 field behavior
*
* Values:
* 0 - P2P specification (Address3 = AP BSSID)
* 1 = IEEE 802.11 standard compliant (Address3 = Wildcard BSSID when
* sent to not-associated AP; if associated, AP BSSID)
*/
int gas_address3;
/**
* ftm_responder - Publish FTM (fine timing measurement)
* responder functionality
*
* Values:
* 0 - do not publish FTM responder functionality (Default)
* 1 - publish FTM responder functionality in
* bit 70 of Extended Capabilities element
* Note, actual FTM responder operation is managed outside
* wpa_supplicant.
*/
int ftm_responder;
/**
* ftm_initiator - Publish FTM (fine timing measurement)
* initiator functionality
*
* Values:
* 0 - do not publish FTM initiator functionality (Default)
* 1 - publish FTM initiator functionality in
* bit 71 of Extended Capabilities element
* Note, actual FTM initiator operation is managed outside
* wpa_supplicant.
*/
int ftm_initiator;
/**
* gas_rand_addr_lifetime - Lifetime of random MAC address for ANQP in
* seconds
*/
unsigned int gas_rand_addr_lifetime;
/**
* gas_rand_mac_addr - GAS MAC address policy
*
* 0 = use permanent MAC address
* 1 = use random MAC address
* 2 = like 1, but maintain OUI (with local admin bit set)
*/
int gas_rand_mac_addr;
/**
* dpp_config_processing - How to process DPP configuration
*
* 0 = report received configuration to an external program for
* processing; do not generate any network profile internally
* 1 = report received configuration to an external program and generate
* a network profile internally, but do not automatically connect
* to the created (disabled) profile; the network profile id is
* reported to external programs
* 2 = report received configuration to an external program, generate
* a network profile internally, try to connect to the created
* profile automatically
*/
int dpp_config_processing;
/**
* dpp_name - Name for Enrollee's DPP Configuration Request
*/
char *dpp_name;
/**
* dpp_mud_url - MUD URL for Enrollee's DPP Configuration Request
*/
char *dpp_mud_url;
/**
* coloc_intf_reporting - Colocated interference reporting
*
* dot11CoLocIntfReportingActivated
* 0 = disabled (false)
* 1 = enabled (true)
*/
int coloc_intf_reporting;
/**
* p2p_device_random_mac_addr - P2P Device MAC address policy default
*
* 0 = use permanent MAC address (the one set by default by the device
* driver). Notice that, if the device driver is configured to
* always use random MAC addresses, this flag breaks reinvoking a
* persistent group, so flags 1 or 2 should be used instead with
* such drivers if persistent groups are used.
* 1 = use random MAC address on creating the interface if there is no
* persistent group. Besides, if a persistent group is created,
* p2p_device_persistent_mac_addr is set to the MAC address of the
* P2P Device interface, so that this address will be subsequently
* used to change the MAC address of the P2P Device interface. With
* no persistent group, the random MAC address is created by
* wpa_supplicant, changing the one set by the device driver.
* The device driver shall support SIOCGIFFLAGS/SIOCSIFFLAGS ioctl
* interface control operations.
* 2 = this flag should be used when the device driver uses random MAC
* addresses by default when a P2P Device interface is created.
* If p2p_device_persistent_mac_addr is set, use this MAC address
* on creating the P2P Device interface. If not set, use the
* default method adopted by the device driver (e.g., random MAC
* address). Besides, if a persistent group is created,
* p2p_device_persistent_mac_addr is set to the MAC address of the
* P2P Device interface, so that this address will be subsequently
* used in place of the default address set by the device driver.
* (This option does not need support of SIOCGIFFLAGS/SIOCSIFFLAGS
* ioctl interface control operations and uses NL80211_ATTR_MAC).
*
* By default, permanent MAC address is used.
*/
int p2p_device_random_mac_addr;
/**
* p2p_device_persistent_mac_addr - Record last used MAC address
*
* If there are saved persistent groups, P2P cannot generate another
* random MAC address, and need to restore to last used MAC address.
*/
u8 p2p_device_persistent_mac_addr[ETH_ALEN];
/**
* p2p_interface_random_mac_addr - P2P Interface MAC address policy default
*
* 0 = use permanent MAC address
* 1 = use random MAC address on creating the interface.
*
* By default, permanent MAC address is used.
*/
int p2p_interface_random_mac_addr;
/**
* disable_btm - Disable BSS transition management in STA
* - Set to 0 to enable BSS transition management
* - Set to 1 to disable BSS transition management
*
* By default BSS transition management is enabled
*/
int disable_btm;
/**
* extended_key_id - Extended Key ID support
*
* IEEE Std 802.11-2016 optionally allows to use Key ID 0 and 1 for PTK
* keys with Extended Key ID.
*
* 0 = don't use Extended Key ID
* 1 = use Extended Key ID when possible
*/
int extended_key_id;
/**
* wowlan_disconnect_on_deinit - Trigger disconnect on wpa_supplicant
* interface deinit even if the driver has enabled WoWLAN.
*
* 0 = Do not disconnect
* 1 = Trigger disconnection
*/
int wowlan_disconnect_on_deinit;
#ifdef CONFIG_PASN
#ifdef CONFIG_TESTING_OPTIONS
/*
* Normally, KDK should be derived if and only if both sides support
* secure LTF. Allow forcing KDK derivation for testing purposes.
*/
int force_kdk_derivation;
/* If set, corrupt the MIC in the 3rd Authentication frame of PASN */
int pasn_corrupt_mic;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_PASN*/
};
/* Prototypes for common functions from config.c */
void wpa_config_free(struct wpa_config *ssid);
void wpa_config_free_ssid(struct wpa_ssid *ssid);
void wpa_config_foreach_network(struct wpa_config *config,
void (*func)(void *, struct wpa_ssid *),
void *arg);
struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id);
struct wpa_ssid * wpa_config_add_network(struct wpa_config *config);
int wpa_config_remove_network(struct wpa_config *config, int id);
void wpa_config_set_network_defaults(struct wpa_ssid *ssid);
int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
int line);
int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
const char *value);
int wpa_config_dump_values(struct wpa_config *config, char *buf,
size_t buflen);
int wpa_config_get_value(const char *name, struct wpa_config *config,
char *buf, size_t buflen);
char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys);
char * wpa_config_get(struct wpa_ssid *ssid, const char *var);
char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var);
void wpa_config_update_psk(struct wpa_ssid *ssid);
int wpa_config_add_prio_network(struct wpa_config *config,
struct wpa_ssid *ssid);
int wpa_config_update_prio_list(struct wpa_config *config);
const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
const char *name);
void wpa_config_set_blob(struct wpa_config *config,
struct wpa_config_blob *blob);
void wpa_config_free_blob(struct wpa_config_blob *blob);
int wpa_config_remove_blob(struct wpa_config *config, const char *name);
void wpa_config_flush_blobs(struct wpa_config *config);
struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id);
struct wpa_cred * wpa_config_add_cred(struct wpa_config *config);
int wpa_config_remove_cred(struct wpa_config *config, int id);
void wpa_config_free_cred(struct wpa_cred *cred);
int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
const char *value, int line);
char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var);
struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
const char *driver_param);
#ifndef CONFIG_NO_STDOUT_DEBUG
void wpa_config_debug_dump_networks(struct wpa_config *config);
#else /* CONFIG_NO_STDOUT_DEBUG */
#define wpa_config_debug_dump_networks(c) do { } while (0)
#endif /* CONFIG_NO_STDOUT_DEBUG */
/* Prototypes for common functions from config.c */
int wpa_config_process_global(struct wpa_config *config, char *pos, int line);
int wpa_config_get_num_global_field_names(void);
const char * wpa_config_get_global_field_name(unsigned int i, int *no_var);
/* Prototypes for backend specific functions from the selected config_*.c */
/**
* wpa_config_read - Read and parse configuration database
* @name: Name of the configuration (e.g., path and file name for the
* configuration file)
* @cfgp: Pointer to previously allocated configuration data or %NULL if none
* Returns: Pointer to allocated configuration data or %NULL on failure
*
* This function reads configuration data, parses its contents, and allocates
* data structures needed for storing configuration information. The allocated
* data can be freed with wpa_config_free().
*
* Each configuration backend needs to implement this function.
*/
struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp);
/**
* wpa_config_write - Write or update configuration data
* @name: Name of the configuration (e.g., path and file name for the
* configuration file)
* @config: Configuration data from wpa_config_read()
* Returns: 0 on success, -1 on failure
*
* This function write all configuration data into an external database (e.g.,
* a text file) in a format that can be read with wpa_config_read(). This can
* be used to allow wpa_supplicant to update its configuration, e.g., when a
* new network is added or a password is changed.
*
* Each configuration backend needs to implement this function.
*/
int wpa_config_write(const char *name, struct wpa_config *config);
#endif /* CONFIG_H */
diff --git a/contrib/wpa/wpa_supplicant/config_file.c b/contrib/wpa/wpa_supplicant/config_file.c
index a535e3f08aad..54fb72d8c1f7 100644
--- a/contrib/wpa/wpa_supplicant/config_file.c
+++ b/contrib/wpa/wpa_supplicant/config_file.c
@@ -1,1628 +1,1641 @@
/*
* WPA Supplicant / Configuration backend: text file
* Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This file implements a configuration backend for text files. All the
* configuration information is stored in a text file that uses a format
* described in the sample configuration file, wpa_supplicant.conf.
*/
#include "includes.h"
#ifdef ANDROID
#include <sys/stat.h>
#endif /* ANDROID */
#include "common.h"
#include "config.h"
#include "base64.h"
#include "uuid.h"
#include "common/ieee802_1x_defs.h"
#include "p2p/p2p.h"
#include "eap_peer/eap_methods.h"
#include "eap_peer/eap.h"
#include "utils/config.h"
static int wpa_config_validate_network(struct wpa_ssid *ssid, int line)
{
int errors = 0;
if (ssid->passphrase) {
if (ssid->psk_set) {
wpa_printf(MSG_ERROR, "Line %d: both PSK and "
"passphrase configured.", line);
errors++;
}
wpa_config_update_psk(ssid);
}
if (ssid->disabled == 2)
ssid->p2p_persistent_group = 1;
if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
!(ssid->pairwise_cipher & (WPA_CIPHER_CCMP | WPA_CIPHER_CCMP_256 |
WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256 |
WPA_CIPHER_NONE))) {
/* Group cipher cannot be stronger than the pairwise cipher. */
wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher"
" list since it was not allowed for pairwise "
"cipher", line);
ssid->group_cipher &= ~WPA_CIPHER_CCMP;
}
if (ssid->mode == WPAS_MODE_MESH &&
(ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
ssid->key_mgmt != WPA_KEY_MGMT_SAE)) {
wpa_printf(MSG_ERROR,
"Line %d: key_mgmt for mesh network should be open or SAE",
line);
errors++;
}
#ifdef CONFIG_OCV
if (ssid->ocv && ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
wpa_printf(MSG_ERROR,
"Line %d: PMF needs to be enabled whenever using OCV",
line);
errors++;
}
#endif /* CONFIG_OCV */
return errors;
}
static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id)
{
struct wpa_ssid *ssid;
int errors = 0, end = 0;
char buf[2000], *pos, *pos2;
wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block",
*line);
ssid = os_zalloc(sizeof(*ssid));
if (ssid == NULL)
return NULL;
dl_list_init(&ssid->psk_list);
ssid->id = id;
wpa_config_set_network_defaults(ssid);
while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
if (os_strcmp(pos, "}") == 0) {
end = 1;
break;
}
pos2 = os_strchr(pos, '=');
if (pos2 == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line "
"'%s'.", *line, pos);
errors++;
continue;
}
*pos2++ = '\0';
if (*pos2 == '"') {
if (os_strchr(pos2 + 1, '"') == NULL) {
wpa_printf(MSG_ERROR, "Line %d: invalid "
"quotation '%s'.", *line, pos2);
errors++;
continue;
}
}
if (wpa_config_set(ssid, pos, pos2, *line) < 0) {
#ifndef CONFIG_WEP
if (os_strcmp(pos, "wep_key0") == 0 ||
os_strcmp(pos, "wep_key1") == 0 ||
os_strcmp(pos, "wep_key2") == 0 ||
os_strcmp(pos, "wep_key3") == 0 ||
os_strcmp(pos, "wep_tx_keyidx") == 0) {
wpa_printf(MSG_ERROR,
"Line %d: unsupported WEP parameter",
*line);
ssid->disabled = 1;
continue;
}
#endif /* CONFIG_WEP */
errors++;
}
}
if (!end) {
wpa_printf(MSG_ERROR, "Line %d: network block was not "
"terminated properly.", *line);
errors++;
}
errors += wpa_config_validate_network(ssid, *line);
if (errors) {
wpa_config_free_ssid(ssid);
ssid = NULL;
}
return ssid;
}
static struct wpa_cred * wpa_config_read_cred(FILE *f, int *line, int id)
{
struct wpa_cred *cred;
int errors = 0, end = 0;
char buf[256], *pos, *pos2;
wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new cred block", *line);
cred = os_zalloc(sizeof(*cred));
if (cred == NULL)
return NULL;
cred->id = id;
cred->sim_num = DEFAULT_USER_SELECTED_SIM;
while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
if (os_strcmp(pos, "}") == 0) {
end = 1;
break;
}
pos2 = os_strchr(pos, '=');
if (pos2 == NULL) {
wpa_printf(MSG_ERROR, "Line %d: Invalid cred line "
"'%s'.", *line, pos);
errors++;
continue;
}
*pos2++ = '\0';
if (*pos2 == '"') {
if (os_strchr(pos2 + 1, '"') == NULL) {
wpa_printf(MSG_ERROR, "Line %d: invalid "
"quotation '%s'.", *line, pos2);
errors++;
continue;
}
}
if (wpa_config_set_cred(cred, pos, pos2, *line) < 0)
errors++;
}
if (!end) {
wpa_printf(MSG_ERROR, "Line %d: cred block was not "
"terminated properly.", *line);
errors++;
}
if (errors) {
wpa_config_free_cred(cred);
cred = NULL;
}
return cred;
}
#ifndef CONFIG_NO_CONFIG_BLOBS
static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line,
const char *name)
{
struct wpa_config_blob *blob;
char buf[256], *pos;
char *encoded = NULL, *nencoded;
int end = 0;
size_t encoded_len = 0, len;
wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new named blob '%s'",
*line, name);
while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
if (os_strcmp(pos, "}") == 0) {
end = 1;
break;
}
len = os_strlen(pos);
nencoded = os_realloc(encoded, encoded_len + len);
if (nencoded == NULL) {
wpa_printf(MSG_ERROR, "Line %d: not enough memory for "
"blob", *line);
os_free(encoded);
return NULL;
}
encoded = nencoded;
os_memcpy(encoded + encoded_len, pos, len);
encoded_len += len;
}
if (!end || !encoded) {
wpa_printf(MSG_ERROR, "Line %d: blob was not terminated "
"properly", *line);
os_free(encoded);
return NULL;
}
blob = os_zalloc(sizeof(*blob));
if (blob == NULL) {
os_free(encoded);
return NULL;
}
blob->name = os_strdup(name);
blob->data = base64_decode(encoded, encoded_len, &blob->len);
os_free(encoded);
if (blob->name == NULL || blob->data == NULL) {
wpa_config_free_blob(blob);
return NULL;
}
return blob;
}
static int wpa_config_process_blob(struct wpa_config *config, FILE *f,
int *line, char *bname)
{
char *name_end;
struct wpa_config_blob *blob;
name_end = os_strchr(bname, '=');
if (name_end == NULL) {
wpa_printf(MSG_ERROR, "Line %d: no blob name terminator",
*line);
return -1;
}
*name_end = '\0';
blob = wpa_config_read_blob(f, line, bname);
if (blob == NULL) {
wpa_printf(MSG_ERROR, "Line %d: failed to read blob %s",
*line, bname);
return -1;
}
wpa_config_set_blob(config, blob);
return 0;
}
#endif /* CONFIG_NO_CONFIG_BLOBS */
struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
{
FILE *f;
char buf[512], *pos;
int errors = 0, line = 0;
struct wpa_ssid *ssid, *tail, *head;
struct wpa_cred *cred, *cred_tail, *cred_head;
struct wpa_config *config;
int id = 0;
int cred_id = 0;
if (name == NULL)
return NULL;
if (cfgp)
config = cfgp;
else
config = wpa_config_alloc_empty(NULL, NULL);
if (config == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate config file "
"structure");
return NULL;
}
tail = head = config->ssid;
while (tail && tail->next)
tail = tail->next;
cred_tail = cred_head = config->cred;
while (cred_tail && cred_tail->next)
cred_tail = cred_tail->next;
wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
f = fopen(name, "r");
if (f == NULL) {
wpa_printf(MSG_ERROR, "Failed to open config file '%s', "
"error: %s", name, strerror(errno));
if (config != cfgp)
os_free(config);
return NULL;
}
while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
if (os_strcmp(pos, "network={") == 0) {
ssid = wpa_config_read_network(f, &line, id++);
if (ssid == NULL) {
wpa_printf(MSG_ERROR, "Line %d: failed to "
"parse network block.", line);
errors++;
continue;
}
if (head == NULL) {
head = tail = ssid;
} else {
tail->next = ssid;
tail = ssid;
}
if (wpa_config_add_prio_network(config, ssid)) {
wpa_printf(MSG_ERROR, "Line %d: failed to add "
"network block to priority list.",
line);
errors++;
continue;
}
} else if (os_strcmp(pos, "cred={") == 0) {
cred = wpa_config_read_cred(f, &line, cred_id++);
if (cred == NULL) {
wpa_printf(MSG_ERROR, "Line %d: failed to "
"parse cred block.", line);
errors++;
continue;
}
if (cred_head == NULL) {
cred_head = cred_tail = cred;
} else {
cred_tail->next = cred;
cred_tail = cred;
}
#ifndef CONFIG_NO_CONFIG_BLOBS
} else if (os_strncmp(pos, "blob-base64-", 12) == 0) {
if (wpa_config_process_blob(config, f, &line, pos + 12)
< 0) {
wpa_printf(MSG_ERROR, "Line %d: failed to "
"process blob.", line);
errors++;
continue;
}
#endif /* CONFIG_NO_CONFIG_BLOBS */
} else if (wpa_config_process_global(config, pos, line) < 0) {
wpa_printf(MSG_ERROR, "Line %d: Invalid configuration "
"line '%s'.", line, pos);
errors++;
continue;
}
}
fclose(f);
config->ssid = head;
wpa_config_debug_dump_networks(config);
config->cred = cred_head;
#ifndef WPA_IGNORE_CONFIG_ERRORS
if (errors) {
if (config != cfgp)
wpa_config_free(config);
config = NULL;
head = NULL;
}
#endif /* WPA_IGNORE_CONFIG_ERRORS */
return config;
}
#ifndef CONFIG_NO_CONFIG_WRITE
static void write_str(FILE *f, const char *field, struct wpa_ssid *ssid)
{
char *value = wpa_config_get(ssid, field);
if (value == NULL)
return;
fprintf(f, "\t%s=%s\n", field, value);
str_clear_free(value);
}
static void write_int(FILE *f, const char *field, int value, int def)
{
if (value == def)
return;
fprintf(f, "\t%s=%d\n", field, value);
}
static void write_bssid(FILE *f, struct wpa_ssid *ssid)
{
char *value = wpa_config_get(ssid, "bssid");
if (value == NULL)
return;
fprintf(f, "\tbssid=%s\n", value);
os_free(value);
}
static void write_bssid_hint(FILE *f, struct wpa_ssid *ssid)
{
char *value = wpa_config_get(ssid, "bssid_hint");
if (!value)
return;
fprintf(f, "\tbssid_hint=%s\n", value);
os_free(value);
}
static void write_psk(FILE *f, struct wpa_ssid *ssid)
{
char *value;
if (ssid->mem_only_psk)
return;
value = wpa_config_get(ssid, "psk");
if (value == NULL)
return;
fprintf(f, "\tpsk=%s\n", value);
os_free(value);
}
static void write_proto(FILE *f, struct wpa_ssid *ssid)
{
char *value;
if (ssid->proto == DEFAULT_PROTO)
return;
value = wpa_config_get(ssid, "proto");
if (value == NULL)
return;
if (value[0])
fprintf(f, "\tproto=%s\n", value);
os_free(value);
}
static void write_key_mgmt(FILE *f, struct wpa_ssid *ssid)
{
char *value;
if (ssid->key_mgmt == DEFAULT_KEY_MGMT)
return;
value = wpa_config_get(ssid, "key_mgmt");
if (value == NULL)
return;
if (value[0])
fprintf(f, "\tkey_mgmt=%s\n", value);
os_free(value);
}
static void write_pairwise(FILE *f, struct wpa_ssid *ssid)
{
char *value;
if (ssid->pairwise_cipher == DEFAULT_PAIRWISE)
return;
value = wpa_config_get(ssid, "pairwise");
if (value == NULL)
return;
if (value[0])
fprintf(f, "\tpairwise=%s\n", value);
os_free(value);
}
static void write_group(FILE *f, struct wpa_ssid *ssid)
{
char *value;
if (ssid->group_cipher == DEFAULT_GROUP)
return;
value = wpa_config_get(ssid, "group");
if (value == NULL)
return;
if (value[0])
fprintf(f, "\tgroup=%s\n", value);
os_free(value);
}
static void write_group_mgmt(FILE *f, struct wpa_ssid *ssid)
{
char *value;
if (!ssid->group_mgmt_cipher)
return;
value = wpa_config_get(ssid, "group_mgmt");
if (!value)
return;
if (value[0])
fprintf(f, "\tgroup_mgmt=%s\n", value);
os_free(value);
}
static void write_auth_alg(FILE *f, struct wpa_ssid *ssid)
{
char *value;
if (ssid->auth_alg == 0)
return;
value = wpa_config_get(ssid, "auth_alg");
if (value == NULL)
return;
if (value[0])
fprintf(f, "\tauth_alg=%s\n", value);
os_free(value);
}
#ifdef IEEE8021X_EAPOL
static void write_eap(FILE *f, struct wpa_ssid *ssid)
{
char *value;
value = wpa_config_get(ssid, "eap");
if (value == NULL)
return;
if (value[0])
fprintf(f, "\teap=%s\n", value);
os_free(value);
}
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_WEP
static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid)
{
char field[20], *value;
int res;
res = os_snprintf(field, sizeof(field), "wep_key%d", idx);
if (os_snprintf_error(sizeof(field), res))
return;
value = wpa_config_get(ssid, field);
if (value) {
fprintf(f, "\t%s=%s\n", field, value);
os_free(value);
}
}
#endif /* CONFIG_WEP */
#ifdef CONFIG_P2P
static void write_go_p2p_dev_addr(FILE *f, struct wpa_ssid *ssid)
{
char *value = wpa_config_get(ssid, "go_p2p_dev_addr");
if (value == NULL)
return;
fprintf(f, "\tgo_p2p_dev_addr=%s\n", value);
os_free(value);
}
static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid)
{
char *value = wpa_config_get(ssid, "p2p_client_list");
if (value == NULL)
return;
fprintf(f, "\tp2p_client_list=%s\n", value);
os_free(value);
}
static void write_psk_list(FILE *f, struct wpa_ssid *ssid)
{
struct psk_list_entry *psk;
char hex[32 * 2 + 1];
dl_list_for_each(psk, &ssid->psk_list, struct psk_list_entry, list) {
wpa_snprintf_hex(hex, sizeof(hex), psk->psk, sizeof(psk->psk));
fprintf(f, "\tpsk_list=%s" MACSTR "-%s\n",
psk->p2p ? "P2P-" : "", MAC2STR(psk->addr), hex);
}
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_MACSEC
static void write_mka_cak(FILE *f, struct wpa_ssid *ssid)
{
char *value;
if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK))
return;
value = wpa_config_get(ssid, "mka_cak");
if (!value)
return;
fprintf(f, "\tmka_cak=%s\n", value);
os_free(value);
}
static void write_mka_ckn(FILE *f, struct wpa_ssid *ssid)
{
char *value;
if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN))
return;
value = wpa_config_get(ssid, "mka_ckn");
if (!value)
return;
fprintf(f, "\tmka_ckn=%s\n", value);
os_free(value);
}
#endif /* CONFIG_MACSEC */
static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
{
#define STR(t) write_str(f, #t, ssid)
#define INT(t) write_int(f, #t, ssid->t, 0)
#define INTe(t, m) write_int(f, #t, ssid->eap.m, 0)
#define INT_DEF(t, def) write_int(f, #t, ssid->t, def)
#define INT_DEFe(t, m, def) write_int(f, #t, ssid->eap.m, def)
STR(ssid);
INT(scan_ssid);
write_bssid(f, ssid);
write_bssid_hint(f, ssid);
write_str(f, "bssid_ignore", ssid);
write_str(f, "bssid_accept", ssid);
write_psk(f, ssid);
INT(mem_only_psk);
STR(sae_password);
STR(sae_password_id);
+ write_int(f, "sae_pwe", ssid->sae_pwe, DEFAULT_SAE_PWE);
write_proto(f, ssid);
write_key_mgmt(f, ssid);
INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
write_pairwise(f, ssid);
write_group(f, ssid);
write_group_mgmt(f, ssid);
write_auth_alg(f, ssid);
STR(bgscan);
STR(autoscan);
STR(scan_freq);
#ifdef IEEE8021X_EAPOL
write_eap(f, ssid);
STR(identity);
STR(anonymous_identity);
STR(imsi_identity);
STR(machine_identity);
STR(password);
STR(machine_password);
STR(ca_cert);
STR(ca_path);
STR(client_cert);
STR(private_key);
STR(private_key_passwd);
STR(dh_file);
STR(subject_match);
STR(check_cert_subject);
STR(altsubject_match);
STR(domain_suffix_match);
STR(domain_match);
STR(ca_cert2);
STR(ca_path2);
STR(client_cert2);
STR(private_key2);
STR(private_key2_passwd);
STR(dh_file2);
STR(subject_match2);
STR(check_cert_subject2);
STR(altsubject_match2);
STR(domain_suffix_match2);
STR(domain_match2);
STR(machine_ca_cert);
STR(machine_ca_path);
STR(machine_client_cert);
STR(machine_private_key);
STR(machine_private_key_passwd);
STR(machine_dh_file);
STR(machine_subject_match);
STR(machine_check_cert_subject);
STR(machine_altsubject_match);
STR(machine_domain_suffix_match);
STR(machine_domain_match);
STR(phase1);
STR(phase2);
STR(machine_phase2);
STR(pcsc);
STR(pin);
STR(engine_id);
STR(key_id);
STR(cert_id);
STR(ca_cert_id);
STR(key2_id);
STR(pin2);
STR(engine2_id);
STR(cert2_id);
STR(ca_cert2_id);
INTe(engine, cert.engine);
INTe(engine2, phase2_cert.engine);
INTe(machine_engine, machine_cert.engine);
INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
STR(openssl_ciphers);
INTe(erp, erp);
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_WEP
{
int i;
for (i = 0; i < 4; i++)
write_wep_key(f, i, ssid);
INT(wep_tx_keyidx);
}
#endif /* CONFIG_WEP */
INT(priority);
#ifdef IEEE8021X_EAPOL
INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
STR(pac_file);
INT_DEFe(fragment_size, fragment_size, DEFAULT_FRAGMENT_SIZE);
INTe(ocsp, cert.ocsp);
INTe(ocsp2, phase2_cert.ocsp);
INTe(machine_ocsp, machine_cert.ocsp);
INT_DEFe(sim_num, sim_num, DEFAULT_USER_SELECTED_SIM);
#endif /* IEEE8021X_EAPOL */
INT(mode);
INT(no_auto_peer);
INT(frequency);
INT(enable_edmg);
INT(edmg_channel);
INT(fixed_freq);
#ifdef CONFIG_ACS
INT(acs);
#endif /* CONFIG_ACS */
write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
INT(disabled);
INT(mixed_cell);
INT_DEF(vht, 1);
INT_DEF(ht, 1);
INT(ht40);
INT_DEF(he, 1);
INT_DEF(max_oper_chwidth, DEFAULT_MAX_OPER_CHWIDTH);
INT(vht_center_freq1);
INT(vht_center_freq2);
INT(pbss);
INT(wps_disabled);
INT(fils_dh_group);
write_int(f, "ieee80211w", ssid->ieee80211w,
MGMT_FRAME_PROTECTION_DEFAULT);
STR(id_str);
#ifdef CONFIG_P2P
write_go_p2p_dev_addr(f, ssid);
write_p2p_client_list(f, ssid);
write_psk_list(f, ssid);
#endif /* CONFIG_P2P */
INT(ap_max_inactivity);
INT(dtim_period);
INT(beacon_int);
#ifdef CONFIG_MACSEC
INT(macsec_policy);
write_mka_cak(f, ssid);
write_mka_ckn(f, ssid);
INT(macsec_integ_only);
INT(macsec_replay_protect);
INT(macsec_replay_window);
INT(macsec_port);
INT_DEF(mka_priority, DEFAULT_PRIO_NOT_KEY_SERVER);
#endif /* CONFIG_MACSEC */
#ifdef CONFIG_HS20
INT(update_identifier);
STR(roaming_consortium_selection);
#endif /* CONFIG_HS20 */
write_int(f, "mac_addr", ssid->mac_addr, -1);
#ifdef CONFIG_MESH
STR(mesh_basic_rates);
INT_DEF(dot11MeshMaxRetries, DEFAULT_MESH_MAX_RETRIES);
INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT);
INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT);
INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT);
INT_DEF(mesh_rssi_threshold, DEFAULT_MESH_RSSI_THRESHOLD);
#endif /* CONFIG_MESH */
INT(wpa_ptk_rekey);
INT(wpa_deny_ptk0_rekey);
INT(group_rekey);
INT(ignore_broadcast_ssid);
#ifdef CONFIG_DPP
STR(dpp_connector);
STR(dpp_netaccesskey);
INT(dpp_netaccesskey_expiry);
STR(dpp_csign);
STR(dpp_pp_key);
INT(dpp_pfs);
#endif /* CONFIG_DPP */
INT(owe_group);
INT(owe_only);
INT(owe_ptk_workaround);
INT(multi_ap_backhaul_sta);
INT(ft_eap_pmksa_caching);
INT(beacon_prot);
INT(transition_disable);
INT(sae_pk);
#ifdef CONFIG_HT_OVERRIDES
INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
INT_DEF(disable_sgi, DEFAULT_DISABLE_SGI);
INT_DEF(disable_ldpc, DEFAULT_DISABLE_LDPC);
INT(ht40_intolerant);
INT_DEF(tx_stbc, DEFAULT_TX_STBC);
INT_DEF(rx_stbc, DEFAULT_RX_STBC);
INT_DEF(disable_max_amsdu, DEFAULT_DISABLE_MAX_AMSDU);
INT_DEF(ampdu_factor, DEFAULT_AMPDU_FACTOR);
INT_DEF(ampdu_density, DEFAULT_AMPDU_DENSITY);
STR(ht_mcs);
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
INT(disable_vht);
INT(vht_capa);
INT(vht_capa_mask);
INT_DEF(vht_rx_mcs_nss_1, -1);
INT_DEF(vht_rx_mcs_nss_2, -1);
INT_DEF(vht_rx_mcs_nss_3, -1);
INT_DEF(vht_rx_mcs_nss_4, -1);
INT_DEF(vht_rx_mcs_nss_5, -1);
INT_DEF(vht_rx_mcs_nss_6, -1);
INT_DEF(vht_rx_mcs_nss_7, -1);
INT_DEF(vht_rx_mcs_nss_8, -1);
INT_DEF(vht_tx_mcs_nss_1, -1);
INT_DEF(vht_tx_mcs_nss_2, -1);
INT_DEF(vht_tx_mcs_nss_3, -1);
INT_DEF(vht_tx_mcs_nss_4, -1);
INT_DEF(vht_tx_mcs_nss_5, -1);
INT_DEF(vht_tx_mcs_nss_6, -1);
INT_DEF(vht_tx_mcs_nss_7, -1);
INT_DEF(vht_tx_mcs_nss_8, -1);
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_HE_OVERRIDES
INT(disable_he);
#endif /* CONFIG_HE_OVERRIDES */
#undef STR
#undef INT
#undef INT_DEF
}
static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
{
size_t i;
if (cred->priority)
fprintf(f, "\tpriority=%d\n", cred->priority);
if (cred->pcsc)
fprintf(f, "\tpcsc=%d\n", cred->pcsc);
if (cred->realm)
fprintf(f, "\trealm=\"%s\"\n", cred->realm);
if (cred->username)
fprintf(f, "\tusername=\"%s\"\n", cred->username);
if (cred->password && cred->ext_password)
fprintf(f, "\tpassword=ext:%s\n", cred->password);
else if (cred->password)
fprintf(f, "\tpassword=\"%s\"\n", cred->password);
if (cred->ca_cert)
fprintf(f, "\tca_cert=\"%s\"\n", cred->ca_cert);
if (cred->client_cert)
fprintf(f, "\tclient_cert=\"%s\"\n", cred->client_cert);
if (cred->private_key)
fprintf(f, "\tprivate_key=\"%s\"\n", cred->private_key);
if (cred->private_key_passwd)
fprintf(f, "\tprivate_key_passwd=\"%s\"\n",
cred->private_key_passwd);
if (cred->imsi)
fprintf(f, "\timsi=\"%s\"\n", cred->imsi);
if (cred->milenage)
fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage);
for (i = 0; i < cred->num_domain; i++)
fprintf(f, "\tdomain=\"%s\"\n", cred->domain[i]);
if (cred->domain_suffix_match)
fprintf(f, "\tdomain_suffix_match=\"%s\"\n",
cred->domain_suffix_match);
if (cred->roaming_consortium_len) {
fprintf(f, "\troaming_consortium=");
for (i = 0; i < cred->roaming_consortium_len; i++)
fprintf(f, "%02x", cred->roaming_consortium[i]);
fprintf(f, "\n");
}
if (cred->eap_method) {
const char *name;
name = eap_get_name(cred->eap_method[0].vendor,
cred->eap_method[0].method);
if (name)
fprintf(f, "\teap=%s\n", name);
}
if (cred->phase1)
fprintf(f, "\tphase1=\"%s\"\n", cred->phase1);
if (cred->phase2)
fprintf(f, "\tphase2=\"%s\"\n", cred->phase2);
if (cred->excluded_ssid) {
size_t j;
for (i = 0; i < cred->num_excluded_ssid; i++) {
struct excluded_ssid *e = &cred->excluded_ssid[i];
fprintf(f, "\texcluded_ssid=");
for (j = 0; j < e->ssid_len; j++)
fprintf(f, "%02x", e->ssid[j]);
fprintf(f, "\n");
}
}
if (cred->roaming_partner) {
for (i = 0; i < cred->num_roaming_partner; i++) {
struct roaming_partner *p = &cred->roaming_partner[i];
fprintf(f, "\troaming_partner=\"%s,%d,%u,%s\"\n",
p->fqdn, p->exact_match, p->priority,
p->country);
}
}
if (cred->update_identifier)
fprintf(f, "\tupdate_identifier=%d\n", cred->update_identifier);
if (cred->provisioning_sp)
fprintf(f, "\tprovisioning_sp=\"%s\"\n", cred->provisioning_sp);
if (cred->sp_priority)
fprintf(f, "\tsp_priority=%d\n", cred->sp_priority);
if (cred->min_dl_bandwidth_home)
fprintf(f, "\tmin_dl_bandwidth_home=%u\n",
cred->min_dl_bandwidth_home);
if (cred->min_ul_bandwidth_home)
fprintf(f, "\tmin_ul_bandwidth_home=%u\n",
cred->min_ul_bandwidth_home);
if (cred->min_dl_bandwidth_roaming)
fprintf(f, "\tmin_dl_bandwidth_roaming=%u\n",
cred->min_dl_bandwidth_roaming);
if (cred->min_ul_bandwidth_roaming)
fprintf(f, "\tmin_ul_bandwidth_roaming=%u\n",
cred->min_ul_bandwidth_roaming);
if (cred->max_bss_load)
fprintf(f, "\tmax_bss_load=%u\n",
cred->max_bss_load);
if (cred->ocsp)
fprintf(f, "\tocsp=%d\n", cred->ocsp);
if (cred->num_req_conn_capab) {
for (i = 0; i < cred->num_req_conn_capab; i++) {
int *ports;
fprintf(f, "\treq_conn_capab=%u",
cred->req_conn_capab_proto[i]);
ports = cred->req_conn_capab_port[i];
if (ports) {
int j;
for (j = 0; ports[j] != -1; j++) {
fprintf(f, "%s%d", j > 0 ? "," : ":",
ports[j]);
}
}
fprintf(f, "\n");
}
}
if (cred->required_roaming_consortium_len) {
fprintf(f, "\trequired_roaming_consortium=");
for (i = 0; i < cred->required_roaming_consortium_len; i++)
fprintf(f, "%02x",
cred->required_roaming_consortium[i]);
fprintf(f, "\n");
}
if (cred->num_roaming_consortiums) {
size_t j;
fprintf(f, "\troaming_consortiums=\"");
for (i = 0; i < cred->num_roaming_consortiums; i++) {
if (i > 0)
fprintf(f, ",");
for (j = 0; j < cred->roaming_consortiums_len[i]; j++)
fprintf(f, "%02x",
cred->roaming_consortiums[i][j]);
}
fprintf(f, "\"\n");
}
if (cred->sim_num != DEFAULT_USER_SELECTED_SIM)
fprintf(f, "\tsim_num=%d\n", cred->sim_num);
}
#ifndef CONFIG_NO_CONFIG_BLOBS
static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob)
{
char *encoded;
encoded = base64_encode(blob->data, blob->len, NULL);
if (encoded == NULL)
return -1;
fprintf(f, "\nblob-base64-%s={\n%s}\n", blob->name, encoded);
os_free(encoded);
return 0;
}
#endif /* CONFIG_NO_CONFIG_BLOBS */
static void write_global_bin(FILE *f, const char *field,
const struct wpabuf *val)
{
size_t i;
const u8 *pos;
if (val == NULL)
return;
fprintf(f, "%s=", field);
pos = wpabuf_head(val);
for (i = 0; i < wpabuf_len(val); i++)
fprintf(f, "%02X", *pos++);
fprintf(f, "\n");
}
static void wpa_config_write_global(FILE *f, struct wpa_config *config)
{
#ifdef CONFIG_CTRL_IFACE
if (config->ctrl_interface)
fprintf(f, "ctrl_interface=%s\n", config->ctrl_interface);
if (config->ctrl_interface_group)
fprintf(f, "ctrl_interface_group=%s\n",
config->ctrl_interface_group);
#endif /* CONFIG_CTRL_IFACE */
if (config->eapol_version != DEFAULT_EAPOL_VERSION)
fprintf(f, "eapol_version=%d\n", config->eapol_version);
if (config->ap_scan != DEFAULT_AP_SCAN)
fprintf(f, "ap_scan=%d\n", config->ap_scan);
if (config->disable_scan_offload)
fprintf(f, "disable_scan_offload=%d\n",
config->disable_scan_offload);
if (config->fast_reauth != DEFAULT_FAST_REAUTH)
fprintf(f, "fast_reauth=%d\n", config->fast_reauth);
if (config->opensc_engine_path)
fprintf(f, "opensc_engine_path=%s\n",
config->opensc_engine_path);
if (config->pkcs11_engine_path)
fprintf(f, "pkcs11_engine_path=%s\n",
config->pkcs11_engine_path);
if (config->pkcs11_module_path)
fprintf(f, "pkcs11_module_path=%s\n",
config->pkcs11_module_path);
if (config->openssl_ciphers)
fprintf(f, "openssl_ciphers=%s\n", config->openssl_ciphers);
if (config->pcsc_reader)
fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader);
if (config->pcsc_pin)
fprintf(f, "pcsc_pin=%s\n", config->pcsc_pin);
if (config->driver_param)
fprintf(f, "driver_param=%s\n", config->driver_param);
if (config->dot11RSNAConfigPMKLifetime)
fprintf(f, "dot11RSNAConfigPMKLifetime=%u\n",
config->dot11RSNAConfigPMKLifetime);
if (config->dot11RSNAConfigPMKReauthThreshold)
fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%u\n",
config->dot11RSNAConfigPMKReauthThreshold);
if (config->dot11RSNAConfigSATimeout)
fprintf(f, "dot11RSNAConfigSATimeout=%u\n",
config->dot11RSNAConfigSATimeout);
if (config->update_config)
fprintf(f, "update_config=%d\n", config->update_config);
#ifdef CONFIG_WPS
if (!is_nil_uuid(config->uuid)) {
char buf[40];
uuid_bin2str(config->uuid, buf, sizeof(buf));
fprintf(f, "uuid=%s\n", buf);
}
if (config->auto_uuid)
fprintf(f, "auto_uuid=%d\n", config->auto_uuid);
if (config->device_name)
fprintf(f, "device_name=%s\n", config->device_name);
if (config->manufacturer)
fprintf(f, "manufacturer=%s\n", config->manufacturer);
if (config->model_name)
fprintf(f, "model_name=%s\n", config->model_name);
if (config->model_number)
fprintf(f, "model_number=%s\n", config->model_number);
if (config->serial_number)
fprintf(f, "serial_number=%s\n", config->serial_number);
{
char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
buf = wps_dev_type_bin2str(config->device_type,
_buf, sizeof(_buf));
if (os_strcmp(buf, "0-00000000-0") != 0)
fprintf(f, "device_type=%s\n", buf);
}
if (WPA_GET_BE32(config->os_version))
fprintf(f, "os_version=%08x\n",
WPA_GET_BE32(config->os_version));
if (config->config_methods)
fprintf(f, "config_methods=%s\n", config->config_methods);
if (config->wps_cred_processing)
fprintf(f, "wps_cred_processing=%d\n",
config->wps_cred_processing);
if (config->wps_cred_add_sae)
fprintf(f, "wps_cred_add_sae=%d\n",
config->wps_cred_add_sae);
if (config->wps_vendor_ext_m1) {
int i, len = wpabuf_len(config->wps_vendor_ext_m1);
const u8 *p = wpabuf_head_u8(config->wps_vendor_ext_m1);
if (len > 0) {
fprintf(f, "wps_vendor_ext_m1=");
for (i = 0; i < len; i++)
fprintf(f, "%02x", *p++);
fprintf(f, "\n");
}
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
{
int i;
char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
for (i = 0; i < config->num_sec_device_types; i++) {
buf = wps_dev_type_bin2str(config->sec_device_type[i],
_buf, sizeof(_buf));
if (buf)
fprintf(f, "sec_device_type=%s\n", buf);
}
}
if (config->p2p_listen_reg_class)
fprintf(f, "p2p_listen_reg_class=%d\n",
config->p2p_listen_reg_class);
if (config->p2p_listen_channel)
fprintf(f, "p2p_listen_channel=%d\n",
config->p2p_listen_channel);
if (config->p2p_oper_reg_class)
fprintf(f, "p2p_oper_reg_class=%d\n",
config->p2p_oper_reg_class);
if (config->p2p_oper_channel)
fprintf(f, "p2p_oper_channel=%d\n", config->p2p_oper_channel);
if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT)
fprintf(f, "p2p_go_intent=%d\n", config->p2p_go_intent);
if (config->p2p_ssid_postfix)
fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix);
if (config->persistent_reconnect)
fprintf(f, "persistent_reconnect=%d\n",
config->persistent_reconnect);
if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS)
fprintf(f, "p2p_intra_bss=%d\n", config->p2p_intra_bss);
if (config->p2p_group_idle)
fprintf(f, "p2p_group_idle=%d\n", config->p2p_group_idle);
if (config->p2p_passphrase_len)
fprintf(f, "p2p_passphrase_len=%u\n",
config->p2p_passphrase_len);
if (config->p2p_pref_chan) {
unsigned int i;
fprintf(f, "p2p_pref_chan=");
for (i = 0; i < config->num_p2p_pref_chan; i++) {
fprintf(f, "%s%u:%u", i > 0 ? "," : "",
config->p2p_pref_chan[i].op_class,
config->p2p_pref_chan[i].chan);
}
fprintf(f, "\n");
}
if (config->p2p_no_go_freq.num) {
char *val = freq_range_list_str(&config->p2p_no_go_freq);
if (val) {
fprintf(f, "p2p_no_go_freq=%s\n", val);
os_free(val);
}
}
if (config->p2p_add_cli_chan)
fprintf(f, "p2p_add_cli_chan=%d\n", config->p2p_add_cli_chan);
if (config->p2p_optimize_listen_chan !=
DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN)
fprintf(f, "p2p_optimize_listen_chan=%d\n",
config->p2p_optimize_listen_chan);
if (config->p2p_go_ht40)
fprintf(f, "p2p_go_ht40=%d\n", config->p2p_go_ht40);
if (config->p2p_go_vht)
fprintf(f, "p2p_go_vht=%d\n", config->p2p_go_vht);
if (config->p2p_go_he)
fprintf(f, "p2p_go_he=%d\n", config->p2p_go_he);
if (config->p2p_go_edmg)
fprintf(f, "p2p_go_edmg=%d\n", config->p2p_go_edmg);
if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW)
fprintf(f, "p2p_go_ctwindow=%d\n", config->p2p_go_ctwindow);
if (config->p2p_disabled)
fprintf(f, "p2p_disabled=%d\n", config->p2p_disabled);
if (config->p2p_no_group_iface)
fprintf(f, "p2p_no_group_iface=%d\n",
config->p2p_no_group_iface);
if (config->p2p_ignore_shared_freq)
fprintf(f, "p2p_ignore_shared_freq=%d\n",
config->p2p_ignore_shared_freq);
if (config->p2p_cli_probe)
fprintf(f, "p2p_cli_probe=%d\n", config->p2p_cli_probe);
if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE)
fprintf(f, "p2p_go_freq_change_policy=%u\n",
config->p2p_go_freq_change_policy);
if (config->p2p_6ghz_disable)
fprintf(f, "p2p_6ghz_disable=%d\n", config->p2p_6ghz_disable);
if (WPA_GET_BE32(config->ip_addr_go))
fprintf(f, "ip_addr_go=%u.%u.%u.%u\n",
config->ip_addr_go[0], config->ip_addr_go[1],
config->ip_addr_go[2], config->ip_addr_go[3]);
if (WPA_GET_BE32(config->ip_addr_mask))
fprintf(f, "ip_addr_mask=%u.%u.%u.%u\n",
config->ip_addr_mask[0], config->ip_addr_mask[1],
config->ip_addr_mask[2], config->ip_addr_mask[3]);
if (WPA_GET_BE32(config->ip_addr_start))
fprintf(f, "ip_addr_start=%u.%u.%u.%u\n",
config->ip_addr_start[0], config->ip_addr_start[1],
config->ip_addr_start[2], config->ip_addr_start[3]);
if (WPA_GET_BE32(config->ip_addr_end))
fprintf(f, "ip_addr_end=%u.%u.%u.%u\n",
config->ip_addr_end[0], config->ip_addr_end[1],
config->ip_addr_end[2], config->ip_addr_end[3]);
#endif /* CONFIG_P2P */
if (config->country[0] && config->country[1]) {
fprintf(f, "country=%c%c\n",
config->country[0], config->country[1]);
}
if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT)
fprintf(f, "bss_max_count=%u\n", config->bss_max_count);
if (config->bss_expiration_age != DEFAULT_BSS_EXPIRATION_AGE)
fprintf(f, "bss_expiration_age=%u\n",
config->bss_expiration_age);
if (config->bss_expiration_scan_count !=
DEFAULT_BSS_EXPIRATION_SCAN_COUNT)
fprintf(f, "bss_expiration_scan_count=%u\n",
config->bss_expiration_scan_count);
if (config->filter_ssids)
fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
if (config->filter_rssi)
fprintf(f, "filter_rssi=%d\n", config->filter_rssi);
if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
if (config->ap_isolate != DEFAULT_AP_ISOLATE)
fprintf(f, "ap_isolate=%u\n", config->ap_isolate);
if (config->disassoc_low_ack)
fprintf(f, "disassoc_low_ack=%d\n", config->disassoc_low_ack);
#ifdef CONFIG_HS20
if (config->hs20)
fprintf(f, "hs20=1\n");
#endif /* CONFIG_HS20 */
#ifdef CONFIG_INTERWORKING
if (config->interworking)
fprintf(f, "interworking=%d\n", config->interworking);
if (!is_zero_ether_addr(config->hessid))
fprintf(f, "hessid=" MACSTR "\n", MAC2STR(config->hessid));
if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE)
fprintf(f, "access_network_type=%d\n",
config->access_network_type);
if (config->go_interworking)
fprintf(f, "go_interworking=%d\n", config->go_interworking);
if (config->go_access_network_type)
fprintf(f, "go_access_network_type=%d\n",
config->go_access_network_type);
if (config->go_internet)
fprintf(f, "go_internet=%d\n", config->go_internet);
if (config->go_venue_group)
fprintf(f, "go_venue_group=%d\n", config->go_venue_group);
if (config->go_venue_type)
fprintf(f, "go_venue_type=%d\n", config->go_venue_type);
#endif /* CONFIG_INTERWORKING */
if (config->pbc_in_m1)
fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1);
if (config->wps_nfc_pw_from_config) {
if (config->wps_nfc_dev_pw_id)
fprintf(f, "wps_nfc_dev_pw_id=%d\n",
config->wps_nfc_dev_pw_id);
write_global_bin(f, "wps_nfc_dh_pubkey",
config->wps_nfc_dh_pubkey);
write_global_bin(f, "wps_nfc_dh_privkey",
config->wps_nfc_dh_privkey);
write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw);
}
if (config->ext_password_backend)
fprintf(f, "ext_password_backend=%s\n",
config->ext_password_backend);
if (config->p2p_go_max_inactivity != DEFAULT_P2P_GO_MAX_INACTIVITY)
fprintf(f, "p2p_go_max_inactivity=%d\n",
config->p2p_go_max_inactivity);
if (config->auto_interworking)
fprintf(f, "auto_interworking=%d\n",
config->auto_interworking);
if (config->okc)
fprintf(f, "okc=%d\n", config->okc);
if (config->pmf)
fprintf(f, "pmf=%d\n", config->pmf);
if (config->dtim_period)
fprintf(f, "dtim_period=%d\n", config->dtim_period);
if (config->beacon_int)
fprintf(f, "beacon_int=%d\n", config->beacon_int);
if (config->sae_groups) {
int i;
fprintf(f, "sae_groups=");
for (i = 0; config->sae_groups[i] > 0; i++) {
fprintf(f, "%s%d", i > 0 ? " " : "",
config->sae_groups[i]);
}
fprintf(f, "\n");
}
if (config->sae_pwe)
fprintf(f, "sae_pwe=%d\n", config->sae_pwe);
if (config->sae_pmkid_in_assoc)
fprintf(f, "sae_pmkid_in_assoc=%d\n",
config->sae_pmkid_in_assoc);
if (config->ap_vendor_elements) {
int i, len = wpabuf_len(config->ap_vendor_elements);
const u8 *p = wpabuf_head_u8(config->ap_vendor_elements);
if (len > 0) {
fprintf(f, "ap_vendor_elements=");
for (i = 0; i < len; i++)
fprintf(f, "%02x", *p++);
fprintf(f, "\n");
}
}
+ if (config->ap_assocresp_elements) {
+ int i, len = wpabuf_len(config->ap_assocresp_elements);
+ const u8 *p = wpabuf_head_u8(config->ap_assocresp_elements);
+
+ if (len > 0) {
+ fprintf(f, "ap_assocresp_elements=");
+ for (i = 0; i < len; i++)
+ fprintf(f, "%02x", *p++);
+ fprintf(f, "\n");
+ }
+ }
+
if (config->ignore_old_scan_res)
fprintf(f, "ignore_old_scan_res=%d\n",
config->ignore_old_scan_res);
if (config->freq_list && config->freq_list[0]) {
int i;
fprintf(f, "freq_list=");
for (i = 0; config->freq_list[i]; i++) {
fprintf(f, "%s%d", i > 0 ? " " : "",
config->freq_list[i]);
}
fprintf(f, "\n");
}
if (config->initial_freq_list && config->initial_freq_list[0]) {
int i;
fprintf(f, "initial_freq_list=");
for (i = 0; config->initial_freq_list[i]; i++) {
fprintf(f, "%s%d", i > 0 ? " " : "",
config->initial_freq_list[i]);
}
fprintf(f, "\n");
}
if (config->scan_cur_freq != DEFAULT_SCAN_CUR_FREQ)
fprintf(f, "scan_cur_freq=%d\n", config->scan_cur_freq);
if (config->scan_res_valid_for_connect !=
DEFAULT_SCAN_RES_VALID_FOR_CONNECT)
fprintf(f, "scan_res_valid_for_connect=%d\n",
config->scan_res_valid_for_connect);
if (config->sched_scan_interval)
fprintf(f, "sched_scan_interval=%u\n",
config->sched_scan_interval);
if (config->sched_scan_start_delay)
fprintf(f, "sched_scan_start_delay=%u\n",
config->sched_scan_start_delay);
if (config->external_sim)
fprintf(f, "external_sim=%d\n", config->external_sim);
if (config->tdls_external_control)
fprintf(f, "tdls_external_control=%d\n",
config->tdls_external_control);
if (config->wowlan_triggers)
fprintf(f, "wowlan_triggers=%s\n",
config->wowlan_triggers);
if (config->bgscan)
fprintf(f, "bgscan=\"%s\"\n", config->bgscan);
if (config->autoscan)
fprintf(f, "autoscan=%s\n", config->autoscan);
if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY)
fprintf(f, "p2p_search_delay=%u\n",
config->p2p_search_delay);
if (config->mac_addr)
fprintf(f, "mac_addr=%d\n", config->mac_addr);
if (config->rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME)
fprintf(f, "rand_addr_lifetime=%u\n",
config->rand_addr_lifetime);
if (config->preassoc_mac_addr)
fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr);
if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD)
fprintf(f, "key_mgmt_offload=%d\n", config->key_mgmt_offload);
if (config->user_mpm != DEFAULT_USER_MPM)
fprintf(f, "user_mpm=%d\n", config->user_mpm);
if (config->max_peer_links != DEFAULT_MAX_PEER_LINKS)
fprintf(f, "max_peer_links=%d\n", config->max_peer_links);
if (config->cert_in_cb != DEFAULT_CERT_IN_CB)
fprintf(f, "cert_in_cb=%d\n", config->cert_in_cb);
if (config->mesh_max_inactivity != DEFAULT_MESH_MAX_INACTIVITY)
fprintf(f, "mesh_max_inactivity=%d\n",
config->mesh_max_inactivity);
if (config->dot11RSNASAERetransPeriod !=
DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD)
fprintf(f, "dot11RSNASAERetransPeriod=%d\n",
config->dot11RSNASAERetransPeriod);
if (config->passive_scan)
fprintf(f, "passive_scan=%d\n", config->passive_scan);
if (config->reassoc_same_bss_optim)
fprintf(f, "reassoc_same_bss_optim=%d\n",
config->reassoc_same_bss_optim);
if (config->wps_priority)
fprintf(f, "wps_priority=%d\n", config->wps_priority);
if (config->wpa_rsc_relaxation != DEFAULT_WPA_RSC_RELAXATION)
fprintf(f, "wpa_rsc_relaxation=%d\n",
config->wpa_rsc_relaxation);
if (config->sched_scan_plans)
fprintf(f, "sched_scan_plans=%s\n", config->sched_scan_plans);
#ifdef CONFIG_MBO
if (config->non_pref_chan)
fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan);
if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA)
fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa);
if (config->disassoc_imminent_rssi_threshold !=
DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD)
fprintf(f, "disassoc_imminent_rssi_threshold=%d\n",
config->disassoc_imminent_rssi_threshold);
if (config->oce != DEFAULT_OCE_SUPPORT)
fprintf(f, "oce=%u\n", config->oce);
#endif /* CONFIG_MBO */
if (config->gas_address3)
fprintf(f, "gas_address3=%d\n", config->gas_address3);
if (config->ftm_responder)
fprintf(f, "ftm_responder=%d\n", config->ftm_responder);
if (config->ftm_initiator)
fprintf(f, "ftm_initiator=%d\n", config->ftm_initiator);
if (config->osu_dir)
fprintf(f, "osu_dir=%s\n", config->osu_dir);
if (config->fst_group_id)
fprintf(f, "fst_group_id=%s\n", config->fst_group_id);
if (config->fst_priority)
fprintf(f, "fst_priority=%d\n", config->fst_priority);
if (config->fst_llt)
fprintf(f, "fst_llt=%d\n", config->fst_llt);
if (config->gas_rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME)
fprintf(f, "gas_rand_addr_lifetime=%u\n",
config->gas_rand_addr_lifetime);
if (config->gas_rand_mac_addr)
fprintf(f, "gas_rand_mac_addr=%d\n", config->gas_rand_mac_addr);
if (config->dpp_config_processing)
fprintf(f, "dpp_config_processing=%d\n",
config->dpp_config_processing);
if (config->coloc_intf_reporting)
fprintf(f, "coloc_intf_reporting=%d\n",
config->coloc_intf_reporting);
if (config->p2p_device_random_mac_addr)
fprintf(f, "p2p_device_random_mac_addr=%d\n",
config->p2p_device_random_mac_addr);
if (!is_zero_ether_addr(config->p2p_device_persistent_mac_addr))
fprintf(f, "p2p_device_persistent_mac_addr=" MACSTR "\n",
MAC2STR(config->p2p_device_persistent_mac_addr));
if (config->p2p_interface_random_mac_addr)
fprintf(f, "p2p_interface_random_mac_addr=%d\n",
config->p2p_interface_random_mac_addr);
if (config->disable_btm)
fprintf(f, "disable_btm=1\n");
if (config->extended_key_id != DEFAULT_EXTENDED_KEY_ID)
fprintf(f, "extended_key_id=%d\n",
config->extended_key_id);
if (config->wowlan_disconnect_on_deinit)
fprintf(f, "wowlan_disconnect_on_deinit=%d\n",
config->wowlan_disconnect_on_deinit);
}
#endif /* CONFIG_NO_CONFIG_WRITE */
int wpa_config_write(const char *name, struct wpa_config *config)
{
#ifndef CONFIG_NO_CONFIG_WRITE
FILE *f;
struct wpa_ssid *ssid;
struct wpa_cred *cred;
#ifndef CONFIG_NO_CONFIG_BLOBS
struct wpa_config_blob *blob;
#endif /* CONFIG_NO_CONFIG_BLOBS */
int ret = 0;
const char *orig_name = name;
int tmp_len;
char *tmp_name;
if (!name) {
wpa_printf(MSG_ERROR, "No configuration file for writing");
return -1;
}
tmp_len = os_strlen(name) + 5; /* allow space for .tmp suffix */
tmp_name = os_malloc(tmp_len);
if (tmp_name) {
os_snprintf(tmp_name, tmp_len, "%s.tmp", name);
name = tmp_name;
}
wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
f = fopen(name, "w");
if (f == NULL) {
wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name);
os_free(tmp_name);
return -1;
}
wpa_config_write_global(f, config);
for (cred = config->cred; cred; cred = cred->next) {
if (cred->temporary)
continue;
fprintf(f, "\ncred={\n");
wpa_config_write_cred(f, cred);
fprintf(f, "}\n");
}
for (ssid = config->ssid; ssid; ssid = ssid->next) {
if (ssid->key_mgmt == WPA_KEY_MGMT_WPS || ssid->temporary)
continue; /* do not save temporary networks */
if (wpa_key_mgmt_wpa_psk_no_sae(ssid->key_mgmt) &&
!ssid->psk_set && !ssid->passphrase)
continue; /* do not save invalid network */
if (wpa_key_mgmt_sae(ssid->key_mgmt) &&
!ssid->passphrase && !ssid->sae_password)
continue; /* do not save invalid network */
fprintf(f, "\nnetwork={\n");
wpa_config_write_network(f, ssid);
fprintf(f, "}\n");
}
#ifndef CONFIG_NO_CONFIG_BLOBS
for (blob = config->blobs; blob; blob = blob->next) {
ret = wpa_config_write_blob(f, blob);
if (ret)
break;
}
#endif /* CONFIG_NO_CONFIG_BLOBS */
os_fdatasync(f);
fclose(f);
if (tmp_name) {
int chmod_ret = 0;
#ifdef ANDROID
chmod_ret = chmod(tmp_name,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
#endif /* ANDROID */
if (chmod_ret != 0 || rename(tmp_name, orig_name) != 0)
ret = -1;
os_free(tmp_name);
}
wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully",
orig_name, ret ? "un" : "");
return ret;
#else /* CONFIG_NO_CONFIG_WRITE */
return -1;
#endif /* CONFIG_NO_CONFIG_WRITE */
}
diff --git a/contrib/wpa/wpa_supplicant/config_none.c b/contrib/wpa/wpa_supplicant/config_none.c
index 2aac28fa3d17..0bc977e3961b 100644
--- a/contrib/wpa/wpa_supplicant/config_none.c
+++ b/contrib/wpa/wpa_supplicant/config_none.c
@@ -1,56 +1,56 @@
/*
* WPA Supplicant / Configuration backend: empty starting point
* Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
- * This file implements dummy example of a configuration backend. None of the
+ * This file implements stub example of a configuration backend. None of the
* functions are actually implemented so this can be used as a simple
* compilation test or a starting point for a new configuration backend.
*/
#include "includes.h"
#include "common.h"
#include "config.h"
#include "base64.h"
struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
{
struct wpa_config *config;
if (name == NULL)
return NULL;
if (cfgp)
config = cfgp;
else
config = wpa_config_alloc_empty(NULL, NULL);
if (config == NULL)
return NULL;
/* TODO: fill in configuration data */
return config;
}
int wpa_config_write(const char *name, struct wpa_config *config)
{
struct wpa_ssid *ssid;
struct wpa_config_blob *blob;
wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
/* TODO: write global config parameters */
for (ssid = config->ssid; ssid; ssid = ssid->next) {
/* TODO: write networks */
}
for (blob = config->blobs; blob; blob = blob->next) {
/* TODO: write blobs */
}
return 0;
}
diff --git a/contrib/wpa/wpa_supplicant/config_ssid.h b/contrib/wpa/wpa_supplicant/config_ssid.h
index 3f7b31480765..339eead1c333 100644
--- a/contrib/wpa/wpa_supplicant/config_ssid.h
+++ b/contrib/wpa/wpa_supplicant/config_ssid.h
@@ -1,1161 +1,1177 @@
/*
* WPA Supplicant / Network configuration structures
* Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef CONFIG_SSID_H
#define CONFIG_SSID_H
#include "common/defs.h"
#include "utils/list.h"
#include "eap_peer/eap_config.h"
#define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
#define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \
EAPOL_FLAG_REQUIRE_KEY_BROADCAST)
#define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN)
#define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X)
#ifdef CONFIG_NO_TKIP
#define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP)
#define DEFAULT_GROUP (WPA_CIPHER_CCMP)
#else /* CONFIG_NO_TKIP */
#define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
#endif /* CONFIG_NO_TKIP */
#define DEFAULT_FRAGMENT_SIZE 1398
#define DEFAULT_BG_SCAN_PERIOD -1
#define DEFAULT_MESH_MAX_RETRIES 2
#define DEFAULT_MESH_RETRY_TIMEOUT 40
#define DEFAULT_MESH_CONFIRM_TIMEOUT 40
#define DEFAULT_MESH_HOLDING_TIMEOUT 40
#define DEFAULT_MESH_RSSI_THRESHOLD 1 /* no change */
#define DEFAULT_DISABLE_HT 0
#define DEFAULT_DISABLE_HT40 0
#define DEFAULT_DISABLE_SGI 0
#define DEFAULT_DISABLE_LDPC 0
#define DEFAULT_TX_STBC -1 /* no change */
#define DEFAULT_RX_STBC -1 /* no change */
#define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */
#define DEFAULT_AMPDU_FACTOR -1 /* no change */
#define DEFAULT_AMPDU_DENSITY -1 /* no change */
#define DEFAULT_USER_SELECTED_SIM 1
#define DEFAULT_MAX_OPER_CHWIDTH -1
+/* Consider global sae_pwe for SAE mechanism for PWE derivation */
+#define DEFAULT_SAE_PWE 4
+
struct psk_list_entry {
struct dl_list list;
u8 addr[ETH_ALEN];
u8 psk[32];
u8 p2p;
};
enum wpas_mode {
WPAS_MODE_INFRA = 0,
WPAS_MODE_IBSS = 1,
WPAS_MODE_AP = 2,
WPAS_MODE_P2P_GO = 3,
WPAS_MODE_P2P_GROUP_FORMATION = 4,
WPAS_MODE_MESH = 5,
};
enum sae_pk_mode {
SAE_PK_MODE_AUTOMATIC = 0,
SAE_PK_MODE_ONLY = 1,
SAE_PK_MODE_DISABLED = 2,
};
/**
* struct wpa_ssid - Network configuration data
*
* This structure includes all the configuration variables for a network. This
* data is included in the per-interface configuration data as an element of
* the network list, struct wpa_config::ssid. Each network block in the
* configuration is mapped to a struct wpa_ssid instance.
*/
struct wpa_ssid {
/**
* next - Next network in global list
*
* This pointer can be used to iterate over all networks. The head of
* this list is stored in the ssid field of struct wpa_config.
*/
struct wpa_ssid *next;
/**
* pnext - Next network in per-priority list
*
* This pointer can be used to iterate over all networks in the same
* priority class. The heads of these list are stored in the pssid
* fields of struct wpa_config.
*/
struct wpa_ssid *pnext;
/**
* id - Unique id for the network
*
* This identifier is used as a unique identifier for each network
* block when using the control interface. Each network is allocated an
* id when it is being created, either when reading the configuration
* file or when a new network is added through the control interface.
*/
int id;
/**
* priority - Priority group
*
* By default, all networks will get same priority group (0). If some
* of the networks are more desirable, this field can be used to change
* the order in which wpa_supplicant goes through the networks when
* selecting a BSS. The priority groups will be iterated in decreasing
* priority (i.e., the larger the priority value, the sooner the
* network is matched against the scan results). Within each priority
* group, networks will be selected based on security policy, signal
* strength, etc.
*
* Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are
* not using this priority to select the order for scanning. Instead,
* they try the networks in the order that used in the configuration
* file.
*/
int priority;
/**
* ssid - Service set identifier (network name)
*
* This is the SSID for the network. For wireless interfaces, this is
* used to select which network will be used. If set to %NULL (or
* ssid_len=0), any SSID can be used. For wired interfaces, this must
* be set to %NULL. Note: SSID may contain any characters, even nul
* (ASCII 0) and as such, this should not be assumed to be a nul
* terminated string. ssid_len defines how many characters are valid
* and the ssid field is not guaranteed to be nul terminated.
*/
u8 *ssid;
/**
* ssid_len - Length of the SSID
*/
size_t ssid_len;
/**
* bssid - BSSID
*
* If set, this network block is used only when associating with the AP
* using the configured BSSID
*
* If this is a persistent P2P group (disabled == 2), this is the GO
* Device Address.
*/
u8 bssid[ETH_ALEN];
/**
* bssid_ignore - List of inacceptable BSSIDs
*/
u8 *bssid_ignore;
size_t num_bssid_ignore;
/**
* bssid_accept - List of acceptable BSSIDs
*/
u8 *bssid_accept;
size_t num_bssid_accept;
/**
* bssid_set - Whether BSSID is configured for this network
*/
int bssid_set;
/**
* bssid_hint - BSSID hint
*
* If set, this is configured to the driver as a preferred initial BSSID
* while connecting to this network.
*/
u8 bssid_hint[ETH_ALEN];
/**
* bssid_hint_set - Whether BSSID hint is configured for this network
*/
int bssid_hint_set;
/**
* go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set
*/
u8 go_p2p_dev_addr[ETH_ALEN];
/**
* psk - WPA pre-shared key (256 bits)
*/
u8 psk[32];
/**
* psk_set - Whether PSK field is configured
*/
int psk_set;
/**
* passphrase - WPA ASCII passphrase
*
* If this is set, psk will be generated using the SSID and passphrase
* configured for the network. ASCII passphrase must be between 8 and
* 63 characters (inclusive).
*/
char *passphrase;
/**
* sae_password - SAE password
*
* This parameter can be used to set a password for SAE. By default, the
* passphrase value is used if this separate parameter is not used, but
* passphrase follows the WPA-PSK constraints (8..63 characters) even
* though SAE passwords do not have such constraints.
*/
char *sae_password;
/**
* sae_password_id - SAE password identifier
*
* This parameter can be used to identify a specific SAE password. If
* not included, the default SAE password is used instead.
*/
char *sae_password_id;
struct sae_pt *pt;
/**
* ext_psk - PSK/passphrase name in external storage
*
* If this is set, PSK/passphrase will be fetched from external storage
* when requesting association with the network.
*/
char *ext_psk;
/**
* mem_only_psk - Whether to keep PSK/passphrase only in memory
*
* 0 = allow psk/passphrase to be stored to the configuration file
* 1 = do not store psk/passphrase to the configuration file
*/
int mem_only_psk;
/**
* pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_*
*/
int pairwise_cipher;
/**
* group_cipher - Bitfield of allowed group ciphers, WPA_CIPHER_*
*/
int group_cipher;
/**
* group_mgmt_cipher - Bitfield of allowed group management ciphers
*
* This is a bitfield of WPA_CIPHER_AES_128_CMAC and WPA_CIPHER_BIP_*
* values. If 0, no constraint is used for the cipher, i.e., whatever
* the AP uses is accepted.
*/
int group_mgmt_cipher;
/**
* key_mgmt - Bitfield of allowed key management protocols
*
* WPA_KEY_MGMT_*
*/
int key_mgmt;
/**
* bg_scan_period - Background scan period in seconds, 0 to disable, or
* -1 to indicate no change to default driver configuration
*/
int bg_scan_period;
/**
* proto - Bitfield of allowed protocols, WPA_PROTO_*
*/
int proto;
/**
* auth_alg - Bitfield of allowed authentication algorithms
*
* WPA_AUTH_ALG_*
*/
int auth_alg;
/**
* scan_ssid - Scan this SSID with Probe Requests
*
* scan_ssid can be used to scan for APs using hidden SSIDs.
* Note: Many drivers do not support this. ap_mode=2 can be used with
* such drivers to use hidden SSIDs. Note2: Most nl80211-based drivers
* do support scan_ssid=1 and that should be used with them instead of
* ap_scan=2.
*/
int scan_ssid;
#ifdef IEEE8021X_EAPOL
#define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0)
#define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1)
/**
* eapol_flags - Bit field of IEEE 802.1X/EAPOL options (EAPOL_FLAG_*)
*/
int eapol_flags;
/**
* eap - EAP peer configuration for this network
*/
struct eap_peer_config eap;
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_WEP
#define NUM_WEP_KEYS 4
#define MAX_WEP_KEY_LEN 16
/**
* wep_key - WEP keys
*/
u8 wep_key[NUM_WEP_KEYS][MAX_WEP_KEY_LEN];
/**
* wep_key_len - WEP key lengths
*/
size_t wep_key_len[NUM_WEP_KEYS];
/**
* wep_tx_keyidx - Default key index for TX frames using WEP
*/
int wep_tx_keyidx;
#endif /* CONFIG_WEP */
/**
* proactive_key_caching - Enable proactive key caching
*
* This field can be used to enable proactive key caching which is also
* known as opportunistic PMKSA caching for WPA2. This is disabled (0)
* by default unless default value is changed with the global okc=1
* parameter. Enable by setting this to 1.
*
* Proactive key caching is used to make supplicant assume that the APs
* are using the same PMK and generate PMKSA cache entries without
* doing RSN pre-authentication. This requires support from the AP side
* and is normally used with wireless switches that co-locate the
* authenticator.
*
* Internally, special value -1 is used to indicate that the parameter
* was not specified in the configuration (i.e., default behavior is
* followed).
*/
int proactive_key_caching;
/**
* mixed_cell - Whether mixed cells are allowed
*
* This option can be used to configure whether so called mixed cells,
* i.e., networks that use both plaintext and encryption in the same
* SSID, are allowed. This is disabled (0) by default. Enable by
* setting this to 1.
*/
int mixed_cell;
#ifdef IEEE8021X_EAPOL
/**
* leap - Number of EAP methods using LEAP
*
* This field should be set to 1 if LEAP is enabled. This is used to
* select IEEE 802.11 authentication algorithm.
*/
int leap;
/**
* non_leap - Number of EAP methods not using LEAP
*
* This field should be set to >0 if any EAP method other than LEAP is
* enabled. This is used to select IEEE 802.11 authentication
* algorithm.
*/
int non_leap;
/**
* eap_workaround - EAP workarounds enabled
*
* wpa_supplicant supports number of "EAP workarounds" to work around
* interoperability issues with incorrectly behaving authentication
* servers. This is recommended to be enabled by default because some
* of the issues are present in large number of authentication servers.
*
* Strict EAP conformance mode can be configured by disabling
* workarounds with eap_workaround = 0.
*/
unsigned int eap_workaround;
#endif /* IEEE8021X_EAPOL */
/**
* mode - IEEE 802.11 operation mode (Infrastucture/IBSS)
*
* 0 = infrastructure (Managed) mode, i.e., associate with an AP.
*
* 1 = IBSS (ad-hoc, peer-to-peer)
*
* 2 = AP (access point)
*
* 3 = P2P Group Owner (can be set in the configuration file)
*
* 4 = P2P Group Formation (used internally; not in configuration
* files)
*
* 5 = Mesh
*
* Note: IBSS can only be used with key_mgmt NONE (plaintext and static
* WEP) and WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE
* (fixed group key TKIP/CCMP) is available for backwards compatibility,
* but its use is deprecated. WPA-None requires following network block
* options: proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or
* CCMP, but not both), and psk must also be set (either directly or
* using ASCII passphrase).
*/
enum wpas_mode mode;
/**
* pbss - Whether to use PBSS. Relevant to DMG networks only.
* 0 = do not use PBSS
* 1 = use PBSS
* 2 = don't care (not allowed in AP mode)
* Used together with mode configuration. When mode is AP, it
* means to start a PCP instead of a regular AP. When mode is INFRA it
* means connect to a PCP instead of AP. In this mode you can also
* specify 2 (don't care) meaning connect to either AP or PCP.
* P2P_GO and P2P_GROUP_FORMATION modes must use PBSS in DMG network.
*/
int pbss;
/**
* disabled - Whether this network is currently disabled
*
* 0 = this network can be used (default).
* 1 = this network block is disabled (can be enabled through
* ctrl_iface, e.g., with wpa_cli or wpa_gui).
* 2 = this network block includes parameters for a persistent P2P
* group (can be used with P2P ctrl_iface commands)
*/
int disabled;
/**
* disabled_for_connect - Whether this network was temporarily disabled
*
* This flag is used to reenable all the temporarily disabled networks
* after either the success or failure of a WPS connection.
*/
int disabled_for_connect;
/**
* id_str - Network identifier string for external scripts
*
* This value is passed to external ctrl_iface monitors in
* WPA_EVENT_CONNECTED event and wpa_cli sets this as WPA_ID_STR
* environment variable for action scripts.
*/
char *id_str;
/**
* ieee80211w - Whether management frame protection is enabled
*
* This value is used to configure policy for management frame
* protection (IEEE 802.11w). 0 = disabled, 1 = optional, 2 = required.
* This is disabled by default unless the default value has been changed
* with the global pmf=1/2 parameter.
*
* Internally, special value 3 is used to indicate that the parameter
* was not specified in the configuration (i.e., default behavior is
* followed).
*/
enum mfp_options ieee80211w;
#ifdef CONFIG_OCV
/**
* ocv - Enable/disable operating channel validation
*
* If this parameter is set to 1, stations will exchange OCI element
* to cryptographically verify the operating channel. Setting this
* parameter to 0 disables this option. Default value: 0.
*/
int ocv;
#endif /* CONFIG_OCV */
/**
* frequency - Channel frequency in megahertz (MHz) for IBSS
*
* This value is used to configure the initial channel for IBSS (adhoc)
* networks, e.g., 2412 = IEEE 802.11b/g channel 1. It is ignored in
* the infrastructure mode. In addition, this value is only used by the
* station that creates the IBSS. If an IBSS network with the
* configured SSID is already present, the frequency of the network
* will be used instead of this configured value.
*/
int frequency;
/**
* enable_edmg - Enable EDMG feature in STA/AP mode
*
* This flag is used for enabling the EDMG capability in STA/AP mode.
*/
int enable_edmg;
/**
* edmg_channel - EDMG channel number
*
* This value is used to configure the EDMG channel bonding feature.
* In AP mode it defines the EDMG channel to start the AP on.
* in STA mode it defines the EDMG channel to use for connection
* (if supported by AP).
*/
u8 edmg_channel;
/**
* fixed_freq - Use fixed frequency for IBSS
*/
int fixed_freq;
#ifdef CONFIG_ACS
/**
* ACS - Automatic Channel Selection for AP mode
*
* If present, it will be handled together with frequency.
* frequency will be used to determine hardware mode only, when it is
* used for both hardware mode and channel when used alone. This will
* force the channel to be set to 0, thus enabling ACS.
*/
int acs;
#endif /* CONFIG_ACS */
/**
* mesh_basic_rates - BSS Basic rate set for mesh network
*
*/
int *mesh_basic_rates;
/**
* Mesh network plink parameters
*/
int dot11MeshMaxRetries;
int dot11MeshRetryTimeout; /* msec */
int dot11MeshConfirmTimeout; /* msec */
int dot11MeshHoldingTimeout; /* msec */
int ht;
int ht40;
int vht;
int he;
int max_oper_chwidth;
unsigned int vht_center_freq1;
unsigned int vht_center_freq2;
/**
* wpa_ptk_rekey - Maximum lifetime for PTK in seconds
*
* This value can be used to enforce rekeying of PTK to mitigate some
* attacks against TKIP deficiencies.
*/
int wpa_ptk_rekey;
/** wpa_deny_ptk0_rekey - Control PTK0 rekeying
*
* Rekeying a pairwise key using only keyid 0 (PTK0 rekey) has many
* broken implementations and should be avoided when using or
* interacting with one.
*
* 0 = always rekey when configured/instructed
* 1 = only rekey when the local driver is explicitly indicating it can
* perform this operation without issues
* 2 = never allow PTK0 rekeys
*/
enum ptk0_rekey_handling wpa_deny_ptk0_rekey;
/**
* group_rekey - Group rekeying time in seconds
*
* This value, if non-zero, is used as the dot11RSNAConfigGroupRekeyTime
* parameter when operating in Authenticator role in IBSS.
*/
int group_rekey;
/**
* scan_freq - Array of frequencies to scan or %NULL for all
*
* This is an optional zero-terminated array of frequencies in
* megahertz (MHz) to include in scan requests when searching for this
* network. This can be used to speed up scanning when the network is
* known to not use all possible channels.
*/
int *scan_freq;
/**
* bgscan - Background scan and roaming parameters or %NULL if none
*
* This is an optional set of parameters for background scanning and
* roaming within a network (ESS) in following format:
* <bgscan module name>:<module parameters>
*/
char *bgscan;
/**
* ignore_broadcast_ssid - Hide SSID in AP mode
*
* Send empty SSID in beacons and ignore probe request frames that do
* not specify full SSID, i.e., require stations to know SSID.
* default: disabled (0)
* 1 = send empty (length=0) SSID in beacon and ignore probe request
* for broadcast SSID
* 2 = clear SSID (ASCII 0), but keep the original length (this may be
* required with some clients that do not support empty SSID) and
* ignore probe requests for broadcast SSID
*/
int ignore_broadcast_ssid;
/**
* freq_list - Array of allowed frequencies or %NULL for all
*
* This is an optional zero-terminated array of frequencies in
* megahertz (MHz) to allow for selecting the BSS. If set, scan results
* that do not match any of the specified frequencies are not
* considered when selecting a BSS.
*/
int *freq_list;
/**
* p2p_client_list - List of P2P Clients in a persistent group (GO)
*
* This is a list of P2P Clients (P2P Device Address) that have joined
* the persistent group. This is maintained on the GO for persistent
* group entries (disabled == 2).
*/
u8 *p2p_client_list;
/**
* num_p2p_clients - Number of entries in p2p_client_list
*/
size_t num_p2p_clients;
#ifndef P2P_MAX_STORED_CLIENTS
#define P2P_MAX_STORED_CLIENTS 100
#endif /* P2P_MAX_STORED_CLIENTS */
/**
* psk_list - Per-client PSKs (struct psk_list_entry)
*/
struct dl_list psk_list;
/**
* p2p_group - Network generated as a P2P group (used internally)
*/
int p2p_group;
/**
* p2p_persistent_group - Whether this is a persistent group
*/
int p2p_persistent_group;
/**
* temporary - Whether this network is temporary and not to be saved
*/
int temporary;
/**
* export_keys - Whether keys may be exported
*
* This attribute will be set when keys are determined through
* WPS or similar so that they may be exported.
*/
int export_keys;
#ifdef CONFIG_HT_OVERRIDES
/**
* disable_ht - Disable HT (IEEE 802.11n) for this network
*
* By default, use it if it is available, but this can be configured
* to 1 to have it disabled.
*/
int disable_ht;
/**
* disable_ht40 - Disable HT40 for this network
*
* By default, use it if it is available, but this can be configured
* to 1 to have it disabled.
*/
int disable_ht40;
/**
* disable_sgi - Disable SGI (Short Guard Interval) for this network
*
* By default, use it if it is available, but this can be configured
* to 1 to have it disabled.
*/
int disable_sgi;
/**
* disable_ldpc - Disable LDPC for this network
*
* By default, use it if it is available, but this can be configured
* to 1 to have it disabled.
*/
int disable_ldpc;
/**
* ht40_intolerant - Indicate 40 MHz intolerant for this network
*/
int ht40_intolerant;
/**
* disable_max_amsdu - Disable MAX A-MSDU
*
* A-MDSU will be 3839 bytes when disabled, or 7935
* when enabled (assuming it is otherwise supported)
* -1 (default) means do not apply any settings to the kernel.
*/
int disable_max_amsdu;
/**
* ampdu_factor - Maximum A-MPDU Length Exponent
*
* Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
*/
int ampdu_factor;
/**
* ampdu_density - Minimum A-MPDU Start Spacing
*
* Value: 0-7, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
*/
int ampdu_density;
/**
* ht_mcs - Allowed HT-MCS rates, in ASCII hex: ffff0000...
*
* By default (empty string): Use whatever the OS has configured.
*/
char *ht_mcs;
/**
* tx_stbc - Indicate STBC support for TX streams
*
* Value: -1..1, by default (-1): use whatever the OS or card has
* configured. See IEEE Std 802.11-2016, 9.4.2.56.2.
*/
int tx_stbc;
/**
* rx_stbc - Indicate STBC support for RX streams
*
* Value: -1..3, by default (-1): use whatever the OS or card has
* configured. See IEEE Std 802.11-2016, 9.4.2.56.2.
*/
int rx_stbc;
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
/**
* disable_vht - Disable VHT (IEEE 802.11ac) for this network
*
* By default, use it if it is available, but this can be configured
* to 1 to have it disabled.
*/
int disable_vht;
/**
* vht_capa - VHT capabilities to use
*/
unsigned int vht_capa;
/**
* vht_capa_mask - mask for VHT capabilities
*/
unsigned int vht_capa_mask;
int vht_rx_mcs_nss_1, vht_rx_mcs_nss_2,
vht_rx_mcs_nss_3, vht_rx_mcs_nss_4,
vht_rx_mcs_nss_5, vht_rx_mcs_nss_6,
vht_rx_mcs_nss_7, vht_rx_mcs_nss_8;
int vht_tx_mcs_nss_1, vht_tx_mcs_nss_2,
vht_tx_mcs_nss_3, vht_tx_mcs_nss_4,
vht_tx_mcs_nss_5, vht_tx_mcs_nss_6,
vht_tx_mcs_nss_7, vht_tx_mcs_nss_8;
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_HE_OVERRIDES
/**
* disable_he - Disable HE (IEEE 802.11ax) for this network
*
* By default, use it if it is available, but this can be configured
* to 1 to have it disabled.
*/
int disable_he;
#endif /* CONFIG_HE_OVERRIDES */
/**
* ap_max_inactivity - Timeout in seconds to detect STA's inactivity
*
* This timeout value is used in AP mode to clean up inactive stations.
* By default: 300 seconds.
*/
int ap_max_inactivity;
/**
* dtim_period - DTIM period in Beacon intervals
* By default: 2
*/
int dtim_period;
/**
* beacon_int - Beacon interval (default: 100 TU)
*/
int beacon_int;
/**
* auth_failures - Number of consecutive authentication failures
*/
unsigned int auth_failures;
/**
* disabled_until - Network block disabled until this time if non-zero
*/
struct os_reltime disabled_until;
/**
* parent_cred - Pointer to parent wpa_cred entry
*
* This pointer can be used to delete temporary networks when a wpa_cred
* that was used to create them is removed. This pointer should not be
* dereferences since it may not be updated in all cases.
*/
void *parent_cred;
#ifdef CONFIG_MACSEC
/**
* macsec_policy - Determines the policy for MACsec secure session
*
* 0: MACsec not in use (default)
* 1: MACsec enabled - Should secure, accept key server's advice to
* determine whether to use a secure session or not.
*/
int macsec_policy;
/**
* macsec_integ_only - Determines how MACsec are transmitted
*
* This setting applies only when MACsec is in use, i.e.,
* - macsec_policy is enabled
* - the key server has decided to enable MACsec
*
* 0: Encrypt traffic (default)
* 1: Integrity only
*/
int macsec_integ_only;
/**
* macsec_replay_protect - Enable MACsec replay protection
*
* This setting applies only when MACsec is in use, i.e.,
* - macsec_policy is enabled
* - the key server has decided to enable MACsec
*
* 0: Replay protection disabled (default)
* 1: Replay protection enabled
*/
int macsec_replay_protect;
/**
* macsec_replay_window - MACsec replay protection window
*
* A window in which replay is tolerated, to allow receipt of frames
* that have been misordered by the network.
*
* This setting applies only when MACsec replay protection active, i.e.,
* - macsec_replay_protect is enabled
* - the key server has decided to enable MACsec
*
* 0: No replay window, strict check (default)
* 1..2^32-1: number of packets that could be misordered
*/
u32 macsec_replay_window;
/**
* macsec_port - MACsec port (in SCI)
*
* Port component of the SCI.
*
* Range: 1-65534 (default: 1)
*/
int macsec_port;
/**
* mka_priority - Priority of MKA Actor
*
* Range: 0-255 (default: 255)
*/
int mka_priority;
/**
* mka_ckn - MKA pre-shared CKN
*/
#define MACSEC_CKN_MAX_LEN 32
size_t mka_ckn_len;
u8 mka_ckn[MACSEC_CKN_MAX_LEN];
/**
* mka_cak - MKA pre-shared CAK
*/
#define MACSEC_CAK_MAX_LEN 32
size_t mka_cak_len;
u8 mka_cak[MACSEC_CAK_MAX_LEN];
#define MKA_PSK_SET_CKN BIT(0)
#define MKA_PSK_SET_CAK BIT(1)
#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK)
/**
* mka_psk_set - Whether mka_ckn and mka_cak are set
*/
u8 mka_psk_set;
#endif /* CONFIG_MACSEC */
#ifdef CONFIG_HS20
int update_identifier;
/**
* roaming_consortium_selection - Roaming Consortium Selection
*
* The matching Roaming Consortium OI that was used to generate this
* network profile.
*/
u8 *roaming_consortium_selection;
/**
* roaming_consortium_selection_len - roaming_consortium_selection len
*/
size_t roaming_consortium_selection_len;
#endif /* CONFIG_HS20 */
unsigned int wps_run;
/**
* mac_addr - MAC address policy
*
* 0 = use permanent MAC address
* 1 = use random MAC address for each ESS connection
* 2 = like 1, but maintain OUI (with local admin bit set)
*
* Internally, special value -1 is used to indicate that the parameter
* was not specified in the configuration (i.e., default behavior is
* followed).
*/
int mac_addr;
/**
* no_auto_peer - Do not automatically peer with compatible mesh peers
*
* When unset, the reception of a beacon from a another mesh peer in
* this MBSS will trigger a peering attempt.
*/
int no_auto_peer;
/**
* mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm)
*
* -255..-1 = threshold value in dBm
* 0 = not using RSSI threshold
* 1 = do not change driver default
*/
int mesh_rssi_threshold;
/**
* wps_disabled - WPS disabled in AP mode
*
* 0 = WPS enabled and configured (default)
* 1 = WPS disabled
*/
int wps_disabled;
/**
* fils_dh_group - FILS DH Group
*
* 0 = PFS disabled with FILS shared key authentication
* 1-65535 DH Group to use for FILS PFS
*/
int fils_dh_group;
/**
* dpp_connector - DPP Connector (signedConnector as string)
*/
char *dpp_connector;
/**
* dpp_netaccesskey - DPP netAccessKey (own private key)
*/
u8 *dpp_netaccesskey;
/**
* dpp_netaccesskey_len - DPP netAccessKey length in octets
*/
size_t dpp_netaccesskey_len;
/**
* net_access_key_expiry - DPP netAccessKey expiry in UNIX time stamp
*
* 0 indicates no expiration.
*/
unsigned int dpp_netaccesskey_expiry;
/**
* dpp_csign - C-sign-key (Configurator public key)
*/
u8 *dpp_csign;
/**
* dpp_csign_len - C-sign-key length in octets
*/
size_t dpp_csign_len;
/**
* dpp_pp_key - ppKey (Configurator privacy protection public key)
*/
u8 *dpp_pp_key;
/**
* dpp_pp_key_len - ppKey length in octets
*/
size_t dpp_pp_key_len;
/**
* dpp_pfs - DPP PFS
* 0: allow PFS to be used or not used
* 1: require PFS to be used (note: not compatible with DPP R1)
* 2: do not allow PFS to be used
*/
int dpp_pfs;
/**
* dpp_pfs_fallback - DPP PFS fallback selection
*
* This is an internally used variable (i.e., not used in external
* configuration) to track state of the DPP PFS fallback mechanism.
*/
int dpp_pfs_fallback;
/**
* owe_group - OWE DH Group
*
* 0 = use default (19) first and then try all supported groups one by
* one if AP rejects the selected group
* 1-65535 DH Group to use for OWE
*
* Groups 19 (NIST P-256), 20 (NIST P-384), and 21 (NIST P-521) are
* currently supported.
*/
int owe_group;
/**
* owe_only - OWE-only mode (disable transition mode)
*
* 0 = enable transition mode (allow connection to either OWE or open
* BSS)
* 1 = disable transition mode (allow connection only with OWE)
*/
int owe_only;
/**
* owe_ptk_workaround - OWE PTK derivation workaround
*
* Initial OWE implementation used SHA256 when deriving the PTK for all
* OWE groups. This was supposed to change to SHA384 for group 20 and
* SHA512 for group 21. This parameter can be used to enable older
* behavior mainly for testing purposes. There is no impact to group 19
* behavior, but if enabled, this will make group 20 and 21 cases use
* SHA256-based PTK derivation which will not work with the updated
* OWE implementation on the AP side.
*/
int owe_ptk_workaround;
/**
* owe_transition_bss_select_count - OWE transition BSS select count
*
* This is an internally used variable (i.e., not used in external
* configuration) to track the number of selection attempts done for
* OWE BSS in transition mode. This allows fallback to an open BSS if
* the selection attempts for OWE BSS exceed the configured threshold.
*/
int owe_transition_bss_select_count;
/**
* multi_ap_backhaul_sta - Multi-AP backhaul STA
* 0 = normal (non-Multi-AP) station
* 1 = Multi-AP backhaul station
*/
int multi_ap_backhaul_sta;
/**
* ft_eap_pmksa_caching - Whether FT-EAP PMKSA caching is allowed
* 0 = do not try to use PMKSA caching with FT-EAP
* 1 = try to use PMKSA caching with FT-EAP
*
* This controls whether to try to use PMKSA caching with FT-EAP for the
* FT initial mobility domain association.
*/
int ft_eap_pmksa_caching;
/**
* beacon_prot - Whether Beacon protection is enabled
*
* This depends on management frame protection (ieee80211w) being
* enabled.
*/
int beacon_prot;
/**
* transition_disable - Transition Disable indication
* The AP can notify authenticated stations to disable transition mode
* in their network profiles when the network has completed transition
* steps, i.e., once sufficiently large number of APs in the ESS have
* been updated to support the more secure alternative. When this
* indication is used, the stations are expected to automatically
* disable transition mode and less secure security options. This
* includes use of WEP, TKIP (including use of TKIP as the group
* cipher), and connections without PMF.
* Bitmap bits:
* bit 0 (0x01): WPA3-Personal (i.e., disable WPA2-Personal = WPA-PSK
* and only allow SAE to be used)
* bit 1 (0x02): SAE-PK (disable SAE without use of SAE-PK)
* bit 2 (0x04): WPA3-Enterprise (move to requiring PMF)
* bit 3 (0x08): Enhanced Open (disable use of open network; require
* OWE)
*/
u8 transition_disable;
/**
* sae_pk - SAE-PK mode
* 0 = automatic SAE/SAE-PK selection based on password; enable
* transition mode (allow SAE authentication without SAE-PK)
* 1 = SAE-PK only (disable transition mode; allow SAE authentication
* only with SAE-PK)
* 2 = disable SAE-PK (allow SAE authentication only without SAE-PK)
*/
enum sae_pk_mode sae_pk;
/**
* was_recently_reconfigured - Whether this SSID config has been changed
* recently
*
* This is an internally used variable, i.e., not used in external
* configuration.
*/
bool was_recently_reconfigured;
+
+ /**
+ * sae_pwe - SAE mechanism for PWE derivation
+ *
+ * Internally, special value 4 (DEFAULT_SAE_PWE) is used to indicate
+ * that the parameter is not set and the global sae_pwe value needs to
+ * be considered.
+ *
+ * 0 = hunting-and-pecking loop only
+ * 1 = hash-to-element only
+ * 2 = both hunting-and-pecking loop and hash-to-element enabled
+ */
+ int sae_pwe;
};
#endif /* CONFIG_SSID_H */
diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.c b/contrib/wpa/wpa_supplicant/ctrl_iface.c
index 8a6a829b6665..9dc17f5eef85 100644
--- a/contrib/wpa/wpa_supplicant/ctrl_iface.c
+++ b/contrib/wpa/wpa_supplicant/ctrl_iface.c
@@ -1,12535 +1,13146 @@
/*
* WPA Supplicant / Control interface (shared code for all backends)
* Copyright (c) 2004-2020, 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/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"
#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 (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) {
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_dummy_cred") == 0) {
- wps_testing_dummy_cred = atoi(value);
- wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
- wps_testing_dummy_cred);
+ } 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);
} 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, "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);
+ } 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);
#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_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) {
wpas_mbo_update_cell_capa(wpa_s, atoi(value));
} else if (os_strcasecmp(cmd, "oce") == 0) {
wpa_s->conf->oce = atoi(value);
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);
#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 */
+ } else if (os_strcasecmp(cmd, "enable_dscp_policy_capa") == 0) {
+ wpa_s->enable_dscp_policy_capa = !!atoi(value);
} 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 */
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);
}
#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;
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);
}
#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 */
char *pos;
int multi_ap = 0;
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 */
} 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;
}
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 */
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)) {
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)) {
ret = os_snprintf(pos, end - pos,
"wifi_generation=%u\n",
wpa_s->connection_he ? 6 :
(wpa_s->connection_vht ? 5 : 4));
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
#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;
#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;
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED
"- connection to " MACSTR
" completed %s [id=%d id_str=%s]",
MAC2STR(wpa_s->bssid), "(auth)",
ssid ? ssid->id : -1,
ssid && ssid->id_str ? ssid->id_str : "");
}
}
#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;
}
#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;
}
#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;
}
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) {
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_strcmp(name, "bssid_hint") != 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 ||
os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0))
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 wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s,
struct wpa_cred *cred)
{
struct wpa_ssid *ssid;
char str[20];
int id;
if (cred == NULL) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
return -1;
}
id = cred->id;
if (wpa_config_remove_cred(wpa_s->conf, id) < 0) {
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
return -1;
}
wpa_msg(wpa_s, MSG_INFO, CRED_REMOVED "%d", id);
/* Remove any network entry created based on the removed credential */
ssid = wpa_s->conf->ssid;
while (ssid) {
if (ssid->parent_cred == cred) {
int res;
wpa_printf(MSG_DEBUG, "Remove network id %d since it "
"used the removed credential", ssid->id);
res = os_snprintf(str, sizeof(str), "%d", ssid->id);
if (os_snprintf_error(sizeof(str), res))
str[sizeof(str) - 1] = '\0';
ssid = ssid->next;
wpa_supplicant_ctrl_iface_remove_network(wpa_s, str);
} else
ssid = ssid->next;
}
return 0;
}
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");
cred = wpa_s->conf->cred;
while (cred) {
prev = cred;
cred = cred->next;
wpas_ctrl_remove_cred(wpa_s, prev);
}
return 0;
}
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_ctrl_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_ctrl_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_ctrl_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;
}
#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;
}
#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_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_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 -1;
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 -1;
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 -1;
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 -1;
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_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 -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 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);
if (ret < 0)
return 0;
pos += ret;
}
#endif /* CONFIG_FILS */
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, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL,
0, KEY_FLAG_GROUP);
wpa_drv_set_key(wpa_s, 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, 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 sanity checks) */
+ /* 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 parse_freq(int chwidth, int freq2)
{
if (freq2 < 0)
return -1;
if (freq2)
return CHANWIDTH_80P80MHZ;
switch (chwidth) {
case 0:
case 20:
case 40:
return CHANWIDTH_USE_HT;
case 80:
return CHANWIDTH_80MHZ;
case 160:
return CHANWIDTH_160MHZ;
default:
wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d",
chwidth);
return -1;
}
}
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 = parse_freq(chwidth, freq2);
if (max_oper_chwidth < 0)
return -1;
+ if (allow_6ghz && chwidth == 40)
+ max_oper_chwidth = CHANWIDTH_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 = parse_freq(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 = CHANWIDTH_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)
{
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,
vht_center_freq2, 0, ht40, vht,
vht_chwidth, he, edmg,
NULL, 0, 0, allow_6ghz);
}
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;
#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 {
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 = parse_freq(chwidth, freq2);
if (max_oper_chwidth < 0)
return -1;
+ if (allow_6ghz && chwidth == 40)
+ max_oper_chwidth = CHANWIDTH_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);
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 (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
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 &&
os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 &&
dialog_token == wpa_s->last_gas_dialog_token)
resp = wpa_s->last_gas_resp;
else if (wpa_s->prev_gas_resp &&
os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 &&
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);
}
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=%d\n"
"NOISE=%d\nFREQUENCY=%u\n",
si.current_signal, si.current_txrate / 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.avg_signal) {
ret = os_snprintf(pos, end - pos,
"AVG_RSSI=%d\n", si.avg_signal);
if (os_snprintf_error(end - pos, ret))
return -1;
pos += ret;
}
if (si.avg_beacon_signal) {
ret = os_snprintf(pos, end - pos,
"AVG_BEACON_RSSI=%d\n", si.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,
unsigned int *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++] = atoi(pos);
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 freq_list[100], num = 100, i;
int ret;
enum wpa_driver_if_type iface_type;
char *pos, *end;
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]);
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_dummy_cred = 0;
+ 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_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_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_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;
#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);
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);
+ wpa_s->disable_scs_support = 0;
+ wpa_s->disable_mscs_support = 0;
+ wpa_s->enable_dscp_policy_capa = 0;
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;
#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;
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;
os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
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);
os_memset(&wpa_s->robust_av, 0, sizeof(struct robust_av_data));
#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);
}
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;
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 = 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;
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;
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);
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 int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd)
{
#ifdef WPA_TRACE_BFD
char *pos;
wpa_trace_fail_after = atoi(cmd);
pos = os_strchr(cmd, ':');
if (pos) {
pos++;
os_strlcpy(wpa_trace_fail_func, pos,
sizeof(wpa_trace_fail_func));
} else {
wpa_trace_fail_after = 0;
}
return 0;
#else /* WPA_TRACE_BFD */
return -1;
#endif /* WPA_TRACE_BFD */
}
static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
#ifdef WPA_TRACE_BFD
return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
wpa_trace_fail_func);
#else /* WPA_TRACE_BFD */
return -1;
#endif /* WPA_TRACE_BFD */
}
static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd)
{
#ifdef WPA_TRACE_BFD
char *pos;
wpa_trace_test_fail_after = atoi(cmd);
pos = os_strchr(cmd, ':');
if (pos) {
pos++;
os_strlcpy(wpa_trace_test_fail_func, pos,
sizeof(wpa_trace_test_fail_func));
} else {
wpa_trace_test_fail_after = 0;
}
return 0;
#else /* WPA_TRACE_BFD */
return -1;
#endif /* WPA_TRACE_BFD */
}
static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
char *buf, size_t buflen)
{
#ifdef WPA_TRACE_BFD
return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
wpa_trace_test_fail_func);
#else /* WPA_TRACE_BFD */
return -1;
#endif /* WPA_TRACE_BFD */
}
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_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s,
const char *cmd)
{
struct wpabuf *buf;
size_t len;
len = os_strlen(cmd);
if (len & 1)
return -1;
len /= 2;
if (len == 0) {
buf = NULL;
} else {
buf = wpabuf_alloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
wpabuf_free(buf);
return -1;
}
}
wpa_sm_set_test_assoc_ie(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, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
wpa_s->last_tk_key_idx, 1, zero, 6,
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, wpa_s->last_tk_alg, wpa_s->last_tk_addr,
wpa_s->last_tk_key_idx, 1, zero, 6,
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 P802.11ax/D8.0 Figure
* 9-687): 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);
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;
}
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;
}
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 */
}
#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]
*/
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;
}
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;
/*
* Entry format:
* <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds>
* <expiration in seconds> <akmp> <opportunistic>
* [FILS Cache Identifier]
*/
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;
}
os_get_reltime(&now);
entry->expiration = now.sec + expiration;
entry->reauth_time = now.sec + reauth_time;
entry->network_ctx = ssid;
entry->external = true;
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 */
#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 */
static 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;
}
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);
}
#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;
#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, 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, bssid);
}
#endif /* CONFIG_PASN */
+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>] [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;
+ unsigned int num_tclas_elem = 0;
+ bool scsid_active = false;
+
+ 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 free_scs_desc;
+ }
+
+ 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;
+ }
+
+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));
+}
+
+
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);
#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 */
#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;
#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 */
} 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;
} 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;
} 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 */
} 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 (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0)
reply_len = -1;
} else if (os_strcmp(buf, "GET_FAIL") == 0) {
reply_len = wpas_ctrl_get_fail(wpa_s, 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_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;
#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;
} else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) {
if (wpas_ctrl_iface_send_neighbor_rep(wpa_s, buf + 20))
reply_len = -1;
} 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_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;
#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 */
#endif /* CONFIG_DPP */
} else if (os_strncmp(buf, "MSCS ", 5) == 0) {
if (wpas_ctrl_iface_configure_mscs(wpa_s, buf + 5))
reply_len = -1;
#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;
#endif /* CONFIG_PASN */
+ } 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;
} 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_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);
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;
}
diff --git a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
index 144654aac684..e4a83698393a 100644
--- a/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/contrib/wpa/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -1,764 +1,764 @@
<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
<refentry>
<refentryinfo>
<date>07 August 2019</date>
</refentryinfo>
<refmeta>
<refentrytitle>wpa_supplicant</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>wpa_supplicant</refname>
<refpurpose>Wi-Fi Protected Access client and IEEE 802.1X supplicant</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>wpa_supplicant</command>
<arg>-BddfhKLqqsTtuvW</arg>
<arg>-i<replaceable>ifname</replaceable></arg>
<arg>-c<replaceable>config file</replaceable></arg>
<arg>-D<replaceable>driver</replaceable></arg>
<arg>-P<replaceable>PID_file</replaceable></arg>
<arg>-f<replaceable>output file</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Overview</title>
<para>
Wireless networks do not require physical access to the network equipment
in the same way as wired networks. This makes it easier for unauthorized
users to passively monitor a network and capture all transmitted frames.
In addition, unauthorized use of the network is much easier. In many cases,
this can happen even without user's explicit knowledge since the wireless
LAN adapter may have been configured to automatically join any available
network.
</para>
<para>
Link-layer encryption can be used to provide a layer of security for
wireless networks. The original wireless LAN standard, IEEE 802.11,
included a simple encryption mechanism, WEP. However, that proved to
be flawed in many areas and network protected with WEP cannot be consider
secure. IEEE 802.1X authentication and frequently changed dynamic WEP keys
can be used to improve the network security, but even that has inherited
security issues due to the use of WEP for encryption. Wi-Fi Protected
Access and IEEE 802.11i amendment to the wireless LAN standard introduce
- a much improvement mechanism for securing wireless networks. IEEE 802.11i
+ a much improved mechanism for securing wireless networks. IEEE 802.11i
enabled networks that are using CCMP (encryption mechanism based on strong
cryptographic algorithm AES) can finally be called secure used for
applications which require efficient protection against unauthorized
access.
</para>
<para><command>wpa_supplicant</command> is an implementation of
the WPA Supplicant component, i.e., the part that runs in the
client stations. It implements WPA key negotiation with a WPA
Authenticator and EAP authentication with Authentication
Server. In addition, it controls the roaming and IEEE 802.11
authentication/association of the wireless LAN driver.</para>
<para><command>wpa_supplicant</command> is designed to be a
"daemon" program that runs in the background and acts as the
backend component controlling the wireless
connection. <command>wpa_supplicant</command> supports separate
frontend programs and an example text-based frontend,
<command>wpa_cli</command>, is included with
wpa_supplicant.</para>
<para>Before wpa_supplicant can do its work, the network interface
must be available. That means that the physical device must be
present and enabled, and the driver for the device must be
loaded. The daemon will exit immediately if the device is not already
available.</para>
<para>After <command>wpa_supplicant</command> has configured the
network device, higher level configuration such as DHCP may
proceed. There are a variety of ways to integrate wpa_supplicant
into a machine's networking scripts, a few of which are described
in sections below.</para>
<para>The following steps are used when associating with an AP
using WPA:</para>
<itemizedlist>
<listitem>
<para><command>wpa_supplicant</command> requests the kernel
driver to scan neighboring BSSes</para>
</listitem>
<listitem>
<para><command>wpa_supplicant</command> selects a BSS based on
its configuration</para>
</listitem>
<listitem>
<para><command>wpa_supplicant</command> requests the kernel
driver to associate with the chosen BSS</para>
</listitem>
<listitem>
<para>If WPA-EAP: integrated IEEE 802.1X Supplicant
completes EAP authentication with the
authentication server (proxied by the Authenticator in the
AP)</para>
</listitem>
<listitem>
<para>If WPA-EAP: master key is received from the IEEE 802.1X
Supplicant</para>
</listitem>
<listitem>
<para>If WPA-PSK: <command>wpa_supplicant</command> uses PSK
as the master session key</para>
</listitem>
<listitem>
<para><command>wpa_supplicant</command> completes WPA 4-Way
Handshake and Group Key Handshake with the Authenticator
(AP)</para>
</listitem>
<listitem>
<para><command>wpa_supplicant</command> configures encryption
keys for unicast and broadcast</para>
</listitem>
<listitem>
<para>normal data packets can be transmitted and received</para>
</listitem>
</itemizedlist>
</refsect1>
<refsect1>
<title>Supported Features</title>
<para>Supported WPA/IEEE 802.11i features:</para>
<itemizedlist>
<listitem>
<para>WPA-PSK ("WPA-Personal")</para>
</listitem>
<listitem>
<para>WPA with EAP (e.g., with RADIUS authentication server)
("WPA-Enterprise") Following authentication methods are
supported with an integrate IEEE 802.1X Supplicant:</para>
<itemizedlist>
<listitem>
<para>EAP-TLS</para>
</listitem>
</itemizedlist>
<itemizedlist>
<listitem>
<para>EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)</para>
</listitem>
<listitem>
<para>EAP-PEAP/TLS (both PEAPv0 and PEAPv1)</para>
</listitem>
<listitem>
<para>EAP-PEAP/GTC (both PEAPv0 and PEAPv1)</para>
</listitem>
<listitem>
<para>EAP-PEAP/OTP (both PEAPv0 and PEAPv1)</para>
</listitem>
<listitem>
<para>EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)</para>
</listitem>
<listitem>
<para>EAP-TTLS/EAP-MD5-Challenge</para>
</listitem>
<listitem>
<para>EAP-TTLS/EAP-GTC</para>
</listitem>
<listitem><para>EAP-TTLS/EAP-OTP</para></listitem>
<listitem><para>EAP-TTLS/EAP-MSCHAPv2</para></listitem>
<listitem><para>EAP-TTLS/EAP-TLS</para></listitem>
<listitem><para>EAP-TTLS/MSCHAPv2</para></listitem>
<listitem><para>EAP-TTLS/MSCHAP</para></listitem>
<listitem><para>EAP-TTLS/PAP</para></listitem>
<listitem><para>EAP-TTLS/CHAP</para></listitem>
<listitem><para>EAP-SIM</para></listitem>
<listitem><para>EAP-AKA</para></listitem>
<listitem><para>EAP-PSK</para></listitem>
<listitem><para>EAP-PAX</para></listitem>
<listitem><para>LEAP (note: requires special support from
the driver for IEEE 802.11 authentication)</para></listitem>
<listitem><para>(following methods are supported, but since
they do not generate keying material, they cannot be used
with WPA or IEEE 802.1X WEP keying)</para></listitem>
<listitem><para>EAP-MD5-Challenge </para></listitem>
<listitem><para>EAP-MSCHAPv2</para></listitem>
<listitem><para>EAP-GTC</para></listitem>
<listitem><para>EAP-OTP</para></listitem>
</itemizedlist>
</listitem>
<listitem>
<para>key management for CCMP, TKIP, WEP104, WEP40</para>
</listitem>
<listitem>
<para>RSN/WPA2 (IEEE 802.11i)</para>
<itemizedlist>
<listitem>
<para>pre-authentication</para>
</listitem>
<listitem>
<para>PMKSA caching</para>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
</refsect1>
<refsect1>
<title>Available Drivers</title>
<para>A summary of available driver backends is below. Support for each
of the driver backends is chosen at wpa_supplicant compile time. For a
list of supported driver backends that may be used with the -D option on
your system, refer to the help output of wpa_supplicant
(<emphasis>wpa_supplicant -h</emphasis>).</para>
<variablelist>
<varlistentry>
<term>nl80211</term>
<listitem>
<para>Uses the modern Linux nl80211/cfg80211 netlink-based
interface (most new drivers).</para>
</listitem>
</varlistentry>
<varlistentry>
<term>wext</term>
<listitem>
<para>Uses the legacy Linux wireless extensions ioctl-based
interface (older hardware/drivers).</para>
</listitem>
</varlistentry>
<varlistentry>
<term>wired</term>
<listitem>
<para>wpa_supplicant wired Ethernet driver</para>
</listitem>
</varlistentry>
<varlistentry>
<term>roboswitch</term>
<listitem>
<para>wpa_supplicant Broadcom switch driver</para>
</listitem>
</varlistentry>
<varlistentry>
<term>bsd</term>
<listitem>
<para>BSD 802.11 support (Atheros, etc.).</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ndis</term>
<listitem>
<para>Windows NDIS driver.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Command Line Options</title>
<para>Most command line options have global scope. Some are given per
interface, and are only valid if at least one <option>-i</option> option
is specified, otherwise they're ignored. Option groups for different
interfaces must be separated by <option>-N</option> option.</para>
<variablelist>
<varlistentry>
<term>-b br_ifname</term>
<listitem>
<para>Optional bridge interface name. (Per interface)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-B</term>
<listitem>
<para>Run daemon in the background.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-c filename</term>
<listitem>
<para>Path to configuration file. (Per interface)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-C ctrl_interface</term>
<listitem>
<para>Path to ctrl_interface socket (Per interface. Only used if
<option>-c</option> is not).</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-i ifname</term>
<listitem>
<para>Interface to listen on. Multiple instances of this option can
be present, one per interface, separated by <option>-N</option>
option (see below).</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-d</term>
<listitem>
<para>Increase debugging verbosity (<option>-dd</option> even
more).</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-D driver</term>
<listitem>
<para>Driver to use (can be multiple drivers: nl80211,wext).
(Per interface, see the available options below.)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-e entropy file</term>
<listitem>
<para>File for <command>wpa_supplicant</command> to use to
maintain its internal entropy store in over restarts.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-f output file</term>
<listitem>
<para>Log output to specified file instead of stdout. (This
is only available if <command>wpa_supplicant</command> was
built with the <literal>CONFIG_DEBUG_FILE</literal>
option.)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-g global ctrl_interface</term>
<listitem>
<para>Path to global ctrl_interface socket. If specified, interface
definitions may be omitted.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-K</term>
<listitem>
<para>Include keys (passwords, etc.) in debug output.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-h</term>
<listitem>
<para>Help. Show a usage message.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-L</term>
<listitem>
<para>Show license (BSD).</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-o override driver</term>
<listitem>
<para>Override the driver parameter for new
interfaces.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-O override ctrl_interface</term>
<listitem>
<para>Override the ctrl_interface parameter for new
interfaces.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-p</term>
<listitem>
<para>Driver parameters. (Per interface)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-P PID_file</term>
<listitem>
<para>Path to PID file.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-q</term>
<listitem>
<para>Decrease debugging verbosity (<option>-qq</option> even
less).</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-s</term>
<listitem>
<para>Log output to syslog instead of stdout. (This is only
available if <command>wpa_supplicant</command> was built
with the <literal>CONFIG_DEBUG_SYSLOG</literal>
option.)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-T</term>
<listitem>
<para>Log output to Linux tracing in addition to any other
destinations. (This is only available
if <command>wpa_supplicant</command> was built with
the <literal>CONFIG_DEBUG_LINUX_TRACING</literal>
option.)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-t</term>
<listitem>
<para>Include timestamp in debug messages.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-u</term>
<listitem>
<para>Enable DBus control interface. If enabled, interface
definitions may be omitted. (This is only available
if <command>wpa_supplicant</command> was built with
the <literal>CONFIG_CTRL_IFACE_DBUS_NEW</literal> option.)</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-v</term>
<listitem>
<para>Show version.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-W</term>
<listitem>
<para>Wait for a control interface monitor before starting.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-N</term>
<listitem>
<para>Start describing new interface.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Examples</title>
<para>In most common cases, <command>wpa_supplicant</command> is
started with:</para>
<blockquote><programlisting>
wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0
</programlisting></blockquote>
<para>This makes the process fork into background.</para>
<para>The easiest way to debug problems, and to get debug log for
bug reports, is to start <command>wpa_supplicant</command> on
foreground with debugging enabled:</para>
<blockquote><programlisting>
wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d
</programlisting></blockquote>
<para>If the specific driver wrapper is not known beforehand, it is
possible to specify multiple comma separated driver wrappers on the command
line. <command>wpa_supplicant</command> will use the first driver
wrapper that is able to initialize the interface.</para>
<blockquote><programlisting>
wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0
</programlisting></blockquote>
<para><command>wpa_supplicant</command> can control multiple
interfaces (radios) either by running one process for each
interface separately or by running just one process and list of
options at command line. Each interface is separated with -N
argument. As an example, following command would start
wpa_supplicant for two interfaces:</para>
<blockquote><programlisting>
wpa_supplicant \
-c wpa1.conf -i wlan0 -D nl80211 -N \
-c wpa2.conf -i ath0 -D wext
</programlisting></blockquote>
</refsect1>
<refsect1>
<title>OS Requirements</title>
<para>Current hardware/software requirements:</para>
<itemizedlist>
<listitem>
<para>Linux kernel 2.6.30 or higher with
nl80211/cfg80211 support</para>
</listitem>
<listitem>
<para>Linux kernel 2.4.x or higher with Linux Wireless
Extensions v15 or newer</para>
</listitem>
<listitem>
<para>FreeBSD 6-CURRENT</para>
</listitem>
<listitem>
<para>Microsoft Windows with WinPcap (at least WinXP, may work
with other versions)</para>
</listitem>
</itemizedlist>
</refsect1>
<refsect1>
<title>Supported Drivers</title>
<variablelist>
<varlistentry>
<term>Linux nl80211/cfg80211</term>
<listitem>
<para>This is the preferred driver for Linux.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Linux wireless extensions</term>
<listitem>
<para>In theory, any driver that supports Linux wireless
extensions can be used with IEEE 802.1X (i.e., not WPA) when
using ap_scan=0 option in configuration file.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Wired Ethernet drivers</term>
<listitem>
<para>Use ap_scan=0.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>BSD net80211 layer (e.g., Atheros driver)</term>
<listitem>
<para>At the moment, this is for FreeBSD 6-CURRENT branch.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Windows NDIS</term>
<listitem>
<para>The current Windows port requires WinPcap
(http://winpcap.polito.it/). See README-Windows.txt for more
information.</para>
</listitem>
</varlistentry>
</variablelist>
<para>wpa_supplicant was designed to be portable for different
drivers and operating systems. Hopefully, support for more wlan
cards and OSes will be added in the future. See developer.txt for
more information about the design of wpa_supplicant and porting to
other drivers. One main goal is to add full WPA/WPA2 support to
Linux wireless extensions to allow new drivers to be supported
without having to implement new driver-specific interface code in
wpa_supplicant.</para>
</refsect1>
<refsect1>
<title>Architecture</title> <para>The
<command>wpa_supplicant</command> system consists of the following
components:</para>
<variablelist>
<varlistentry>
<term><filename>wpa_supplicant.conf</filename> </term>
<listitem>
<para>the configuration file describing all networks that the
user wants the computer to connect to. </para>
</listitem>
</varlistentry>
<varlistentry>
<term><command>wpa_supplicant</command></term>
<listitem><para>the program that directly interacts with the
network interface. </para></listitem>
</varlistentry>
<varlistentry>
<term><command>wpa_cli</command></term> <listitem><para> the
client program that provides a high-level interface to the
functionality of the daemon. </para></listitem>
</varlistentry>
<varlistentry>
<term><command>wpa_passphrase</command></term>
<listitem><para>a utility needed to construct
<filename>wpa_supplicant.conf</filename> files that include
encrypted passwords.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Quick Start</title>
<para>First, make a configuration file, e.g.
<filename>/etc/wpa_supplicant.conf</filename>, that describes the networks
you are interested in. See <citerefentry>
<refentrytitle>wpa_supplicant.conf</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry>
for details.</para>
<para>Once the configuration is ready, you can test whether the
configuration works by running <command>wpa_supplicant</command>
with following command to start it on foreground with debugging
enabled:</para>
<blockquote><programlisting>
wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
</programlisting></blockquote>
<para>Assuming everything goes fine, you can start using following
command to start <command>wpa_supplicant</command> on background
without debugging:</para>
<blockquote><programlisting>
wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
</programlisting></blockquote>
<para>Please note that if you included more than one driver
interface in the build time configuration (.config), you may need
to specify which interface to use by including -D&lt;driver
name&gt; option on the command line.</para>
<!-- XXX at this point, the page could include a little script
based on wpa_cli to wait for a connection and then run
dhclient -->
</refsect1>
<refsect1>
<title>Interface to pcmcia-cs/cardmrg</title>
<para>For example, following small changes to pcmcia-cs scripts
can be used to enable WPA support:</para>
<para>Add MODE="Managed" and WPA="y" to the network scheme in
<filename>/etc/pcmcia/wireless.opts</filename>.</para>
<para>Add the following block to the end of <emphasis>start</emphasis>
action handler in <filename>/etc/pcmcia/wireless</filename>:</para>
<blockquote><programlisting>
if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
/usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf -i$DEVICE
fi
</programlisting></blockquote>
<para>Add the following block to the end of <emphasis>stop</emphasis>
action handler (may need to be separated from other actions) in
<filename>/etc/pcmcia/wireless</filename>:</para>
<blockquote><programlisting>
if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
killall wpa_supplicant
fi
</programlisting></blockquote>
<para>This will make <command>cardmgr</command> start
<command>wpa_supplicant</command> when the card is plugged
in.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para>
<citerefentry>
<refentrytitle>wpa_background</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>
<citerefentry>
<refentrytitle>wpa_supplicant.conf</refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry>
<citerefentry>
<refentrytitle>wpa_cli</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>
<citerefentry>
<refentrytitle>wpa_passphrase</refentrytitle>
<manvolnum>8</manvolnum>
</citerefentry>
</para>
</refsect1>
<refsect1>
<title>Legal</title>
<para>wpa_supplicant is copyright (c) 2003-2019,
Jouni Malinen <email>j@w1.fi</email> and
contributors.
All Rights Reserved.</para>
<para>This program is licensed under the BSD license (the one with
advertisement clause removed).</para>
</refsect1>
</refentry>
diff --git a/contrib/wpa/wpa_supplicant/eapol_test.py b/contrib/wpa/wpa_supplicant/eapol_test.py
index 734428d29e66..88c83f343597 100755
--- a/contrib/wpa/wpa_supplicant/eapol_test.py
+++ b/contrib/wpa/wpa_supplicant/eapol_test.py
@@ -1,142 +1,159 @@
#!/usr/bin/env python2
#
# eapol_test controller
# Copyright (c) 2015, Jouni Malinen <j@w1.fi>
#
# This software may be distributed under the terms of the BSD license.
# See README for more details.
import argparse
import logging
import os
import Queue
import sys
import threading
logger = logging.getLogger()
dir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
sys.path.append(os.path.join(dir, '..', 'wpaspy'))
import wpaspy
wpas_ctrl = '/tmp/eapol_test'
class eapol_test:
def __init__(self, ifname):
self.ifname = ifname
self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
if "PONG" not in self.ctrl.request("PING"):
raise Exception("Failed to connect to eapol_test (%s)" % ifname)
self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
self.mon.attach()
def add_network(self):
id = self.request("ADD_NETWORK")
if "FAIL" in id:
raise Exception("ADD_NETWORK failed")
return int(id)
def remove_network(self, id):
id = self.request("REMOVE_NETWORK " + str(id))
if "FAIL" in id:
raise Exception("REMOVE_NETWORK failed")
return None
def set_network(self, id, field, value):
res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
if "FAIL" in res:
raise Exception("SET_NETWORK failed")
return None
def set_network_quoted(self, id, field, value):
res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
if "FAIL" in res:
raise Exception("SET_NETWORK failed")
return None
def request(self, cmd, timeout=10):
return self.ctrl.request(cmd, timeout=timeout)
def wait_event(self, events, timeout=10):
start = os.times()[4]
while True:
while self.mon.pending():
ev = self.mon.recv()
logger.debug(self.ifname + ": " + ev)
for event in events:
if event in ev:
return ev
now = os.times()[4]
remaining = start + timeout - now
if remaining <= 0:
break
if not self.mon.pending(timeout=remaining):
break
return None
-def run(ifname, count, no_fast_reauth, res):
+def run(ifname, count, no_fast_reauth, res, conf):
et = eapol_test(ifname)
et.request("AP_SCAN 0")
if no_fast_reauth:
et.request("SET fast_reauth 0")
else:
et.request("SET fast_reauth 1")
id = et.add_network()
- et.set_network(id, "key_mgmt", "IEEE8021X")
- et.set_network(id, "eapol_flags", "0")
- et.set_network(id, "eap", "TLS")
- et.set_network_quoted(id, "identity", "user")
- et.set_network_quoted(id, "ca_cert", 'ca.pem')
- et.set_network_quoted(id, "client_cert", 'client.pem')
- et.set_network_quoted(id, "private_key", 'client.key')
- et.set_network_quoted(id, "private_key_passwd", 'whatever')
+
+ if len(conf):
+ for item in conf:
+ et.set_network(id, item, conf[item])
+ else:
+ et.set_network(id, "key_mgmt", "IEEE8021X")
+ et.set_network(id, "eapol_flags", "0")
+ et.set_network(id, "eap", "TLS")
+ et.set_network_quoted(id, "identity", "user")
+ et.set_network_quoted(id, "ca_cert", 'ca.pem')
+ et.set_network_quoted(id, "client_cert", 'client.pem')
+ et.set_network_quoted(id, "private_key", 'client.key')
+ et.set_network_quoted(id, "private_key_passwd", 'whatever')
+
et.set_network(id, "disabled", "0")
fail = False
for i in range(count):
et.request("REASSOCIATE")
ev = et.wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-EAP-FAILURE"])
if ev is None or "CTRL-EVENT-CONNECTED" not in ev:
fail = True
break
et.remove_network(id)
if fail:
res.put("FAIL (%d OK)" % i)
else:
res.put("PASS %d" % (i + 1))
def main():
parser = argparse.ArgumentParser(description='eapol_test controller')
parser.add_argument('--ctrl', help='control interface directory')
parser.add_argument('--num', help='number of processes')
parser.add_argument('--iter', help='number of iterations')
parser.add_argument('--no-fast-reauth', action='store_true',
dest='no_fast_reauth',
help='disable TLS session resumption')
+ parser.add_argument('--conf', help='file of network conf items')
args = parser.parse_args()
num = int(args.num)
iter = int(args.iter)
if args.ctrl:
global wpas_ctrl
wpas_ctrl = args.ctrl
+ conf = {}
+ if args.conf:
+ f = open(args.conf, "r")
+ for line in f:
+ confitem = line.split("=")
+ if len(confitem) == 2:
+ conf[confitem[0].strip()] = confitem[1].strip()
+ f.close()
+
t = {}
res = {}
for i in range(num):
res[i] = Queue.Queue()
t[i] = threading.Thread(target=run, args=(str(i), iter,
- args.no_fast_reauth, res[i]))
+ args.no_fast_reauth, res[i],
+ conf))
for i in range(num):
t[i].start()
for i in range(num):
t[i].join()
try:
results = res[i].get(False)
except:
results = "N/A"
print("%d: %s" % (i, results))
if __name__ == "__main__":
main()
diff --git a/contrib/wpa/wpa_supplicant/events.c b/contrib/wpa/wpa_supplicant/events.c
index a565e658f33d..5f5c50ba9754 100644
--- a/contrib/wpa/wpa_supplicant/events.c
+++ b/contrib/wpa/wpa_supplicant/events.c
@@ -1,5550 +1,5783 @@
/*
* WPA Supplicant - Driver event processing
* Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "rsn_supp/wpa.h"
#include "eloop.h"
#include "config.h"
#include "l2_packet/l2_packet.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "pcsc_funcs.h"
#include "rsn_supp/preauth.h"
#include "rsn_supp/pmksa_cache.h"
#include "common/wpa_ctrl.h"
#include "eap_peer/eap.h"
#include "ap/hostapd.h"
#include "p2p/p2p.h"
#include "fst/fst.h"
#include "wnm_sta.h"
#include "notify.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/gas_server.h"
#include "common/dpp.h"
#include "common/ptksa_cache.h"
#include "crypto/random.h"
#include "bssid_ignore.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
#include "ibss_rsn.h"
#include "sme.h"
#include "gas_query.h"
#include "p2p_supplicant.h"
#include "bgscan.h"
#include "autoscan.h"
#include "ap.h"
#include "bss.h"
#include "scan.h"
#include "offchannel.h"
#include "interworking.h"
#include "mesh.h"
#include "mesh_mpm.h"
#include "wmm_ac.h"
#include "dpp_supplicant.h"
#define MAX_OWE_TRANSITION_BSS_SELECT_COUNT 5
#ifndef CONFIG_NO_SCAN_PROCESSING
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
int new_scan, int own_request);
#endif /* CONFIG_NO_SCAN_PROCESSING */
int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
struct os_reltime now;
if (ssid == NULL || ssid->disabled_until.sec == 0)
return 0;
os_get_reltime(&now);
if (ssid->disabled_until.sec > now.sec)
return ssid->disabled_until.sec - now.sec;
wpas_clear_temp_disabled(wpa_s, ssid, 0);
return 0;
}
#ifndef CONFIG_NO_SCAN_PROCESSING
/**
* wpas_reenabled_network_time - Time until first network is re-enabled
* @wpa_s: Pointer to wpa_supplicant data
* Returns: If all enabled networks are temporarily disabled, returns the time
* (in sec) until the first network is re-enabled. Otherwise returns 0.
*
* This function is used in case all enabled networks are temporarily disabled,
* in which case it returns the time (in sec) that the first network will be
* re-enabled. The function assumes that at least one network is enabled.
*/
static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid;
int disabled_for, res = 0;
#ifdef CONFIG_INTERWORKING
if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking &&
wpa_s->conf->cred)
return 0;
#endif /* CONFIG_INTERWORKING */
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (ssid->disabled)
continue;
disabled_for = wpas_temp_disabled(wpa_s, ssid);
if (!disabled_for)
return 0;
if (!res || disabled_for < res)
res = disabled_for;
}
return res;
}
#endif /* CONFIG_NO_SCAN_PROCESSING */
void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (wpa_s->disconnected || wpa_s->wpa_state != WPA_SCANNING)
return;
wpa_dbg(wpa_s, MSG_DEBUG,
"Try to associate due to network getting re-enabled");
if (wpa_supplicant_fast_associate(wpa_s) != 1) {
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
}
static struct wpa_bss * wpa_supplicant_get_new_bss(
struct wpa_supplicant *wpa_s, const u8 *bssid)
{
struct wpa_bss *bss = NULL;
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid->ssid_len > 0)
bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
if (!bss)
bss = wpa_bss_get_bssid(wpa_s, bssid);
return bss;
}
static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
if (!bss) {
wpa_supplicant_update_scan_results(wpa_s);
/* Get the BSS from the new scan results */
bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
}
if (bss)
wpa_s->current_bss = bss;
}
static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid, *old_ssid;
u8 drv_ssid[SSID_MAX_LEN];
size_t drv_ssid_len;
int res;
if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) {
wpa_supplicant_update_current_bss(wpa_s);
if (wpa_s->current_ssid->ssid_len == 0)
return 0; /* current profile still in use */
res = wpa_drv_get_ssid(wpa_s, drv_ssid);
if (res < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Failed to read SSID from driver");
return 0; /* try to use current profile */
}
drv_ssid_len = res;
if (drv_ssid_len == wpa_s->current_ssid->ssid_len &&
os_memcmp(drv_ssid, wpa_s->current_ssid->ssid,
drv_ssid_len) == 0)
return 0; /* current profile still in use */
#ifdef CONFIG_OWE
if ((wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
wpa_s->current_bss &&
(wpa_s->current_bss->flags & WPA_BSS_OWE_TRANSITION) &&
drv_ssid_len == wpa_s->current_bss->ssid_len &&
os_memcmp(drv_ssid, wpa_s->current_bss->ssid,
drv_ssid_len) == 0)
return 0; /* current profile still in use */
#endif /* CONFIG_OWE */
wpa_msg(wpa_s, MSG_DEBUG,
"Driver-initiated BSS selection changed the SSID to %s",
wpa_ssid_txt(drv_ssid, drv_ssid_len));
/* continue selecting a new network profile */
}
wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
"information");
ssid = wpa_supplicant_get_ssid(wpa_s);
if (ssid == NULL) {
wpa_msg(wpa_s, MSG_INFO,
"No network configuration found for the current AP");
return -1;
}
if (wpas_network_disabled(wpa_s, ssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is disabled");
return -1;
}
if (disallowed_bssid(wpa_s, wpa_s->bssid) ||
disallowed_ssid(wpa_s, ssid->ssid, ssid->ssid_len)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS is disallowed");
return -1;
}
res = wpas_temp_disabled(wpa_s, ssid);
if (res > 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily "
"disabled for %d second(s)", res);
return -1;
}
wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the "
"current AP");
if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
u8 wpa_ie[80];
size_t wpa_ie_len = sizeof(wpa_ie);
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_ie, &wpa_ie_len) < 0)
wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites");
} else {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
}
if (wpa_s->current_ssid && wpa_s->current_ssid != ssid)
eapol_sm_invalidate_cached_session(wpa_s->eapol);
old_ssid = wpa_s->current_ssid;
wpa_s->current_ssid = ssid;
wpa_supplicant_update_current_bss(wpa_s);
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
wpa_supplicant_initiate_eapol(wpa_s);
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
return 0;
}
void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (wpa_s->countermeasures) {
wpa_s->countermeasures = 0;
wpa_drv_set_countermeasures(wpa_s, 0);
wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped");
/*
* It is possible that the device is sched scanning, which means
* that a connection attempt will be done only when we receive
* scan results. However, in this case, it would be preferable
* to scan and connect immediately, so cancel the sched_scan and
* issue a regular scan flow.
*/
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
}
void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
{
int bssid_changed;
wnm_bss_keep_alive_deinit(wpa_s);
#ifdef CONFIG_IBSS_RSN
ibss_rsn_deinit(wpa_s->ibss_rsn);
wpa_s->ibss_rsn = NULL;
#endif /* CONFIG_IBSS_RSN */
#ifdef CONFIG_AP
wpa_supplicant_ap_deinit(wpa_s);
#endif /* CONFIG_AP */
#ifdef CONFIG_HS20
/* Clear possibly configured frame filters */
wpa_drv_configure_frame_filters(wpa_s, 0);
#endif /* CONFIG_HS20 */
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
return;
if (os_reltime_initialized(&wpa_s->session_start)) {
os_reltime_age(&wpa_s->session_start, &wpa_s->session_length);
wpa_s->session_start.sec = 0;
wpa_s->session_start.usec = 0;
wpas_notify_session_length(wpa_s);
}
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
sme_clear_on_disassoc(wpa_s);
wpa_s->current_bss = NULL;
wpa_s->assoc_freq = 0;
if (bssid_changed)
wpas_notify_bssid_changed(wpa_s);
eapol_sm_notify_portEnabled(wpa_s->eapol, false);
eapol_sm_notify_portValid(wpa_s->eapol, false);
if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_DPP || wpa_s->drv_authorized_port)
eapol_sm_notify_eap_success(wpa_s->eapol, false);
wpa_s->drv_authorized_port = 0;
wpa_s->ap_ies_from_associnfo = 0;
wpa_s->current_ssid = NULL;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_s->key_mgmt = 0;
wpas_rrm_reset(wpa_s);
wpa_s->wnmsleep_used = 0;
wnm_clear_coloc_intf_reporting(wpa_s);
wpa_s->disable_mbo_oce = 0;
#ifdef CONFIG_TESTING_OPTIONS
wpa_s->last_tk_alg = WPA_ALG_NONE;
os_memset(wpa_s->last_tk, 0, sizeof(wpa_s->last_tk));
#endif /* CONFIG_TESTING_OPTIONS */
wpa_s->ieee80211ac = 0;
if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0)
wpa_s->enabled_4addr_mode = 0;
}
static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
{
struct wpa_ie_data ie;
int pmksa_set = -1;
size_t i;
+ struct rsn_pmksa_cache_entry *cur_pmksa;
- /* Start with assumption of no PMKSA cache entry match */
- pmksa_cache_clear_current(wpa_s->wpa);
+ /* Start with assumption of no PMKSA cache entry match for cases other
+ * than SAE. In particular, this is needed to generate the PMKSA cache
+ * entries for Suite B cases with driver-based roaming indication. */
+ cur_pmksa = pmksa_cache_get_current(wpa_s->wpa);
+ if (cur_pmksa && !wpa_key_mgmt_sae(cur_pmksa->akmp))
+ pmksa_cache_clear_current(wpa_s->wpa);
if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 ||
ie.pmkid == NULL)
return;
for (i = 0; i < ie.num_pmkid; i++) {
pmksa_set = pmksa_cache_set_current(wpa_s->wpa,
ie.pmkid + i * PMKID_LEN,
NULL, NULL, 0, NULL, 0);
if (pmksa_set == 0) {
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
break;
}
}
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from "
"PMKSA cache", pmksa_set == 0 ? "" : "not ");
}
static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
if (data == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: No data in PMKID candidate "
"event");
return;
}
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
" index=%d preauth=%d",
MAC2STR(data->pmkid_candidate.bssid),
data->pmkid_candidate.index,
data->pmkid_candidate.preauth);
pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid,
data->pmkid_candidate.index,
data->pmkid_candidate.preauth);
}
static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s)
{
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
return 0;
#ifdef IEEE8021X_EAPOL
if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
wpa_s->current_ssid &&
!(wpa_s->current_ssid->eapol_flags &
(EAPOL_FLAG_REQUIRE_KEY_UNICAST |
EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) {
/* IEEE 802.1X, but not using dynamic WEP keys (i.e., either
* plaintext or static WEP keys). */
return 0;
}
#endif /* IEEE8021X_EAPOL */
return 1;
}
/**
* wpa_supplicant_scard_init - Initialize SIM/USIM access with PC/SC
* @wpa_s: pointer to wpa_supplicant data
* @ssid: Configuration data for the network
* Returns: 0 on success, -1 on failure
*
* This function is called when starting authentication with a network that is
* configured to use PC/SC for SIM/USIM access (EAP-SIM or EAP-AKA).
*/
int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
#ifdef IEEE8021X_EAPOL
#ifdef PCSC_FUNCS
int aka = 0, sim = 0;
if ((ssid != NULL && ssid->eap.pcsc == NULL) ||
wpa_s->scard != NULL || wpa_s->conf->external_sim)
return 0;
if (ssid == NULL || ssid->eap.eap_methods == NULL) {
sim = 1;
aka = 1;
} else {
struct eap_method_type *eap = ssid->eap.eap_methods;
while (eap->vendor != EAP_VENDOR_IETF ||
eap->method != EAP_TYPE_NONE) {
if (eap->vendor == EAP_VENDOR_IETF) {
if (eap->method == EAP_TYPE_SIM)
sim = 1;
else if (eap->method == EAP_TYPE_AKA ||
eap->method == EAP_TYPE_AKA_PRIME)
aka = 1;
}
eap++;
}
}
if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL)
sim = 0;
if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL &&
eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME) ==
NULL)
aka = 0;
if (!sim && !aka) {
wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to "
"use SIM, but neither EAP-SIM nor EAP-AKA are "
"enabled");
return 0;
}
wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM "
"(sim=%d aka=%d) - initialize PCSC", sim, aka);
wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
if (wpa_s->scard == NULL) {
wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
"(pcsc-lite)");
return -1;
}
wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
#endif /* PCSC_FUNCS */
#endif /* IEEE8021X_EAPOL */
return 0;
}
#ifndef CONFIG_NO_SCAN_PROCESSING
#ifdef CONFIG_WEP
static int has_wep_key(struct wpa_ssid *ssid)
{
int i;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i])
return 1;
}
return 0;
}
#endif /* CONFIG_WEP */
static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
struct wpa_ssid *ssid)
{
int privacy = 0;
if (ssid->mixed_cell)
return 1;
#ifdef CONFIG_WPS
if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
return 1;
#endif /* CONFIG_WPS */
#ifdef CONFIG_OWE
if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only)
return 1;
#endif /* CONFIG_OWE */
#ifdef CONFIG_WEP
if (has_wep_key(ssid))
privacy = 1;
#endif /* CONFIG_WEP */
#ifdef IEEE8021X_EAPOL
if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
EAPOL_FLAG_REQUIRE_KEY_BROADCAST))
privacy = 1;
#endif /* IEEE8021X_EAPOL */
if (wpa_key_mgmt_wpa(ssid->key_mgmt))
privacy = 1;
if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)
privacy = 1;
if (bss->caps & IEEE80211_CAP_PRIVACY)
return privacy;
return !privacy;
}
static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct wpa_bss *bss, int debug_print)
{
struct wpa_ie_data ie;
int proto_match = 0;
const u8 *rsn_ie, *wpa_ie;
int ret;
#ifdef CONFIG_WEP
int wep_ok;
#endif /* CONFIG_WEP */
ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
if (ret >= 0)
return ret;
#ifdef CONFIG_WEP
/* Allow TSN if local configuration accepts WEP use without WPA/WPA2 */
wep_ok = !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
(((ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
ssid->wep_key_len[ssid->wep_tx_keyidx] > 0) ||
(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA));
#endif /* CONFIG_WEP */
rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
while ((ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) && rsn_ie) {
proto_match++;
if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip RSN IE - parse failed");
break;
}
if (!ie.has_pairwise)
ie.pairwise_cipher = wpa_default_rsn_cipher(bss->freq);
if (!ie.has_group)
ie.group_cipher = wpa_default_rsn_cipher(bss->freq);
#ifdef CONFIG_WEP
if (wep_ok &&
(ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
{
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" selected based on TSN in RSN IE");
return 1;
}
#endif /* CONFIG_WEP */
if (!(ie.proto & ssid->proto) &&
!(ssid->proto & WPA_PROTO_OSEN)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip RSN IE - proto mismatch");
break;
}
if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip RSN IE - PTK cipher mismatch");
break;
}
if (!(ie.group_cipher & ssid->group_cipher)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip RSN IE - GTK cipher mismatch");
break;
}
if (ssid->group_mgmt_cipher &&
!(ie.mgmt_group_cipher & ssid->group_mgmt_cipher)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip RSN IE - group mgmt cipher mismatch");
break;
}
if (!(ie.key_mgmt & ssid->key_mgmt)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip RSN IE - key mgmt mismatch");
break;
}
if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
wpas_get_ssid_pmf(wpa_s, ssid) ==
MGMT_FRAME_PROTECTION_REQUIRED) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip RSN IE - no mgmt frame protection");
break;
}
if ((ie.capabilities & WPA_CAPABILITY_MFPR) &&
wpas_get_ssid_pmf(wpa_s, ssid) ==
NO_MGMT_FRAME_PROTECTION) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip RSN IE - no mgmt frame protection enabled but AP requires it");
break;
}
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" selected based on RSN IE");
return 1;
}
if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED &&
(!(ssid->key_mgmt & WPA_KEY_MGMT_OWE) || ssid->owe_only)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - MFP Required but network not MFP Capable");
return 0;
}
wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
proto_match++;
if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip WPA IE - parse failed");
break;
}
#ifdef CONFIG_WEP
if (wep_ok &&
(ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
{
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" selected based on TSN in WPA IE");
return 1;
}
#endif /* CONFIG_WEP */
if (!(ie.proto & ssid->proto)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip WPA IE - proto mismatch");
break;
}
if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip WPA IE - PTK cipher mismatch");
break;
}
if (!(ie.group_cipher & ssid->group_cipher)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip WPA IE - GTK cipher mismatch");
break;
}
if (!(ie.key_mgmt & ssid->key_mgmt)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip WPA IE - key mgmt mismatch");
break;
}
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" selected based on WPA IE");
return 1;
}
if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie &&
!rsn_ie) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" allow for non-WPA IEEE 802.1X");
return 1;
}
#ifdef CONFIG_OWE
if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only &&
!wpa_ie && !rsn_ie) {
if (wpa_s->owe_transition_select &&
wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE) &&
ssid->owe_transition_bss_select_count + 1 <=
MAX_OWE_TRANSITION_BSS_SELECT_COUNT) {
ssid->owe_transition_bss_select_count++;
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip OWE transition BSS (selection count %d does not exceed %d)",
ssid->owe_transition_bss_select_count,
MAX_OWE_TRANSITION_BSS_SELECT_COUNT);
wpa_s->owe_transition_search = 1;
return 0;
}
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" allow in OWE transition mode");
return 1;
}
#endif /* CONFIG_OWE */
if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) &&
wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - no WPA/RSN proto match");
return 0;
}
if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) &&
wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " allow in OSEN");
return 1;
}
if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2");
return 1;
}
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" reject due to mismatch with WPA/WPA2");
return 0;
}
static int freq_allowed(int *freqs, int freq)
{
int i;
if (freqs == NULL)
return 1;
for (i = 0; freqs[i]; i++)
if (freqs[i] == freq)
return 1;
return 0;
}
static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_bss *bss, int debug_print)
{
const struct hostapd_hw_modes *mode = NULL, *modes;
const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES };
const u8 *rate_ie;
int i, j, k;
if (bss->freq == 0)
return 1; /* Cannot do matching without knowing band */
modes = wpa_s->hw.modes;
if (modes == NULL) {
/*
* The driver does not provide any additional information
* about the utilized hardware, so allow the connection attempt
* to continue.
*/
return 1;
}
for (i = 0; i < wpa_s->hw.num_modes; i++) {
for (j = 0; j < modes[i].num_channels; j++) {
int freq = modes[i].channels[j].freq;
if (freq == bss->freq) {
if (mode &&
mode->mode == HOSTAPD_MODE_IEEE80211G)
break; /* do not allow 802.11b replace
* 802.11g */
mode = &modes[i];
break;
}
}
}
if (mode == NULL)
return 0;
for (i = 0; i < (int) sizeof(scan_ie); i++) {
rate_ie = wpa_bss_get_ie(bss, scan_ie[i]);
if (rate_ie == NULL)
continue;
for (j = 2; j < rate_ie[1] + 2; j++) {
int flagged = !!(rate_ie[j] & 0x80);
int r = (rate_ie[j] & 0x7f) * 5;
/*
* IEEE Std 802.11n-2009 7.3.2.2:
* The new BSS Membership selector value is encoded
* like a legacy basic rate, but it is not a rate and
* only indicates if the BSS members are required to
* support the mandatory features of Clause 20 [HT PHY]
* in order to join the BSS.
*/
if (flagged && ((rate_ie[j] & 0x7f) ==
BSS_MEMBERSHIP_SELECTOR_HT_PHY)) {
if (!ht_supported(mode)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" hardware does not support HT PHY");
return 0;
}
continue;
}
/* There's also a VHT selector for 802.11ac */
if (flagged && ((rate_ie[j] & 0x7f) ==
BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) {
if (!vht_supported(mode)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" hardware does not support VHT PHY");
return 0;
}
continue;
}
#ifdef CONFIG_SAE
if (flagged && ((rate_ie[j] & 0x7f) ==
BSS_MEMBERSHIP_SELECTOR_SAE_H2E_ONLY)) {
if (wpa_s->conf->sae_pwe == 0 &&
!ssid->sae_password_id &&
wpa_key_mgmt_sae(ssid->key_mgmt)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" SAE H2E disabled");
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->ignore_sae_h2e_only) {
wpa_dbg(wpa_s, MSG_DEBUG,
"TESTING: Ignore SAE H2E requirement mismatch");
continue;
}
#endif /* CONFIG_TESTING_OPTIONS */
return 0;
}
continue;
}
#endif /* CONFIG_SAE */
if (!flagged)
continue;
/* check for legacy basic rates */
for (k = 0; k < mode->num_rates; k++) {
if (mode->rates[k] == r)
break;
}
if (k == mode->num_rates) {
/*
* IEEE Std 802.11-2007 7.3.2.2 demands that in
* order to join a BSS all required rates
* have to be supported by the hardware.
*/
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)",
r / 10, r % 10,
bss->freq, mode->mode, mode->num_rates);
return 0;
}
}
}
return 1;
}
/*
* Test whether BSS is in an ESS.
* This is done differently in DMG (60 GHz) and non-DMG bands
*/
static int bss_is_ess(struct wpa_bss *bss)
{
if (bss_is_dmg(bss)) {
return (bss->caps & IEEE80211_CAP_DMG_MASK) ==
IEEE80211_CAP_DMG_AP;
}
return ((bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
IEEE80211_CAP_ESS);
}
static int match_mac_mask(const u8 *addr_a, const u8 *addr_b, const u8 *mask)
{
size_t i;
for (i = 0; i < ETH_ALEN; i++) {
if ((addr_a[i] & mask[i]) != (addr_b[i] & mask[i]))
return 0;
}
return 1;
}
static int addr_in_list(const u8 *addr, const u8 *list, size_t num)
{
size_t i;
for (i = 0; i < num; i++) {
const u8 *a = list + i * ETH_ALEN * 2;
const u8 *m = a + ETH_ALEN;
if (match_mac_mask(a, addr, m))
return 1;
}
return 0;
}
static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
const u8 **ret_ssid, size_t *ret_ssid_len)
{
#ifdef CONFIG_OWE
const u8 *owe, *pos, *end, *bssid;
u8 ssid_len;
struct wpa_bss *open_bss;
owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
if (!owe || !wpa_bss_get_ie(bss, WLAN_EID_RSN))
return;
pos = owe + 6;
end = owe + 2 + owe[1];
if (end - pos < ETH_ALEN + 1)
return;
bssid = pos;
pos += ETH_ALEN;
ssid_len = *pos++;
if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
return;
/* Match the profile SSID against the OWE transition mode SSID on the
* open network. */
wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode BSSID: " MACSTR
" SSID: %s", MAC2STR(bssid), wpa_ssid_txt(pos, ssid_len));
*ret_ssid = pos;
*ret_ssid_len = ssid_len;
if (!(bss->flags & WPA_BSS_OWE_TRANSITION)) {
struct wpa_ssid *ssid;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (wpas_network_disabled(wpa_s, ssid))
continue;
if (ssid->ssid_len == ssid_len &&
os_memcmp(ssid->ssid, pos, ssid_len) == 0) {
/* OWE BSS in transition mode for a currently
* enabled OWE network. */
wpa_dbg(wpa_s, MSG_DEBUG,
"OWE: transition mode OWE SSID for active OWE profile");
bss->flags |= WPA_BSS_OWE_TRANSITION;
break;
}
}
}
if (bss->ssid_len > 0)
return;
open_bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
if (!open_bss)
return;
if (ssid_len != open_bss->ssid_len ||
os_memcmp(pos, open_bss->ssid, ssid_len) != 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"OWE: transition mode SSID mismatch: %s",
wpa_ssid_txt(open_bss->ssid, open_bss->ssid_len));
return;
}
owe = wpa_bss_get_vendor_ie(open_bss, OWE_IE_VENDOR_TYPE);
if (!owe || wpa_bss_get_ie(open_bss, WLAN_EID_RSN)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"OWE: transition mode open BSS unexpected info");
return;
}
pos = owe + 6;
end = owe + 2 + owe[1];
if (end - pos < ETH_ALEN + 1)
return;
if (os_memcmp(pos, bss->bssid, ETH_ALEN) != 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"OWE: transition mode BSSID mismatch: " MACSTR,
MAC2STR(pos));
return;
}
pos += ETH_ALEN;
ssid_len = *pos++;
if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
return;
wpa_dbg(wpa_s, MSG_DEBUG, "OWE: learned transition mode OWE SSID: %s",
wpa_ssid_txt(pos, ssid_len));
os_memcpy(bss->ssid, pos, ssid_len);
bss->ssid_len = ssid_len;
bss->flags |= WPA_BSS_OWE_TRANSITION;
#endif /* CONFIG_OWE */
}
static int disabled_freq(struct wpa_supplicant *wpa_s, int freq)
{
int i, j;
if (!wpa_s->hw.modes || !wpa_s->hw.num_modes)
return 0;
for (j = 0; j < wpa_s->hw.num_modes; j++) {
struct hostapd_hw_modes *mode = &wpa_s->hw.modes[j];
for (i = 0; i < mode->num_channels; i++) {
struct hostapd_channel_data *chan = &mode->channels[i];
if (chan->freq == freq)
return !!(chan->flag & HOSTAPD_CHAN_DISABLED);
}
}
return 1;
}
static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
const u8 *match_ssid, size_t match_ssid_len,
struct wpa_bss *bss, int bssid_ignore_count,
bool debug_print);
#ifdef CONFIG_SAE_PK
static bool sae_pk_acceptable_bss_with_pk(struct wpa_supplicant *wpa_s,
struct wpa_bss *orig_bss,
struct wpa_ssid *ssid,
const u8 *match_ssid,
size_t match_ssid_len)
{
struct wpa_bss *bss;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
int count;
const u8 *ie;
if (bss == orig_bss)
continue;
ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
if (!(ieee802_11_rsnx_capab(ie, WLAN_RSNX_CAPAB_SAE_PK)))
continue;
/* TODO: Could be more thorough in checking what kind of
* signal strength or throughput estimate would be acceptable
* compared to the originally selected BSS. */
if (bss->est_throughput < 2000)
return false;
count = wpa_bssid_ignore_is_listed(wpa_s, bss->bssid);
if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
bss, count, 0))
return true;
}
return false;
}
#endif /* CONFIG_SAE_PK */
static bool wpa_scan_res_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
const u8 *match_ssid, size_t match_ssid_len,
struct wpa_bss *bss, int bssid_ignore_count,
bool debug_print)
{
int res;
bool wpa, check_ssid, osen, rsn_osen = false;
struct wpa_ie_data data;
#ifdef CONFIG_MBO
const u8 *assoc_disallow;
#endif /* CONFIG_MBO */
#ifdef CONFIG_SAE
u8 rsnxe_capa = 0;
#endif /* CONFIG_SAE */
const u8 *ie;
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
wpa = ie && ie[1];
ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
wpa |= ie && ie[1];
if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 &&
(data.key_mgmt & WPA_KEY_MGMT_OSEN))
rsn_osen = true;
ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
osen = ie != NULL;
#ifdef CONFIG_SAE
ie = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
if (ie && ie[1] >= 1)
rsnxe_capa = ie[2];
#endif /* CONFIG_SAE */
check_ssid = wpa || ssid->ssid_len > 0;
if (wpas_network_disabled(wpa_s, ssid)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled");
return false;
}
res = wpas_temp_disabled(wpa_s, ssid);
if (res > 0) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - disabled temporarily for %d second(s)",
res);
return false;
}
#ifdef CONFIG_WPS
if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && bssid_ignore_count) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - BSSID ignored (WPS)");
return false;
}
if (wpa && ssid->ssid_len == 0 &&
wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
check_ssid = false;
if (!wpa && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
/* Only allow wildcard SSID match if an AP advertises active
* WPS operation that matches our mode. */
check_ssid = ssid->ssid_len > 0 ||
!wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss);
}
#endif /* CONFIG_WPS */
if (ssid->bssid_set && ssid->ssid_len == 0 &&
os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) == 0)
check_ssid = false;
if (check_ssid &&
(match_ssid_len != ssid->ssid_len ||
os_memcmp(match_ssid, ssid->ssid, match_ssid_len) != 0)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch");
return false;
}
if (ssid->bssid_set &&
os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch");
return false;
}
/* check the list of BSSIDs to ignore */
if (ssid->num_bssid_ignore &&
addr_in_list(bss->bssid, ssid->bssid_ignore,
ssid->num_bssid_ignore)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - BSSID configured to be ignored");
return false;
}
/* if there is a list of accepted BSSIDs, only accept those APs */
if (ssid->num_bssid_accept &&
!addr_in_list(bss->bssid, ssid->bssid_accept,
ssid->num_bssid_accept)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - BSSID not in list of accepted values");
return false;
}
if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss, debug_print))
return false;
if (!osen && !wpa &&
!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
!(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
!(ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - non-WPA network not allowed");
return false;
}
#ifdef CONFIG_WEP
if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) && has_wep_key(ssid)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - ignore WPA/WPA2 AP for WEP network block");
return false;
}
#endif /* CONFIG_WEP */
if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen && !rsn_osen) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - non-OSEN network not allowed");
return false;
}
if (!wpa_supplicant_match_privacy(bss, ssid)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy mismatch");
return false;
}
if (ssid->mode != WPAS_MODE_MESH && !bss_is_ess(bss) &&
!bss_is_pbss(bss)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - not ESS, PBSS, or MBSS");
return false;
}
if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - PBSS mismatch (ssid %d bss %d)",
ssid->pbss, bss_is_pbss(bss));
return false;
}
if (!freq_allowed(ssid->freq_list, bss->freq)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - frequency not allowed");
return false;
}
#ifdef CONFIG_MESH
if (ssid->mode == WPAS_MODE_MESH && ssid->frequency > 0 &&
ssid->frequency != bss->freq) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - frequency not allowed (mesh)");
return false;
}
#endif /* CONFIG_MESH */
if (!rate_match(wpa_s, ssid, bss, debug_print)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - rate sets do not match");
return false;
}
#ifdef CONFIG_SAE
if ((wpa_s->conf->sae_pwe == 1 || ssid->sae_password_id) &&
wpa_s->conf->sae_pwe != 3 && wpa_key_mgmt_sae(ssid->key_mgmt) &&
!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E))) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - SAE H2E required, but not supported by the AP");
return false;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_SAE_PK
if (ssid->sae_pk == SAE_PK_MODE_ONLY &&
!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK))) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - SAE-PK required, but not supported by the AP");
return false;
}
#endif /* CONFIG_SAE_PK */
#ifndef CONFIG_IBSS_RSN
if (ssid->mode == WPAS_MODE_IBSS &&
!(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - IBSS RSN not supported in the build");
return false;
}
#endif /* !CONFIG_IBSS_RSN */
#ifdef CONFIG_P2P
if (ssid->p2p_group &&
!wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
!wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P IE seen");
return false;
}
if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) {
struct wpabuf *p2p_ie;
u8 dev_addr[ETH_ALEN];
ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
if (!ie) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - no P2P element");
return false;
}
p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
if (!p2p_ie) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - could not fetch P2P element");
return false;
}
if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0 ||
os_memcmp(dev_addr, ssid->go_p2p_dev_addr, ETH_ALEN) != 0) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - no matching GO P2P Device Address in P2P element");
wpabuf_free(p2p_ie);
return false;
}
wpabuf_free(p2p_ie);
}
/*
* TODO: skip the AP if its P2P IE has Group Formation bit set in the
* P2P Group Capability Bitmap and we are not in Group Formation with
* that device.
*/
#endif /* CONFIG_P2P */
if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time)) {
struct os_reltime diff;
os_reltime_sub(&wpa_s->scan_min_time, &bss->last_update, &diff);
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - scan result not recent enough (%u.%06u seconds too old)",
(unsigned int) diff.sec,
(unsigned int) diff.usec);
return false;
}
#ifdef CONFIG_MBO
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->ignore_assoc_disallow)
goto skip_assoc_disallow;
#endif /* CONFIG_TESTING_OPTIONS */
assoc_disallow = wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_ASSOC_DISALLOW);
if (assoc_disallow && assoc_disallow[1] >= 1) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - MBO association disallowed (reason %u)",
assoc_disallow[2]);
return false;
}
if (wpa_is_bss_tmp_disallowed(wpa_s, bss)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - AP temporarily disallowed");
return false;
}
#ifdef CONFIG_TESTING_OPTIONS
skip_assoc_disallow:
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_MBO */
#ifdef CONFIG_DPP
if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
!wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) &&
(!ssid->dpp_connector || !ssid->dpp_netaccesskey ||
!ssid->dpp_csign)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - no PMKSA entry for DPP");
return false;
}
#endif /* CONFIG_DPP */
#ifdef CONFIG_SAE_PK
if (ssid->sae_pk == SAE_PK_MODE_AUTOMATIC &&
wpa_key_mgmt_sae(ssid->key_mgmt) &&
((ssid->sae_password &&
sae_pk_valid_password(ssid->sae_password)) ||
(!ssid->sae_password && ssid->passphrase &&
sae_pk_valid_password(ssid->passphrase))) &&
!(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_PK)) &&
sae_pk_acceptable_bss_with_pk(wpa_s, bss, ssid, match_ssid,
match_ssid_len)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - another acceptable BSS with SAE-PK in the same ESS");
return false;
}
#endif /* CONFIG_SAE_PK */
if (bss->ssid_len == 0) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - no SSID known for the BSS");
return false;
}
/* Matching configuration found */
return true;
}
struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int i, struct wpa_bss *bss,
struct wpa_ssid *group,
int only_first_ssid, int debug_print)
{
u8 wpa_ie_len, rsn_ie_len;
const u8 *ie;
struct wpa_ssid *ssid;
int osen;
const u8 *match_ssid;
size_t match_ssid_len;
int bssid_ignore_count;
ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
wpa_ie_len = ie ? ie[1] : 0;
ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
rsn_ie_len = ie ? ie[1] : 0;
ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
osen = ie != NULL;
if (debug_print) {
wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR
" ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s",
i, MAC2STR(bss->bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len),
wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
bss->freq,
wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ?
" wps" : "",
(wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE))
? " p2p" : "",
osen ? " osen=1" : "");
}
bssid_ignore_count = wpa_bssid_ignore_is_listed(wpa_s, bss->bssid);
if (bssid_ignore_count) {
int limit = 1;
if (wpa_supplicant_enabled_networks(wpa_s) == 1) {
/*
* When only a single network is enabled, we can
* trigger BSSID ignoring on the first failure. This
* should not be done with multiple enabled networks to
* avoid getting forced to move into a worse ESS on
* single error if there are no other BSSes of the
* current ESS.
*/
limit = 0;
}
if (bssid_ignore_count > limit) {
if (debug_print) {
wpa_dbg(wpa_s, MSG_DEBUG,
" skip - BSSID ignored (count=%d limit=%d)",
bssid_ignore_count, limit);
}
return NULL;
}
}
match_ssid = bss->ssid;
match_ssid_len = bss->ssid_len;
owe_trans_ssid(wpa_s, bss, &match_ssid, &match_ssid_len);
if (match_ssid_len == 0) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known");
return NULL;
}
if (disallowed_bssid(wpa_s, bss->bssid)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed");
return NULL;
}
if (disallowed_ssid(wpa_s, match_ssid, match_ssid_len)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed");
return NULL;
}
if (disabled_freq(wpa_s, bss->freq)) {
if (debug_print)
wpa_dbg(wpa_s, MSG_DEBUG, " skip - channel disabled");
return NULL;
}
for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
if (wpa_scan_res_ok(wpa_s, ssid, match_ssid, match_ssid_len,
bss, bssid_ignore_count, debug_print))
return ssid;
}
/* No matching configuration found */
return NULL;
}
static struct wpa_bss *
wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
struct wpa_ssid *group,
struct wpa_ssid **selected_ssid,
int only_first_ssid)
{
unsigned int i;
if (wpa_s->current_ssid) {
struct wpa_ssid *ssid;
wpa_dbg(wpa_s, MSG_DEBUG,
"Scan results matching the currently selected network");
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
struct wpa_bss *bss = wpa_s->last_scan_res[i];
ssid = wpa_scan_res_match(wpa_s, i, bss, group,
only_first_ssid, 0);
if (ssid != wpa_s->current_ssid)
continue;
wpa_dbg(wpa_s, MSG_DEBUG, "%u: " MACSTR
" freq=%d level=%d snr=%d est_throughput=%u",
i, MAC2STR(bss->bssid), bss->freq, bss->level,
bss->snr, bss->est_throughput);
}
}
if (only_first_ssid)
wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d",
group->id);
else
wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
group->priority);
for (i = 0; i < wpa_s->last_scan_res_used; i++) {
struct wpa_bss *bss = wpa_s->last_scan_res[i];
wpa_s->owe_transition_select = 1;
*selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
only_first_ssid, 1);
wpa_s->owe_transition_select = 0;
if (!*selected_ssid)
continue;
wpa_dbg(wpa_s, MSG_DEBUG, " selected %sBSS " MACSTR
" ssid='%s'",
bss == wpa_s->current_bss ? "current ": "",
MAC2STR(bss->bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len));
return bss;
}
return NULL;
}
struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid **selected_ssid)
{
struct wpa_bss *selected = NULL;
size_t prio;
struct wpa_ssid *next_ssid = NULL;
struct wpa_ssid *ssid;
if (wpa_s->last_scan_res == NULL ||
wpa_s->last_scan_res_used == 0)
return NULL; /* no scan results from last update */
if (wpa_s->next_ssid) {
/* check that next_ssid is still valid */
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (ssid == wpa_s->next_ssid)
break;
}
next_ssid = ssid;
wpa_s->next_ssid = NULL;
}
while (selected == NULL) {
for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
if (next_ssid && next_ssid->priority ==
wpa_s->conf->pssid[prio]->priority) {
selected = wpa_supplicant_select_bss(
wpa_s, next_ssid, selected_ssid, 1);
if (selected)
break;
}
selected = wpa_supplicant_select_bss(
wpa_s, wpa_s->conf->pssid[prio],
selected_ssid, 0);
if (selected)
break;
}
if (selected == NULL && wpa_s->bssid_ignore &&
!wpa_s->countermeasures) {
wpa_dbg(wpa_s, MSG_DEBUG,
"No APs found - clear BSSID ignore list and try again");
wpa_bssid_ignore_clear(wpa_s);
wpa_s->bssid_ignore_cleared = true;
} else if (selected == NULL)
break;
}
ssid = *selected_ssid;
if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set &&
!ssid->passphrase && !ssid->ext_psk) {
const char *field_name, *txt = NULL;
wpa_dbg(wpa_s, MSG_DEBUG,
"PSK/passphrase not yet available for the selected network");
wpas_notify_network_request(wpa_s, ssid,
WPA_CTRL_REQ_PSK_PASSPHRASE, NULL);
field_name = wpa_supplicant_ctrl_req_to_string(
WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt);
if (field_name == NULL)
return NULL;
wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
selected = NULL;
}
return selected;
}
static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s,
int timeout_sec, int timeout_usec)
{
if (!wpa_supplicant_enabled_networks(wpa_s)) {
/*
* No networks are enabled; short-circuit request so
* we don't wait timeout seconds before transitioning
* to INACTIVE state.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request "
"since there are no enabled networks");
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
return;
}
wpa_s->scan_for_connection = 1;
wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec);
}
int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected,
struct wpa_ssid *ssid)
{
if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
"PBC session overlap");
wpas_notify_wps_event_pbc_overlap(wpa_s);
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
wpa_s->p2p_in_provisioning) {
eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb,
wpa_s, NULL);
return -1;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS
wpas_wps_pbc_overlap(wpa_s);
wpas_wps_cancel(wpa_s);
#endif /* CONFIG_WPS */
return -1;
}
wpa_msg(wpa_s, MSG_DEBUG,
"Considering connect request: reassociate: %d selected: "
MACSTR " bssid: " MACSTR " pending: " MACSTR
" wpa_state: %s ssid=%p current_ssid=%p",
wpa_s->reassociate, MAC2STR(selected->bssid),
MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
wpa_supplicant_state_txt(wpa_s->wpa_state),
ssid, wpa_s->current_ssid);
/*
* Do not trigger new association unless the BSSID has changed or if
* reassociation is requested. If we are in process of associating with
* the selected BSSID, do not trigger new attempt.
*/
if (wpa_s->reassociate ||
(os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
((wpa_s->wpa_state != WPA_ASSOCIATING &&
wpa_s->wpa_state != WPA_AUTHENTICATING) ||
(!is_zero_ether_addr(wpa_s->pending_bssid) &&
os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
0) ||
(is_zero_ether_addr(wpa_s->pending_bssid) &&
ssid != wpa_s->current_ssid)))) {
if (wpa_supplicant_scard_init(wpa_s, ssid)) {
wpa_supplicant_req_new_scan(wpa_s, 10, 0);
return 0;
}
wpa_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR,
MAC2STR(selected->bssid));
wpa_supplicant_associate(wpa_s, selected, ssid);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to "
"connect with the selected AP");
}
return 0;
}
static struct wpa_ssid *
wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s)
{
size_t prio;
struct wpa_ssid *ssid;
for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext)
{
if (wpas_network_disabled(wpa_s, ssid))
continue;
#ifndef CONFIG_IBSS_RSN
if (ssid->mode == WPAS_MODE_IBSS &&
!(ssid->key_mgmt & (WPA_KEY_MGMT_NONE |
WPA_KEY_MGMT_WPA_NONE))) {
wpa_msg(wpa_s, MSG_INFO,
"IBSS RSN not supported in the build - cannot use the profile for SSID '%s'",
wpa_ssid_txt(ssid->ssid,
ssid->ssid_len));
continue;
}
#endif /* !CONFIG_IBSS_RSN */
if (ssid->mode == WPAS_MODE_IBSS ||
ssid->mode == WPAS_MODE_AP ||
ssid->mode == WPAS_MODE_MESH)
return ssid;
}
}
return NULL;
}
/* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based
* on BSS added and BSS changed events */
static void wpa_supplicant_rsn_preauth_scan_results(
struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss;
if (rsn_preauth_scan_results(wpa_s->wpa) < 0)
return;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
const u8 *ssid, *rsn;
ssid = wpa_bss_get_ie(bss, WLAN_EID_SSID);
if (ssid == NULL)
continue;
rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
if (rsn == NULL)
continue;
rsn_preauth_scan_result(wpa_s->wpa, bss->bssid, ssid, rsn);
}
}
#ifndef CONFIG_NO_ROAMING
static int wpas_get_snr_signal_info(u32 frequency, int avg_signal, int noise)
{
if (noise == WPA_INVALID_NOISE)
noise = IS_5GHZ(frequency) ? DEFAULT_NOISE_FLOOR_5GHZ :
DEFAULT_NOISE_FLOOR_2GHZ;
return avg_signal - noise;
}
static unsigned int
wpas_get_est_throughput_from_bss_snr(const struct wpa_supplicant *wpa_s,
const struct wpa_bss *bss, int snr)
{
int rate = wpa_bss_get_max_rate(bss);
const u8 *ies = wpa_bss_ie_ptr(bss);
size_t ie_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
return wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr, bss->freq);
}
int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
struct wpa_bss *current_bss,
struct wpa_bss *selected)
{
int min_diff, diff;
int to_5ghz;
int cur_level;
unsigned int cur_est, sel_est;
struct wpa_signal_info si;
int cur_snr = 0;
int ret = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR
" freq=%d level=%d snr=%d est_throughput=%u",
MAC2STR(current_bss->bssid),
current_bss->freq, current_bss->level,
current_bss->snr, current_bss->est_throughput);
wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR
" freq=%d level=%d snr=%d est_throughput=%u",
MAC2STR(selected->bssid), selected->freq, selected->level,
selected->snr, selected->est_throughput);
if (wpa_s->current_ssid->bssid_set &&
os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) ==
0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Allow reassociation - selected BSS "
"has preferred BSSID");
return 1;
}
cur_level = current_bss->level;
cur_est = current_bss->est_throughput;
sel_est = selected->est_throughput;
/*
* Try to poll the signal from the driver since this will allow to get
* more accurate values. In some cases, there can be big differences
* between the RSSI of the Probe Response frames of the AP we are
* associated with and the Beacon frames we hear from the same AP after
* association. This can happen, e.g., when there are two antennas that
* hear the AP very differently. If the driver chooses to hear the
* Probe Response frames during the scan on the "bad" antenna because
* it wants to save power, but knows to choose the other antenna after
* association, we will hear our AP with a low RSSI as part of the
* scan even when we can hear it decently on the other antenna. To cope
* with this, ask the driver to teach us how it hears the AP. Also, the
* scan results may be a bit old, since we can very quickly get fresh
* information about our currently associated AP.
*/
if (wpa_drv_signal_poll(wpa_s, &si) == 0 &&
(si.avg_beacon_signal || si.avg_signal)) {
cur_level = si.avg_beacon_signal ? si.avg_beacon_signal :
si.avg_signal;
cur_snr = wpas_get_snr_signal_info(si.frequency, cur_level,
si.current_noise);
cur_est = wpas_get_est_throughput_from_bss_snr(wpa_s,
current_bss,
cur_snr);
wpa_dbg(wpa_s, MSG_DEBUG,
"Using signal poll values for the current BSS: level=%d snr=%d est_throughput=%u",
cur_level, cur_snr, cur_est);
}
if (sel_est > cur_est + 5000) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Allow reassociation - selected BSS has better estimated throughput");
return 1;
}
to_5ghz = selected->freq > 4000 && current_bss->freq < 4000;
if (cur_level < 0 && cur_level > selected->level + to_5ghz * 2 &&
sel_est < cur_est * 1.2) {
wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
"signal level");
return 0;
}
if (cur_est > sel_est + 5000) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Skip roam - Current BSS has better estimated throughput");
return 0;
}
if (cur_snr > GREAT_SNR) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Skip roam - Current BSS has good SNR (%u > %u)",
cur_snr, GREAT_SNR);
return 0;
}
if (cur_level < -85) /* ..-86 dBm */
min_diff = 1;
else if (cur_level < -80) /* -85..-81 dBm */
min_diff = 2;
else if (cur_level < -75) /* -80..-76 dBm */
min_diff = 3;
else if (cur_level < -70) /* -75..-71 dBm */
min_diff = 4;
else if (cur_level < 0) /* -70..-1 dBm */
min_diff = 5;
else /* unspecified units (not in dBm) */
min_diff = 2;
if (cur_est > sel_est * 1.5)
min_diff += 10;
else if (cur_est > sel_est * 1.2)
min_diff += 5;
else if (cur_est > sel_est * 1.1)
min_diff += 2;
else if (cur_est > sel_est)
min_diff++;
else if (sel_est > cur_est * 1.5)
min_diff -= 10;
else if (sel_est > cur_est * 1.2)
min_diff -= 5;
else if (sel_est > cur_est * 1.1)
min_diff -= 2;
else if (sel_est > cur_est)
min_diff--;
if (to_5ghz)
min_diff -= 2;
diff = selected->level - cur_level;
if (diff < min_diff) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Skip roam - too small difference in signal level (%d < %d)",
diff, min_diff);
ret = 0;
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"Allow reassociation due to difference in signal level (%d >= %d)",
diff, min_diff);
ret = 1;
}
wpa_msg_ctrl(wpa_s, MSG_INFO, "%scur_bssid=" MACSTR
" cur_freq=%d cur_level=%d cur_est=%d sel_bssid=" MACSTR
" sel_freq=%d sel_level=%d sel_est=%d",
ret ? WPA_EVENT_DO_ROAM : WPA_EVENT_SKIP_ROAM,
MAC2STR(current_bss->bssid),
current_bss->freq, cur_level, cur_est,
MAC2STR(selected->bssid),
selected->freq, selected->level, sel_est);
return ret;
}
#endif /* CONFIG_NO_ROAMING */
static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected,
struct wpa_ssid *ssid)
{
struct wpa_bss *current_bss = NULL;
if (wpa_s->reassociate)
return 1; /* explicit request to reassociate */
if (wpa_s->wpa_state < WPA_ASSOCIATED)
return 1; /* we are not associated; continue */
if (wpa_s->current_ssid == NULL)
return 1; /* unknown current SSID */
if (wpa_s->current_ssid != ssid)
return 1; /* different network block */
if (wpas_driver_bss_selection(wpa_s))
return 0; /* Driver-based roaming */
if (wpa_s->current_ssid->ssid)
current_bss = wpa_bss_get(wpa_s, wpa_s->bssid,
wpa_s->current_ssid->ssid,
wpa_s->current_ssid->ssid_len);
if (!current_bss)
current_bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
if (!current_bss)
return 1; /* current BSS not seen in scan results */
if (current_bss == selected)
return 0;
if (selected->last_update_idx > current_bss->last_update_idx)
return 1; /* current BSS not seen in the last scan */
#ifndef CONFIG_NO_ROAMING
return wpa_supplicant_need_to_roam_within_ess(wpa_s, current_bss,
selected);
#else /* CONFIG_NO_ROAMING */
return 0;
#endif /* CONFIG_NO_ROAMING */
}
/*
* Return a negative value if no scan results could be fetched or if scan
* results should not be shared with other virtual interfaces.
* Return 0 if scan results were fetched and may be shared with other
* interfaces.
* Return 1 if scan results may be shared with other virtual interfaces but may
* not trigger any operations.
* Return 2 if the interface was removed and cannot be used.
*/
static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data,
int own_request, int update_only)
{
struct wpa_scan_results *scan_res = NULL;
int ret = 0;
int ap = 0;
#ifndef CONFIG_NO_RANDOM_POOL
size_t i, num;
#endif /* CONFIG_NO_RANDOM_POOL */
#ifdef CONFIG_AP
if (wpa_s->ap_iface)
ap = 1;
#endif /* CONFIG_AP */
wpa_supplicant_notify_scanning(wpa_s, 0);
scan_res = wpa_supplicant_get_scan_results(wpa_s,
data ? &data->scan_info :
NULL, 1);
if (scan_res == NULL) {
if (wpa_s->conf->ap_scan == 2 || ap ||
wpa_s->scan_res_handler == scan_only_handler)
return -1;
if (!own_request)
return -1;
if (data && data->scan_info.external_scan)
return -1;
if (wpa_s->scan_res_fail_handler) {
void (*handler)(struct wpa_supplicant *wpa_s);
handler = wpa_s->scan_res_fail_handler;
wpa_s->scan_res_fail_handler = NULL;
handler(wpa_s);
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"Failed to get scan results - try scanning again");
wpa_supplicant_req_new_scan(wpa_s, 1, 0);
}
ret = -1;
goto scan_work_done;
}
#ifndef CONFIG_NO_RANDOM_POOL
num = scan_res->num;
if (num > 10)
num = 10;
for (i = 0; i < num; i++) {
u8 buf[5];
struct wpa_scan_res *res = scan_res->res[i];
buf[0] = res->bssid[5];
buf[1] = res->qual & 0xff;
buf[2] = res->noise & 0xff;
buf[3] = res->level & 0xff;
buf[4] = res->tsf & 0xff;
random_add_randomness(buf, sizeof(buf));
}
#endif /* CONFIG_NO_RANDOM_POOL */
if (update_only) {
ret = 1;
goto scan_work_done;
}
if (own_request && wpa_s->scan_res_handler &&
!(data && data->scan_info.external_scan)) {
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
scan_res_handler = wpa_s->scan_res_handler;
wpa_s->scan_res_handler = NULL;
scan_res_handler(wpa_s, scan_res);
ret = 1;
goto scan_work_done;
}
wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
wpa_s->own_scan_running,
data ? data->scan_info.external_scan : 0);
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
wpa_s->manual_scan_use_id && wpa_s->own_scan_running &&
own_request && !(data && data->scan_info.external_scan)) {
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
wpa_s->manual_scan_id);
wpa_s->manual_scan_use_id = 0;
} else {
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
}
wpas_notify_scan_results(wpa_s);
wpas_notify_scan_done(wpa_s, 1);
if (ap) {
wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode");
#ifdef CONFIG_AP
if (wpa_s->ap_iface->scan_cb)
wpa_s->ap_iface->scan_cb(wpa_s->ap_iface);
#endif /* CONFIG_AP */
goto scan_work_done;
}
if (data && data->scan_info.external_scan) {
wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
wpa_scan_results_free(scan_res);
return 0;
}
if (wnm_scan_process(wpa_s, 1) > 0)
goto scan_work_done;
if (sme_proc_obss_scan(wpa_s, scan_res) > 0)
goto scan_work_done;
if (own_request && data &&
wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0)
goto scan_work_done;
if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)))
goto scan_work_done;
if (autoscan_notify_scan(wpa_s, scan_res))
goto scan_work_done;
if (wpa_s->disconnected) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
goto scan_work_done;
}
if (!wpas_driver_bss_selection(wpa_s) &&
bgscan_notify_scan(wpa_s, scan_res) == 1)
goto scan_work_done;
wpas_wps_update_ap_info(wpa_s, scan_res);
if (wpa_s->wpa_state >= WPA_AUTHENTICATING &&
wpa_s->wpa_state < WPA_COMPLETED)
goto scan_work_done;
wpa_scan_results_free(scan_res);
if (own_request && wpa_s->scan_work) {
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
}
os_free(wpa_s->last_scan_freqs);
wpa_s->last_scan_freqs = NULL;
wpa_s->num_last_scan_freqs = 0;
if (own_request && data &&
data->scan_info.freqs && data->scan_info.num_freqs) {
wpa_s->last_scan_freqs = os_malloc(sizeof(int) *
data->scan_info.num_freqs);
if (wpa_s->last_scan_freqs) {
os_memcpy(wpa_s->last_scan_freqs,
data->scan_info.freqs,
sizeof(int) * data->scan_info.num_freqs);
wpa_s->num_last_scan_freqs = data->scan_info.num_freqs;
}
}
return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
scan_work_done:
wpa_scan_results_free(scan_res);
if (own_request && wpa_s->scan_work) {
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
}
return ret;
}
static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
int new_scan, int own_request)
{
struct wpa_bss *selected;
struct wpa_ssid *ssid = NULL;
int time_to_reenable = wpas_reenabled_network_time(wpa_s);
if (time_to_reenable > 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Postpone network selection by %d seconds since all networks are disabled",
time_to_reenable);
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
eloop_register_timeout(time_to_reenable, 0,
wpas_network_reenabled, wpa_s, NULL);
return 0;
}
if (wpa_s->p2p_mgmt)
return 0; /* no normal connection on p2p_mgmt interface */
wpa_s->owe_transition_search = 0;
selected = wpa_supplicant_pick_network(wpa_s, &ssid);
#ifdef CONFIG_MESH
if (wpa_s->ifmsh) {
wpa_msg(wpa_s, MSG_INFO,
"Avoiding join because we already joined a mesh group");
return 0;
}
#endif /* CONFIG_MESH */
if (selected) {
int skip;
skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
if (skip) {
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
return 0;
}
wpa_s->suitable_network++;
if (ssid != wpa_s->current_ssid &&
wpa_s->wpa_state >= WPA_AUTHENTICATING) {
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
return -1;
}
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
/*
* Do not allow other virtual radios to trigger operations based
* on these scan results since we do not want them to start
* other associations at the same time.
*/
return 1;
} else {
wpa_s->no_suitable_network++;
wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
ssid = wpa_supplicant_pick_new_network(wpa_s);
if (ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Setup a new network");
wpa_supplicant_associate(wpa_s, NULL, ssid);
if (new_scan)
wpa_supplicant_rsn_preauth_scan_results(wpa_s);
} else if (own_request) {
/*
* No SSID found. If SCAN results are as a result of
* own scan request and not due to a scan request on
* another shared interface, try another scan.
*/
int timeout_sec = wpa_s->scan_interval;
int timeout_usec = 0;
#ifdef CONFIG_P2P
int res;
res = wpas_p2p_scan_no_go_seen(wpa_s);
if (res == 2)
return 2;
if (res == 1)
return 0;
if (wpa_s->p2p_in_provisioning ||
wpa_s->show_group_started ||
wpa_s->p2p_in_invitation) {
/*
* Use shorter wait during P2P Provisioning
* state and during P2P join-a-group operation
* to speed up group formation.
*/
timeout_sec = 0;
timeout_usec = 250000;
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
return 0;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_INTERWORKING
if (wpa_s->conf->auto_interworking &&
wpa_s->conf->interworking &&
wpa_s->conf->cred) {
wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: "
"start ANQP fetch since no matching "
"networks found");
wpa_s->network_select = 1;
wpa_s->auto_network_select = 1;
interworking_start_fetch_anqp(wpa_s);
return 1;
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_WPS
if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing");
timeout_sec = 0;
timeout_usec = 500000;
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
return 0;
}
#endif /* CONFIG_WPS */
#ifdef CONFIG_OWE
if (wpa_s->owe_transition_search) {
wpa_dbg(wpa_s, MSG_DEBUG,
"OWE: Use shorter wait during transition mode search");
timeout_sec = 0;
timeout_usec = 500000;
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
return 0;
}
#endif /* CONFIG_OWE */
if (wpa_supplicant_req_sched_scan(wpa_s))
wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
timeout_usec);
wpa_msg_ctrl(wpa_s, MSG_INFO,
WPA_EVENT_NETWORK_NOT_FOUND);
}
}
return 0;
}
static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
struct wpa_supplicant *ifs;
int res;
res = _wpa_supplicant_event_scan_results(wpa_s, data, 1, 0);
if (res == 2) {
/*
* Interface may have been removed, so must not dereference
* wpa_s after this.
*/
return 1;
}
if (res < 0) {
/*
* If no scan results could be fetched, then no need to
* notify those interfaces that did not actually request
* this scan. Similarly, if scan results started a new operation on this
* interface, do not notify other interfaces to avoid concurrent
* operations during a connection attempt.
*/
return 0;
}
/*
* Check other interfaces to see if they share the same radio. If
* so, they get updated with this same scan info.
*/
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
radio_list) {
if (ifs != wpa_s) {
wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
"sibling", ifs->ifname);
res = _wpa_supplicant_event_scan_results(ifs, data, 0,
res > 0);
if (res < 0)
return 0;
}
}
return 0;
}
#endif /* CONFIG_NO_SCAN_PROCESSING */
int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_NO_SCAN_PROCESSING
return -1;
#else /* CONFIG_NO_SCAN_PROCESSING */
struct os_reltime now;
wpa_s->ignore_post_flush_scan_res = 0;
if (wpa_s->last_scan_res_used == 0)
return -1;
os_get_reltime(&now);
if (os_reltime_expired(&now, &wpa_s->last_scan,
wpa_s->conf->scan_res_valid_for_connect)) {
wpa_printf(MSG_DEBUG, "Fast associate: Old scan results");
return -1;
}
return wpas_select_network_from_last_scan(wpa_s, 0, 1);
#endif /* CONFIG_NO_SCAN_PROCESSING */
}
#ifdef CONFIG_WNM
static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (wpa_s->wpa_state < WPA_ASSOCIATED)
return;
if (!wpa_s->no_keep_alive) {
wpa_printf(MSG_DEBUG, "WNM: Send keep-alive to AP " MACSTR,
MAC2STR(wpa_s->bssid));
/* TODO: could skip this if normal data traffic has been sent */
/* TODO: Consider using some more appropriate data frame for
* this */
if (wpa_s->l2)
l2_packet_send(wpa_s->l2, wpa_s->bssid, 0x0800,
(u8 *) "", 0);
}
#ifdef CONFIG_SME
if (wpa_s->sme.bss_max_idle_period) {
unsigned int msec;
msec = wpa_s->sme.bss_max_idle_period * 1024; /* times 1000 */
if (msec > 100)
msec -= 100;
eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
wnm_bss_keep_alive, wpa_s, NULL);
}
#endif /* CONFIG_SME */
}
static void wnm_process_assoc_resp(struct wpa_supplicant *wpa_s,
const u8 *ies, size_t ies_len)
{
struct ieee802_11_elems elems;
if (ies == NULL)
return;
if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed)
return;
#ifdef CONFIG_SME
if (elems.bss_max_idle_period) {
unsigned int msec;
wpa_s->sme.bss_max_idle_period =
WPA_GET_LE16(elems.bss_max_idle_period);
wpa_printf(MSG_DEBUG, "WNM: BSS Max Idle Period: %u (* 1000 "
"TU)%s", wpa_s->sme.bss_max_idle_period,
(elems.bss_max_idle_period[2] & 0x01) ?
" (protected keep-live required)" : "");
if (wpa_s->sme.bss_max_idle_period == 0)
wpa_s->sme.bss_max_idle_period = 1;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) {
eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
/* msec times 1000 */
msec = wpa_s->sme.bss_max_idle_period * 1024;
if (msec > 100)
msec -= 100;
eloop_register_timeout(msec / 1000, msec % 1000 * 1000,
wnm_bss_keep_alive, wpa_s,
NULL);
}
}
#endif /* CONFIG_SME */
}
#endif /* CONFIG_WNM */
void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_WNM
eloop_cancel_timeout(wnm_bss_keep_alive, wpa_s, NULL);
#endif /* CONFIG_WNM */
}
#ifdef CONFIG_INTERWORKING
static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
size_t len)
{
int res;
wpa_hexdump(MSG_DEBUG, "Interworking: QoS Map Set", qos_map, len);
res = wpa_drv_set_qos_map(wpa_s, qos_map, len);
if (res) {
wpa_printf(MSG_DEBUG, "Interworking: Failed to configure QoS Map Set to the driver");
}
return res;
}
static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
const u8 *ies, size_t ies_len)
{
struct ieee802_11_elems elems;
if (ies == NULL)
return;
if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed)
return;
if (elems.qos_map_set) {
wpas_qos_map_set(wpa_s, elems.qos_map_set,
elems.qos_map_set_len);
}
}
#endif /* CONFIG_INTERWORKING */
static void multi_ap_process_assoc_resp(struct wpa_supplicant *wpa_s,
const u8 *ies, size_t ies_len)
{
struct ieee802_11_elems elems;
const u8 *map_sub_elem, *pos;
size_t len;
wpa_s->multi_ap_ie = 0;
if (!ies ||
ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed ||
!elems.multi_ap || elems.multi_ap_len < 7)
return;
pos = elems.multi_ap + 4;
len = elems.multi_ap_len - 4;
map_sub_elem = get_ie(pos, len, MULTI_AP_SUB_ELEM_TYPE);
if (!map_sub_elem || map_sub_elem[1] < 1)
return;
wpa_s->multi_ap_backhaul = !!(map_sub_elem[2] & MULTI_AP_BACKHAUL_BSS);
wpa_s->multi_ap_fronthaul = !!(map_sub_elem[2] &
MULTI_AP_FRONTHAUL_BSS);
wpa_s->multi_ap_ie = 1;
}
static void multi_ap_set_4addr_mode(struct wpa_supplicant *wpa_s)
{
if (!wpa_s->current_ssid ||
!wpa_s->current_ssid->multi_ap_backhaul_sta)
return;
if (!wpa_s->multi_ap_ie) {
wpa_printf(MSG_INFO,
"AP does not include valid Multi-AP element");
goto fail;
}
if (!wpa_s->multi_ap_backhaul) {
if (wpa_s->multi_ap_fronthaul &&
wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
wpa_printf(MSG_INFO,
"WPS active, accepting fronthaul-only BSS");
/* Don't set 4addr mode in this case, so just return */
return;
}
wpa_printf(MSG_INFO, "AP doesn't support backhaul BSS");
goto fail;
}
if (wpa_drv_set_4addr_mode(wpa_s, 1) < 0) {
wpa_printf(MSG_ERROR, "Failed to set 4addr mode");
goto fail;
}
wpa_s->enabled_4addr_mode = 1;
return;
fail:
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
#ifdef CONFIG_FST
static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s,
const u8 *ie, size_t ie_len)
{
struct mb_ies_info mb_ies;
if (!ie || !ie_len || !wpa_s->fst)
return -ENOENT;
os_memset(&mb_ies, 0, sizeof(mb_ies));
while (ie_len >= 2 && mb_ies.nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
size_t len;
len = 2 + ie[1];
if (len > ie_len) {
wpa_hexdump(MSG_DEBUG, "FST: Truncated IE found",
ie, ie_len);
break;
}
if (ie[0] == WLAN_EID_MULTI_BAND) {
wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
(unsigned int) len);
mb_ies.ies[mb_ies.nof_ies].ie = ie + 2;
mb_ies.ies[mb_ies.nof_ies].ie_len = len - 2;
mb_ies.nof_ies++;
}
ie_len -= len;
ie += len;
}
if (mb_ies.nof_ies > 0) {
wpabuf_free(wpa_s->received_mb_ies);
wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies);
return 0;
}
return -ENOENT;
}
#endif /* CONFIG_FST */
+static int wpa_supplicant_use_own_rsne_params(struct wpa_supplicant *wpa_s,
+ union wpa_event_data *data)
+{
+ int sel;
+ const u8 *p;
+ int l, len;
+ bool found = false;
+ struct wpa_ie_data ie;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ struct wpa_bss *bss = wpa_s->current_bss;
+ int pmf;
+
+ if (!ssid)
+ return 0;
+
+ p = data->assoc_info.req_ies;
+ l = data->assoc_info.req_ies_len;
+
+ while (p && l >= 2) {
+ len = p[1] + 2;
+ if (len > l) {
+ wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
+ p, l);
+ break;
+ }
+ if (((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+ (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
+ (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 &&
+ (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) ||
+ (p[0] == WLAN_EID_RSN && p[1] >= 2))) {
+ found = true;
+ break;
+ }
+ l -= len;
+ p += len;
+ }
+
+ if (!found || wpa_parse_wpa_ie(p, len, &ie) < 0)
+ return 0;
+
+ wpa_hexdump(MSG_DEBUG,
+ "WPA: Update cipher suite selection based on IEs in driver-generated WPA/RSNE in AssocReq",
+ p, l);
+
+ /* Update proto from (Re)Association Request frame info */
+ wpa_s->wpa_proto = ie.proto;
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, wpa_s->wpa_proto);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
+ !!(wpa_s->wpa_proto &
+ (WPA_PROTO_RSN | WPA_PROTO_OSEN)));
+
+ /* Update AKMP suite from (Re)Association Request frame info */
+ sel = ie.key_mgmt;
+ if (ssid->key_mgmt)
+ sel &= ssid->key_mgmt;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: AP key_mgmt 0x%x network key_mgmt 0x%x; available key_mgmt 0x%x",
+ ie.key_mgmt, ssid->key_mgmt, sel);
+ if (ie.key_mgmt && !sel) {
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_AKMP_NOT_VALID);
+ return -1;
+ }
+
+ wpa_s->key_mgmt = ie.key_mgmt;
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT %s and proto %d",
+ wpa_key_mgmt_txt(wpa_s->key_mgmt, wpa_s->wpa_proto),
+ wpa_s->wpa_proto);
+
+ /* Update pairwise cipher from (Re)Association Request frame info */
+ sel = ie.pairwise_cipher;
+ if (ssid->pairwise_cipher)
+ sel &= ssid->pairwise_cipher;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: AP pairwise cipher 0x%x network pairwise cipher 0x%x; available pairwise cipher 0x%x",
+ ie.pairwise_cipher, ssid->pairwise_cipher, sel);
+ if (ie.pairwise_cipher && !sel) {
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID);
+ return -1;
+ }
+
+ wpa_s->pairwise_cipher = ie.pairwise_cipher;
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
+ wpa_s->pairwise_cipher);
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
+ wpa_cipher_txt(wpa_s->pairwise_cipher));
+
+ /* Update other parameters based on AP's WPA IE/RSNE, if available */
+ if (!bss) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: current_bss == NULL - skip AP IE check");
+ return 0;
+ }
+
+ /* Update GTK and IGTK from AP's RSNE */
+ found = false;
+
+ if (wpa_s->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) {
+ const u8 *bss_rsn;
+
+ bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (bss_rsn) {
+ p = bss_rsn;
+ len = 2 + bss_rsn[1];
+ found = true;
+ }
+ } else if (wpa_s->wpa_proto & WPA_PROTO_WPA) {
+ const u8 *bss_wpa;
+
+ bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+ if (bss_wpa) {
+ p = bss_wpa;
+ len = 2 + bss_wpa[1];
+ found = true;
+ }
+ }
+
+ if (!found || wpa_parse_wpa_ie(p, len, &ie) < 0)
+ return 0;
+
+ pmf = wpas_get_ssid_pmf(wpa_s, ssid);
+ if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
+ pmf == MGMT_FRAME_PROTECTION_REQUIRED) {
+ /* AP does not support MFP, local configuration requires it */
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_INVALID_RSN_IE_CAPAB);
+ return -1;
+ }
+ if ((ie.capabilities & WPA_CAPABILITY_MFPR) &&
+ pmf == NO_MGMT_FRAME_PROTECTION) {
+ /* AP requires MFP, local configuration disables it */
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_INVALID_RSN_IE_CAPAB);
+ return -1;
+ }
+
+ /* Update PMF from local configuration now that MFP validation was done
+ * above */
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, pmf);
+
+ /* Update GTK from AP's RSNE */
+ sel = ie.group_cipher;
+ if (ssid->group_cipher)
+ sel &= ssid->group_cipher;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: AP group cipher 0x%x network group cipher 0x%x; available group cipher 0x%x",
+ ie.group_cipher, ssid->group_cipher, sel);
+ if (ie.group_cipher && !sel) {
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_GROUP_CIPHER_NOT_VALID);
+ return -1;
+ }
+
+ wpa_s->group_cipher = ie.group_cipher;
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK %s",
+ wpa_cipher_txt(wpa_s->group_cipher));
+
+ /* Update IGTK from AP RSN IE */
+ sel = ie.mgmt_group_cipher;
+ if (ssid->group_mgmt_cipher)
+ sel &= ssid->group_mgmt_cipher;
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: AP mgmt_group_cipher 0x%x network mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x",
+ ie.mgmt_group_cipher, ssid->group_mgmt_cipher, sel);
+
+ if (pmf == NO_MGMT_FRAME_PROTECTION ||
+ !(ie.capabilities & WPA_CAPABILITY_MFPC)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: STA/AP is not MFP capable; AP RSNE caps 0x%x",
+ ie.capabilities);
+ ie.mgmt_group_cipher = 0;
+ }
+
+ if (ie.mgmt_group_cipher && !sel) {
+ wpa_supplicant_deauthenticate(
+ wpa_s, WLAN_REASON_CIPHER_SUITE_REJECTED);
+ return -1;
+ }
+
+ wpa_s->mgmt_group_cipher = ie.mgmt_group_cipher;
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
+ wpa_s->mgmt_group_cipher);
+ if (wpa_s->mgmt_group_cipher)
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher %s",
+ wpa_cipher_txt(wpa_s->mgmt_group_cipher));
+ else
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
+
+ return 0;
+}
+
+
static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
int l, len, found = 0, found_x = 0, wpa_found, rsn_found;
const u8 *p;
u8 bssid[ETH_ALEN];
bool bssid_known;
wpa_dbg(wpa_s, MSG_DEBUG, "Association info event");
bssid_known = wpa_drv_get_bssid(wpa_s, bssid) == 0;
if (data->assoc_info.req_ies)
wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
data->assoc_info.req_ies_len);
if (data->assoc_info.resp_ies) {
wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
#ifdef CONFIG_TDLS
wpa_tdls_assoc_resp_ies(wpa_s->wpa, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
#endif /* CONFIG_TDLS */
#ifdef CONFIG_WNM
wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
#endif /* CONFIG_WNM */
#ifdef CONFIG_INTERWORKING
interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
#endif /* CONFIG_INTERWORKING */
if (wpa_s->hw_capab == CAPAB_VHT &&
get_ie(data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP))
wpa_s->ieee80211ac = 1;
multi_ap_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
}
if (data->assoc_info.beacon_ies)
wpa_hexdump(MSG_DEBUG, "beacon_ies",
data->assoc_info.beacon_ies,
data->assoc_info.beacon_ies_len);
if (data->assoc_info.freq)
wpa_dbg(wpa_s, MSG_DEBUG, "freq=%u MHz",
data->assoc_info.freq);
wpa_s->connection_set = 0;
if (data->assoc_info.req_ies && data->assoc_info.resp_ies) {
struct ieee802_11_elems req_elems, resp_elems;
if (ieee802_11_parse_elems(data->assoc_info.req_ies,
data->assoc_info.req_ies_len,
&req_elems, 0) != ParseFailed &&
ieee802_11_parse_elems(data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
&resp_elems, 0) != ParseFailed) {
wpa_s->connection_set = 1;
wpa_s->connection_ht = req_elems.ht_capabilities &&
resp_elems.ht_capabilities;
/* Do not include subset of VHT on 2.4 GHz vendor
* extension in consideration for reporting VHT
* association. */
wpa_s->connection_vht = req_elems.vht_capabilities &&
resp_elems.vht_capabilities &&
(!data->assoc_info.freq ||
wpas_freq_to_band(data->assoc_info.freq) !=
BAND_2_4_GHZ);
wpa_s->connection_he = req_elems.he_capabilities &&
resp_elems.he_capabilities;
}
}
p = data->assoc_info.req_ies;
l = data->assoc_info.req_ies_len;
/* Go through the IEs and make a copy of the WPA/RSN IE, if present. */
while (p && l >= 2) {
len = p[1] + 2;
if (len > l) {
wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
p, l);
break;
}
if (!found &&
((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
(os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
(p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 &&
(os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) ||
(p[0] == WLAN_EID_RSN && p[1] >= 2))) {
if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
break;
found = 1;
wpa_find_assoc_pmkid(wpa_s);
}
if (!found_x && p[0] == WLAN_EID_RSNX) {
if (wpa_sm_set_assoc_rsnxe(wpa_s->wpa, p, len))
break;
found_x = 1;
}
l -= len;
p += len;
}
if (!found && data->assoc_info.req_ies)
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
if (!found_x && data->assoc_info.req_ies)
wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
#ifdef CONFIG_FILS
#ifdef CONFIG_SME
if ((wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS ||
wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) &&
(!data->assoc_info.resp_frame ||
fils_process_assoc_resp(wpa_s->wpa,
data->assoc_info.resp_frame,
data->assoc_info.resp_frame_len) < 0)) {
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
return -1;
}
#endif /* CONFIG_SME */
/* Additional processing for FILS when SME is in driver */
if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
wpa_sm_set_reset_fils_completed(wpa_s->wpa, 1);
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
(!bssid_known ||
owe_process_assoc_resp(wpa_s->wpa, bssid,
data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len) < 0)) {
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
return -1;
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
wpa_sm_set_dpp_z(wpa_s->wpa, NULL);
if (DPP_VERSION > 1 && wpa_s->key_mgmt == WPA_KEY_MGMT_DPP &&
wpa_s->dpp_pfs) {
struct ieee802_11_elems elems;
if (ieee802_11_parse_elems(data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
&elems, 0) == ParseFailed ||
!elems.owe_dh)
goto no_pfs;
if (dpp_pfs_process(wpa_s->dpp_pfs, elems.owe_dh,
elems.owe_dh_len) < 0) {
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_UNSPECIFIED);
return -1;
}
wpa_sm_set_dpp_z(wpa_s->wpa, wpa_s->dpp_pfs->secret);
}
no_pfs:
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_IEEE80211R
#ifdef CONFIG_SME
if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
if (!bssid_known ||
wpa_ft_validate_reassoc_resp(wpa_s->wpa,
data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
bssid) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
"Reassociation Response failed");
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_INVALID_IE);
return -1;
}
}
p = data->assoc_info.resp_ies;
l = data->assoc_info.resp_ies_len;
#ifdef CONFIG_WPS_STRICT
if (p && wpa_s->current_ssid &&
wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
struct wpabuf *wps;
wps = ieee802_11_vendor_ie_concat(p, l, WPS_IE_VENDOR_TYPE);
if (wps == NULL) {
wpa_msg(wpa_s, MSG_INFO, "WPS-STRICT: AP did not "
"include WPS IE in (Re)Association Response");
return -1;
}
if (wps_validate_assoc_resp(wps) < 0) {
wpabuf_free(wps);
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_INVALID_IE);
return -1;
}
wpabuf_free(wps);
}
#endif /* CONFIG_WPS_STRICT */
/* Go through the IEs and make a copy of the MDIE, if present. */
while (p && l >= 2) {
len = p[1] + 2;
if (len > l) {
wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
p, l);
break;
}
if (p[0] == WLAN_EID_MOBILITY_DOMAIN &&
p[1] >= MOBILITY_DOMAIN_ID_LEN) {
wpa_s->sme.ft_used = 1;
os_memcpy(wpa_s->sme.mobility_domain, p + 2,
MOBILITY_DOMAIN_ID_LEN);
break;
}
l -= len;
p += len;
}
#endif /* CONFIG_SME */
/* Process FT when SME is in the driver */
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
wpa_ft_is_completed(wpa_s->wpa)) {
if (!bssid_known ||
wpa_ft_validate_reassoc_resp(wpa_s->wpa,
data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
bssid) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of "
"Reassociation Response failed");
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_INVALID_IE);
return -1;
}
wpa_dbg(wpa_s, MSG_DEBUG, "FT: Reassociation Response done");
}
wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
#endif /* CONFIG_IEEE80211R */
if (bssid_known)
wpas_handle_assoc_resp_mscs(wpa_s, bssid,
data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len);
/* WPA/RSN IE from Beacon/ProbeResp */
p = data->assoc_info.beacon_ies;
l = data->assoc_info.beacon_ies_len;
/* Go through the IEs and make a copy of the WPA/RSN IEs, if present.
*/
wpa_found = rsn_found = 0;
while (p && l >= 2) {
len = p[1] + 2;
if (len > l) {
wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies",
p, l);
break;
}
if (!wpa_found &&
p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) {
wpa_found = 1;
wpa_sm_set_ap_wpa_ie(wpa_s->wpa, p, len);
}
if (!rsn_found &&
p[0] == WLAN_EID_RSN && p[1] >= 2) {
rsn_found = 1;
wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len);
}
if (p[0] == WLAN_EID_RSNX && p[1] >= 1)
wpa_sm_set_ap_rsnxe(wpa_s->wpa, p, len);
l -= len;
p += len;
}
if (!wpa_found && data->assoc_info.beacon_ies)
wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
if (!rsn_found && data->assoc_info.beacon_ies) {
wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0);
}
if (wpa_found || rsn_found)
wpa_s->ap_ies_from_associnfo = 1;
if (wpa_s->assoc_freq && data->assoc_info.freq &&
wpa_s->assoc_freq != data->assoc_info.freq) {
wpa_printf(MSG_DEBUG, "Operating frequency changed from "
"%u to %u MHz",
wpa_s->assoc_freq, data->assoc_info.freq);
wpa_supplicant_update_scan_results(wpa_s);
}
wpa_s->assoc_freq = data->assoc_info.freq;
+ wpas_handle_assoc_resp_qos_mgmt(wpa_s, data->assoc_info.resp_ies,
+ data->assoc_info.resp_ies_len);
+
return 0;
}
static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
{
const u8 *bss_wpa = NULL, *bss_rsn = NULL, *bss_rsnx = NULL;
if (!wpa_s->current_bss || !wpa_s->current_ssid)
return -1;
if (!wpa_key_mgmt_wpa_any(wpa_s->current_ssid->key_mgmt))
return 0;
bss_wpa = wpa_bss_get_vendor_ie(wpa_s->current_bss,
WPA_IE_VENDOR_TYPE);
bss_rsn = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSN);
bss_rsnx = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_RSNX);
if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
bss_wpa ? 2 + bss_wpa[1] : 0) ||
wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
bss_rsn ? 2 + bss_rsn[1] : 0) ||
wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx,
bss_rsnx ? 2 + bss_rsnx[1] : 0))
return -1;
return 0;
}
static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
#ifdef CONFIG_FST
struct assoc_info *ai = data ? &data->assoc_info : NULL;
struct wpa_bss *bss = wpa_s->current_bss;
const u8 *ieprb, *iebcn;
wpabuf_free(wpa_s->received_mb_ies);
wpa_s->received_mb_ies = NULL;
if (ai &&
!wpas_fst_update_mbie(wpa_s, ai->resp_ies, ai->resp_ies_len)) {
wpa_printf(MSG_DEBUG,
"FST: MB IEs updated from Association Response frame");
return;
}
if (ai &&
!wpas_fst_update_mbie(wpa_s, ai->beacon_ies, ai->beacon_ies_len)) {
wpa_printf(MSG_DEBUG,
"FST: MB IEs updated from association event Beacon IEs");
return;
}
if (!bss)
return;
ieprb = wpa_bss_ie_ptr(bss);
iebcn = ieprb + bss->ie_len;
if (!wpas_fst_update_mbie(wpa_s, ieprb, bss->ie_len))
wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss IE");
else if (!wpas_fst_update_mbie(wpa_s, iebcn, bss->beacon_ie_len))
wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss beacon IE");
#endif /* CONFIG_FST */
}
static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
u8 bssid[ETH_ALEN];
int ft_completed, already_authorized;
int new_bss = 0;
#if defined(CONFIG_FILS) || defined(CONFIG_MBO)
struct wpa_bss *bss;
#endif /* CONFIG_FILS || CONFIG_MBO */
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
if (!data)
return;
hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
data->assoc_info.addr,
data->assoc_info.req_ies,
data->assoc_info.req_ies_len,
data->assoc_info.reassoc);
return;
}
#endif /* CONFIG_AP */
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
wpa_s->own_reconnect_req = 0;
ft_completed = wpa_ft_is_completed(wpa_s->wpa);
if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
return;
/*
* FILS authentication can share the same mechanism to mark the
* connection fully authenticated, so set ft_completed also based on
* FILS result.
*/
if (!ft_completed)
ft_completed = wpa_fils_is_completed(wpa_s->wpa);
if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
return;
}
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
if (os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0) {
if (os_reltime_initialized(&wpa_s->session_start)) {
os_reltime_age(&wpa_s->session_start,
&wpa_s->session_length);
wpa_s->session_start.sec = 0;
wpa_s->session_start.usec = 0;
wpas_notify_session_length(wpa_s);
} else {
wpas_notify_auth_changed(wpa_s);
os_get_reltime(&wpa_s->session_start);
}
wpa_dbg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
MACSTR, MAC2STR(bssid));
new_bss = 1;
random_add_randomness(bssid, ETH_ALEN);
os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
wpas_notify_bssid_changed(wpa_s);
if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
wpa_clear_keys(wpa_s, bssid);
}
if (wpa_supplicant_select_config(wpa_s) < 0) {
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
return;
}
}
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+ data && wpa_supplicant_use_own_rsne_params(wpa_s, data) < 0)
+ return;
+
multi_ap_set_4addr_mode(wpa_s);
if (wpa_s->conf->ap_scan == 1 &&
wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) {
if (wpa_supplicant_assoc_update_ie(wpa_s) < 0 && new_bss)
wpa_msg(wpa_s, MSG_WARNING,
"WPA/RSN IEs not updated");
}
wpas_fst_update_mb_assoc(wpa_s, data);
#ifdef CONFIG_SME
os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
wpa_s->sme.prev_bssid_set = 1;
wpa_s->sme.last_unprot_disconnect.sec = 0;
#endif /* CONFIG_SME */
wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid));
if (wpa_s->current_ssid) {
/* When using scanning (ap_scan=1), SIM PC/SC interface can be
* initialized before association, but for other modes,
* initialize PC/SC here, if the current configuration needs
* smartcard or SIM/USIM. */
wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid);
}
wpa_sm_notify_assoc(wpa_s->wpa, bssid);
if (wpa_s->l2)
l2_packet_notify_auth_start(wpa_s->l2);
already_authorized = data && data->assoc_info.authorized;
/*
* Set portEnabled first to false in order to get EAP state machine out
* of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE
* state machine may transit to AUTHENTICATING state based on obsolete
* eapSuccess and then trigger BE_AUTH to SUCCESS and PAE to
* AUTHENTICATED without ever giving chance to EAP state machine to
* reset the state.
*/
if (!ft_completed && !already_authorized) {
eapol_sm_notify_portEnabled(wpa_s->eapol, false);
eapol_sm_notify_portValid(wpa_s->eapol, false);
}
if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
wpa_s->key_mgmt == WPA_KEY_MGMT_DPP ||
wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || ft_completed ||
already_authorized || wpa_s->drv_authorized_port)
eapol_sm_notify_eap_success(wpa_s->eapol, false);
/* 802.1X::portControl = Auto */
eapol_sm_notify_portEnabled(wpa_s->eapol, true);
wpa_s->eapol_received = 0;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE ||
(wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_IBSS)) {
if (wpa_s->current_ssid &&
wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE &&
(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
/*
* Set the key after having received joined-IBSS event
* from the driver.
*/
wpa_supplicant_set_wpa_none_key(wpa_s,
wpa_s->current_ssid);
}
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
} else if (!ft_completed) {
/* Timeout for receiving the first EAPOL packet */
wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
}
wpa_supplicant_cancel_scan(wpa_s);
if (ft_completed) {
/*
* FT protocol completed - make sure EAPOL state machine ends
* up in authenticated.
*/
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
eapol_sm_notify_portValid(wpa_s->eapol, true);
eapol_sm_notify_eap_success(wpa_s->eapol, true);
} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) &&
wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
/*
* We are done; the driver will take care of RSN 4-way
* handshake.
*/
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
eapol_sm_notify_portValid(wpa_s->eapol, true);
eapol_sm_notify_eap_success(wpa_s->eapol, true);
} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) &&
wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
/*
* The driver will take care of RSN 4-way handshake, so we need
* to allow EAPOL supplicant to complete its work without
* waiting for WPA supplicant.
*/
eapol_sm_notify_portValid(wpa_s->eapol, true);
}
wpa_s->last_eapol_matches_bssid = 0;
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->rsne_override_eapol) {
wpa_printf(MSG_DEBUG,
"TESTING: RSNE EAPOL-Key msg 2/4 override");
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa,
wpabuf_head(wpa_s->rsne_override_eapol),
wpabuf_len(wpa_s->rsne_override_eapol));
}
if (wpa_s->rsnxe_override_eapol) {
wpa_printf(MSG_DEBUG,
"TESTING: RSNXE EAPOL-Key msg 2/4 override");
wpa_sm_set_assoc_rsnxe(wpa_s->wpa,
wpabuf_head(wpa_s->rsnxe_override_eapol),
wpabuf_len(wpa_s->rsnxe_override_eapol));
}
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->pending_eapol_rx) {
struct os_reltime now, age;
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
if (age.sec == 0 && age.usec < 200000 &&
os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
"frame that was received just before "
"association notification");
wpa_supplicant_rx_eapol(
wpa_s, wpa_s->pending_eapol_rx_src,
wpabuf_head(wpa_s->pending_eapol_rx),
wpabuf_len(wpa_s->pending_eapol_rx));
}
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = NULL;
}
#ifdef CONFIG_WEP
if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
wpa_s->current_ssid &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
/* Set static WEP keys again */
wpa_set_wep_keys(wpa_s, wpa_s->current_ssid);
}
#endif /* CONFIG_WEP */
#ifdef CONFIG_IBSS_RSN
if (wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE &&
wpa_s->ibss_rsn == NULL) {
wpa_s->ibss_rsn = ibss_rsn_init(wpa_s, wpa_s->current_ssid);
if (!wpa_s->ibss_rsn) {
wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN");
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
return;
}
ibss_rsn_set_psk(wpa_s->ibss_rsn, wpa_s->current_ssid->psk);
}
#endif /* CONFIG_IBSS_RSN */
wpas_wps_notify_assoc(wpa_s, bssid);
if (data) {
wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies,
data->assoc_info.resp_ies_len,
&data->assoc_info.wmm_params);
if (wpa_s->reassoc_same_bss)
wmm_ac_restore_tspecs(wpa_s);
}
#if defined(CONFIG_FILS) || defined(CONFIG_MBO)
bss = wpa_bss_get_bssid(wpa_s, bssid);
#endif /* CONFIG_FILS || CONFIG_MBO */
#ifdef CONFIG_FILS
if (wpa_key_mgmt_fils(wpa_s->key_mgmt)) {
const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss);
if (fils_cache_id)
wpa_sm_set_fils_cache_id(wpa_s->wpa, fils_cache_id);
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_MBO
wpas_mbo_check_pmf(wpa_s, bss, wpa_s->current_ssid);
#endif /* CONFIG_MBO */
#ifdef CONFIG_DPP2
wpa_s->dpp_pfs_fallback = 0;
#endif /* CONFIG_DPP2 */
}
static int disconnect_reason_recoverable(u16 reason_code)
{
return reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY ||
reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
}
static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
u16 reason_code,
int locally_generated)
{
const u8 *bssid;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/*
* At least Host AP driver and a Prism3 card seemed to be
* generating streams of disconnected events when configuring
* IBSS for WPA-None. Ignore them for now.
*/
return;
}
bssid = wpa_s->bssid;
if (is_zero_ether_addr(bssid))
bssid = wpa_s->pending_bssid;
if (!is_zero_ether_addr(bssid) ||
wpa_s->wpa_state >= WPA_AUTHENTICATING) {
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
" reason=%d%s",
MAC2STR(bssid), reason_code,
locally_generated ? " locally_generated=1" : "");
}
}
static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code,
int locally_generated)
{
if (wpa_s->wpa_state != WPA_4WAY_HANDSHAKE ||
!wpa_s->new_connection ||
!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
wpa_key_mgmt_sae(wpa_s->key_mgmt))
return 0; /* Not in initial 4-way handshake with PSK */
/*
* It looks like connection was lost while trying to go through PSK
* 4-way handshake. Filter out known disconnection cases that are caused
* by something else than PSK mismatch to avoid confusing reports.
*/
if (locally_generated) {
if (reason_code == WLAN_REASON_IE_IN_4WAY_DIFFERS)
return 0;
}
return 1;
}
static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
u16 reason_code,
int locally_generated)
{
const u8 *bssid;
int authenticating;
u8 prev_pending_bssid[ETH_ALEN];
struct wpa_bss *fast_reconnect = NULL;
struct wpa_ssid *fast_reconnect_ssid = NULL;
struct wpa_ssid *last_ssid;
struct wpa_bss *curr = NULL;
authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/*
* At least Host AP driver and a Prism3 card seemed to be
* generating streams of disconnected events when configuring
* IBSS for WPA-None. Ignore them for now.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - ignore in "
"IBSS/WPA-None mode");
return;
}
if (!wpa_s->disconnected && wpa_s->wpa_state >= WPA_AUTHENTICATING &&
reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY &&
locally_generated)
/*
* Remove the inactive AP (which is probably out of range) from
* the BSS list after marking disassociation. In particular
* mac80211-based drivers use the
* WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY reason code in
* locally generated disconnection events for cases where the
* AP does not reply anymore.
*/
curr = wpa_s->current_bss;
if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
"pre-shared key may be incorrect");
if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
return; /* P2P group removed */
wpas_auth_failed(wpa_s, "WRONG_KEY");
#ifdef CONFIG_DPP2
wpas_dpp_send_conn_status_result(wpa_s,
DPP_STATUS_AUTH_FAILURE);
#endif /* CONFIG_DPP2 */
}
if (!wpa_s->disconnected &&
(!wpa_s->auto_reconnect_disabled ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPS ||
wpas_wps_searching(wpa_s) ||
wpas_wps_reenable_networks_pending(wpa_s))) {
wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to "
"reconnect (wps=%d/%d wpa_state=%d)",
wpa_s->key_mgmt == WPA_KEY_MGMT_WPS,
wpas_wps_searching(wpa_s),
wpa_s->wpa_state);
if (wpa_s->wpa_state == WPA_COMPLETED &&
wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
(wpa_s->own_reconnect_req ||
(!locally_generated &&
disconnect_reason_recoverable(reason_code)))) {
/*
* It looks like the AP has dropped association with
* us, but could allow us to get back in. This is also
* triggered for cases where local reconnection request
* is used to force reassociation with the same BSS.
* Try to reconnect to the same BSS without a full scan
* to save time for some common cases.
*/
fast_reconnect = wpa_s->current_bss;
fast_reconnect_ssid = wpa_s->current_ssid;
} else if (wpa_s->wpa_state >= WPA_ASSOCIATING) {
wpa_supplicant_req_scan(wpa_s, 0, 100000);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new "
"immediate scan");
}
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect disabled: do not "
"try to re-connect");
wpa_s->reassociate = 0;
wpa_s->disconnected = 1;
if (!wpa_s->pno)
wpa_supplicant_cancel_sched_scan(wpa_s);
}
bssid = wpa_s->bssid;
if (is_zero_ether_addr(bssid))
bssid = wpa_s->pending_bssid;
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
wpas_connection_failed(wpa_s, bssid);
wpa_sm_notify_disassoc(wpa_s->wpa);
ptksa_cache_flush(wpa_s->ptksa, wpa_s->bssid, WPA_CIPHER_NONE);
if (locally_generated)
wpa_s->disconnect_reason = -reason_code;
else
wpa_s->disconnect_reason = reason_code;
wpas_notify_disconnect_reason(wpa_s);
if (wpa_supplicant_dynamic_keys(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
wpa_clear_keys(wpa_s, wpa_s->bssid);
}
last_ssid = wpa_s->current_ssid;
wpa_supplicant_mark_disassoc(wpa_s);
if (curr)
wpa_bss_remove(wpa_s, curr, "Connection to AP lost");
if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
wpa_s->current_ssid = last_ssid;
}
if (fast_reconnect &&
!wpas_network_disabled(wpa_s, fast_reconnect_ssid) &&
!disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
!disallowed_ssid(wpa_s, fast_reconnect->ssid,
fast_reconnect->ssid_len) &&
!wpas_temp_disabled(wpa_s, fast_reconnect_ssid) &&
!wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect)) {
#ifndef CONFIG_NO_SCAN_PROCESSING
wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
if (wpa_supplicant_connect(wpa_s, fast_reconnect,
fast_reconnect_ssid) < 0) {
/* Recover through full scan */
wpa_supplicant_req_scan(wpa_s, 0, 100000);
}
#endif /* CONFIG_NO_SCAN_PROCESSING */
} else if (fast_reconnect) {
/*
* Could not reconnect to the same BSS due to network being
* disabled. Use a new scan to match the alternative behavior
* above, i.e., to continue automatic reconnection attempt in a
* way that enforces disabled network rules.
*/
wpa_supplicant_req_scan(wpa_s, 0, 100000);
}
}
#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (!wpa_s->pending_mic_error_report)
return;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Sending pending MIC error report");
wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise);
wpa_s->pending_mic_error_report = 0;
}
#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
static void
wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
int pairwise;
struct os_reltime t;
wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
pairwise = (data && data->michael_mic_failure.unicast);
os_get_reltime(&t);
if ((wpa_s->last_michael_mic_error.sec &&
!os_reltime_expired(&t, &wpa_s->last_michael_mic_error, 60)) ||
wpa_s->pending_mic_error_report) {
if (wpa_s->pending_mic_error_report) {
/*
* Send the pending MIC error report immediately since
* we are going to start countermeasures and AP better
* do the same.
*/
wpa_sm_key_request(wpa_s->wpa, 1,
wpa_s->pending_mic_error_pairwise);
}
/* Send the new MIC error report immediately since we are going
* to start countermeasures and AP better do the same.
*/
wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
/* initialize countermeasures */
wpa_s->countermeasures = 1;
wpa_bssid_ignore_add(wpa_s, wpa_s->bssid);
wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started");
/*
* Need to wait for completion of request frame. We do not get
* any callback for the message completion, so just wait a
* short while and hope for the best. */
os_sleep(0, 10000);
wpa_drv_set_countermeasures(wpa_s, 1);
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_MICHAEL_MIC_FAILURE);
eloop_cancel_timeout(wpa_supplicant_stop_countermeasures,
wpa_s, NULL);
eloop_register_timeout(60, 0,
wpa_supplicant_stop_countermeasures,
wpa_s, NULL);
/* TODO: mark the AP rejected for 60 second. STA is
* allowed to associate with another AP.. */
} else {
#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
if (wpa_s->mic_errors_seen) {
/*
* Reduce the effectiveness of Michael MIC error
* reports as a means for attacking against TKIP if
* more than one MIC failure is noticed with the same
* PTK. We delay the transmission of the reports by a
* random time between 0 and 60 seconds in order to
* force the attacker wait 60 seconds before getting
* the information on whether a frame resulted in a MIC
* failure.
*/
u8 rval[4];
int sec;
if (os_get_random(rval, sizeof(rval)) < 0)
sec = os_random() % 60;
else
sec = WPA_GET_BE32(rval) % 60;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Delay MIC error "
"report %d seconds", sec);
wpa_s->pending_mic_error_report = 1;
wpa_s->pending_mic_error_pairwise = pairwise;
eloop_cancel_timeout(
wpa_supplicant_delayed_mic_error_report,
wpa_s, NULL);
eloop_register_timeout(
sec, os_random() % 1000000,
wpa_supplicant_delayed_mic_error_report,
wpa_s, NULL);
} else {
wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
}
#else /* CONFIG_DELAYED_MIC_ERROR_REPORT */
wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
}
wpa_s->last_michael_mic_error = t;
wpa_s->mic_errors_seen++;
}
#ifdef CONFIG_TERMINATE_ONLASTIF
static int any_interfaces(struct wpa_supplicant *head)
{
struct wpa_supplicant *wpa_s;
for (wpa_s = head; wpa_s != NULL; wpa_s = wpa_s->next)
if (!wpa_s->interface_removed)
return 1;
return 0;
}
#endif /* CONFIG_TERMINATE_ONLASTIF */
static void
wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
if (os_strcmp(wpa_s->ifname, data->interface_status.ifname) != 0)
return;
switch (data->interface_status.ievent) {
case EVENT_INTERFACE_ADDED:
if (!wpa_s->interface_removed)
break;
wpa_s->interface_removed = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was added");
if (wpa_supplicant_driver_init(wpa_s) < 0) {
wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
"driver after interface was added");
}
#ifdef CONFIG_P2P
if (!wpa_s->global->p2p &&
!wpa_s->global->p2p_disabled &&
!wpa_s->conf->p2p_disabled &&
(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
wpas_p2p_add_p2pdev_interface(
wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
wpa_printf(MSG_INFO,
"P2P: Failed to enable P2P Device interface");
/* Try to continue without. P2P will be disabled. */
}
#endif /* CONFIG_P2P */
break;
case EVENT_INTERFACE_REMOVED:
wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
wpa_s->interface_removed = 1;
wpa_supplicant_mark_disassoc(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = NULL;
#ifdef CONFIG_P2P
if (wpa_s->global->p2p &&
wpa_s->global->p2p_init_wpa_s->parent == wpa_s &&
(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Removing P2P Device interface");
wpa_supplicant_remove_iface(
wpa_s->global, wpa_s->global->p2p_init_wpa_s,
0);
wpa_s->global->p2p_init_wpa_s = NULL;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_MATCH_IFACE
if (wpa_s->matched) {
wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
break;
}
#endif /* CONFIG_MATCH_IFACE */
#ifdef CONFIG_TERMINATE_ONLASTIF
/* check if last interface */
if (!any_interfaces(wpa_s->global->ifaces))
eloop_terminate();
#endif /* CONFIG_TERMINATE_ONLASTIF */
break;
}
}
#ifdef CONFIG_TDLS
static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
if (data == NULL)
return;
switch (data->tdls.oper) {
case TDLS_REQUEST_SETUP:
wpa_tdls_remove(wpa_s->wpa, data->tdls.peer);
if (wpa_tdls_is_external_setup(wpa_s->wpa))
wpa_tdls_start(wpa_s->wpa, data->tdls.peer);
else
wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, data->tdls.peer);
break;
case TDLS_REQUEST_TEARDOWN:
if (wpa_tdls_is_external_setup(wpa_s->wpa))
wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer,
data->tdls.reason_code);
else
wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN,
data->tdls.peer);
break;
case TDLS_REQUEST_DISCOVER:
wpa_tdls_send_discovery_request(wpa_s->wpa,
data->tdls.peer);
break;
}
}
#endif /* CONFIG_TDLS */
#ifdef CONFIG_WNM
static void wpa_supplicant_event_wnm(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
if (data == NULL)
return;
switch (data->wnm.oper) {
case WNM_OPER_SLEEP:
wpa_printf(MSG_DEBUG, "Start sending WNM-Sleep Request "
"(action=%d, intval=%d)",
data->wnm.sleep_action, data->wnm.sleep_intval);
ieee802_11_send_wnmsleep_req(wpa_s, data->wnm.sleep_action,
data->wnm.sleep_intval, NULL);
break;
}
}
#endif /* CONFIG_WNM */
#ifdef CONFIG_IEEE80211R
static void
wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
if (data == NULL)
return;
if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies,
data->ft_ies.ies_len,
data->ft_ies.ft_action,
data->ft_ies.target_ap,
data->ft_ies.ric_ies,
data->ft_ies.ric_ies_len) < 0) {
/* TODO: prevent MLME/driver from trying to associate? */
}
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IBSS_RSN
static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
struct wpa_ssid *ssid;
if (wpa_s->wpa_state < WPA_ASSOCIATED)
return;
if (data == NULL)
return;
ssid = wpa_s->current_ssid;
if (ssid == NULL)
return;
if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
return;
ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer);
}
static void wpa_supplicant_event_ibss_auth(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid == NULL)
return;
/* check if the ssid is correctly configured as IBSS/RSN */
if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
return;
ibss_rsn_handle_auth(wpa_s->ibss_rsn, data->rx_mgmt.frame,
data->rx_mgmt.frame_len);
}
#endif /* CONFIG_IBSS_RSN */
#ifdef CONFIG_IEEE80211R
static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data,
size_t len)
{
const u8 *sta_addr, *target_ap_addr;
u16 status;
wpa_hexdump(MSG_MSGDUMP, "FT: RX Action", data, len);
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
return; /* only SME case supported for now */
if (len < 1 + 2 * ETH_ALEN + 2)
return;
if (data[0] != 2)
return; /* Only FT Action Response is supported for now */
sta_addr = data + 1;
target_ap_addr = data + 1 + ETH_ALEN;
status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN);
wpa_dbg(wpa_s, MSG_DEBUG, "FT: Received FT Action Response: STA "
MACSTR " TargetAP " MACSTR " status %u",
MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "FT: Foreign STA Address " MACSTR
" in FT Action Response", MAC2STR(sta_addr));
return;
}
if (status) {
wpa_dbg(wpa_s, MSG_DEBUG, "FT: FT Action Response indicates "
"failure (status code %d)", status);
/* TODO: report error to FT code(?) */
return;
}
if (wpa_ft_process_response(wpa_s->wpa, data + 1 + 2 * ETH_ALEN + 2,
len - (1 + 2 * ETH_ALEN + 2), 1,
target_ap_addr, NULL, 0) < 0)
return;
#ifdef CONFIG_SME
{
struct wpa_bss *bss;
bss = wpa_bss_get_bssid(wpa_s, target_ap_addr);
if (bss)
wpa_s->sme.freq = bss->freq;
wpa_s->sme.auth_alg = WPA_AUTH_ALG_FT;
sme_associate(wpa_s, WPAS_MODE_INFRA, target_ap_addr,
WLAN_AUTH_FT);
}
#endif /* CONFIG_SME */
}
#endif /* CONFIG_IEEE80211R */
static void wpa_supplicant_event_unprot_deauth(struct wpa_supplicant *wpa_s,
struct unprot_deauth *e)
{
wpa_printf(MSG_DEBUG, "Unprotected Deauthentication frame "
"dropped: " MACSTR " -> " MACSTR
" (reason code %u)",
MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
}
static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s,
struct unprot_disassoc *e)
{
wpa_printf(MSG_DEBUG, "Unprotected Disassociation frame "
"dropped: " MACSTR " -> " MACSTR
" (reason code %u)",
MAC2STR(e->sa), MAC2STR(e->da), e->reason_code);
sme_event_unprot_disconnect(wpa_s, e->sa, e->da, e->reason_code);
}
static void wpas_event_disconnect(struct wpa_supplicant *wpa_s, const u8 *addr,
u16 reason_code, int locally_generated,
const u8 *ie, size_t ie_len, int deauth)
{
#ifdef CONFIG_AP
if (wpa_s->ap_iface && addr) {
hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], addr);
return;
}
if (wpa_s->ap_iface) {
wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in AP mode");
return;
}
#endif /* CONFIG_AP */
if (!locally_generated)
wpa_s->own_disconnect_req = 0;
wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated);
if (((reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
(wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
eapol_sm_failed(wpa_s->eapol))) &&
!wpa_s->eap_expected_failure))
wpas_auth_failed(wpa_s, "AUTH_FAILED");
#ifdef CONFIG_P2P
if (deauth && reason_code > 0) {
if (wpas_p2p_deauth_notif(wpa_s, addr, reason_code, ie, ie_len,
locally_generated) > 0) {
/*
* The interface was removed, so cannot continue
* processing any additional operations after this.
*/
return;
}
}
#endif /* CONFIG_P2P */
wpa_supplicant_event_disassoc_finish(wpa_s, reason_code,
locally_generated);
}
static void wpas_event_disassoc(struct wpa_supplicant *wpa_s,
struct disassoc_info *info)
{
u16 reason_code = 0;
int locally_generated = 0;
const u8 *addr = NULL;
const u8 *ie = NULL;
size_t ie_len = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification");
if (info) {
addr = info->addr;
ie = info->ie;
ie_len = info->ie_len;
reason_code = info->reason_code;
locally_generated = info->locally_generated;
wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u (%s)%s", reason_code,
reason2str(reason_code),
locally_generated ? " locally_generated=1" : "");
if (addr)
wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
MAC2STR(addr));
wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)",
ie, ie_len);
}
#ifdef CONFIG_AP
if (wpa_s->ap_iface && info && info->addr) {
hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], info->addr);
return;
}
if (wpa_s->ap_iface) {
wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in AP mode");
return;
}
#endif /* CONFIG_AP */
#ifdef CONFIG_P2P
if (info) {
wpas_p2p_disassoc_notif(
wpa_s, info->addr, reason_code, info->ie, info->ie_len,
locally_generated);
}
#endif /* CONFIG_P2P */
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
sme_event_disassoc(wpa_s, info);
wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated,
ie, ie_len, 0);
}
static void wpas_event_deauth(struct wpa_supplicant *wpa_s,
struct deauth_info *info)
{
u16 reason_code = 0;
int locally_generated = 0;
const u8 *addr = NULL;
const u8 *ie = NULL;
size_t ie_len = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "Deauthentication notification");
if (info) {
addr = info->addr;
ie = info->ie;
ie_len = info->ie_len;
reason_code = info->reason_code;
locally_generated = info->locally_generated;
wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u (%s)%s",
reason_code, reason2str(reason_code),
locally_generated ? " locally_generated=1" : "");
if (addr) {
wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
MAC2STR(addr));
}
wpa_hexdump(MSG_DEBUG, "Deauthentication frame IE(s)",
ie, ie_len);
}
wpa_reset_ft_completed(wpa_s->wpa);
wpas_event_disconnect(wpa_s, addr, reason_code,
locally_generated, ie, ie_len, 1);
}
static const char * reg_init_str(enum reg_change_initiator init)
{
switch (init) {
case REGDOM_SET_BY_CORE:
return "CORE";
case REGDOM_SET_BY_USER:
return "USER";
case REGDOM_SET_BY_DRIVER:
return "DRIVER";
case REGDOM_SET_BY_COUNTRY_IE:
return "COUNTRY_IE";
case REGDOM_BEACON_HINT:
return "BEACON_HINT";
}
return "?";
}
static const char * reg_type_str(enum reg_type type)
{
switch (type) {
case REGDOM_TYPE_UNKNOWN:
return "UNKNOWN";
case REGDOM_TYPE_COUNTRY:
return "COUNTRY";
case REGDOM_TYPE_WORLD:
return "WORLD";
case REGDOM_TYPE_CUSTOM_WORLD:
return "CUSTOM_WORLD";
case REGDOM_TYPE_INTERSECTION:
return "INTERSECTION";
}
return "?";
}
void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s,
struct channel_list_changed *info)
{
struct wpa_supplicant *ifs;
u8 dfs_domain;
/*
* To allow backwards compatibility with higher level layers that
* assumed the REGDOM_CHANGE event is sent over the initially added
* interface. Find the highest parent of this interface and use it to
* send the event.
*/
for (ifs = wpa_s; ifs->parent && ifs != ifs->parent; ifs = ifs->parent)
;
if (info) {
wpa_msg(ifs, MSG_INFO,
WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s",
reg_init_str(info->initiator), reg_type_str(info->type),
info->alpha2[0] ? " alpha2=" : "",
info->alpha2[0] ? info->alpha2 : "");
}
if (wpa_s->drv_priv == NULL)
return; /* Ignore event during drv initialization */
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
radio_list) {
wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
ifs->ifname);
free_hw_features(ifs);
ifs->hw.modes = wpa_drv_get_hw_feature_data(
ifs, &ifs->hw.num_modes, &ifs->hw.flags, &dfs_domain);
/* Restart PNO/sched_scan with updated channel list */
if (ifs->pno) {
wpas_stop_pno(ifs);
wpas_start_pno(ifs);
} else if (ifs->sched_scanning && !ifs->pno_sched_pending) {
wpa_dbg(ifs, MSG_DEBUG,
"Channel list changed - restart sched_scan");
wpas_scan_restart_sched_scan(ifs);
}
}
wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER);
}
static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
const u8 *frame, size_t len, int freq,
int rssi)
{
const struct ieee80211_mgmt *mgmt;
const u8 *payload;
size_t plen;
u8 category;
if (len < IEEE80211_HDRLEN + 2)
return;
mgmt = (const struct ieee80211_mgmt *) frame;
payload = frame + IEEE80211_HDRLEN;
category = *payload++;
plen = len - IEEE80211_HDRLEN - 1;
wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
" Category=%u DataLen=%d freq=%d MHz",
MAC2STR(mgmt->sa), category, (int) plen, freq);
if (category == WLAN_ACTION_WMM) {
wmm_ac_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen);
return;
}
#ifdef CONFIG_IEEE80211R
if (category == WLAN_ACTION_FT) {
ft_rx_action(wpa_s, payload, plen);
return;
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_SME
if (category == WLAN_ACTION_SA_QUERY) {
sme_sa_query_rx(wpa_s, mgmt->da, mgmt->sa, payload, plen);
return;
}
#endif /* CONFIG_SME */
#ifdef CONFIG_WNM
if (mgmt->u.action.category == WLAN_ACTION_WNM) {
ieee802_11_rx_wnm_action(wpa_s, mgmt, len);
return;
}
#endif /* CONFIG_WNM */
#ifdef CONFIG_GAS
if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
gas_query_rx(wpa_s->gas, mgmt->da, mgmt->sa, mgmt->bssid,
mgmt->u.action.category,
payload, plen, freq) == 0)
return;
#endif /* CONFIG_GAS */
#ifdef CONFIG_GAS_SERVER
if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
gas_server_rx(wpa_s->gas_server, mgmt->da, mgmt->sa, mgmt->bssid,
mgmt->u.action.category,
payload, plen, freq) == 0)
return;
#endif /* CONFIG_GAS_SERVER */
#ifdef CONFIG_TDLS
if (category == WLAN_ACTION_PUBLIC && plen >= 4 &&
payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
wpa_dbg(wpa_s, MSG_DEBUG,
"TDLS: Received Discovery Response from " MACSTR,
MAC2STR(mgmt->sa));
return;
}
#endif /* CONFIG_TDLS */
#ifdef CONFIG_INTERWORKING
if (category == WLAN_ACTION_QOS && plen >= 1 &&
payload[0] == QOS_QOS_MAP_CONFIG) {
const u8 *pos = payload + 1;
size_t qlen = plen - 1;
wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from "
MACSTR, MAC2STR(mgmt->sa));
if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) == 0 &&
qlen > 2 && pos[0] == WLAN_EID_QOS_MAP_SET &&
pos[1] <= qlen - 2 && pos[1] >= 16)
wpas_qos_map_set(wpa_s, pos + 2, pos[1]);
return;
}
#endif /* CONFIG_INTERWORKING */
if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) {
wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa,
mgmt->da,
payload + 1,
plen - 1);
return;
}
if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1);
return;
}
if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) {
wpas_rrm_handle_link_measurement_request(wpa_s, mgmt->sa,
payload + 1, plen - 1,
rssi);
return;
}
#ifdef CONFIG_FST
if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) {
fst_rx_action(wpa_s->fst, mgmt, len);
return;
}
#endif /* CONFIG_FST */
#ifdef CONFIG_DPP
if (category == WLAN_ACTION_PUBLIC && plen >= 5 &&
payload[0] == WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(&payload[1]) == OUI_WFA &&
payload[4] == DPP_OUI_TYPE) {
payload++;
plen--;
wpas_dpp_rx_action(wpa_s, mgmt->sa, payload, plen, freq);
return;
}
#endif /* CONFIG_DPP */
+ if (category == WLAN_ACTION_ROBUST_AV_STREAMING &&
+ payload[0] == ROBUST_AV_SCS_RESP) {
+ wpas_handle_robust_av_scs_recv_action(wpa_s, mgmt->sa,
+ payload + 1, plen - 1);
+ return;
+ }
+
if (category == WLAN_ACTION_ROBUST_AV_STREAMING &&
payload[0] == ROBUST_AV_MSCS_RESP) {
wpas_handle_robust_av_recv_action(wpa_s, mgmt->sa,
payload + 1, plen - 1);
return;
}
+ if (category == WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED && plen > 4 &&
+ WPA_GET_BE32(payload) == QM_ACTION_VENDOR_TYPE) {
+ wpas_handle_qos_mgmt_recv_action(wpa_s, mgmt->sa,
+ payload + 4, plen - 4);
+ return;
+ }
+
wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
category, payload, plen, freq);
if (wpa_s->ifmsh)
mesh_mpm_action_rx(wpa_s, mgmt, len);
}
static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
union wpa_event_data *event)
{
struct wpa_freq_range_list *list;
char *str = NULL;
list = &event->freq_range;
if (list->num)
str = freq_range_list_str(list);
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s",
str ? str : "");
#ifdef CONFIG_P2P
if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) {
wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range",
__func__);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event");
/*
* The update channel flow will also take care of moving a GO
* from the unsafe frequency if needed.
*/
wpas_p2p_update_channel_list(wpa_s,
WPAS_P2P_CHANNEL_UPDATE_AVOID);
}
#endif /* CONFIG_P2P */
os_free(str);
}
static void wpa_supplicant_event_port_authorized(struct wpa_supplicant *wpa_s)
{
if (wpa_s->wpa_state == WPA_ASSOCIATED) {
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
eapol_sm_notify_portValid(wpa_s->eapol, true);
eapol_sm_notify_eap_success(wpa_s->eapol, true);
wpa_s->drv_authorized_port = 1;
}
}
static unsigned int wpas_event_cac_ms(const struct wpa_supplicant *wpa_s,
int freq)
{
size_t i;
int j;
for (i = 0; i < wpa_s->hw.num_modes; i++) {
const struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
for (j = 0; j < mode->num_channels; j++) {
const struct hostapd_channel_data *chan;
chan = &mode->channels[j];
if (chan->freq == freq)
return chan->dfs_cac_ms;
}
}
return 0;
}
static void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
struct dfs_event *radar)
{
#if defined(NEED_AP_MLME) && defined(CONFIG_AP)
if (wpa_s->ap_iface || wpa_s->ifmsh) {
wpas_ap_event_dfs_cac_started(wpa_s, radar);
} else
#endif /* NEED_AP_MLME && CONFIG_AP */
{
unsigned int cac_time = wpas_event_cac_ms(wpa_s, radar->freq);
cac_time /= 1000; /* convert from ms to sec */
if (!cac_time)
cac_time = 10 * 60; /* max timeout: 10 minutes */
/* Restart auth timeout: CAC time added to initial timeout */
wpas_auth_timeout_restart(wpa_s, cac_time);
}
}
static void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
struct dfs_event *radar)
{
#if defined(NEED_AP_MLME) && defined(CONFIG_AP)
if (wpa_s->ap_iface || wpa_s->ifmsh) {
wpas_ap_event_dfs_cac_finished(wpa_s, radar);
} else
#endif /* NEED_AP_MLME && CONFIG_AP */
{
/* Restart auth timeout with original value after CAC is
* finished */
wpas_auth_timeout_restart(wpa_s, 0);
}
}
static void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
struct dfs_event *radar)
{
#if defined(NEED_AP_MLME) && defined(CONFIG_AP)
if (wpa_s->ap_iface || wpa_s->ifmsh) {
wpas_ap_event_dfs_cac_aborted(wpa_s, radar);
} else
#endif /* NEED_AP_MLME && CONFIG_AP */
{
/* Restart auth timeout with original value after CAC is
* aborted */
wpas_auth_timeout_restart(wpa_s, 0);
}
}
static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
wpa_dbg(wpa_s, MSG_DEBUG,
"Connection authorized by device, previous state %d",
wpa_s->wpa_state);
wpa_supplicant_event_port_authorized(wpa_s);
wpa_s->last_eapol_matches_bssid = 1;
wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr);
wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck,
data->assoc_info.ptk_kck_len,
data->assoc_info.ptk_kek,
data->assoc_info.ptk_kek_len);
#ifdef CONFIG_FILS
if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) {
struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid);
const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss);
/* Update ERP next sequence number */
eapol_sm_update_erp_next_seq_num(
wpa_s->eapol, data->assoc_info.fils_erp_next_seq_num);
if (data->assoc_info.fils_pmk && data->assoc_info.fils_pmkid) {
/* Add the new PMK and PMKID to the PMKSA cache */
wpa_sm_pmksa_cache_add(wpa_s->wpa,
data->assoc_info.fils_pmk,
data->assoc_info.fils_pmk_len,
data->assoc_info.fils_pmkid,
wpa_s->bssid, fils_cache_id);
} else if (data->assoc_info.fils_pmkid) {
/* Update the current PMKSA used for this connection */
pmksa_cache_set_current(wpa_s->wpa,
data->assoc_info.fils_pmkid,
NULL, NULL, 0, NULL, 0);
}
}
#endif /* CONFIG_FILS */
}
static const char * connect_fail_reason(enum sta_connect_fail_reason_codes code)
{
switch (code) {
case STA_CONNECT_FAIL_REASON_UNSPECIFIED:
return "";
case STA_CONNECT_FAIL_REASON_NO_BSS_FOUND:
return "no_bss_found";
case STA_CONNECT_FAIL_REASON_AUTH_TX_FAIL:
return "auth_tx_fail";
case STA_CONNECT_FAIL_REASON_AUTH_NO_ACK_RECEIVED:
return "auth_no_ack_received";
case STA_CONNECT_FAIL_REASON_AUTH_NO_RESP_RECEIVED:
return "auth_no_resp_received";
case STA_CONNECT_FAIL_REASON_ASSOC_REQ_TX_FAIL:
return "assoc_req_tx_fail";
case STA_CONNECT_FAIL_REASON_ASSOC_NO_ACK_RECEIVED:
return "assoc_no_ack_received";
case STA_CONNECT_FAIL_REASON_ASSOC_NO_RESP_RECEIVED:
return "assoc_no_resp_received";
default:
return "unknown_reason";
}
}
static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s,
union wpa_event_data *data)
{
const u8 *bssid = data->assoc_reject.bssid;
#ifdef CONFIG_MBO
struct wpa_bss *reject_bss;
#endif /* CONFIG_MBO */
if (!bssid || is_zero_ether_addr(bssid))
bssid = wpa_s->pending_bssid;
#ifdef CONFIG_MBO
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
reject_bss = wpa_s->current_bss;
else
reject_bss = wpa_bss_get_bssid(wpa_s, bssid);
#endif /* CONFIG_MBO */
if (data->assoc_reject.bssid)
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
"bssid=" MACSTR " status_code=%u%s%s%s%s%s",
MAC2STR(data->assoc_reject.bssid),
data->assoc_reject.status_code,
data->assoc_reject.timed_out ? " timeout" : "",
data->assoc_reject.timeout_reason ? "=" : "",
data->assoc_reject.timeout_reason ?
data->assoc_reject.timeout_reason : "",
data->assoc_reject.reason_code !=
STA_CONNECT_FAIL_REASON_UNSPECIFIED ?
" qca_driver_reason=" : "",
connect_fail_reason(data->assoc_reject.reason_code));
else
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
"status_code=%u%s%s%s%s%s",
data->assoc_reject.status_code,
data->assoc_reject.timed_out ? " timeout" : "",
data->assoc_reject.timeout_reason ? "=" : "",
data->assoc_reject.timeout_reason ?
data->assoc_reject.timeout_reason : "",
data->assoc_reject.reason_code !=
STA_CONNECT_FAIL_REASON_UNSPECIFIED ?
" qca_driver_reason=" : "",
connect_fail_reason(data->assoc_reject.reason_code));
wpa_s->assoc_status_code = data->assoc_reject.status_code;
wpas_notify_assoc_status_code(wpa_s);
#ifdef CONFIG_OWE
if (data->assoc_reject.status_code ==
WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
wpa_s->current_ssid &&
wpa_s->current_ssid->owe_group == 0 &&
wpa_s->last_owe_group != 21) {
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct wpa_bss *bss = wpa_s->current_bss;
if (!bss) {
bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
if (!bss) {
wpas_connection_failed(wpa_s, bssid);
wpa_supplicant_mark_disassoc(wpa_s);
return;
}
}
wpa_printf(MSG_DEBUG, "OWE: Try next supported DH group");
wpas_connect_work_done(wpa_s);
wpa_supplicant_mark_disassoc(wpa_s);
wpa_supplicant_connect(wpa_s, bss, ssid);
return;
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
/* Try to follow AP's PFS policy. WLAN_STATUS_ASSOC_DENIED_UNSPEC is
* the status code defined in the DPP R2 tech spec.
* WLAN_STATUS_AKMP_NOT_VALID is addressed in the same manner as an
* interoperability workaround with older hostapd implementation. */
if (DPP_VERSION > 1 && wpa_s->current_ssid &&
(wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP ||
((wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_DPP) &&
wpa_s->key_mgmt == WPA_KEY_MGMT_DPP)) &&
wpa_s->current_ssid->dpp_pfs == 0 &&
(data->assoc_reject.status_code ==
WLAN_STATUS_ASSOC_DENIED_UNSPEC ||
data->assoc_reject.status_code == WLAN_STATUS_AKMP_NOT_VALID)) {
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct wpa_bss *bss = wpa_s->current_bss;
wpa_s->current_ssid->dpp_pfs_fallback ^= 1;
if (!bss)
bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
if (!bss || wpa_s->dpp_pfs_fallback) {
wpa_printf(MSG_DEBUG,
"DPP: Updated PFS policy for next try");
wpas_connection_failed(wpa_s, bssid);
wpa_supplicant_mark_disassoc(wpa_s);
return;
}
wpa_printf(MSG_DEBUG, "DPP: Try again with updated PFS policy");
wpa_s->dpp_pfs_fallback = 1;
wpas_connect_work_done(wpa_s);
wpa_supplicant_mark_disassoc(wpa_s);
wpa_supplicant_connect(wpa_s, bss, ssid);
return;
}
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_MBO
if (data->assoc_reject.status_code ==
WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS &&
reject_bss && data->assoc_reject.resp_ies) {
const u8 *rssi_rej;
rssi_rej = mbo_get_attr_from_ies(
data->assoc_reject.resp_ies,
data->assoc_reject.resp_ies_len,
OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT);
if (rssi_rej && rssi_rej[1] == 2) {
wpa_printf(MSG_DEBUG,
"OCE: RSSI-based association rejection from "
MACSTR " (Delta RSSI: %u, Retry Delay: %u)",
MAC2STR(reject_bss->bssid),
rssi_rej[2], rssi_rej[3]);
wpa_bss_tmp_disallow(wpa_s,
reject_bss->bssid,
rssi_rej[3],
rssi_rej[2] + reject_bss->level);
}
}
#endif /* CONFIG_MBO */
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) {
sme_event_assoc_reject(wpa_s, data);
return;
}
/* Driver-based SME cases */
#ifdef CONFIG_SAE
if (wpa_s->current_ssid &&
wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt) &&
!data->assoc_reject.timed_out) {
wpa_dbg(wpa_s, MSG_DEBUG, "SAE: Drop PMKSA cache entry");
wpa_sm_aborted_cached(wpa_s->wpa);
wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid);
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_DPP
if (wpa_s->current_ssid &&
wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP &&
!data->assoc_reject.timed_out) {
wpa_dbg(wpa_s, MSG_DEBUG, "DPP: Drop PMKSA cache entry");
wpa_sm_aborted_cached(wpa_s->wpa);
wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid);
}
#endif /* CONFIG_DPP */
#ifdef CONFIG_FILS
/* Update ERP next sequence number */
if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) {
fils_pmksa_cache_flush(wpa_s);
eapol_sm_update_erp_next_seq_num(
wpa_s->eapol,
data->assoc_reject.fils_erp_next_seq_num);
fils_connection_failure(wpa_s);
}
#endif /* CONFIG_FILS */
wpas_connection_failed(wpa_s, bssid);
wpa_supplicant_mark_disassoc(wpa_s);
}
static void wpas_event_unprot_beacon(struct wpa_supplicant *wpa_s,
struct unprot_beacon *data)
{
struct wpabuf *buf;
int res;
if (!data || wpa_s->wpa_state != WPA_COMPLETED ||
os_memcmp(data->sa, wpa_s->bssid, ETH_ALEN) != 0)
return;
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_UNPROT_BEACON MACSTR,
MAC2STR(data->sa));
buf = wpabuf_alloc(4);
if (!buf)
return;
wpabuf_put_u8(buf, WLAN_ACTION_WNM);
wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
wpabuf_put_u8(buf, 1); /* Dialog Token */
wpabuf_put_u8(buf, WNM_NOTIF_TYPE_BEACON_PROTECTION_FAILURE);
res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
wpa_s->own_addr, wpa_s->bssid,
wpabuf_head(buf), wpabuf_len(buf), 0);
if (res < 0)
wpa_printf(MSG_DEBUG,
"Failed to send WNM-Notification Request frame");
wpabuf_free(buf);
}
void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
struct wpa_supplicant *wpa_s = ctx;
int resched;
struct os_reltime age, clear_at;
#ifndef CONFIG_NO_STDOUT_DEBUG
int level = MSG_DEBUG;
#endif /* CONFIG_NO_STDOUT_DEBUG */
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
event != EVENT_INTERFACE_ENABLED &&
event != EVENT_INTERFACE_STATUS &&
event != EVENT_SCAN_RESULTS &&
event != EVENT_SCHED_SCAN_STOPPED) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Ignore event %s (%d) while interface is disabled",
event_to_string(event), event);
return;
}
#ifndef CONFIG_NO_STDOUT_DEBUG
if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) {
const struct ieee80211_hdr *hdr;
u16 fc;
hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
fc = le_to_host16(hdr->frame_control);
if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
level = MSG_EXCESSIVE;
}
wpa_dbg(wpa_s, level, "Event %s (%d) received",
event_to_string(event), event);
#endif /* CONFIG_NO_STDOUT_DEBUG */
switch (event) {
case EVENT_AUTH:
#ifdef CONFIG_FST
if (!wpas_fst_update_mbie(wpa_s, data->auth.ies,
data->auth.ies_len))
wpa_printf(MSG_DEBUG,
"FST: MB IEs updated from auth IE");
#endif /* CONFIG_FST */
sme_event_auth(wpa_s, data);
wpa_s->auth_status_code = data->auth.status_code;
wpas_notify_auth_status_code(wpa_s);
break;
case EVENT_ASSOC:
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->ignore_auth_resp) {
wpa_printf(MSG_INFO,
"EVENT_ASSOC - ignore_auth_resp active!");
break;
}
if (wpa_s->testing_resend_assoc) {
wpa_printf(MSG_INFO,
"EVENT_DEAUTH - testing_resend_assoc");
break;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->disconnected) {
wpa_printf(MSG_INFO,
"Ignore unexpected EVENT_ASSOC in disconnected state");
break;
}
wpa_supplicant_event_assoc(wpa_s, data);
wpa_s->assoc_status_code = WLAN_STATUS_SUCCESS;
if (data &&
(data->assoc_info.authorized ||
(!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
wpa_fils_is_completed(wpa_s->wpa))))
wpa_supplicant_event_assoc_auth(wpa_s, data);
if (data) {
wpa_msg(wpa_s, MSG_INFO,
WPA_EVENT_SUBNET_STATUS_UPDATE "status=%u",
data->assoc_info.subnet_status);
}
break;
case EVENT_DISASSOC:
wpas_event_disassoc(wpa_s,
data ? &data->disassoc_info : NULL);
break;
case EVENT_DEAUTH:
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->ignore_auth_resp) {
wpa_printf(MSG_INFO,
"EVENT_DEAUTH - ignore_auth_resp active!");
break;
}
if (wpa_s->testing_resend_assoc) {
wpa_printf(MSG_INFO,
"EVENT_DEAUTH - testing_resend_assoc");
break;
}
#endif /* CONFIG_TESTING_OPTIONS */
wpas_event_deauth(wpa_s,
data ? &data->deauth_info : NULL);
break;
case EVENT_MICHAEL_MIC_FAILURE:
wpa_supplicant_event_michael_mic_failure(wpa_s, data);
break;
#ifndef CONFIG_NO_SCAN_PROCESSING
case EVENT_SCAN_STARTED:
if (wpa_s->own_scan_requested ||
(data && !data->scan_info.external_scan)) {
struct os_reltime diff;
os_get_reltime(&wpa_s->scan_start_time);
os_reltime_sub(&wpa_s->scan_start_time,
&wpa_s->scan_trigger_time, &diff);
wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds",
diff.sec, diff.usec);
wpa_s->own_scan_requested = 0;
wpa_s->own_scan_running = 1;
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
wpa_s->manual_scan_use_id) {
wpa_msg_ctrl(wpa_s, MSG_INFO,
WPA_EVENT_SCAN_STARTED "id=%u",
wpa_s->manual_scan_id);
} else {
wpa_msg_ctrl(wpa_s, MSG_INFO,
WPA_EVENT_SCAN_STARTED);
}
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan");
wpa_s->radio->external_scan_req_interface = wpa_s;
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED);
}
break;
case EVENT_SCAN_RESULTS:
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_s->scan_res_handler = NULL;
wpa_s->own_scan_running = 0;
wpa_s->radio->external_scan_req_interface = NULL;
wpa_s->last_scan_req = NORMAL_SCAN_REQ;
break;
}
if (!(data && data->scan_info.external_scan) &&
os_reltime_initialized(&wpa_s->scan_start_time)) {
struct os_reltime now, diff;
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
wpa_s->scan_start_time.sec = 0;
wpa_s->scan_start_time.usec = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",
diff.sec, diff.usec);
}
if (wpa_supplicant_event_scan_results(wpa_s, data))
break; /* interface may have been removed */
if (!(data && data->scan_info.external_scan))
wpa_s->own_scan_running = 0;
if (data && data->scan_info.nl_scan_event)
wpa_s->radio->external_scan_req_interface = NULL;
radio_work_check_next(wpa_s);
break;
#endif /* CONFIG_NO_SCAN_PROCESSING */
case EVENT_ASSOCINFO:
wpa_supplicant_event_associnfo(wpa_s, data);
break;
case EVENT_INTERFACE_STATUS:
wpa_supplicant_event_interface_status(wpa_s, data);
break;
case EVENT_PMKID_CANDIDATE:
wpa_supplicant_event_pmkid_candidate(wpa_s, data);
break;
#ifdef CONFIG_TDLS
case EVENT_TDLS:
wpa_supplicant_event_tdls(wpa_s, data);
break;
#endif /* CONFIG_TDLS */
#ifdef CONFIG_WNM
case EVENT_WNM:
wpa_supplicant_event_wnm(wpa_s, data);
break;
#endif /* CONFIG_WNM */
#ifdef CONFIG_IEEE80211R
case EVENT_FT_RESPONSE:
wpa_supplicant_event_ft_response(wpa_s, data);
break;
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IBSS_RSN
case EVENT_IBSS_RSN_START:
wpa_supplicant_event_ibss_rsn_start(wpa_s, data);
break;
#endif /* CONFIG_IBSS_RSN */
case EVENT_ASSOC_REJECT:
wpas_event_assoc_reject(wpa_s, data);
break;
case EVENT_AUTH_TIMED_OUT:
/* It is possible to get this event from earlier connection */
if (wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_MESH) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Ignore AUTH_TIMED_OUT in mesh configuration");
break;
}
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
sme_event_auth_timed_out(wpa_s, data);
break;
case EVENT_ASSOC_TIMED_OUT:
/* It is possible to get this event from earlier connection */
if (wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_MESH) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Ignore ASSOC_TIMED_OUT in mesh configuration");
break;
}
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
sme_event_assoc_timed_out(wpa_s, data);
break;
case EVENT_TX_STATUS:
wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS dst=" MACSTR
" type=%d stype=%d",
MAC2STR(data->tx_status.dst),
data->tx_status.type, data->tx_status.stype);
#ifdef CONFIG_PASN
if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
data->tx_status.stype == WLAN_FC_STYPE_AUTH &&
wpas_pasn_auth_tx_status(wpa_s, data->tx_status.data,
data->tx_status.data_len,
data->tx_status.ack) == 0)
break;
#endif /* CONFIG_PASN */
#ifdef CONFIG_AP
if (wpa_s->ap_iface == NULL) {
#ifdef CONFIG_OFFCHANNEL
if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
data->tx_status.stype == WLAN_FC_STYPE_ACTION)
offchannel_send_action_tx_status(
wpa_s, data->tx_status.dst,
data->tx_status.data,
data->tx_status.data_len,
data->tx_status.ack ?
OFFCHANNEL_SEND_ACTION_SUCCESS :
OFFCHANNEL_SEND_ACTION_NO_ACK);
#endif /* CONFIG_OFFCHANNEL */
break;
}
#endif /* CONFIG_AP */
#ifdef CONFIG_OFFCHANNEL
wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst="
MACSTR, MAC2STR(wpa_s->p2pdev->pending_action_dst));
/*
* Catch TX status events for Action frames we sent via group
* interface in GO mode, or via standalone AP interface.
* Note, wpa_s->p2pdev will be the same as wpa_s->parent,
* except when the primary interface is used as a GO interface
* (for drivers which do not have group interface concurrency)
*/
if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
os_memcmp(wpa_s->p2pdev->pending_action_dst,
data->tx_status.dst, ETH_ALEN) == 0) {
offchannel_send_action_tx_status(
wpa_s->p2pdev, data->tx_status.dst,
data->tx_status.data,
data->tx_status.data_len,
data->tx_status.ack ?
OFFCHANNEL_SEND_ACTION_SUCCESS :
OFFCHANNEL_SEND_ACTION_NO_ACK);
break;
}
#endif /* CONFIG_OFFCHANNEL */
#ifdef CONFIG_AP
switch (data->tx_status.type) {
case WLAN_FC_TYPE_MGMT:
ap_mgmt_tx_cb(wpa_s, data->tx_status.data,
data->tx_status.data_len,
data->tx_status.stype,
data->tx_status.ack);
break;
case WLAN_FC_TYPE_DATA:
ap_tx_status(wpa_s, data->tx_status.dst,
data->tx_status.data,
data->tx_status.data_len,
data->tx_status.ack);
break;
}
#endif /* CONFIG_AP */
break;
#ifdef CONFIG_AP
case EVENT_EAPOL_TX_STATUS:
ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst,
data->eapol_tx_status.data,
data->eapol_tx_status.data_len,
data->eapol_tx_status.ack);
break;
case EVENT_DRIVER_CLIENT_POLL_OK:
ap_client_poll_ok(wpa_s, data->client_poll.addr);
break;
case EVENT_RX_FROM_UNKNOWN:
if (wpa_s->ap_iface == NULL)
break;
ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr,
data->rx_from_unknown.wds);
break;
#endif /* CONFIG_AP */
case EVENT_CH_SWITCH_STARTED:
case EVENT_CH_SWITCH:
if (!data || !wpa_s->current_ssid)
break;
wpa_msg(wpa_s, MSG_INFO,
"%sfreq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d",
event == EVENT_CH_SWITCH ? WPA_EVENT_CHANNEL_SWITCH :
WPA_EVENT_CHANNEL_SWITCH_STARTED,
data->ch_switch.freq,
data->ch_switch.ht_enabled,
data->ch_switch.ch_offset,
channel_width_to_string(data->ch_switch.ch_width),
data->ch_switch.cf1,
data->ch_switch.cf2);
if (event == EVENT_CH_SWITCH_STARTED)
break;
wpa_s->assoc_freq = data->ch_switch.freq;
wpa_s->current_ssid->frequency = data->ch_switch.freq;
if (wpa_s->current_bss &&
wpa_s->current_bss->freq != data->ch_switch.freq) {
wpa_s->current_bss->freq = data->ch_switch.freq;
notify_bss_changes(wpa_s, WPA_BSS_FREQ_CHANGED_FLAG,
wpa_s->current_bss);
}
#ifdef CONFIG_SME
switch (data->ch_switch.ch_offset) {
case 1:
wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_ABOVE;
break;
case -1:
wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_BELOW;
break;
default:
wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_UNKNOWN;
break;
}
#endif /* CONFIG_SME */
#ifdef CONFIG_AP
if (wpa_s->current_ssid->mode == WPAS_MODE_AP ||
wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO ||
wpa_s->current_ssid->mode == WPAS_MODE_MESH ||
wpa_s->current_ssid->mode ==
WPAS_MODE_P2P_GROUP_FORMATION) {
wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
data->ch_switch.ht_enabled,
data->ch_switch.ch_offset,
data->ch_switch.ch_width,
data->ch_switch.cf1,
data->ch_switch.cf2,
1);
}
#endif /* CONFIG_AP */
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
sme_event_ch_switch(wpa_s);
wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS);
wnm_clear_coloc_intf_reporting(wpa_s);
break;
#ifdef CONFIG_AP
#ifdef NEED_AP_MLME
case EVENT_DFS_RADAR_DETECTED:
if (data)
wpas_ap_event_dfs_radar_detected(wpa_s,
&data->dfs_event);
break;
case EVENT_DFS_NOP_FINISHED:
if (data)
wpas_ap_event_dfs_cac_nop_finished(wpa_s,
&data->dfs_event);
break;
#endif /* NEED_AP_MLME */
#endif /* CONFIG_AP */
case EVENT_DFS_CAC_STARTED:
if (data)
wpas_event_dfs_cac_started(wpa_s, &data->dfs_event);
break;
case EVENT_DFS_CAC_FINISHED:
if (data)
wpas_event_dfs_cac_finished(wpa_s, &data->dfs_event);
break;
case EVENT_DFS_CAC_ABORTED:
if (data)
wpas_event_dfs_cac_aborted(wpa_s, &data->dfs_event);
break;
case EVENT_RX_MGMT: {
u16 fc, stype;
const struct ieee80211_mgmt *mgmt;
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->ext_mgmt_frame_handling) {
struct rx_mgmt *rx = &data->rx_mgmt;
size_t hex_len = 2 * rx->frame_len + 1;
char *hex = os_malloc(hex_len);
if (hex) {
wpa_snprintf_hex(hex, hex_len,
rx->frame, rx->frame_len);
wpa_msg(wpa_s, MSG_INFO, "MGMT-RX freq=%d datarate=%u ssi_signal=%d %s",
rx->freq, rx->datarate, rx->ssi_signal,
hex);
os_free(hex);
}
break;
}
#endif /* CONFIG_TESTING_OPTIONS */
mgmt = (const struct ieee80211_mgmt *)
data->rx_mgmt.frame;
fc = le_to_host16(mgmt->frame_control);
stype = WLAN_FC_GET_STYPE(fc);
#ifdef CONFIG_AP
if (wpa_s->ap_iface == NULL) {
#endif /* CONFIG_AP */
#ifdef CONFIG_P2P
if (stype == WLAN_FC_STYPE_PROBE_REQ &&
data->rx_mgmt.frame_len > IEEE80211_HDRLEN) {
const u8 *src = mgmt->sa;
const u8 *ie;
size_t ie_len;
ie = data->rx_mgmt.frame + IEEE80211_HDRLEN;
ie_len = data->rx_mgmt.frame_len -
IEEE80211_HDRLEN;
wpas_p2p_probe_req_rx(
wpa_s, src, mgmt->da,
mgmt->bssid, ie, ie_len,
data->rx_mgmt.freq,
data->rx_mgmt.ssi_signal);
break;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_IBSS_RSN
if (wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
stype == WLAN_FC_STYPE_AUTH &&
data->rx_mgmt.frame_len >= 30) {
wpa_supplicant_event_ibss_auth(wpa_s, data);
break;
}
#endif /* CONFIG_IBSS_RSN */
if (stype == WLAN_FC_STYPE_ACTION) {
wpas_event_rx_mgmt_action(
wpa_s, data->rx_mgmt.frame,
data->rx_mgmt.frame_len,
data->rx_mgmt.freq,
data->rx_mgmt.ssi_signal);
break;
}
if (wpa_s->ifmsh) {
mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt);
break;
}
#ifdef CONFIG_PASN
if (stype == WLAN_FC_STYPE_AUTH &&
wpas_pasn_auth_rx(wpa_s, mgmt,
data->rx_mgmt.frame_len) != -2)
break;
#endif /* CONFIG_PASN */
#ifdef CONFIG_SAE
if (stype == WLAN_FC_STYPE_AUTH &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) {
sme_external_auth_mgmt_rx(
wpa_s, data->rx_mgmt.frame,
data->rx_mgmt.frame_len);
break;
}
#endif /* CONFIG_SAE */
wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
"management frame in non-AP mode");
break;
#ifdef CONFIG_AP
}
if (stype == WLAN_FC_STYPE_PROBE_REQ &&
data->rx_mgmt.frame_len > IEEE80211_HDRLEN) {
const u8 *ie;
size_t ie_len;
ie = data->rx_mgmt.frame + IEEE80211_HDRLEN;
ie_len = data->rx_mgmt.frame_len - IEEE80211_HDRLEN;
wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da,
mgmt->bssid, ie, ie_len,
data->rx_mgmt.ssi_signal);
}
ap_mgmt_rx(wpa_s, &data->rx_mgmt);
#endif /* CONFIG_AP */
break;
}
case EVENT_RX_PROBE_REQ:
if (data->rx_probe_req.sa == NULL ||
data->rx_probe_req.ie == NULL)
break;
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
hostapd_probe_req_rx(wpa_s->ap_iface->bss[0],
data->rx_probe_req.sa,
data->rx_probe_req.da,
data->rx_probe_req.bssid,
data->rx_probe_req.ie,
data->rx_probe_req.ie_len,
data->rx_probe_req.ssi_signal);
break;
}
#endif /* CONFIG_AP */
wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa,
data->rx_probe_req.da,
data->rx_probe_req.bssid,
data->rx_probe_req.ie,
data->rx_probe_req.ie_len,
0,
data->rx_probe_req.ssi_signal);
break;
case EVENT_REMAIN_ON_CHANNEL:
#ifdef CONFIG_OFFCHANNEL
offchannel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq,
data->remain_on_channel.duration);
#endif /* CONFIG_OFFCHANNEL */
wpas_p2p_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq,
data->remain_on_channel.duration);
#ifdef CONFIG_DPP
wpas_dpp_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq,
data->remain_on_channel.duration);
#endif /* CONFIG_DPP */
break;
case EVENT_CANCEL_REMAIN_ON_CHANNEL:
#ifdef CONFIG_OFFCHANNEL
offchannel_cancel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq);
#endif /* CONFIG_OFFCHANNEL */
wpas_p2p_cancel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq);
#ifdef CONFIG_DPP
wpas_dpp_cancel_remain_on_channel_cb(
wpa_s, data->remain_on_channel.freq);
#endif /* CONFIG_DPP */
break;
case EVENT_EAPOL_RX:
wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
data->eapol_rx.data,
data->eapol_rx.data_len);
break;
case EVENT_SIGNAL_CHANGE:
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
"above=%d signal=%d noise=%d txrate=%d",
data->signal_change.above_threshold,
data->signal_change.current_signal,
data->signal_change.current_noise,
data->signal_change.current_txrate);
wpa_bss_update_level(wpa_s->current_bss,
data->signal_change.current_signal);
bgscan_notify_signal_change(
wpa_s, data->signal_change.above_threshold,
data->signal_change.current_signal,
data->signal_change.current_noise,
data->signal_change.current_txrate);
break;
case EVENT_INTERFACE_MAC_CHANGED:
wpa_supplicant_update_mac_addr(wpa_s);
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
break;
case EVENT_INTERFACE_ENABLED:
wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+ u8 addr[ETH_ALEN];
+
eloop_cancel_timeout(wpas_clear_disabled_interface,
wpa_s, NULL);
+ os_memcpy(addr, wpa_s->own_addr, ETH_ALEN);
wpa_supplicant_update_mac_addr(wpa_s);
+ if (os_memcmp(addr, wpa_s->own_addr, ETH_ALEN) != 0)
+ wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+ else
+ wpa_sm_pmksa_cache_reconfig(wpa_s->wpa);
wpa_supplicant_set_default_scan_ies(wpa_s);
if (wpa_s->p2p_mgmt) {
wpa_supplicant_set_state(wpa_s,
WPA_DISCONNECTED);
break;
}
#ifdef CONFIG_AP
if (!wpa_s->ap_iface) {
wpa_supplicant_set_state(wpa_s,
WPA_DISCONNECTED);
wpa_s->scan_req = NORMAL_SCAN_REQ;
wpa_supplicant_req_scan(wpa_s, 0, 0);
} else
wpa_supplicant_set_state(wpa_s,
WPA_COMPLETED);
#else /* CONFIG_AP */
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
wpa_supplicant_req_scan(wpa_s, 0, 0);
#endif /* CONFIG_AP */
}
break;
case EVENT_INTERFACE_DISABLED:
wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled");
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO ||
(wpa_s->current_ssid && wpa_s->current_ssid->p2p_group &&
wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)) {
/*
* Mark interface disabled if this happens to end up not
* being removed as a separate P2P group interface.
*/
wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
/*
* The interface was externally disabled. Remove
* it assuming an external entity will start a
* new session if needed.
*/
if (wpa_s->current_ssid &&
wpa_s->current_ssid->p2p_group)
wpas_p2p_interface_unavailable(wpa_s);
else
wpas_p2p_disconnect(wpa_s);
/*
* wpa_s instance may have been freed, so must not use
* it here anymore.
*/
break;
}
if (wpa_s->p2p_scan_work && wpa_s->global->p2p &&
p2p_in_progress(wpa_s->global->p2p) > 1) {
/* This radio work will be cancelled, so clear P2P
* state as well.
*/
p2p_stop_find(wpa_s->global->p2p);
}
#endif /* CONFIG_P2P */
if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
/*
* Indicate disconnection to keep ctrl_iface events
* consistent.
*/
wpa_supplicant_event_disassoc(
wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1);
}
wpa_supplicant_mark_disassoc(wpa_s);
os_reltime_age(&wpa_s->last_scan, &age);
if (age.sec >= wpa_s->conf->scan_res_valid_for_connect) {
clear_at.sec = wpa_s->conf->scan_res_valid_for_connect;
clear_at.usec = 0;
} else {
struct os_reltime tmp;
tmp.sec = wpa_s->conf->scan_res_valid_for_connect;
tmp.usec = 0;
os_reltime_sub(&tmp, &age, &clear_at);
}
eloop_register_timeout(clear_at.sec, clear_at.usec,
wpas_clear_disabled_interface,
wpa_s, NULL);
radio_remove_works(wpa_s, NULL, 0);
wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
break;
case EVENT_CHANNEL_LIST_CHANGED:
wpa_supplicant_update_channel_list(
wpa_s, &data->channel_list_changed);
break;
case EVENT_INTERFACE_UNAVAILABLE:
wpas_p2p_interface_unavailable(wpa_s);
break;
case EVENT_BEST_CHANNEL:
wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received "
"(%d %d %d)",
data->best_chan.freq_24, data->best_chan.freq_5,
data->best_chan.freq_overall);
wpa_s->best_24_freq = data->best_chan.freq_24;
wpa_s->best_5_freq = data->best_chan.freq_5;
wpa_s->best_overall_freq = data->best_chan.freq_overall;
wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24,
data->best_chan.freq_5,
data->best_chan.freq_overall);
break;
case EVENT_UNPROT_DEAUTH:
wpa_supplicant_event_unprot_deauth(wpa_s,
&data->unprot_deauth);
break;
case EVENT_UNPROT_DISASSOC:
wpa_supplicant_event_unprot_disassoc(wpa_s,
&data->unprot_disassoc);
break;
case EVENT_STATION_LOW_ACK:
#ifdef CONFIG_AP
if (wpa_s->ap_iface && data)
hostapd_event_sta_low_ack(wpa_s->ap_iface->bss[0],
data->low_ack.addr);
#endif /* CONFIG_AP */
#ifdef CONFIG_TDLS
if (data)
wpa_tdls_disable_unreachable_link(wpa_s->wpa,
data->low_ack.addr);
#endif /* CONFIG_TDLS */
break;
case EVENT_IBSS_PEER_LOST:
#ifdef CONFIG_IBSS_RSN
ibss_rsn_stop(wpa_s->ibss_rsn, data->ibss_peer_lost.peer);
#endif /* CONFIG_IBSS_RSN */
break;
case EVENT_DRIVER_GTK_REKEY:
if (os_memcmp(data->driver_gtk_rekey.bssid,
wpa_s->bssid, ETH_ALEN))
break;
if (!wpa_s->wpa)
break;
wpa_sm_update_replay_ctr(wpa_s->wpa,
data->driver_gtk_rekey.replay_ctr);
break;
case EVENT_SCHED_SCAN_STOPPED:
wpa_s->sched_scanning = 0;
resched = wpa_s->scanning && wpas_scan_scheduled(wpa_s);
wpa_supplicant_notify_scanning(wpa_s, 0);
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
break;
/*
* If the driver stopped scanning without being requested to,
* request a new scan to continue scanning for networks.
*/
if (!wpa_s->sched_scan_stop_req &&
wpa_s->wpa_state == WPA_SCANNING) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Restart scanning after unexpected sched_scan stop event");
wpa_supplicant_req_scan(wpa_s, 1, 0);
break;
}
wpa_s->sched_scan_stop_req = 0;
/*
* Start a new sched scan to continue searching for more SSIDs
* either if timed out or PNO schedule scan is pending.
*/
if (wpa_s->sched_scan_timed_out) {
wpa_supplicant_req_sched_scan(wpa_s);
} else if (wpa_s->pno_sched_pending) {
wpa_s->pno_sched_pending = 0;
wpas_start_pno(wpa_s);
} else if (resched) {
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
break;
case EVENT_WPS_BUTTON_PUSHED:
#ifdef CONFIG_WPS
wpas_wps_start_pbc(wpa_s, NULL, 0, 0);
#endif /* CONFIG_WPS */
break;
case EVENT_AVOID_FREQUENCIES:
wpa_supplicant_notify_avoid_freq(wpa_s, data);
break;
case EVENT_CONNECT_FAILED_REASON:
#ifdef CONFIG_AP
if (!wpa_s->ap_iface || !data)
break;
hostapd_event_connect_failed_reason(
wpa_s->ap_iface->bss[0],
data->connect_failed_reason.addr,
data->connect_failed_reason.code);
#endif /* CONFIG_AP */
break;
case EVENT_NEW_PEER_CANDIDATE:
#ifdef CONFIG_MESH
if (!wpa_s->ifmsh || !data)
break;
wpa_mesh_notify_peer(wpa_s, data->mesh_peer.peer,
data->mesh_peer.ies,
data->mesh_peer.ie_len);
#endif /* CONFIG_MESH */
break;
case EVENT_SURVEY:
#ifdef CONFIG_AP
if (!wpa_s->ap_iface)
break;
hostapd_event_get_survey(wpa_s->ap_iface,
&data->survey_results);
#endif /* CONFIG_AP */
break;
case EVENT_ACS_CHANNEL_SELECTED:
#ifdef CONFIG_AP
#ifdef CONFIG_ACS
if (!wpa_s->ap_iface)
break;
hostapd_acs_channel_selected(wpa_s->ap_iface->bss[0],
&data->acs_selected_channels);
#endif /* CONFIG_ACS */
#endif /* CONFIG_AP */
break;
case EVENT_P2P_LO_STOP:
#ifdef CONFIG_P2P
wpa_s->p2p_lo_started = 0;
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_LISTEN_OFFLOAD_STOP
P2P_LISTEN_OFFLOAD_STOP_REASON "reason=%d",
data->p2p_lo_stop.reason_code);
#endif /* CONFIG_P2P */
break;
case EVENT_BEACON_LOSS:
if (!wpa_s->current_bss || !wpa_s->current_ssid)
break;
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_BEACON_LOSS);
bgscan_notify_beacon_loss(wpa_s);
break;
case EVENT_EXTERNAL_AUTH:
#ifdef CONFIG_SAE
if (!wpa_s->current_ssid) {
wpa_printf(MSG_DEBUG, "SAE: current_ssid is NULL");
break;
}
sme_external_auth_trigger(wpa_s, data);
#endif /* CONFIG_SAE */
break;
case EVENT_PORT_AUTHORIZED:
wpa_supplicant_event_port_authorized(wpa_s);
break;
case EVENT_STATION_OPMODE_CHANGED:
#ifdef CONFIG_AP
if (!wpa_s->ap_iface || !data)
break;
hostapd_event_sta_opmode_changed(wpa_s->ap_iface->bss[0],
data->sta_opmode.addr,
data->sta_opmode.smps_mode,
data->sta_opmode.chan_width,
data->sta_opmode.rx_nss);
#endif /* CONFIG_AP */
break;
case EVENT_UNPROT_BEACON:
wpas_event_unprot_beacon(wpa_s, &data->unprot_beacon);
break;
default:
wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
break;
}
}
void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
union wpa_event_data *data)
{
struct wpa_supplicant *wpa_s;
if (event != EVENT_INTERFACE_STATUS)
return;
wpa_s = wpa_supplicant_get_iface(ctx, data->interface_status.ifname);
if (wpa_s && wpa_s->driver->get_ifindex) {
unsigned int ifindex;
ifindex = wpa_s->driver->get_ifindex(wpa_s->drv_priv);
if (ifindex != data->interface_status.ifindex) {
wpa_dbg(wpa_s, MSG_DEBUG,
"interface status ifindex %d mismatch (%d)",
ifindex, data->interface_status.ifindex);
return;
}
}
#ifdef CONFIG_MATCH_IFACE
else if (data->interface_status.ievent == EVENT_INTERFACE_ADDED) {
struct wpa_interface *wpa_i;
wpa_i = wpa_supplicant_match_iface(
ctx, data->interface_status.ifname);
if (!wpa_i)
return;
wpa_s = wpa_supplicant_add_iface(ctx, wpa_i, NULL);
os_free(wpa_i);
}
#endif /* CONFIG_MATCH_IFACE */
if (wpa_s)
wpa_supplicant_event(wpa_s, event, data);
}
diff --git a/contrib/wpa/wpa_supplicant/gas_query.c b/contrib/wpa/wpa_supplicant/gas_query.c
index e60a8c1fe6aa..a6172d69233b 100644
--- a/contrib/wpa/wpa_supplicant/gas_query.c
+++ b/contrib/wpa/wpa_supplicant/gas_query.c
@@ -1,907 +1,897 @@
/*
* Generic advertisement service (GAS) query
* Copyright (c) 2009, Atheros Communications
* Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
* Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/gas.h"
#include "common/wpa_ctrl.h"
#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "config.h"
#include "driver_i.h"
#include "offchannel.h"
#include "gas_query.h"
/** GAS query timeout in seconds */
#define GAS_QUERY_TIMEOUT_PERIOD 2
/* GAS query wait-time / duration in ms */
#define GAS_QUERY_WAIT_TIME_INITIAL 1000
#define GAS_QUERY_WAIT_TIME_COMEBACK 150
/**
* struct gas_query_pending - Pending GAS query
*/
struct gas_query_pending {
struct dl_list list;
struct gas_query *gas;
u8 addr[ETH_ALEN];
u8 dialog_token;
u8 next_frag_id;
unsigned int wait_comeback:1;
unsigned int offchannel_tx_started:1;
unsigned int retry:1;
unsigned int wildcard_bssid:1;
unsigned int maintain_addr:1;
int freq;
u16 status_code;
struct wpabuf *req;
struct wpabuf *adv_proto;
struct wpabuf *resp;
struct os_reltime last_oper;
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
const struct wpabuf *resp, u16 status_code);
void *ctx;
u8 sa[ETH_ALEN];
};
/**
* struct gas_query - Internal GAS query data
*/
struct gas_query {
struct wpa_supplicant *wpa_s;
struct dl_list pending; /* struct gas_query_pending */
struct gas_query_pending *current;
struct wpa_radio_work *work;
struct os_reltime last_mac_addr_rand;
int last_rand_sa_type;
u8 rand_addr[ETH_ALEN];
};
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
static void gas_query_timeout(void *eloop_data, void *user_ctx);
static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
static void gas_query_tx_initial_req(struct gas_query *gas,
struct gas_query_pending *query);
static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst);
static int ms_from_time(struct os_reltime *last)
{
struct os_reltime now, res;
os_get_reltime(&now);
os_reltime_sub(&now, last, &res);
return res.sec * 1000 + res.usec / 1000;
}
/**
* gas_query_init - Initialize GAS query component
* @wpa_s: Pointer to wpa_supplicant data
* Returns: Pointer to GAS query data or %NULL on failure
*/
struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
{
struct gas_query *gas;
gas = os_zalloc(sizeof(*gas));
if (gas == NULL)
return NULL;
gas->wpa_s = wpa_s;
dl_list_init(&gas->pending);
return gas;
}
static const char * gas_result_txt(enum gas_query_result result)
{
switch (result) {
case GAS_QUERY_SUCCESS:
return "SUCCESS";
case GAS_QUERY_FAILURE:
return "FAILURE";
case GAS_QUERY_TIMEOUT:
return "TIMEOUT";
case GAS_QUERY_PEER_ERROR:
return "PEER_ERROR";
case GAS_QUERY_INTERNAL_ERROR:
return "INTERNAL_ERROR";
case GAS_QUERY_STOPPED:
return "STOPPED";
case GAS_QUERY_DELETED_AT_DEINIT:
return "DELETED_AT_DEINIT";
}
return "N/A";
}
static void gas_query_free(struct gas_query_pending *query, int del_list)
{
struct gas_query *gas = query->gas;
if (del_list)
dl_list_del(&query->list);
if (gas->work && gas->work->ctx == query) {
radio_work_done(gas->work);
gas->work = NULL;
}
wpabuf_free(query->req);
wpabuf_free(query->adv_proto);
wpabuf_free(query->resp);
os_free(query);
}
static void gas_query_done(struct gas_query *gas,
struct gas_query_pending *query,
enum gas_query_result result)
{
wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
" dialog_token=%u freq=%d status_code=%u result=%s",
MAC2STR(query->addr), query->dialog_token, query->freq,
query->status_code, gas_result_txt(result));
if (gas->current == query)
gas->current = NULL;
if (query->offchannel_tx_started)
offchannel_send_action_done(gas->wpa_s);
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
eloop_cancel_timeout(gas_query_timeout, gas, query);
eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
dl_list_del(&query->list);
query->cb(query->ctx, query->addr, query->dialog_token, result,
query->adv_proto, query->resp, query->status_code);
gas_query_free(query, 0);
}
/**
* gas_query_deinit - Deinitialize GAS query component
* @gas: GAS query data from gas_query_init()
*/
void gas_query_deinit(struct gas_query *gas)
{
struct gas_query_pending *query, *next;
if (gas == NULL)
return;
dl_list_for_each_safe(query, next, &gas->pending,
struct gas_query_pending, list)
gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
os_free(gas);
}
static struct gas_query_pending *
gas_query_get_pending(struct gas_query *gas, const u8 *addr, u8 dialog_token)
{
struct gas_query_pending *q;
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 &&
q->dialog_token == dialog_token)
return q;
}
return NULL;
}
static int gas_query_append(struct gas_query_pending *query, const u8 *data,
size_t len)
{
if (wpabuf_resize(&query->resp, len) < 0) {
wpa_printf(MSG_DEBUG, "GAS: No memory to store the response");
return -1;
}
wpabuf_put_data(query->resp, data, len);
return 0;
}
static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
const u8 *data, size_t data_len,
enum offchannel_send_action_result result)
{
struct gas_query_pending *query;
struct gas_query *gas = wpa_s->gas;
int dur;
if (gas->current == NULL) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
MACSTR " result=%d - no query in progress",
freq, MAC2STR(dst), result);
return;
}
query = gas->current;
dur = ms_from_time(&query->last_oper);
wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
" result=%d query=%p dialog_token=%u dur=%d ms",
freq, MAC2STR(dst), result, query, query->dialog_token, dur);
if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
return;
}
os_get_reltime(&query->last_oper);
if (result == OFFCHANNEL_SEND_ACTION_SUCCESS ||
result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
eloop_cancel_timeout(gas_query_timeout, gas, query);
if (result == OFFCHANNEL_SEND_ACTION_NO_ACK) {
wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request");
eloop_register_timeout(0, 250000,
gas_query_timeout, gas, query);
} else {
eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
gas_query_timeout, gas, query);
}
if (query->wait_comeback && !query->retry) {
eloop_cancel_timeout(gas_query_rx_comeback_timeout,
gas, query);
eloop_register_timeout(
0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
gas_query_rx_comeback_timeout, gas, query);
}
}
if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
eloop_cancel_timeout(gas_query_timeout, gas, query);
eloop_register_timeout(0, 0, gas_query_timeout, gas, query);
}
}
-int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
-{
- if (wpa_s->current_ssid == NULL ||
- wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
- os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
- return 0;
- return wpa_sm_pmf_enabled(wpa_s->wpa);
-}
-
-
static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
struct wpabuf *req, unsigned int wait_time)
{
int res, prot = pmf_in_use(gas->wpa_s, query->addr);
const u8 *bssid;
const u8 wildcard_bssid[ETH_ALEN] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
"freq=%d prot=%d using src addr " MACSTR,
MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
query->freq, prot, MAC2STR(query->sa));
if (prot) {
u8 *categ = wpabuf_mhead_u8(req);
*categ = WLAN_ACTION_PROTECTED_DUAL;
}
os_get_reltime(&query->last_oper);
if (gas->wpa_s->max_remain_on_chan &&
wait_time > gas->wpa_s->max_remain_on_chan)
wait_time = gas->wpa_s->max_remain_on_chan;
if (!query->wildcard_bssid &&
(!gas->wpa_s->conf->gas_address3 ||
(gas->wpa_s->current_ssid &&
gas->wpa_s->wpa_state >= WPA_ASSOCIATED &&
os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0)))
bssid = query->addr;
else
bssid = wildcard_bssid;
res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
query->sa, bssid, wpabuf_head(req),
wpabuf_len(req), wait_time,
gas_query_tx_status, 0);
if (res == 0)
query->offchannel_tx_started = 1;
return res;
}
static void gas_query_tx_comeback_req(struct gas_query *gas,
struct gas_query_pending *query)
{
struct wpabuf *req;
unsigned int wait_time;
req = gas_build_comeback_req(query->dialog_token);
if (req == NULL) {
gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
return;
}
wait_time = (query->retry || !query->offchannel_tx_started) ?
GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
if (gas_query_tx(gas, query, req, wait_time) < 0) {
wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
MACSTR, MAC2STR(query->addr));
gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
}
wpabuf_free(req);
}
static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
{
struct gas_query *gas = eloop_data;
struct gas_query_pending *query = user_ctx;
int dialog_token;
wpa_printf(MSG_DEBUG,
"GAS: No response to comeback request received (retry=%u)",
query->retry);
if (gas->current != query || query->retry)
return;
dialog_token = gas_query_new_dialog_token(gas, query->addr);
if (dialog_token < 0)
return;
wpa_printf(MSG_DEBUG,
"GAS: Retry GAS query due to comeback response timeout");
query->retry = 1;
query->dialog_token = dialog_token;
*(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
query->wait_comeback = 0;
query->next_frag_id = 0;
wpabuf_free(query->adv_proto);
query->adv_proto = NULL;
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
eloop_cancel_timeout(gas_query_timeout, gas, query);
gas_query_tx_initial_req(gas, query);
}
static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
{
struct gas_query *gas = eloop_data;
struct gas_query_pending *query = user_ctx;
wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR,
MAC2STR(query->addr));
gas_query_tx_comeback_req(gas, query);
}
static void gas_query_tx_comeback_req_delay(struct gas_query *gas,
struct gas_query_pending *query,
u16 comeback_delay)
{
unsigned int secs, usecs;
if (comeback_delay > 1 && query->offchannel_tx_started) {
offchannel_send_action_done(gas->wpa_s);
query->offchannel_tx_started = 0;
}
secs = (comeback_delay * 1024) / 1000000;
usecs = comeback_delay * 1024 - secs * 1000000;
wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
" in %u secs %u usecs", MAC2STR(query->addr), secs, usecs);
eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout,
gas, query);
}
static void gas_query_rx_initial(struct gas_query *gas,
struct gas_query_pending *query,
const u8 *adv_proto, const u8 *resp,
size_t len, u16 comeback_delay)
{
wpa_printf(MSG_DEBUG, "GAS: Received initial response from "
MACSTR " (dialog_token=%u comeback_delay=%u)",
MAC2STR(query->addr), query->dialog_token, comeback_delay);
query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]);
if (query->adv_proto == NULL) {
gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
return;
}
if (comeback_delay) {
eloop_cancel_timeout(gas_query_timeout, gas, query);
query->wait_comeback = 1;
gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
return;
}
/* Query was completed without comeback mechanism */
if (gas_query_append(query, resp, len) < 0) {
gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
return;
}
gas_query_done(gas, query, GAS_QUERY_SUCCESS);
}
static void gas_query_rx_comeback(struct gas_query *gas,
struct gas_query_pending *query,
const u8 *adv_proto, const u8 *resp,
size_t len, u8 frag_id, u8 more_frags,
u16 comeback_delay)
{
wpa_printf(MSG_DEBUG, "GAS: Received comeback response from "
MACSTR " (dialog_token=%u frag_id=%u more_frags=%u "
"comeback_delay=%u)",
MAC2STR(query->addr), query->dialog_token, frag_id,
more_frags, comeback_delay);
eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
wpabuf_len(query->adv_proto)) != 0) {
wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed "
"between initial and comeback response from "
MACSTR, MAC2STR(query->addr));
gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
return;
}
if (comeback_delay) {
if (frag_id) {
wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response "
"with non-zero frag_id and comeback_delay "
"from " MACSTR, MAC2STR(query->addr));
gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
return;
}
gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
return;
}
if (frag_id != query->next_frag_id) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response "
"from " MACSTR, MAC2STR(query->addr));
if (frag_id + 1 == query->next_frag_id) {
wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible "
"retry of previous fragment");
return;
}
gas_query_done(gas, query, GAS_QUERY_PEER_ERROR);
return;
}
query->next_frag_id++;
if (gas_query_append(query, resp, len) < 0) {
gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
return;
}
if (more_frags) {
gas_query_tx_comeback_req(gas, query);
return;
}
gas_query_done(gas, query, GAS_QUERY_SUCCESS);
}
/**
* gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
* @gas: GAS query data from gas_query_init()
* @da: Destination MAC address of the Action frame
* @sa: Source MAC address of the Action frame
* @bssid: BSSID of the Action frame
* @categ: Category of the Action frame
* @data: Payload of the Action frame
* @len: Length of @data
* @freq: Frequency (in MHz) on which the frame was received
* Returns: 0 if the Public Action frame was a GAS frame or -1 if not
*/
int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
const u8 *bssid, u8 categ, const u8 *data, size_t len,
int freq)
{
struct gas_query_pending *query;
u8 action, dialog_token, frag_id = 0, more_frags = 0;
u16 comeback_delay, resp_len;
const u8 *pos, *adv_proto;
int prot, pmf;
unsigned int left;
if (gas == NULL || len < 4)
return -1;
pos = data;
action = *pos++;
dialog_token = *pos++;
if (action != WLAN_PA_GAS_INITIAL_RESP &&
action != WLAN_PA_GAS_COMEBACK_RESP)
return -1; /* Not a GAS response */
prot = categ == WLAN_ACTION_PROTECTED_DUAL;
pmf = pmf_in_use(gas->wpa_s, sa);
if (prot && !pmf) {
wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
return 0;
}
if (!prot && pmf) {
wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
return 0;
}
query = gas_query_get_pending(gas, sa, dialog_token);
if (query == NULL) {
wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
" dialog token %u", MAC2STR(sa), dialog_token);
return -1;
}
wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
ms_from_time(&query->last_oper), MAC2STR(sa));
if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
MACSTR " dialog token %u when waiting for comeback "
"response", MAC2STR(sa), dialog_token);
return 0;
}
if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from "
MACSTR " dialog token %u when waiting for initial "
"response", MAC2STR(sa), dialog_token);
return 0;
}
query->status_code = WPA_GET_LE16(pos);
pos += 2;
if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
action == WLAN_PA_GAS_COMEBACK_RESP) {
wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
} else if (query->status_code != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
"%u failed - status code %u",
MAC2STR(sa), dialog_token, query->status_code);
gas_query_done(gas, query, GAS_QUERY_FAILURE);
return 0;
}
if (action == WLAN_PA_GAS_COMEBACK_RESP) {
if (pos + 1 > data + len)
return 0;
frag_id = *pos & 0x7f;
more_frags = (*pos & 0x80) >> 7;
pos++;
}
/* Comeback Delay */
if (pos + 2 > data + len)
return 0;
comeback_delay = WPA_GET_LE16(pos);
pos += 2;
/* Advertisement Protocol element */
if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) {
wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement "
"Protocol element in the response from " MACSTR,
MAC2STR(sa));
return 0;
}
if (*pos != WLAN_EID_ADV_PROTO) {
wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement "
"Protocol element ID %u in response from " MACSTR,
*pos, MAC2STR(sa));
return 0;
}
adv_proto = pos;
pos += 2 + pos[1];
/* Query Response Length */
if (pos + 2 > data + len) {
wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length");
return 0;
}
resp_len = WPA_GET_LE16(pos);
pos += 2;
left = data + len - pos;
if (resp_len > left) {
wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
"response from " MACSTR, MAC2STR(sa));
return 0;
}
if (resp_len < left) {
wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
"after Query Response from " MACSTR,
left - resp_len, MAC2STR(sa));
}
if (action == WLAN_PA_GAS_COMEBACK_RESP)
gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len,
frag_id, more_frags, comeback_delay);
else
gas_query_rx_initial(gas, query, adv_proto, pos, resp_len,
comeback_delay);
return 0;
}
static void gas_query_timeout(void *eloop_data, void *user_ctx)
{
struct gas_query *gas = eloop_data;
struct gas_query_pending *query = user_ctx;
wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
" dialog token %u",
MAC2STR(query->addr), query->dialog_token);
gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
}
static int gas_query_dialog_token_available(struct gas_query *gas,
const u8 *dst, u8 dialog_token)
{
struct gas_query_pending *q;
dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) {
if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 &&
dialog_token == q->dialog_token)
return 0;
}
return 1;
}
static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
{
struct gas_query_pending *query = work->ctx;
struct gas_query *gas = query->gas;
struct wpa_supplicant *wpa_s = gas->wpa_s;
if (deinit) {
if (work->started) {
gas->work = NULL;
gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
return;
}
gas_query_free(query, 1);
return;
}
if (!query->maintain_addr && !wpa_s->conf->gas_rand_mac_addr) {
if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Failed to assign random MAC address for GAS");
gas_query_free(query, 1);
radio_work_done(work);
return;
}
os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN);
}
gas->work = work;
gas_query_tx_initial_req(gas, query);
}
static void gas_query_tx_initial_req(struct gas_query *gas,
struct gas_query_pending *query)
{
if (gas_query_tx(gas, query, query->req,
GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
MACSTR, MAC2STR(query->addr));
gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
return;
}
gas->current = query;
wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
query->dialog_token);
eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
gas_query_timeout, gas, query);
}
static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst)
{
u8 dialog_token;
int i;
/* There should never be more than couple active GAS queries in
* progress, so it should be very likely to find an available dialog
* token by checking random values. Use a limit on the number of
* iterations to handle the unexpected case of large number of pending
* queries cleanly. */
for (i = 0; i < 256; i++) {
/* Get a random number and check if the slot is available */
if (os_get_random(&dialog_token, sizeof(dialog_token)) < 0)
break;
if (gas_query_dialog_token_available(gas, dst, dialog_token))
return dialog_token;
}
/* No dialog token value available */
return -1;
}
static int gas_query_set_sa(struct gas_query *gas,
struct gas_query_pending *query)
{
struct wpa_supplicant *wpa_s = gas->wpa_s;
struct os_reltime now;
if (query->maintain_addr ||
!wpa_s->conf->gas_rand_mac_addr ||
!(wpa_s->current_bss ?
(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED) :
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA))) {
/* Use own MAC address as the transmitter address */
wpa_printf(MSG_DEBUG,
"GAS: Use own MAC address as the transmitter address%s%s%s",
query->maintain_addr ? " (maintain_addr)" : "",
!wpa_s->conf->gas_rand_mac_addr ? " (no gas_rand_mac_adr set)" : "",
!(wpa_s->current_bss ?
(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED) :
(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA)) ?
" (no driver rand capa" : "");
os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN);
return 0;
}
os_get_reltime(&now);
if (wpa_s->conf->gas_rand_mac_addr == gas->last_rand_sa_type &&
gas->last_mac_addr_rand.sec != 0 &&
!os_reltime_expired(&now, &gas->last_mac_addr_rand,
wpa_s->conf->gas_rand_addr_lifetime)) {
wpa_printf(MSG_DEBUG,
"GAS: Use the previously selected random transmitter address "
MACSTR, MAC2STR(gas->rand_addr));
os_memcpy(query->sa, gas->rand_addr, ETH_ALEN);
return 0;
}
if (wpa_s->conf->gas_rand_mac_addr == 1 &&
random_mac_addr(gas->rand_addr) < 0) {
wpa_printf(MSG_ERROR, "GAS: Failed to get random address");
return -1;
}
if (wpa_s->conf->gas_rand_mac_addr == 2 &&
random_mac_addr_keep_oui(gas->rand_addr) < 0) {
wpa_printf(MSG_ERROR,
"GAS: Failed to get random address with same OUI");
return -1;
}
wpa_printf(MSG_DEBUG, "GAS: Use a new random transmitter address "
MACSTR, MAC2STR(gas->rand_addr));
os_memcpy(query->sa, gas->rand_addr, ETH_ALEN);
os_get_reltime(&gas->last_mac_addr_rand);
gas->last_rand_sa_type = wpa_s->conf->gas_rand_mac_addr;
return 0;
}
/**
* gas_query_req - Request a GAS query
* @gas: GAS query data from gas_query_init()
* @dst: Destination MAC address for the query
* @freq: Frequency (in MHz) for the channel on which to send the query
* @wildcard_bssid: Force use of wildcard BSSID value
* @maintain_addr: Maintain own MAC address for exchange (i.e., ignore MAC
* address randomization rules)
* @req: GAS query payload (to be freed by gas_query module in case of success
* return)
* @cb: Callback function for reporting GAS query result and response
* @ctx: Context pointer to use with the @cb call
* Returns: dialog token (>= 0) on success or -1 on failure
*/
int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
int wildcard_bssid, int maintain_addr, struct wpabuf *req,
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
const struct wpabuf *resp, u16 status_code),
void *ctx)
{
struct gas_query_pending *query;
int dialog_token;
if (wpabuf_len(req) < 3)
return -1;
dialog_token = gas_query_new_dialog_token(gas, dst);
if (dialog_token < 0)
return -1;
query = os_zalloc(sizeof(*query));
if (query == NULL)
return -1;
query->gas = gas;
query->maintain_addr = !!maintain_addr;
if (gas_query_set_sa(gas, query)) {
os_free(query);
return -1;
}
os_memcpy(query->addr, dst, ETH_ALEN);
query->dialog_token = dialog_token;
query->wildcard_bssid = !!wildcard_bssid;
query->freq = freq;
query->cb = cb;
query->ctx = ctx;
query->req = req;
dl_list_add(&gas->pending, &query->list);
*(wpabuf_mhead_u8(req) + 2) = dialog_token;
wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
" dialog_token=%u freq=%d",
MAC2STR(query->addr), query->dialog_token, query->freq);
if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
query) < 0) {
query->req = NULL; /* caller will free this in error case */
gas_query_free(query, 1);
return -1;
}
return dialog_token;
}
int gas_query_stop(struct gas_query *gas, u8 dialog_token)
{
struct gas_query_pending *query;
dl_list_for_each(query, &gas->pending, struct gas_query_pending, list) {
if (query->dialog_token == dialog_token) {
if (!gas->work) {
/* The pending radio work has not yet been
* started, but the pending entry has a
* reference to the soon to be freed query.
* Need to remove that radio work now to avoid
* leaving behind a reference to freed memory.
*/
radio_remove_pending_work(gas->wpa_s, query);
}
gas_query_done(gas, query, GAS_QUERY_STOPPED);
return 0;
}
}
return -1;
}
diff --git a/contrib/wpa/wpa_supplicant/gas_query.h b/contrib/wpa/wpa_supplicant/gas_query.h
index f9ce7b680fda..6ccecd4ddbe1 100644
--- a/contrib/wpa/wpa_supplicant/gas_query.h
+++ b/contrib/wpa/wpa_supplicant/gas_query.h
@@ -1,60 +1,59 @@
/*
* Generic advertisement service (GAS) query
* Copyright (c) 2009, Atheros Communications
* Copyright (c) 2011, Qualcomm Atheros
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef GAS_QUERY_H
#define GAS_QUERY_H
struct gas_query;
#ifdef CONFIG_GAS
struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s);
void gas_query_deinit(struct gas_query *gas);
int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
const u8 *bssid, u8 categ, const u8 *data, size_t len,
int freq);
-int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr);
/**
* enum gas_query_result - GAS query result
*/
enum gas_query_result {
GAS_QUERY_SUCCESS,
GAS_QUERY_FAILURE,
GAS_QUERY_TIMEOUT,
GAS_QUERY_PEER_ERROR,
GAS_QUERY_INTERNAL_ERROR,
GAS_QUERY_STOPPED,
GAS_QUERY_DELETED_AT_DEINIT
};
int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
int wildcard_bssid, int maintain_addr, struct wpabuf *req,
void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
enum gas_query_result result,
const struct wpabuf *adv_proto,
const struct wpabuf *resp, u16 status_code),
void *ctx);
int gas_query_stop(struct gas_query *gas, u8 dialog_token);
#else /* CONFIG_GAS */
static inline struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
{
return (void *) 1;
}
static inline void gas_query_deinit(struct gas_query *gas)
{
}
#endif /* CONFIG_GAS */
#endif /* GAS_QUERY_H */
diff --git a/contrib/wpa/wpa_supplicant/mesh.c b/contrib/wpa/wpa_supplicant/mesh.c
index 901b49b4d257..7938b8b4903e 100644
--- a/contrib/wpa/wpa_supplicant/mesh.c
+++ b/contrib/wpa/wpa_supplicant/mesh.c
@@ -1,855 +1,886 @@
/*
* WPA Supplicant - Basic mesh mode routines
* Copyright (c) 2013-2014, cozybit, Inc. All rights reserved.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/uuid.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "common/hw_features_common.h"
#include "ap/sta_info.h"
#include "ap/hostapd.h"
#include "ap/ieee802_11.h"
#include "config_ssid.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "notify.h"
#include "ap.h"
#include "mesh_mpm.h"
#include "mesh_rsn.h"
#include "mesh.h"
static void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s,
bool also_clear_hostapd)
{
wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh,
also_clear_hostapd);
if (also_clear_hostapd) {
wpa_s->ifmsh = NULL;
wpa_s->current_ssid = NULL;
os_free(wpa_s->mesh_params);
wpa_s->mesh_params = NULL;
}
os_free(wpa_s->mesh_rsn);
wpa_s->mesh_rsn = NULL;
if (!also_clear_hostapd)
wpa_supplicant_leave_mesh(wpa_s, false);
}
void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s,
struct hostapd_iface *ifmsh,
bool also_clear_hostapd)
{
if (!ifmsh)
return;
if (ifmsh->mconf) {
mesh_mpm_deinit(wpa_s, ifmsh);
if (ifmsh->mconf->rsn_ie) {
ifmsh->mconf->rsn_ie = NULL;
/* We cannot free this struct
* because wpa_authenticator on
* hostapd side is also using it
* for now just set to NULL and
* let hostapd code free it.
*/
}
os_free(ifmsh->mconf);
ifmsh->mconf = NULL;
}
/* take care of shared data */
if (also_clear_hostapd) {
hostapd_interface_deinit(ifmsh);
hostapd_interface_free(ifmsh);
}
}
static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct mesh_conf *conf;
int cipher;
conf = os_zalloc(sizeof(struct mesh_conf));
if (!conf)
return NULL;
os_memcpy(conf->meshid, ssid->ssid, ssid->ssid_len);
conf->meshid_len = ssid->ssid_len;
if (ssid->key_mgmt & WPA_KEY_MGMT_SAE)
conf->security |= MESH_CONF_SEC_AUTH |
MESH_CONF_SEC_AMPE;
else
conf->security |= MESH_CONF_SEC_NONE;
conf->ieee80211w = ssid->ieee80211w;
if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)
conf->ieee80211w = wpa_s->conf->pmf;
else
conf->ieee80211w = NO_MGMT_FRAME_PROTECTION;
}
#ifdef CONFIG_OCV
conf->ocv = ssid->ocv;
#endif /* CONFIG_OCV */
cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0);
if (cipher < 0 || cipher == WPA_CIPHER_TKIP) {
wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid pairwise cipher");
os_free(conf);
return NULL;
}
conf->pairwise_cipher = cipher;
cipher = wpa_pick_group_cipher(ssid->group_cipher);
if (cipher < 0 || cipher == WPA_CIPHER_TKIP ||
cipher == WPA_CIPHER_GTK_NOT_USED) {
wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid group cipher");
os_free(conf);
return NULL;
}
conf->group_cipher = cipher;
if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
if (ssid->group_mgmt_cipher == WPA_CIPHER_BIP_GMAC_128 ||
ssid->group_mgmt_cipher == WPA_CIPHER_BIP_GMAC_256 ||
ssid->group_mgmt_cipher == WPA_CIPHER_BIP_CMAC_256)
conf->mgmt_group_cipher = ssid->group_mgmt_cipher;
else
conf->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
}
/* defaults */
conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP;
conf->mesh_pm_id = MESH_PATH_METRIC_AIRTIME;
conf->mesh_cc_id = 0;
conf->mesh_sp_id = MESH_SYNC_METHOD_NEIGHBOR_OFFSET;
conf->mesh_auth_id = (conf->security & MESH_CONF_SEC_AUTH) ? 1 : 0;
conf->dot11MeshMaxRetries = ssid->dot11MeshMaxRetries;
conf->dot11MeshRetryTimeout = ssid->dot11MeshRetryTimeout;
conf->dot11MeshConfirmTimeout = ssid->dot11MeshConfirmTimeout;
conf->dot11MeshHoldingTimeout = ssid->dot11MeshHoldingTimeout;
return conf;
}
static void wpas_mesh_copy_groups(struct hostapd_data *bss,
struct wpa_supplicant *wpa_s)
{
int num_groups;
size_t groups_size;
for (num_groups = 0; wpa_s->conf->sae_groups[num_groups] > 0;
num_groups++)
;
groups_size = (num_groups + 1) * sizeof(wpa_s->conf->sae_groups[0]);
bss->conf->sae_groups = os_malloc(groups_size);
if (bss->conf->sae_groups)
os_memcpy(bss->conf->sae_groups, wpa_s->conf->sae_groups,
groups_size);
}
static int wpas_mesh_init_rsn(struct wpa_supplicant *wpa_s)
{
struct hostapd_iface *ifmsh = wpa_s->ifmsh;
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct hostapd_data *bss = ifmsh->bss[0];
static int default_groups[] = { 19, 20, 21, 25, 26, -1 };
const char *password;
size_t len;
password = ssid->sae_password;
if (!password)
password = ssid->passphrase;
if (!password) {
wpa_printf(MSG_ERROR,
"mesh: Passphrase for SAE not configured");
return -1;
}
bss->conf->wpa = ssid->proto;
bss->conf->wpa_key_mgmt = ssid->key_mgmt;
if (wpa_s->conf->sae_groups && wpa_s->conf->sae_groups[0] > 0) {
wpas_mesh_copy_groups(bss, wpa_s);
} else {
bss->conf->sae_groups = os_memdup(default_groups,
sizeof(default_groups));
if (!bss->conf->sae_groups)
return -1;
}
len = os_strlen(password);
bss->conf->ssid.wpa_passphrase = dup_binstr(password, len);
wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, ifmsh->mconf);
return !wpa_s->mesh_rsn ? -1 : 0;
}
static int wpas_mesh_update_freq_params(struct wpa_supplicant *wpa_s)
{
struct wpa_driver_mesh_join_params *params = wpa_s->mesh_params;
struct hostapd_iface *ifmsh = wpa_s->ifmsh;
struct he_capabilities *he_capab = NULL;
if (ifmsh->current_mode)
he_capab = &ifmsh->current_mode->he_capab[IEEE80211_MODE_MESH];
if (hostapd_set_freq_params(
&params->freq,
ifmsh->conf->hw_mode,
ifmsh->freq,
ifmsh->conf->channel,
ifmsh->conf->enable_edmg,
ifmsh->conf->edmg_channel,
ifmsh->conf->ieee80211n,
ifmsh->conf->ieee80211ac,
ifmsh->conf->ieee80211ax,
ifmsh->conf->secondary_channel,
hostapd_get_oper_chwidth(ifmsh->conf),
hostapd_get_oper_centr_freq_seg0_idx(ifmsh->conf),
hostapd_get_oper_centr_freq_seg1_idx(ifmsh->conf),
ifmsh->conf->vht_capab,
he_capab)) {
wpa_printf(MSG_ERROR, "Error updating mesh frequency params");
wpa_supplicant_mesh_deinit(wpa_s, true);
return -1;
}
return 0;
}
static int wpas_mesh_complete(struct wpa_supplicant *wpa_s)
{
struct hostapd_iface *ifmsh = wpa_s->ifmsh;
struct wpa_driver_mesh_join_params *params = wpa_s->mesh_params;
struct wpa_ssid *ssid = wpa_s->current_ssid;
int ret;
if (!params || !ssid || !ifmsh) {
wpa_printf(MSG_ERROR, "mesh: %s called without active mesh",
__func__);
return -1;
}
/*
* Update channel configuration if the channel has changed since the
* initial setting, i.e., due to DFS radar detection during CAC.
*/
if (ifmsh->freq > 0 && ifmsh->freq != params->freq.freq) {
wpa_s->assoc_freq = ifmsh->freq;
ssid->frequency = ifmsh->freq;
if (wpas_mesh_update_freq_params(wpa_s) < 0)
return -1;
}
if (ifmsh->mconf->security != MESH_CONF_SEC_NONE &&
wpas_mesh_init_rsn(wpa_s)) {
wpa_printf(MSG_ERROR,
"mesh: RSN initialization failed - deinit mesh");
wpa_supplicant_mesh_deinit(wpa_s, false);
return -1;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
wpa_s->pairwise_cipher = wpa_s->mesh_rsn->pairwise_cipher;
wpa_s->group_cipher = wpa_s->mesh_rsn->group_cipher;
wpa_s->mgmt_group_cipher = wpa_s->mesh_rsn->mgmt_group_cipher;
}
params->ies = ifmsh->mconf->rsn_ie;
params->ie_len = ifmsh->mconf->rsn_ie_len;
params->basic_rates = ifmsh->basic_rates;
params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE;
params->conf.ht_opmode = ifmsh->bss[0]->iface->ht_op_mode;
wpa_msg(wpa_s, MSG_INFO, "joining mesh %s",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
ret = wpa_drv_join_mesh(wpa_s, params);
if (ret)
wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d", ret);
/* hostapd sets the interface down until we associate */
wpa_drv_set_operstate(wpa_s, 1);
if (!ret) {
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
ssid->id);
wpas_notify_mesh_group_started(wpa_s, ssid);
}
return ret;
}
static void wpas_mesh_complete_cb(void *arg)
{
struct wpa_supplicant *wpa_s = arg;
wpas_mesh_complete(wpa_s);
}
static int wpa_supplicant_mesh_enable_iface_cb(struct hostapd_iface *ifmsh)
{
struct wpa_supplicant *wpa_s = ifmsh->owner;
struct hostapd_data *bss;
ifmsh->mconf = mesh_config_create(wpa_s, wpa_s->current_ssid);
bss = ifmsh->bss[0];
bss->msg_ctx = wpa_s;
os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN);
bss->driver = wpa_s->driver;
bss->drv_priv = wpa_s->drv_priv;
bss->iface = ifmsh;
bss->mesh_sta_free_cb = mesh_mpm_free_sta;
bss->setup_complete_cb = wpas_mesh_complete_cb;
bss->setup_complete_cb_ctx = wpa_s;
bss->conf->start_disabled = 1;
bss->conf->mesh = MESH_ENABLED;
bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity;
if (wpa_drv_init_mesh(wpa_s)) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver");
return -1;
}
if (hostapd_setup_interface(ifmsh)) {
wpa_printf(MSG_ERROR,
"Failed to initialize hostapd interface for mesh");
return -1;
}
return 0;
}
static int wpa_supplicant_mesh_disable_iface_cb(struct hostapd_iface *ifmsh)
{
struct wpa_supplicant *wpa_s = ifmsh->owner;
size_t j;
wpa_supplicant_mesh_deinit(wpa_s, false);
#ifdef NEED_AP_MLME
for (j = 0; j < ifmsh->num_bss; j++)
hostapd_cleanup_cs_params(ifmsh->bss[j]);
#endif /* NEED_AP_MLME */
/* Same as hostapd_interface_deinit() without deinitializing control
* interface */
for (j = 0; j < ifmsh->num_bss; j++) {
struct hostapd_data *hapd = ifmsh->bss[j];
hostapd_bss_deinit_no_free(hapd);
hostapd_free_hapd_data(hapd);
}
hostapd_cleanup_iface_partial(ifmsh);
return 0;
}
static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct hostapd_freq_params *freq)
{
struct hostapd_iface *ifmsh;
struct hostapd_data *bss;
struct hostapd_config *conf;
struct mesh_conf *mconf;
int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 };
int rate_len;
int frequency;
if (!wpa_s->conf->user_mpm) {
/* not much for us to do here */
wpa_msg(wpa_s, MSG_WARNING,
"user_mpm is not enabled in configuration");
return 0;
}
wpa_s->ifmsh = ifmsh = hostapd_alloc_iface();
if (!ifmsh)
return -ENOMEM;
ifmsh->owner = wpa_s;
ifmsh->drv_flags = wpa_s->drv_flags;
ifmsh->drv_flags2 = wpa_s->drv_flags2;
ifmsh->num_bss = 1;
ifmsh->enable_iface_cb = wpa_supplicant_mesh_enable_iface_cb;
ifmsh->disable_iface_cb = wpa_supplicant_mesh_disable_iface_cb;
ifmsh->bss = os_calloc(wpa_s->ifmsh->num_bss,
sizeof(struct hostapd_data *));
if (!ifmsh->bss)
goto out_free;
ifmsh->bss[0] = bss = hostapd_alloc_bss_data(NULL, NULL, NULL);
if (!bss)
goto out_free;
ifmsh->bss[0]->msg_ctx = wpa_s;
os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN);
bss->driver = wpa_s->driver;
bss->drv_priv = wpa_s->drv_priv;
bss->iface = ifmsh;
bss->mesh_sta_free_cb = mesh_mpm_free_sta;
bss->setup_complete_cb = wpas_mesh_complete_cb;
bss->setup_complete_cb_ctx = wpa_s;
frequency = ssid->frequency;
if (frequency != freq->freq &&
frequency == freq->freq + freq->sec_channel_offset * 20) {
wpa_printf(MSG_DEBUG, "mesh: pri/sec channels switched");
frequency = freq->freq;
ssid->frequency = frequency;
}
wpa_s->assoc_freq = frequency;
wpa_s->current_ssid = ssid;
/* setup an AP config for auth processing */
conf = hostapd_config_defaults();
if (!conf)
goto out_free;
+ if (is_6ghz_freq(freq->freq)) {
+ /*
+ * IEEE Std 802.11ax-2021, 12.12.2:
+ * The STA shall use management frame protection (MFPR=1) when
+ * using RSN.
+ */
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
+
+ /* Set mandatory op_class parameter for setting up BSS */
+ switch (freq->bandwidth) {
+ case 20:
+ if (freq->freq == 5935)
+ conf->op_class = 136;
+ else
+ conf->op_class = 131;
+ break;
+ case 40:
+ conf->op_class = 132;
+ break;
+ case 80:
+ conf->op_class = 133;
+ break;
+ case 160:
+ conf->op_class = 134;
+ break;
+ default:
+ conf->op_class = 131;
+ break;
+ }
+ }
+
bss->conf = *conf->bss;
bss->conf->start_disabled = 1;
bss->conf->mesh = MESH_ENABLED;
bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity;
if (ieee80211_is_dfs(ssid->frequency, wpa_s->hw.modes,
wpa_s->hw.num_modes) && wpa_s->conf->country[0]) {
conf->ieee80211h = 1;
conf->ieee80211d = 1;
conf->country[0] = wpa_s->conf->country[0];
conf->country[1] = wpa_s->conf->country[1];
conf->country[2] = ' ';
wpa_s->mesh_params->handle_dfs = true;
}
bss->iconf = conf;
ifmsh->conf = conf;
ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links;
ifmsh->bss[0]->dot11RSNASAERetransPeriod =
wpa_s->conf->dot11RSNASAERetransPeriod;
os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface));
mconf = mesh_config_create(wpa_s, ssid);
if (!mconf)
goto out_free;
ifmsh->mconf = mconf;
/* need conf->hw_mode for supported rates. */
conf->hw_mode = ieee80211_freq_to_chan(frequency, &conf->channel);
if (conf->hw_mode == NUM_HOSTAPD_MODES) {
wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz",
frequency);
goto out_free;
}
if (ssid->mesh_basic_rates == NULL) {
/*
* XXX: Hack! This is so an MPM which correctly sets the ERP
* mandatory rates as BSSBasicRateSet doesn't reject us. We
* could add a new hw_mode HOSTAPD_MODE_IEEE80211G_ERP, but
* this is way easier. This also makes our BSSBasicRateSet
* advertised in beacons match the one in peering frames, sigh.
*/
if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
conf->basic_rates = os_memdup(basic_rates_erp,
sizeof(basic_rates_erp));
if (!conf->basic_rates)
goto out_free;
}
} else {
rate_len = 0;
while (1) {
if (ssid->mesh_basic_rates[rate_len] < 1)
break;
rate_len++;
}
conf->basic_rates = os_calloc(rate_len + 1, sizeof(int));
if (conf->basic_rates == NULL)
goto out_free;
os_memcpy(conf->basic_rates, ssid->mesh_basic_rates,
rate_len * sizeof(int));
conf->basic_rates[rate_len] = -1;
}
/* While it can enhance performance to switch the primary channel, which
* is also the secondary channel of another network at the same time),
* to the other primary channel, problems exist with this in mesh
* networks.
*
* Example with problems:
* - 3 mesh nodes M1-M3, freq (5200, 5180)
* - other node O1, e.g. AP mode, freq (5180, 5200),
* Locations: O1 M1 M2 M3
*
* M3 can only send frames to M1 over M2, no direct connection is
* possible
* Start O1, M1 and M3 first, M1 or O1 will switch channels to align
* with* each other. M3 does not swap, because M1 or O1 cannot be
* reached. M2 is started afterwards and can either connect to M3 or M1
* because of this primary secondary channel switch.
*
* Solutions: (1) central coordination -> not always possible
* (2) disable pri/sec channel switch in mesh networks
*
* In AP mode, when all nodes can work independently, this poses of
* course no problem, therefore disable it only in mesh mode. */
conf->no_pri_sec_switch = 1;
wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
if (wpa_drv_init_mesh(wpa_s)) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver");
return -1;
}
if (hostapd_setup_interface(ifmsh)) {
wpa_printf(MSG_ERROR,
"Failed to initialize hostapd interface for mesh");
return -1;
}
return 0;
out_free:
wpa_supplicant_mesh_deinit(wpa_s, true);
return -ENOMEM;
}
void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
const u8 *ies, size_t ie_len)
{
struct ieee802_11_elems elems;
wpa_msg(wpa_s, MSG_INFO,
"new peer notification for " MACSTR, MAC2STR(addr));
if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) {
wpa_msg(wpa_s, MSG_INFO, "Could not parse beacon from " MACSTR,
MAC2STR(addr));
return;
}
wpa_mesh_new_mesh_peer(wpa_s, addr, &elems);
}
void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
struct wpabuf **extra_ie)
{
/* EID + 0-length (wildcard) mesh-id */
size_t ielen = 2;
if (wpabuf_resize(extra_ie, ielen) == 0) {
wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID);
wpabuf_put_u8(*extra_ie, 0);
}
}
int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct wpa_driver_mesh_join_params *params = os_zalloc(sizeof(*params));
int ret = 0;
if (!ssid || !ssid->ssid || !ssid->ssid_len || !ssid->frequency ||
!params) {
ret = -ENOENT;
os_free(params);
goto out;
}
wpa_supplicant_mesh_deinit(wpa_s, true);
wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
wpa_s->group_cipher = WPA_CIPHER_NONE;
wpa_s->mgmt_group_cipher = 0;
params->meshid = ssid->ssid;
params->meshid_len = ssid->ssid_len;
ibss_mesh_setup_freq(wpa_s, ssid, &params->freq);
wpa_s->mesh_ht_enabled = !!params->freq.ht_enabled;
wpa_s->mesh_vht_enabled = !!params->freq.vht_enabled;
wpa_s->mesh_he_enabled = !!params->freq.he_enabled;
if (params->freq.ht_enabled && params->freq.sec_channel_offset)
ssid->ht40 = params->freq.sec_channel_offset;
if (wpa_s->mesh_vht_enabled) {
ssid->vht = 1;
ssid->vht_center_freq1 = params->freq.center_freq1;
switch (params->freq.bandwidth) {
case 80:
if (params->freq.center_freq2) {
ssid->max_oper_chwidth = CHANWIDTH_80P80MHZ;
ssid->vht_center_freq2 =
params->freq.center_freq2;
} else {
ssid->max_oper_chwidth = CHANWIDTH_80MHZ;
}
break;
case 160:
ssid->max_oper_chwidth = CHANWIDTH_160MHZ;
break;
default:
ssid->max_oper_chwidth = CHANWIDTH_USE_HT;
break;
}
}
if (wpa_s->mesh_he_enabled)
ssid->he = 1;
if (ssid->beacon_int > 0)
params->beacon_int = ssid->beacon_int;
else if (wpa_s->conf->beacon_int > 0)
params->beacon_int = wpa_s->conf->beacon_int;
if (ssid->dtim_period > 0)
params->dtim_period = ssid->dtim_period;
else if (wpa_s->conf->dtim_period > 0)
params->dtim_period = wpa_s->conf->dtim_period;
params->conf.max_peer_links = wpa_s->conf->max_peer_links;
if (ssid->mesh_rssi_threshold < DEFAULT_MESH_RSSI_THRESHOLD) {
params->conf.rssi_threshold = ssid->mesh_rssi_threshold;
params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD;
}
if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
params->flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH;
params->flags |= WPA_DRIVER_MESH_FLAG_AMPE;
wpa_s->conf->user_mpm = 1;
}
if (wpa_s->conf->user_mpm) {
params->flags |= WPA_DRIVER_MESH_FLAG_USER_MPM;
params->conf.auto_plinks = 0;
} else {
params->flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM;
params->conf.auto_plinks = 1;
}
params->conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity;
os_free(wpa_s->mesh_params);
wpa_s->mesh_params = params;
if (wpa_supplicant_mesh_init(wpa_s, ssid, &params->freq)) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh");
wpa_supplicant_leave_mesh(wpa_s, true);
ret = -1;
goto out;
}
out:
return ret;
}
int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s, bool need_deinit)
{
int ret = 0;
wpa_msg(wpa_s, MSG_INFO, "leaving mesh");
/* Need to send peering close messages first */
if (need_deinit)
wpa_supplicant_mesh_deinit(wpa_s, true);
ret = wpa_drv_leave_mesh(wpa_s);
if (ret)
wpa_msg(wpa_s, MSG_ERROR, "mesh leave error=%d", ret);
wpa_drv_set_operstate(wpa_s, 1);
return ret;
}
static int mesh_attr_text(const u8 *ies, size_t ies_len, char *buf, char *end)
{
struct ieee802_11_elems elems;
char *mesh_id, *pos = buf;
u8 *bss_basic_rate_set;
int bss_basic_rate_set_len, ret, i;
if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed)
return -1;
if (elems.mesh_id_len < 1)
return 0;
mesh_id = os_malloc(elems.mesh_id_len + 1);
if (mesh_id == NULL)
return -1;
os_memcpy(mesh_id, elems.mesh_id, elems.mesh_id_len);
mesh_id[elems.mesh_id_len] = '\0';
ret = os_snprintf(pos, end - pos, "mesh_id=%s\n", mesh_id);
os_free(mesh_id);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
if (elems.mesh_config_len > 6) {
ret = os_snprintf(pos, end - pos,
"active_path_selection_protocol_id=0x%02x\n"
"active_path_selection_metric_id=0x%02x\n"
"congestion_control_mode_id=0x%02x\n"
"synchronization_method_id=0x%02x\n"
"authentication_protocol_id=0x%02x\n"
"mesh_formation_info=0x%02x\n"
"mesh_capability=0x%02x\n",
elems.mesh_config[0], elems.mesh_config[1],
elems.mesh_config[2], elems.mesh_config[3],
elems.mesh_config[4], elems.mesh_config[5],
elems.mesh_config[6]);
if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
bss_basic_rate_set = os_malloc(elems.supp_rates_len +
elems.ext_supp_rates_len);
if (bss_basic_rate_set == NULL)
return -1;
bss_basic_rate_set_len = 0;
for (i = 0; i < elems.supp_rates_len; i++) {
if (elems.supp_rates[i] & 0x80) {
bss_basic_rate_set[bss_basic_rate_set_len++] =
(elems.supp_rates[i] & 0x7f) * 5;
}
}
for (i = 0; i < elems.ext_supp_rates_len; i++) {
if (elems.ext_supp_rates[i] & 0x80) {
bss_basic_rate_set[bss_basic_rate_set_len++] =
(elems.ext_supp_rates[i] & 0x7f) * 5;
}
}
if (bss_basic_rate_set_len > 0) {
ret = os_snprintf(pos, end - pos, "bss_basic_rate_set=%d",
bss_basic_rate_set[0]);
if (os_snprintf_error(end - pos, ret))
goto fail;
pos += ret;
for (i = 1; i < bss_basic_rate_set_len; i++) {
ret = os_snprintf(pos, end - pos, " %d",
bss_basic_rate_set[i]);
if (os_snprintf_error(end - pos, ret))
goto fail;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "\n");
if (os_snprintf_error(end - pos, ret))
goto fail;
pos += ret;
}
fail:
os_free(bss_basic_rate_set);
return pos - buf;
}
int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
char *end)
{
return mesh_attr_text(ies, ies_len, buf, end);
}
static int wpas_mesh_get_ifname(struct wpa_supplicant *wpa_s, char *ifname,
size_t len)
{
char *ifname_ptr = wpa_s->ifname;
int res;
res = os_snprintf(ifname, len, "mesh-%s-%d", ifname_ptr,
wpa_s->mesh_if_idx);
if (os_snprintf_error(len, res) ||
(os_strlen(ifname) >= IFNAMSIZ &&
os_strlen(wpa_s->ifname) < IFNAMSIZ)) {
/* Try to avoid going over the IFNAMSIZ length limit */
res = os_snprintf(ifname, len, "mesh-%d", wpa_s->mesh_if_idx);
if (os_snprintf_error(len, res))
return -1;
}
wpa_s->mesh_if_idx++;
return 0;
}
int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
size_t len)
{
struct wpa_interface iface;
struct wpa_supplicant *mesh_wpa_s;
u8 addr[ETH_ALEN];
if (ifname[0] == '\0' && wpas_mesh_get_ifname(wpa_s, ifname, len) < 0)
return -1;
if (wpa_drv_if_add(wpa_s, WPA_IF_MESH, ifname, NULL, NULL, NULL, addr,
NULL) < 0) {
wpa_printf(MSG_ERROR,
"mesh: Failed to create new mesh interface");
return -1;
}
wpa_printf(MSG_INFO, "mesh: Created virtual interface %s addr "
MACSTR, ifname, MAC2STR(addr));
os_memset(&iface, 0, sizeof(iface));
iface.ifname = ifname;
iface.driver = wpa_s->driver->name;
iface.driver_param = wpa_s->conf->driver_param;
iface.ctrl_interface = wpa_s->conf->ctrl_interface;
mesh_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
if (!mesh_wpa_s) {
wpa_printf(MSG_ERROR,
"mesh: Failed to create new wpa_supplicant interface");
wpa_drv_if_remove(wpa_s, WPA_IF_MESH, ifname);
return -1;
}
mesh_wpa_s->mesh_if_created = 1;
return 0;
}
int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr)
{
return mesh_mpm_close_peer(wpa_s, addr);
}
int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr,
int duration)
{
return mesh_mpm_connect_peer(wpa_s, addr, duration);
}
diff --git a/contrib/wpa/wpa_supplicant/mesh_mpm.c b/contrib/wpa/wpa_supplicant/mesh_mpm.c
index b6a5e8857db9..38f0d641a03b 100644
--- a/contrib/wpa/wpa_supplicant/mesh_mpm.c
+++ b/contrib/wpa/wpa_supplicant/mesh_mpm.c
@@ -1,1394 +1,1402 @@
/*
* WPA Supplicant - Basic mesh peer management
* Copyright (c) 2013-2014, cozybit, Inc. All rights reserved.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/hw_features_common.h"
#include "common/ocv.h"
#include "ap/hostapd.h"
#include "ap/sta_info.h"
#include "ap/ieee802_11.h"
#include "ap/wpa_auth.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "mesh_mpm.h"
#include "mesh_rsn.h"
#include "notify.h"
struct mesh_peer_mgmt_ie {
const u8 *proto_id; /* Mesh Peering Protocol Identifier (2 octets) */
const u8 *llid; /* Local Link ID (2 octets) */
const u8 *plid; /* Peer Link ID (conditional, 2 octets) */
const u8 *reason; /* Reason Code (conditional, 2 octets) */
const u8 *chosen_pmk; /* Chosen PMK (optional, 16 octets) */
};
static void plink_timer(void *eloop_ctx, void *user_data);
enum plink_event {
PLINK_UNDEFINED,
OPN_ACPT,
OPN_RJCT,
CNF_ACPT,
CNF_RJCT,
CLS_ACPT,
REQ_RJCT
};
static const char * const mplstate[] = {
[0] = "UNINITIALIZED",
[PLINK_IDLE] = "IDLE",
[PLINK_OPN_SNT] = "OPN_SNT",
[PLINK_OPN_RCVD] = "OPN_RCVD",
[PLINK_CNF_RCVD] = "CNF_RCVD",
[PLINK_ESTAB] = "ESTAB",
[PLINK_HOLDING] = "HOLDING",
[PLINK_BLOCKED] = "BLOCKED"
};
static const char * const mplevent[] = {
[PLINK_UNDEFINED] = "UNDEFINED",
[OPN_ACPT] = "OPN_ACPT",
[OPN_RJCT] = "OPN_RJCT",
[CNF_ACPT] = "CNF_ACPT",
[CNF_RJCT] = "CNF_RJCT",
[CLS_ACPT] = "CLS_ACPT",
[REQ_RJCT] = "REQ_RJCT",
};
static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s,
u8 action_field,
const u8 *ie, size_t len,
struct mesh_peer_mgmt_ie *mpm_ie)
{
os_memset(mpm_ie, 0, sizeof(*mpm_ie));
/* Remove optional Chosen PMK field at end */
if (len >= SAE_PMKID_LEN) {
mpm_ie->chosen_pmk = ie + len - SAE_PMKID_LEN;
len -= SAE_PMKID_LEN;
}
if ((action_field == PLINK_OPEN && len != 4) ||
(action_field == PLINK_CONFIRM && len != 6) ||
(action_field == PLINK_CLOSE && len != 6 && len != 8)) {
wpa_msg(wpa_s, MSG_DEBUG, "MPM: Invalid peer mgmt ie");
return -1;
}
/* required fields */
if (len < 4)
return -1;
mpm_ie->proto_id = ie;
mpm_ie->llid = ie + 2;
ie += 4;
len -= 4;
/* close reason is always present at end for close */
if (action_field == PLINK_CLOSE) {
if (len < 2)
return -1;
mpm_ie->reason = ie + len - 2;
len -= 2;
}
/* Peer Link ID, present for confirm, and possibly close */
if (len >= 2)
mpm_ie->plid = ie;
return 0;
}
static int plink_free_count(struct hostapd_data *hapd)
{
if (hapd->max_plinks > hapd->num_plinks)
return hapd->max_plinks - hapd->num_plinks;
return 0;
}
static u16 copy_supp_rates(struct wpa_supplicant *wpa_s,
struct sta_info *sta,
struct ieee802_11_elems *elems)
{
if (!elems->supp_rates) {
wpa_msg(wpa_s, MSG_ERROR, "no supported rates from " MACSTR,
MAC2STR(sta->addr));
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (elems->supp_rates_len + elems->ext_supp_rates_len >
sizeof(sta->supported_rates)) {
wpa_msg(wpa_s, MSG_ERROR,
"Invalid supported rates element length " MACSTR
" %d+%d", MAC2STR(sta->addr), elems->supp_rates_len,
elems->ext_supp_rates_len);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sta->supported_rates_len = merge_byte_arrays(
sta->supported_rates, sizeof(sta->supported_rates),
elems->supp_rates, elems->supp_rates_len,
elems->ext_supp_rates, elems->ext_supp_rates_len);
return WLAN_STATUS_SUCCESS;
}
/* return true if elems from a neighbor match this MBSS */
static bool matches_local(struct wpa_supplicant *wpa_s,
struct ieee802_11_elems *elems)
{
struct mesh_conf *mconf = wpa_s->ifmsh->mconf;
if (elems->mesh_config_len < 5)
return false;
return (mconf->meshid_len == elems->mesh_id_len &&
os_memcmp(mconf->meshid, elems->mesh_id,
elems->mesh_id_len) == 0 &&
mconf->mesh_pp_id == elems->mesh_config[0] &&
mconf->mesh_pm_id == elems->mesh_config[1] &&
mconf->mesh_cc_id == elems->mesh_config[2] &&
mconf->mesh_sp_id == elems->mesh_config[3] &&
mconf->mesh_auth_id == elems->mesh_config[4]);
}
/* check if local link id is already used with another peer */
static bool llid_in_use(struct wpa_supplicant *wpa_s, u16 llid)
{
struct sta_info *sta;
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (sta->my_lid == llid)
return true;
}
return false;
}
/* generate an llid for a link and set to initial state */
static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s,
struct sta_info *sta)
{
u16 llid;
do {
if (os_get_random((u8 *) &llid, sizeof(llid)) < 0)
llid = 0; /* continue */
} while (!llid || llid_in_use(wpa_s, llid));
sta->my_lid = llid;
sta->peer_lid = 0;
sta->peer_aid = 0;
/*
* We do not use wpa_mesh_set_plink_state() here because there is no
* entry in kernel yet.
*/
sta->plink_state = PLINK_IDLE;
}
static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s,
struct sta_info *sta,
enum plink_action_field type,
u16 close_reason)
{
struct wpabuf *buf;
struct hostapd_iface *ifmsh = wpa_s->ifmsh;
struct hostapd_data *bss = ifmsh->bss[0];
struct mesh_conf *conf = ifmsh->mconf;
u8 supp_rates[2 + 2 + 32];
u8 *pos, *cat;
u8 ie_len, add_plid = 0;
int ret;
int ampe = conf->security & MESH_CONF_SEC_AMPE;
size_t buf_len;
if (!sta)
return;
buf_len = 2 + /* Category and Action */
2 + /* capability info */
2 + /* AID */
2 + 8 + /* supported rates */
2 + (32 - 8) +
2 + 32 + /* mesh ID */
2 + 7 + /* mesh config */
2 + 24 + /* peering management */
2 + 96 + 32 + 32 + /* AMPE (96 + max GTKlen + max IGTKlen) */
2 + 16; /* MIC */
if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
buf_len += 2 + 26 + /* HT capabilities */
2 + 22; /* HT operation */
}
#ifdef CONFIG_IEEE80211AC
if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) {
buf_len += 2 + 12 + /* VHT Capabilities */
2 + 5; /* VHT Operation */
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
if (type != PLINK_CLOSE && wpa_s->mesh_he_enabled) {
buf_len += 3 +
HE_MAX_MAC_CAPAB_SIZE +
HE_MAX_PHY_CAPAB_SIZE +
HE_MAX_MCS_CAPAB_SIZE +
HE_MAX_PPET_CAPAB_SIZE;
buf_len += 3 + sizeof(struct ieee80211_he_operation);
+ if (is_6ghz_op_class(bss->iconf->op_class))
+ buf_len += sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap);
}
#endif /* CONFIG_IEEE80211AX */
if (type != PLINK_CLOSE)
buf_len += conf->rsn_ie_len; /* RSN IE */
#ifdef CONFIG_OCV
/* OCI is included even when the other STA doesn't support OCV */
if (type != PLINK_CLOSE && conf->ocv)
buf_len += OCV_OCI_EXTENDED_LEN;
#endif /* CONFIG_OCV */
buf = wpabuf_alloc(buf_len);
if (!buf)
return;
cat = wpabuf_mhead_u8(buf);
wpabuf_put_u8(buf, WLAN_ACTION_SELF_PROTECTED);
wpabuf_put_u8(buf, type);
if (type != PLINK_CLOSE) {
u8 info;
/* capability info */
wpabuf_put_le16(buf, ampe ? IEEE80211_CAP_PRIVACY : 0);
/* aid */
if (type == PLINK_CONFIRM)
wpabuf_put_le16(buf, sta->aid);
/* IE: supp + ext. supp rates */
pos = hostapd_eid_supp_rates(bss, supp_rates);
pos = hostapd_eid_ext_supp_rates(bss, pos);
wpabuf_put_data(buf, supp_rates, pos - supp_rates);
/* IE: RSN IE */
wpabuf_put_data(buf, conf->rsn_ie, conf->rsn_ie_len);
/* IE: Mesh ID */
wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
wpabuf_put_u8(buf, conf->meshid_len);
wpabuf_put_data(buf, conf->meshid, conf->meshid_len);
/* IE: mesh conf */
wpabuf_put_u8(buf, WLAN_EID_MESH_CONFIG);
wpabuf_put_u8(buf, 7);
wpabuf_put_u8(buf, conf->mesh_pp_id);
wpabuf_put_u8(buf, conf->mesh_pm_id);
wpabuf_put_u8(buf, conf->mesh_cc_id);
wpabuf_put_u8(buf, conf->mesh_sp_id);
wpabuf_put_u8(buf, conf->mesh_auth_id);
info = (bss->num_plinks > 63 ? 63 : bss->num_plinks) << 1;
/* TODO: Add Connected to Mesh Gate/AS subfields */
wpabuf_put_u8(buf, info);
/* always forwarding & accepting plinks for now */
wpabuf_put_u8(buf, MESH_CAP_ACCEPT_ADDITIONAL_PEER |
MESH_CAP_FORWARDING);
} else { /* Peer closing frame */
/* IE: Mesh ID */
wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
wpabuf_put_u8(buf, conf->meshid_len);
wpabuf_put_data(buf, conf->meshid, conf->meshid_len);
}
/* IE: Mesh Peering Management element */
ie_len = 4;
if (ampe)
ie_len += PMKID_LEN;
switch (type) {
case PLINK_OPEN:
break;
case PLINK_CONFIRM:
ie_len += 2;
add_plid = 1;
break;
case PLINK_CLOSE:
ie_len += 2;
add_plid = 1;
ie_len += 2; /* reason code */
break;
}
wpabuf_put_u8(buf, WLAN_EID_PEER_MGMT);
wpabuf_put_u8(buf, ie_len);
/* peering protocol */
if (ampe)
wpabuf_put_le16(buf, 1);
else
wpabuf_put_le16(buf, 0);
wpabuf_put_le16(buf, sta->my_lid);
if (add_plid)
wpabuf_put_le16(buf, sta->peer_lid);
if (type == PLINK_CLOSE)
wpabuf_put_le16(buf, close_reason);
if (ampe) {
if (sta->sae == NULL) {
wpa_msg(wpa_s, MSG_INFO, "Mesh MPM: no SAE session");
goto fail;
}
mesh_rsn_get_pmkid(wpa_s->mesh_rsn, sta,
wpabuf_put(buf, PMKID_LEN));
}
if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
u8 ht_capa_oper[2 + 26 + 2 + 22];
pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper);
pos = hostapd_eid_ht_operation(bss, pos);
wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper);
}
#ifdef CONFIG_IEEE80211AC
if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) {
u8 vht_capa_oper[2 + 12 + 2 + 5];
pos = hostapd_eid_vht_capabilities(bss, vht_capa_oper, 0);
pos = hostapd_eid_vht_operation(bss, pos);
wpabuf_put_data(buf, vht_capa_oper, pos - vht_capa_oper);
}
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
if (type != PLINK_CLOSE && wpa_s->mesh_he_enabled) {
u8 he_capa_oper[3 +
HE_MAX_MAC_CAPAB_SIZE +
HE_MAX_PHY_CAPAB_SIZE +
HE_MAX_MCS_CAPAB_SIZE +
HE_MAX_PPET_CAPAB_SIZE +
- 3 + sizeof(struct ieee80211_he_operation)];
+ 3 + sizeof(struct ieee80211_he_operation) +
+ sizeof(struct ieee80211_he_6ghz_oper_info) +
+ 3 + sizeof(struct ieee80211_he_6ghz_band_cap)];
pos = hostapd_eid_he_capab(bss, he_capa_oper,
IEEE80211_MODE_MESH);
pos = hostapd_eid_he_operation(bss, pos);
+ pos = hostapd_eid_he_6ghz_band_cap(bss, pos);
wpabuf_put_data(buf, he_capa_oper, pos - he_capa_oper);
}
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_OCV
if (type != PLINK_CLOSE && conf->ocv) {
struct wpa_channel_info ci;
if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
wpa_printf(MSG_WARNING,
"Mesh MPM: Failed to get channel info for OCI element");
goto fail;
}
pos = wpabuf_put(buf, OCV_OCI_EXTENDED_LEN);
if (ocv_insert_extended_oci(&ci, pos) < 0)
goto fail;
}
#endif /* CONFIG_OCV */
if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) {
wpa_msg(wpa_s, MSG_INFO,
"Mesh MPM: failed to add AMPE and MIC IE");
goto fail;
}
wpa_msg(wpa_s, MSG_DEBUG, "Mesh MPM: Sending peering frame type %d to "
MACSTR " (my_lid=0x%x peer_lid=0x%x)",
type, MAC2STR(sta->addr), sta->my_lid, sta->peer_lid);
ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0,
sta->addr, wpa_s->own_addr, wpa_s->own_addr,
wpabuf_head(buf), wpabuf_len(buf), 0);
if (ret < 0)
wpa_msg(wpa_s, MSG_INFO,
"Mesh MPM: failed to send peering frame");
fail:
wpabuf_free(buf);
}
/* configure peering state in ours and driver's station entry */
void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
struct sta_info *sta,
enum mesh_plink_state state)
{
struct hostapd_sta_add_params params;
int ret;
wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " from %s into %s",
MAC2STR(sta->addr), mplstate[sta->plink_state],
mplstate[state]);
sta->plink_state = state;
os_memset(&params, 0, sizeof(params));
params.addr = sta->addr;
params.plink_state = state;
params.peer_aid = sta->peer_aid;
params.set = 1;
ret = wpa_drv_sta_add(wpa_s, &params);
if (ret) {
wpa_msg(wpa_s, MSG_ERROR, "Driver failed to set " MACSTR
": %d", MAC2STR(sta->addr), ret);
}
}
static void mesh_mpm_fsm_restart(struct wpa_supplicant *wpa_s,
struct sta_info *sta)
{
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
eloop_cancel_timeout(plink_timer, wpa_s, sta);
ap_free_sta(hapd, sta);
}
static void plink_timer(void *eloop_ctx, void *user_data)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct sta_info *sta = user_data;
u16 reason = 0;
struct mesh_conf *conf = wpa_s->ifmsh->mconf;
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
switch (sta->plink_state) {
case PLINK_OPN_RCVD:
case PLINK_OPN_SNT:
/* retry timer */
if (sta->mpm_retries < conf->dot11MeshMaxRetries) {
eloop_register_timeout(
conf->dot11MeshRetryTimeout / 1000,
(conf->dot11MeshRetryTimeout % 1000) * 1000,
plink_timer, wpa_s, sta);
mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0);
sta->mpm_retries++;
break;
}
reason = WLAN_REASON_MESH_MAX_RETRIES;
/* fall through */
case PLINK_CNF_RCVD:
/* confirm timer */
if (!reason)
reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
eloop_register_timeout(conf->dot11MeshHoldingTimeout / 1000,
(conf->dot11MeshHoldingTimeout % 1000) * 1000,
plink_timer, wpa_s, sta);
mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
break;
case PLINK_HOLDING:
/* holding timer */
if (sta->mesh_sae_pmksa_caching) {
wpa_printf(MSG_DEBUG, "MPM: Peer " MACSTR
" looks like it does not support mesh SAE PMKSA caching, so remove the cached entry for it",
MAC2STR(sta->addr));
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
}
mesh_mpm_fsm_restart(wpa_s, sta);
break;
default:
break;
}
}
/* initiate peering with station */
static void
mesh_mpm_plink_open(struct wpa_supplicant *wpa_s, struct sta_info *sta,
enum mesh_plink_state next_state)
{
struct mesh_conf *conf = wpa_s->ifmsh->mconf;
eloop_cancel_timeout(plink_timer, wpa_s, sta);
eloop_register_timeout(conf->dot11MeshRetryTimeout / 1000,
(conf->dot11MeshRetryTimeout % 1000) * 1000,
plink_timer, wpa_s, sta);
mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0);
wpa_mesh_set_plink_state(wpa_s, sta, next_state);
}
static int mesh_mpm_plink_close(struct hostapd_data *hapd, struct sta_info *sta,
void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
int reason = WLAN_REASON_MESH_PEERING_CANCELLED;
if (sta) {
if (sta->plink_state == PLINK_ESTAB)
hapd->num_plinks--;
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR,
MAC2STR(sta->addr));
eloop_cancel_timeout(plink_timer, wpa_s, sta);
eloop_cancel_timeout(mesh_auth_timer, wpa_s, sta);
return 0;
}
return 1;
}
int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
{
struct hostapd_data *hapd;
struct sta_info *sta;
if (!wpa_s->ifmsh) {
wpa_msg(wpa_s, MSG_INFO, "Mesh is not prepared yet");
return -1;
}
hapd = wpa_s->ifmsh->bss[0];
sta = ap_get_sta(hapd, addr);
if (!sta) {
wpa_msg(wpa_s, MSG_INFO, "No such mesh peer");
return -1;
}
return mesh_mpm_plink_close(hapd, sta, wpa_s) == 0 ? 0 : -1;
}
static void peer_add_timer(void *eloop_ctx, void *user_data)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
os_memset(hapd->mesh_required_peer, 0, ETH_ALEN);
}
int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
int duration)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct hostapd_data *hapd;
struct sta_info *sta;
struct mesh_conf *conf;
if (!wpa_s->ifmsh) {
wpa_msg(wpa_s, MSG_INFO, "Mesh is not prepared yet");
return -1;
}
if (!ssid || !ssid->no_auto_peer) {
wpa_msg(wpa_s, MSG_INFO,
"This command is available only with no_auto_peer mesh network");
return -1;
}
hapd = wpa_s->ifmsh->bss[0];
conf = wpa_s->ifmsh->mconf;
sta = ap_get_sta(hapd, addr);
if (!sta) {
wpa_msg(wpa_s, MSG_INFO, "No such mesh peer");
return -1;
}
if ((PLINK_OPN_SNT <= sta->plink_state &&
sta->plink_state <= PLINK_ESTAB) ||
(sta->sae && sta->sae->state > SAE_NOTHING)) {
wpa_msg(wpa_s, MSG_INFO,
"Specified peer is connecting/connected");
return -1;
}
if (conf->security == MESH_CONF_SEC_NONE) {
mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT);
} else {
mesh_rsn_auth_sae_sta(wpa_s, sta);
os_memcpy(hapd->mesh_required_peer, addr, ETH_ALEN);
eloop_register_timeout(duration == -1 ? 10 : duration, 0,
peer_add_timer, wpa_s, NULL);
}
return 0;
}
void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh)
{
struct hostapd_data *hapd = ifmsh->bss[0];
/* notify peers we're leaving */
ap_for_each_sta(hapd, mesh_mpm_plink_close, wpa_s);
hapd->num_plinks = 0;
hostapd_free_stas(hapd);
eloop_cancel_timeout(peer_add_timer, wpa_s, NULL);
}
/* for mesh_rsn to indicate this peer has completed authentication, and we're
* ready to start AMPE */
void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
{
struct hostapd_data *data = wpa_s->ifmsh->bss[0];
struct hostapd_sta_add_params params;
struct sta_info *sta;
int ret;
sta = ap_get_sta(data, addr);
if (!sta) {
wpa_msg(wpa_s, MSG_DEBUG, "no such mesh peer");
return;
}
/* TODO: Should do nothing if this STA is already authenticated, but
* the AP code already sets this flag. */
sta->flags |= WLAN_STA_AUTH;
mesh_rsn_init_ampe_sta(wpa_s, sta);
os_memset(&params, 0, sizeof(params));
params.addr = sta->addr;
params.flags = WPA_STA_AUTHENTICATED | WPA_STA_AUTHORIZED;
params.set = 1;
wpa_msg(wpa_s, MSG_DEBUG, "MPM authenticating " MACSTR,
MAC2STR(sta->addr));
ret = wpa_drv_sta_add(wpa_s, &params);
if (ret) {
wpa_msg(wpa_s, MSG_ERROR,
"Driver failed to set " MACSTR ": %d",
MAC2STR(sta->addr), ret);
}
if (!sta->my_lid)
mesh_mpm_init_link(wpa_s, sta);
mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT);
}
/*
* Initialize a sta_info structure for a peer and upload it into the driver
* in preparation for beginning authentication or peering. This is done when a
* Beacon (secure or open mesh) or a peering open frame (for open mesh) is
* received from the peer for the first time.
*/
static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s,
const u8 *addr,
struct ieee802_11_elems *elems)
{
struct hostapd_sta_add_params params;
struct mesh_conf *conf = wpa_s->ifmsh->mconf;
struct hostapd_data *data = wpa_s->ifmsh->bss[0];
struct sta_info *sta;
struct ieee80211_ht_operation *oper;
int ret;
if (elems->mesh_config_len >= 7 &&
!(elems->mesh_config[6] & MESH_CAP_ACCEPT_ADDITIONAL_PEER)) {
wpa_msg(wpa_s, MSG_DEBUG,
"mesh: Ignore a crowded peer " MACSTR,
MAC2STR(addr));
return NULL;
}
sta = ap_get_sta(data, addr);
if (sta)
return NULL;
sta = ap_sta_add(data, addr);
if (!sta)
return NULL;
/* Set WMM by default since Mesh STAs are QoS STAs */
sta->flags |= WLAN_STA_WMM;
/* initialize sta */
if (copy_supp_rates(wpa_s, sta, elems)) {
ap_free_sta(data, sta);
return NULL;
}
if (!sta->my_lid)
mesh_mpm_init_link(wpa_s, sta);
copy_sta_ht_capab(data, sta, elems->ht_capabilities);
oper = (struct ieee80211_ht_operation *) elems->ht_operation;
if (oper &&
!(oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) &&
sta->ht_capabilities) {
wpa_msg(wpa_s, MSG_DEBUG, MACSTR
" does not support 40 MHz bandwidth",
MAC2STR(sta->addr));
set_disable_ht40(sta->ht_capabilities, 1);
}
update_ht_state(data, sta);
#ifdef CONFIG_IEEE80211AC
copy_sta_vht_capab(data, sta, elems->vht_capabilities);
copy_sta_vht_oper(data, sta, elems->vht_operation);
set_sta_vht_opmode(data, sta, elems->vht_opmode_notif);
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
copy_sta_he_capab(data, sta, IEEE80211_MODE_MESH,
elems->he_capabilities, elems->he_capabilities_len);
+ copy_sta_he_6ghz_capab(data, sta, elems->he_6ghz_band_cap);
#endif /* CONFIG_IEEE80211AX */
if (hostapd_get_aid(data, sta) < 0) {
wpa_msg(wpa_s, MSG_ERROR, "No AIDs available");
ap_free_sta(data, sta);
return NULL;
}
/* insert into driver */
os_memset(&params, 0, sizeof(params));
params.supp_rates = sta->supported_rates;
params.supp_rates_len = sta->supported_rates_len;
params.addr = addr;
params.plink_state = sta->plink_state;
params.aid = sta->aid;
params.peer_aid = sta->peer_aid;
params.listen_interval = 100;
params.ht_capabilities = sta->ht_capabilities;
params.vht_capabilities = sta->vht_capabilities;
params.he_capab = sta->he_capab;
params.he_capab_len = sta->he_capab_len;
+ params.he_6ghz_capab = sta->he_6ghz_capab;
params.flags |= WPA_STA_WMM;
params.flags_mask |= WPA_STA_AUTHENTICATED;
if (conf->security == MESH_CONF_SEC_NONE) {
params.flags |= WPA_STA_AUTHORIZED;
params.flags |= WPA_STA_AUTHENTICATED;
} else {
sta->flags |= WLAN_STA_MFP;
params.flags |= WPA_STA_MFP;
}
ret = wpa_drv_sta_add(wpa_s, &params);
if (ret) {
wpa_msg(wpa_s, MSG_ERROR,
"Driver failed to insert " MACSTR ": %d",
MAC2STR(addr), ret);
ap_free_sta(data, sta);
return NULL;
}
return sta;
}
void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
struct ieee802_11_elems *elems)
{
struct mesh_conf *conf = wpa_s->ifmsh->mconf;
struct hostapd_data *data = wpa_s->ifmsh->bss[0];
struct sta_info *sta;
struct wpa_ssid *ssid = wpa_s->current_ssid;
sta = mesh_mpm_add_peer(wpa_s, addr, elems);
if (!sta)
return;
if (ssid && ssid->no_auto_peer &&
(is_zero_ether_addr(data->mesh_required_peer) ||
os_memcmp(data->mesh_required_peer, addr, ETH_ALEN) != 0)) {
wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with "
MACSTR " because of no_auto_peer", MAC2STR(addr));
if (data->mesh_pending_auth) {
struct os_reltime age;
const struct ieee80211_mgmt *mgmt;
struct hostapd_frame_info fi;
mgmt = wpabuf_head(data->mesh_pending_auth);
os_reltime_age(&data->mesh_pending_auth_time, &age);
if (age.sec < 2 &&
os_memcmp(mgmt->sa, addr, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG,
"mesh: Process pending Authentication frame from %u.%06u seconds ago",
(unsigned int) age.sec,
(unsigned int) age.usec);
os_memset(&fi, 0, sizeof(fi));
ieee802_11_mgmt(
data,
wpabuf_head(data->mesh_pending_auth),
wpabuf_len(data->mesh_pending_auth),
&fi);
}
wpabuf_free(data->mesh_pending_auth);
data->mesh_pending_auth = NULL;
}
return;
}
if (conf->security == MESH_CONF_SEC_NONE) {
if (sta->plink_state < PLINK_OPN_SNT ||
sta->plink_state > PLINK_ESTAB)
mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT);
} else {
mesh_rsn_auth_sae_sta(wpa_s, sta);
}
}
void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt)
{
struct hostapd_frame_info fi;
os_memset(&fi, 0, sizeof(fi));
fi.datarate = rx_mgmt->datarate;
fi.ssi_signal = rx_mgmt->ssi_signal;
ieee802_11_mgmt(wpa_s->ifmsh->bss[0], rx_mgmt->frame,
rx_mgmt->frame_len, &fi);
}
static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
struct sta_info *sta)
{
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
struct mesh_conf *conf = wpa_s->ifmsh->mconf;
u8 seq[6] = {};
wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR " established",
MAC2STR(sta->addr));
if (conf->security & MESH_CONF_SEC_AMPE) {
wpa_hexdump_key(MSG_DEBUG, "mesh: MTK", sta->mtk, sta->mtk_len);
wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->pairwise_cipher),
sta->addr, 0, 0, seq, sizeof(seq),
sta->mtk, sta->mtk_len,
KEY_FLAG_PAIRWISE_RX_TX);
wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK Key RSC",
sta->mgtk_rsc, sizeof(sta->mgtk_rsc));
wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK",
sta->mgtk, sta->mgtk_len);
wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->group_cipher),
sta->addr, sta->mgtk_key_id, 0,
sta->mgtk_rsc, sizeof(sta->mgtk_rsc),
sta->mgtk, sta->mgtk_len,
KEY_FLAG_GROUP_RX);
if (sta->igtk_len) {
wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK Key RSC",
sta->igtk_rsc, sizeof(sta->igtk_rsc));
wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK",
sta->igtk, sta->igtk_len);
wpa_drv_set_key(
wpa_s,
wpa_cipher_to_alg(conf->mgmt_group_cipher),
sta->addr, sta->igtk_key_id, 0,
sta->igtk_rsc, sizeof(sta->igtk_rsc),
sta->igtk, sta->igtk_len,
KEY_FLAG_GROUP_RX);
}
}
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB);
hapd->num_plinks++;
sta->flags |= WLAN_STA_ASSOC;
sta->mesh_sae_pmksa_caching = 0;
eloop_cancel_timeout(peer_add_timer, wpa_s, NULL);
peer_add_timer(wpa_s, NULL);
eloop_cancel_timeout(plink_timer, wpa_s, sta);
/* Send ctrl event */
wpa_msg(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR,
MAC2STR(sta->addr));
/* Send D-Bus event */
wpas_notify_mesh_peer_connected(wpa_s, sta->addr);
}
static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta,
enum plink_event event, u16 reason)
{
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
struct mesh_conf *conf = wpa_s->ifmsh->mconf;
wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s",
MAC2STR(sta->addr), mplstate[sta->plink_state],
mplevent[event]);
switch (sta->plink_state) {
case PLINK_IDLE:
switch (event) {
case CLS_ACPT:
mesh_mpm_fsm_restart(wpa_s, sta);
break;
case OPN_ACPT:
mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_RCVD);
mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM,
0);
break;
case REQ_RJCT:
mesh_mpm_send_plink_action(wpa_s, sta,
PLINK_CLOSE, reason);
break;
default:
break;
}
break;
case PLINK_OPN_SNT:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
if (!reason)
reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
/* fall-through */
case CLS_ACPT:
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
if (!reason)
reason = WLAN_REASON_MESH_CLOSE_RCVD;
eloop_register_timeout(
conf->dot11MeshHoldingTimeout / 1000,
(conf->dot11MeshHoldingTimeout % 1000) * 1000,
plink_timer, wpa_s, sta);
mesh_mpm_send_plink_action(wpa_s, sta,
PLINK_CLOSE, reason);
break;
case OPN_ACPT:
/* retry timer is left untouched */
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPN_RCVD);
mesh_mpm_send_plink_action(wpa_s, sta,
PLINK_CONFIRM, 0);
break;
case CNF_ACPT:
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD);
eloop_cancel_timeout(plink_timer, wpa_s, sta);
eloop_register_timeout(
conf->dot11MeshConfirmTimeout / 1000,
(conf->dot11MeshConfirmTimeout % 1000) * 1000,
plink_timer, wpa_s, sta);
break;
default:
break;
}
break;
case PLINK_OPN_RCVD:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
if (!reason)
reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
/* fall-through */
case CLS_ACPT:
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
if (!reason)
reason = WLAN_REASON_MESH_CLOSE_RCVD;
eloop_register_timeout(
conf->dot11MeshHoldingTimeout / 1000,
(conf->dot11MeshHoldingTimeout % 1000) * 1000,
plink_timer, wpa_s, sta);
sta->mpm_close_reason = reason;
mesh_mpm_send_plink_action(wpa_s, sta,
PLINK_CLOSE, reason);
break;
case OPN_ACPT:
mesh_mpm_send_plink_action(wpa_s, sta,
PLINK_CONFIRM, 0);
break;
case CNF_ACPT:
if (conf->security & MESH_CONF_SEC_AMPE)
mesh_rsn_derive_mtk(wpa_s, sta);
mesh_mpm_plink_estab(wpa_s, sta);
break;
default:
break;
}
break;
case PLINK_CNF_RCVD:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
if (!reason)
reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
/* fall-through */
case CLS_ACPT:
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
if (!reason)
reason = WLAN_REASON_MESH_CLOSE_RCVD;
eloop_register_timeout(
conf->dot11MeshHoldingTimeout / 1000,
(conf->dot11MeshHoldingTimeout % 1000) * 1000,
plink_timer, wpa_s, sta);
sta->mpm_close_reason = reason;
mesh_mpm_send_plink_action(wpa_s, sta,
PLINK_CLOSE, reason);
break;
case OPN_ACPT:
if (conf->security & MESH_CONF_SEC_AMPE)
mesh_rsn_derive_mtk(wpa_s, sta);
mesh_mpm_plink_estab(wpa_s, sta);
mesh_mpm_send_plink_action(wpa_s, sta,
PLINK_CONFIRM, 0);
break;
default:
break;
}
break;
case PLINK_ESTAB:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
case CLS_ACPT:
wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
if (!reason)
reason = WLAN_REASON_MESH_CLOSE_RCVD;
eloop_register_timeout(
conf->dot11MeshHoldingTimeout / 1000,
(conf->dot11MeshHoldingTimeout % 1000) * 1000,
plink_timer, wpa_s, sta);
sta->mpm_close_reason = reason;
wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR
" closed with reason %d",
MAC2STR(sta->addr), reason);
wpa_msg(wpa_s, MSG_INFO, MESH_PEER_DISCONNECTED MACSTR,
MAC2STR(sta->addr));
/* Send D-Bus event */
wpas_notify_mesh_peer_disconnected(wpa_s, sta->addr,
reason);
hapd->num_plinks--;
mesh_mpm_send_plink_action(wpa_s, sta,
PLINK_CLOSE, reason);
break;
case OPN_ACPT:
mesh_mpm_send_plink_action(wpa_s, sta,
PLINK_CONFIRM, 0);
break;
default:
break;
}
break;
case PLINK_HOLDING:
switch (event) {
case CLS_ACPT:
mesh_mpm_fsm_restart(wpa_s, sta);
break;
case OPN_ACPT:
case CNF_ACPT:
case OPN_RJCT:
case CNF_RJCT:
reason = sta->mpm_close_reason;
mesh_mpm_send_plink_action(wpa_s, sta,
PLINK_CLOSE, reason);
break;
default:
break;
}
break;
default:
wpa_msg(wpa_s, MSG_DEBUG,
"Unsupported MPM event %s for state %s",
mplevent[event], mplstate[sta->plink_state]);
break;
}
}
void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
const struct ieee80211_mgmt *mgmt, size_t len)
{
u8 action_field;
struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
struct mesh_conf *mconf = wpa_s->ifmsh->mconf;
struct sta_info *sta;
u16 plid = 0, llid = 0, aid = 0;
enum plink_event event;
struct ieee802_11_elems elems;
struct mesh_peer_mgmt_ie peer_mgmt_ie;
const u8 *ies;
size_t ie_len;
int ret;
u16 reason = 0;
if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED)
return;
action_field = mgmt->u.action.u.slf_prot_action.action;
if (action_field != PLINK_OPEN &&
action_field != PLINK_CONFIRM &&
action_field != PLINK_CLOSE)
return;
ies = mgmt->u.action.u.slf_prot_action.variable;
ie_len = (const u8 *) mgmt + len -
mgmt->u.action.u.slf_prot_action.variable;
/* at least expect mesh id and peering mgmt */
if (ie_len < 2 + 2) {
wpa_printf(MSG_DEBUG,
"MPM: Ignore too short action frame %u ie_len %u",
action_field, (unsigned int) ie_len);
return;
}
wpa_printf(MSG_DEBUG, "MPM: Received PLINK action %u", action_field);
if (action_field == PLINK_OPEN || action_field == PLINK_CONFIRM) {
wpa_printf(MSG_DEBUG, "MPM: Capability 0x%x",
WPA_GET_LE16(ies));
ies += 2; /* capability */
ie_len -= 2;
}
if (action_field == PLINK_CONFIRM) {
aid = WPA_GET_LE16(ies);
wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", aid);
ies += 2; /* aid */
ie_len -= 2;
}
/* check for mesh peering, mesh id and mesh config IEs */
if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) {
wpa_printf(MSG_DEBUG, "MPM: Failed to parse PLINK IEs");
return;
}
if (!elems.peer_mgmt) {
wpa_printf(MSG_DEBUG,
"MPM: No Mesh Peering Management element");
return;
}
if (action_field != PLINK_CLOSE) {
if (!elems.mesh_id || !elems.mesh_config) {
wpa_printf(MSG_DEBUG,
"MPM: No Mesh ID or Mesh Configuration element");
return;
}
if (!matches_local(wpa_s, &elems)) {
wpa_printf(MSG_DEBUG,
"MPM: Mesh ID or Mesh Configuration element do not match local MBSS");
return;
}
}
ret = mesh_mpm_parse_peer_mgmt(wpa_s, action_field,
elems.peer_mgmt,
elems.peer_mgmt_len,
&peer_mgmt_ie);
if (ret) {
wpa_printf(MSG_DEBUG, "MPM: Mesh parsing rejected frame");
return;
}
/* the sender's llid is our plid and vice-versa */
plid = WPA_GET_LE16(peer_mgmt_ie.llid);
if (peer_mgmt_ie.plid)
llid = WPA_GET_LE16(peer_mgmt_ie.plid);
wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid);
if (action_field == PLINK_CLOSE)
wpa_printf(MSG_DEBUG, "MPM: close reason=%u",
WPA_GET_LE16(peer_mgmt_ie.reason));
sta = ap_get_sta(hapd, mgmt->sa);
/*
* If this is an open frame from an unknown STA, and this is an
* open mesh, then go ahead and add the peer before proceeding.
*/
if (!sta && action_field == PLINK_OPEN &&
(!(mconf->security & MESH_CONF_SEC_AMPE) ||
wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa, NULL)))
sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
if (!sta) {
wpa_printf(MSG_DEBUG, "MPM: No STA entry for peer");
return;
}
#ifdef CONFIG_SAE
/* peer is in sae_accepted? */
if (sta->sae && sta->sae->state != SAE_ACCEPTED) {
wpa_printf(MSG_DEBUG, "MPM: SAE not yet accepted for peer");
return;
}
#endif /* CONFIG_SAE */
if (!sta->my_lid)
mesh_mpm_init_link(wpa_s, sta);
if (mconf->security & MESH_CONF_SEC_AMPE) {
int res;
res = mesh_rsn_process_ampe(wpa_s, sta, &elems,
&mgmt->u.action.category,
peer_mgmt_ie.chosen_pmk,
ies, ie_len);
if (res) {
wpa_printf(MSG_DEBUG,
"MPM: RSN process rejected frame (res=%d)",
res);
if (action_field == PLINK_OPEN && res == -2) {
/* AES-SIV decryption failed */
mesh_mpm_fsm(wpa_s, sta, OPN_RJCT,
WLAN_REASON_MESH_INVALID_GTK);
}
return;
}
#ifdef CONFIG_OCV
if (action_field == PLINK_OPEN && elems.rsn_ie) {
struct wpa_state_machine *sm = sta->wpa_sm;
struct wpa_ie_data data;
res = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2,
elems.rsn_ie_len + 2,
&data);
if (res) {
wpa_printf(MSG_DEBUG,
"Failed to parse RSN IE (res=%d)",
res);
wpa_hexdump(MSG_DEBUG, "RSN IE", elems.rsn_ie,
elems.rsn_ie_len);
return;
}
wpa_auth_set_ocv(sm, mconf->ocv &&
(data.capabilities &
WPA_CAPABILITY_OCVC));
}
if (action_field != PLINK_CLOSE &&
wpa_auth_uses_ocv(sta->wpa_sm)) {
struct wpa_channel_info ci;
int tx_chanwidth;
int tx_seg1_idx;
if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
wpa_printf(MSG_WARNING,
"MPM: Failed to get channel info to validate received OCI in MPM Confirm");
return;
}
if (get_tx_parameters(
sta, channel_width_to_int(ci.chanwidth),
ci.seg1_idx, &tx_chanwidth,
&tx_seg1_idx) < 0)
return;
if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
tx_chanwidth, tx_seg1_idx) !=
OCI_SUCCESS) {
wpa_printf(MSG_WARNING, "MPM: OCV failed: %s",
ocv_errorstr);
return;
}
}
#endif /* CONFIG_OCV */
}
if (sta->plink_state == PLINK_BLOCKED) {
wpa_printf(MSG_DEBUG, "MPM: PLINK_BLOCKED");
return;
}
/* Now we will figure out the appropriate event... */
switch (action_field) {
case PLINK_OPEN:
if (plink_free_count(hapd) == 0) {
event = REQ_RJCT;
reason = WLAN_REASON_MESH_MAX_PEERS;
wpa_printf(MSG_INFO,
"MPM: Peer link num over quota(%d)",
hapd->max_plinks);
} else if (sta->peer_lid && sta->peer_lid != plid) {
wpa_printf(MSG_DEBUG,
"MPM: peer_lid mismatch: 0x%x != 0x%x",
sta->peer_lid, plid);
return; /* no FSM event */
} else {
sta->peer_lid = plid;
event = OPN_ACPT;
}
break;
case PLINK_CONFIRM:
if (plink_free_count(hapd) == 0) {
event = REQ_RJCT;
reason = WLAN_REASON_MESH_MAX_PEERS;
wpa_printf(MSG_INFO,
"MPM: Peer link num over quota(%d)",
hapd->max_plinks);
} else if (sta->my_lid != llid ||
(sta->peer_lid && sta->peer_lid != plid)) {
wpa_printf(MSG_DEBUG,
"MPM: lid mismatch: my_lid: 0x%x != 0x%x or peer_lid: 0x%x != 0x%x",
sta->my_lid, llid, sta->peer_lid, plid);
return; /* no FSM event */
} else {
if (!sta->peer_lid)
sta->peer_lid = plid;
sta->peer_aid = aid;
event = CNF_ACPT;
}
break;
case PLINK_CLOSE:
if (sta->plink_state == PLINK_ESTAB)
/* Do not check for llid or plid. This does not
* follow the standard but since multiple plinks
* per cand are not supported, it is necessary in
* order to avoid a livelock when MP A sees an
* establish peer link to MP B but MP B does not
* see it. This can be caused by a timeout in
* B's peer link establishment or B being
* restarted.
*/
event = CLS_ACPT;
else if (sta->peer_lid != plid) {
wpa_printf(MSG_DEBUG,
"MPM: peer_lid mismatch: 0x%x != 0x%x",
sta->peer_lid, plid);
return; /* no FSM event */
} else if (peer_mgmt_ie.plid && sta->my_lid != llid) {
wpa_printf(MSG_DEBUG,
"MPM: my_lid mismatch: 0x%x != 0x%x",
sta->my_lid, llid);
return; /* no FSM event */
} else {
event = CLS_ACPT;
}
break;
default:
/*
* This cannot be hit due to the action_field check above, but
* compilers may not be able to figure that out and can warn
* about uninitialized event below.
*/
return;
}
mesh_mpm_fsm(wpa_s, sta, event, reason);
}
/* called by ap_free_sta */
void mesh_mpm_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
if (sta->plink_state == PLINK_ESTAB)
hapd->num_plinks--;
eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta);
eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta);
}
diff --git a/contrib/wpa/wpa_supplicant/notify.c b/contrib/wpa/wpa_supplicant/notify.c
index e0e7e5433d77..fe5e072c24c2 100644
--- a/contrib/wpa/wpa_supplicant/notify.c
+++ b/contrib/wpa/wpa_supplicant/notify.c
@@ -1,939 +1,945 @@
/*
* wpa_supplicant - Event notifications
* Copyright (c) 2009-2010, 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"
#include "utils/common.h"
#include "common/wpa_ctrl.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "wps_supplicant.h"
#include "binder/binder.h"
#include "dbus/dbus_common.h"
#include "dbus/dbus_new.h"
#include "rsn_supp/wpa.h"
#include "fst/fst.h"
#include "crypto/tls.h"
#include "driver_i.h"
#include "scan.h"
#include "p2p_supplicant.h"
#include "sme.h"
#include "notify.h"
int wpas_notify_supplicant_initialized(struct wpa_global *global)
{
#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
if (global->params.dbus_ctrl_interface) {
global->dbus = wpas_dbus_init(global);
if (global->dbus == NULL)
return -1;
}
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
#ifdef CONFIG_BINDER
global->binder = wpas_binder_init(global);
if (!global->binder)
return -1;
#endif /* CONFIG_BINDER */
return 0;
}
void wpas_notify_supplicant_deinitialized(struct wpa_global *global)
{
#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
if (global->dbus)
wpas_dbus_deinit(global->dbus);
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
#ifdef CONFIG_BINDER
if (global->binder)
wpas_binder_deinit(global->binder);
#endif /* CONFIG_BINDER */
}
int wpas_notify_iface_added(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return 0;
if (wpas_dbus_register_interface(wpa_s))
return -1;
return 0;
}
void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
/* unregister interface in new DBus ctrl iface */
wpas_dbus_unregister_interface(wpa_s);
}
void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
enum wpa_states new_state,
enum wpa_states old_state)
{
if (wpa_s->p2p_mgmt)
return;
/* notify the new DBus API */
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
#ifdef CONFIG_FST
if (wpa_s->fst && !is_zero_ether_addr(wpa_s->bssid)) {
if (new_state == WPA_COMPLETED)
fst_notify_peer_connected(wpa_s->fst, wpa_s->bssid);
else if (old_state >= WPA_ASSOCIATED &&
new_state < WPA_ASSOCIATED)
fst_notify_peer_disconnected(wpa_s->fst, wpa_s->bssid);
}
#endif /* CONFIG_FST */
if (new_state == WPA_COMPLETED)
wpas_p2p_notif_connected(wpa_s);
else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
wpas_p2p_notif_disconnected(wpa_s);
sme_state_changed(wpa_s);
#ifdef ANDROID
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,
new_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) : "");
#endif /* ANDROID */
}
void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON);
}
void wpas_notify_auth_status_code(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AUTH_STATUS_CODE);
}
void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ASSOC_STATUS_CODE);
}
void wpas_notify_roam_time(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ROAM_TIME);
}
void wpas_notify_roam_complete(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ROAM_COMPLETE);
}
void wpas_notify_session_length(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SESSION_LENGTH);
}
void wpas_notify_bss_tm_status(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_BSS_TM_STATUS);
}
void wpas_notify_network_changed(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK);
}
void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AP_SCAN);
}
void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS);
}
void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE);
}
void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_network_enabled_changed(wpa_s, ssid);
}
void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_network_selected(wpa_s, ssid->id);
}
void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
enum wpa_ctrl_req_type rtype,
const char *default_txt)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt);
}
void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
/* notify the new DBus API */
wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SCANNING);
}
void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_scan_done(wpa_s, success);
}
void wpas_notify_scan_results(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
wpas_wps_notify_scan_results(wpa_s);
}
void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
const struct wps_credential *cred)
{
if (wpa_s->p2p_mgmt)
return;
#ifdef CONFIG_WPS
/* notify the new DBus API */
wpas_dbus_signal_wps_cred(wpa_s, cred);
#endif /* CONFIG_WPS */
}
void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
struct wps_event_m2d *m2d)
{
if (wpa_s->p2p_mgmt)
return;
#ifdef CONFIG_WPS
wpas_dbus_signal_wps_event_m2d(wpa_s, m2d);
#endif /* CONFIG_WPS */
}
void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
struct wps_event_fail *fail)
{
if (wpa_s->p2p_mgmt)
return;
#ifdef CONFIG_WPS
wpas_dbus_signal_wps_event_fail(wpa_s, fail);
#endif /* CONFIG_WPS */
}
void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
#ifdef CONFIG_WPS
wpas_dbus_signal_wps_event_success(wpa_s);
#endif /* CONFIG_WPS */
}
void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_mgmt)
return;
#ifdef CONFIG_WPS
wpas_dbus_signal_wps_event_pbc_overlap(wpa_s);
#endif /* CONFIG_WPS */
}
void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (wpa_s->p2p_mgmt)
return;
/*
* Networks objects created during any P2P activities should not be
* exposed out. They might/will confuse certain non-P2P aware
* applications since these network objects won't behave like
* regular ones.
*/
- if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
+ if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) {
wpas_dbus_register_network(wpa_s, ssid);
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_NETWORK_ADDED "%d",
+ ssid->id);
+ }
}
void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
#ifdef CONFIG_P2P
wpas_dbus_register_persistent_group(wpa_s, ssid);
#endif /* CONFIG_P2P */
}
void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
#ifdef CONFIG_P2P
wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
#endif /* CONFIG_P2P */
}
void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (wpa_s->next_ssid == ssid)
wpa_s->next_ssid = NULL;
if (wpa_s->wpa)
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s &&
- !wpa_s->p2p_mgmt)
+ !wpa_s->p2p_mgmt) {
wpas_dbus_unregister_network(wpa_s, ssid->id);
+ wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_NETWORK_REMOVED "%d",
+ ssid->id);
+ }
if (network_is_persistent_group(ssid))
wpas_notify_persistent_group_removed(wpa_s, ssid);
wpas_p2p_network_removed(wpa_s, ssid);
#ifdef CONFIG_PASN
if (wpa_s->pasn.ssid == ssid)
wpa_s->pasn.ssid = NULL;
#endif /* CONFIG_PASN */
}
void wpas_notify_bss_added(struct wpa_supplicant *wpa_s,
u8 bssid[], unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_register_bss(wpa_s, bssid, id);
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_ADDED "%u " MACSTR,
id, MAC2STR(bssid));
}
void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s,
u8 bssid[], unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_unregister_bss(wpa_s, bssid, id);
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_REMOVED "%u " MACSTR,
id, MAC2STR(bssid));
}
void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_FREQ, id);
}
void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_SIGNAL,
id);
}
void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_PRIVACY,
id);
}
void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_MODE, id);
}
void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPA, id);
}
void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RSN, id);
}
void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
#ifdef CONFIG_WPS
wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPS, id);
#endif /* CONFIG_WPS */
}
void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_IES, id);
}
void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RATES, id);
}
void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_AGE, id);
}
void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_blob_added(wpa_s, name);
}
void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_blob_removed(wpa_s, name);
}
void wpas_notify_debug_level_changed(struct wpa_global *global)
{
wpas_dbus_signal_debug_level_changed(global);
}
void wpas_notify_debug_timestamp_changed(struct wpa_global *global)
{
wpas_dbus_signal_debug_timestamp_changed(global);
}
void wpas_notify_debug_show_keys_changed(struct wpa_global *global)
{
wpas_dbus_signal_debug_show_keys_changed(global);
}
void wpas_notify_suspend(struct wpa_global *global)
{
struct wpa_supplicant *wpa_s;
os_get_time(&global->suspend_time);
wpa_printf(MSG_DEBUG, "System suspend notification");
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
wpa_drv_suspend(wpa_s);
}
void wpas_notify_resume(struct wpa_global *global)
{
struct os_time now;
int slept;
struct wpa_supplicant *wpa_s;
if (global->suspend_time.sec == 0)
slept = -1;
else {
os_get_time(&now);
slept = now.sec - global->suspend_time.sec;
}
wpa_printf(MSG_DEBUG, "System resume notification (slept %d seconds)",
slept);
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
wpa_drv_resume(wpa_s);
if (wpa_s->wpa_state == WPA_DISCONNECTED)
wpa_supplicant_req_scan(wpa_s, 0, 100000);
}
}
#ifdef CONFIG_P2P
void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s)
{
/* Notify P2P find has stopped */
wpas_dbus_signal_p2p_find_stopped(wpa_s);
}
void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
const u8 *dev_addr, int new_device)
{
if (new_device) {
/* Create the new peer object */
wpas_dbus_register_peer(wpa_s, dev_addr);
}
/* Notify a new peer has been detected*/
wpas_dbus_signal_peer_device_found(wpa_s, dev_addr);
}
void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
const u8 *dev_addr)
{
wpas_dbus_unregister_peer(wpa_s, dev_addr);
/* Create signal on interface object*/
wpas_dbus_signal_peer_device_lost(wpa_s, dev_addr);
}
void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
const struct wpa_ssid *ssid,
const char *role)
{
wpas_dbus_signal_p2p_group_removed(wpa_s, role);
wpas_dbus_unregister_p2p_group(wpa_s, ssid);
}
void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
const u8 *src, u16 dev_passwd_id, u8 go_intent)
{
wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
}
void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *res)
{
wpas_dbus_signal_p2p_go_neg_resp(wpa_s, res);
}
void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
int status, const u8 *bssid)
{
wpas_dbus_signal_p2p_invitation_result(wpa_s, status, bssid);
}
void wpas_notify_p2p_sd_request(struct wpa_supplicant *wpa_s,
int freq, const u8 *sa, u8 dialog_token,
u16 update_indic, const u8 *tlvs,
size_t tlvs_len)
{
wpas_dbus_signal_p2p_sd_request(wpa_s, freq, sa, dialog_token,
update_indic, tlvs, tlvs_len);
}
void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
const u8 *sa, u16 update_indic,
const u8 *tlvs, size_t tlvs_len)
{
wpas_dbus_signal_p2p_sd_response(wpa_s, sa, update_indic,
tlvs, tlvs_len);
}
/**
* wpas_notify_p2p_provision_discovery - Notification of provision discovery
* @dev_addr: Who sent the request or responded to our request.
* @request: Will be 1 if request, 0 for response.
* @status: Valid only in case of response (0 in case of success)
* @config_methods: WPS config methods
* @generated_pin: PIN to be displayed in case of WPS_CONFIG_DISPLAY method
*
* This can be used to notify:
* - Requests or responses
* - Various config methods
* - Failure condition in case of response
*/
void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
const u8 *dev_addr, int request,
enum p2p_prov_disc_status status,
u16 config_methods,
unsigned int generated_pin)
{
wpas_dbus_signal_p2p_provision_discovery(wpa_s, dev_addr, request,
status, config_methods,
generated_pin);
}
void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int persistent,
int client, const u8 *ip)
{
/* Notify a group has been started */
wpas_dbus_register_p2p_group(wpa_s, ssid);
wpas_dbus_signal_p2p_group_started(wpa_s, client, persistent, ip);
}
void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
const char *reason)
{
/* Notify a group formation failed */
wpas_dbus_signal_p2p_group_formation_failure(wpa_s, reason);
}
void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
struct wps_event_fail *fail)
{
wpas_dbus_signal_p2p_wps_failed(wpa_s, fail);
}
void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
const u8 *sa, const u8 *go_dev_addr,
const u8 *bssid, int id, int op_freq)
{
/* Notify a P2P Invitation Request */
wpas_dbus_signal_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
id, op_freq);
}
#endif /* CONFIG_P2P */
static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
const u8 *sta,
const u8 *p2p_dev_addr)
{
#ifdef CONFIG_P2P
wpas_p2p_notify_ap_sta_authorized(wpa_s, p2p_dev_addr);
/*
* Create 'peer-joined' signal on group object -- will also
* check P2P itself.
*/
if (p2p_dev_addr)
wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr);
#endif /* CONFIG_P2P */
/* Register the station */
wpas_dbus_register_sta(wpa_s, sta);
/* Notify listeners a new station has been authorized */
wpas_dbus_signal_sta_authorized(wpa_s, sta);
}
static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s,
const u8 *sta,
const u8 *p2p_dev_addr)
{
#ifdef CONFIG_P2P
/*
* Create 'peer-disconnected' signal on group object if this
* is a P2P group.
*/
if (p2p_dev_addr)
wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr);
#endif /* CONFIG_P2P */
/* Notify listeners a station has been deauthorized */
wpas_dbus_signal_sta_deauthorized(wpa_s, sta);
/* Unregister the station */
wpas_dbus_unregister_sta(wpa_s, sta);
}
void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
const u8 *mac_addr, int authorized,
const u8 *p2p_dev_addr)
{
if (authorized)
wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr);
else
wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr);
}
void wpas_notify_certification(struct wpa_supplicant *wpa_s,
struct tls_cert_data *cert,
const char *cert_hash)
{
int i;
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
"depth=%d subject='%s'%s%s%s%s",
cert->depth, cert->subject, cert_hash ? " hash=" : "",
cert_hash ? cert_hash : "",
cert->tod == 2 ? " tod=2" : "",
cert->tod == 1 ? " tod=1" : "");
if (cert->cert) {
char *cert_hex;
size_t len = wpabuf_len(cert->cert) * 2 + 1;
cert_hex = os_malloc(len);
if (cert_hex) {
wpa_snprintf_hex(cert_hex, len, wpabuf_head(cert->cert),
wpabuf_len(cert->cert));
wpa_msg_ctrl(wpa_s, MSG_INFO,
WPA_EVENT_EAP_PEER_CERT
"depth=%d subject='%s' cert=%s",
cert->depth, cert->subject, cert_hex);
os_free(cert_hex);
}
}
for (i = 0; i < cert->num_altsubject; i++)
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
"depth=%d %s", cert->depth, cert->altsubject[i]);
/* notify the new DBus API */
wpas_dbus_signal_certification(wpa_s, cert->depth, cert->subject,
cert->altsubject, cert->num_altsubject,
cert_hash, cert->cert);
}
void wpas_notify_preq(struct wpa_supplicant *wpa_s,
const u8 *addr, const u8 *dst, const u8 *bssid,
const u8 *ie, size_t ie_len, u32 ssi_signal)
{
#ifdef CONFIG_AP
wpas_dbus_signal_preq(wpa_s, addr, dst, bssid, ie, ie_len, ssi_signal);
#endif /* CONFIG_AP */
}
void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
const char *parameter)
{
wpas_dbus_signal_eap_status(wpa_s, status, parameter);
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_EAP_STATUS
"status='%s' parameter='%s'",
status, parameter);
}
void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code)
{
wpa_msg(wpa_s, MSG_ERROR, WPA_EVENT_EAP_ERROR_CODE "%d", error_code);
}
void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (wpa_s->current_ssid != ssid)
return;
wpa_dbg(wpa_s, MSG_DEBUG,
"Network bssid config changed for the current network - within-ESS roaming %s",
ssid->bssid_set ? "disabled" : "enabled");
wpa_drv_roaming(wpa_s, !ssid->bssid_set,
ssid->bssid_set ? ssid->bssid : NULL);
}
void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
#ifdef CONFIG_P2P
if (ssid->disabled == 2) {
/* Changed from normal network profile to persistent group */
ssid->disabled = 0;
wpas_dbus_unregister_network(wpa_s, ssid->id);
ssid->disabled = 2;
ssid->p2p_persistent_group = 1;
wpas_dbus_register_persistent_group(wpa_s, ssid);
} else {
/* Changed from persistent group to normal network profile */
wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
ssid->p2p_persistent_group = 0;
wpas_dbus_register_network(wpa_s, ssid);
}
#endif /* CONFIG_P2P */
}
#ifdef CONFIG_MESH
void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_mesh_group_started(wpa_s, ssid);
}
void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s,
const u8 *meshid, u8 meshid_len,
u16 reason_code)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_mesh_group_removed(wpa_s, meshid, meshid_len,
reason_code);
}
void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s,
const u8 *peer_addr)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_mesh_peer_connected(wpa_s, peer_addr);
}
void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s,
const u8 *peer_addr, u16 reason_code)
{
if (wpa_s->p2p_mgmt)
return;
wpas_dbus_signal_mesh_peer_disconnected(wpa_s, peer_addr, reason_code);
}
#endif /* CONFIG_MESH */
diff --git a/contrib/wpa/wpa_supplicant/op_classes.c b/contrib/wpa/wpa_supplicant/op_classes.c
index a0ad0c2ff572..bd53c5ceceaf 100644
--- a/contrib/wpa/wpa_supplicant/op_classes.c
+++ b/contrib/wpa/wpa_supplicant/op_classes.c
@@ -1,530 +1,534 @@
/*
* Operating classes
* Copyright(c) 2015 Intel Deutschland GmbH
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "common/ieee802_11_common.h"
#include "wpa_supplicant_i.h"
#include "bss.h"
static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode,
u8 op_class, u8 chan,
unsigned int *flags)
{
int i;
bool is_6ghz = op_class >= 131 && op_class <= 136;
for (i = 0; i < mode->num_channels; i++) {
bool chan_is_6ghz;
chan_is_6ghz = mode->channels[i].freq >= 5935 &&
mode->channels[i].freq <= 7115;
if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan)
break;
}
if (i == mode->num_channels ||
(mode->channels[i].flag & HOSTAPD_CHAN_DISABLED))
return NOT_ALLOWED;
if (flags)
*flags = mode->channels[i].flag;
if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR)
return NO_IR;
return ALLOWED;
}
static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel,
const u8 *center_channels, size_t num_chan)
{
size_t i;
if (mode->mode != HOSTAPD_MODE_IEEE80211A)
return 0;
for (i = 0; i < num_chan; i++) {
/*
* In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
* so the center channel is 6 channels away from the start/end.
*/
if (channel >= center_channels[i] - 6 &&
channel <= center_channels[i] + 6)
return center_channels[i];
}
return 0;
}
static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode,
u8 op_class, u8 channel)
{
u8 center_chan;
unsigned int i;
unsigned int no_ir = 0;
const u8 *center_channels;
size_t num_chan;
const u8 center_channels_5ghz[] = { 42, 58, 106, 122, 138, 155, 171 };
const u8 center_channels_6ghz[] = { 7, 23, 39, 55, 71, 87, 103, 119,
135, 151, 167, 183, 199, 215 };
if (is_6ghz_op_class(op_class)) {
center_channels = center_channels_6ghz;
num_chan = ARRAY_SIZE(center_channels_6ghz);
} else {
center_channels = center_channels_5ghz;
num_chan = ARRAY_SIZE(center_channels_5ghz);
}
center_chan = get_center_80mhz(mode, channel, center_channels,
num_chan);
if (!center_chan)
return NOT_ALLOWED;
/* check all the channels are available */
for (i = 0; i < 4; i++) {
unsigned int flags;
u8 adj_chan = center_chan - 6 + i * 4;
if (allow_channel(mode, op_class, adj_chan, &flags) ==
NOT_ALLOWED)
return NOT_ALLOWED;
if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) ||
(i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) ||
(i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) ||
(i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)))
return NOT_ALLOWED;
if (flags & HOSTAPD_CHAN_NO_IR)
no_ir = 1;
}
if (no_ir)
return NO_IR;
return ALLOWED;
}
static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel,
const u8 *center_channels, size_t num_chan)
{
unsigned int i;
if (mode->mode != HOSTAPD_MODE_IEEE80211A)
return 0;
for (i = 0; i < num_chan; i++) {
/*
* In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
* so the center channel is 14 channels away from the start/end.
*/
if (channel >= center_channels[i] - 14 &&
channel <= center_channels[i] + 14)
return center_channels[i];
}
return 0;
}
static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode,
u8 op_class, u8 channel)
{
u8 center_chan;
unsigned int i;
unsigned int no_ir = 0;
const u8 *center_channels;
size_t num_chan;
const u8 center_channels_5ghz[] = { 50, 114, 163 };
const u8 center_channels_6ghz[] = { 15, 47, 79, 111, 143, 175, 207 };
if (is_6ghz_op_class(op_class)) {
center_channels = center_channels_6ghz;
num_chan = ARRAY_SIZE(center_channels_6ghz);
} else {
center_channels = center_channels_5ghz;
num_chan = ARRAY_SIZE(center_channels_5ghz);
}
center_chan = get_center_160mhz(mode, channel, center_channels,
num_chan);
if (!center_chan)
return NOT_ALLOWED;
/* Check all the channels are available */
for (i = 0; i < 8; i++) {
unsigned int flags;
u8 adj_chan = center_chan - 14 + i * 4;
if (allow_channel(mode, op_class, adj_chan, &flags) ==
NOT_ALLOWED)
return NOT_ALLOWED;
if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) ||
(i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) ||
(i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) ||
(i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) ||
(i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) ||
(i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) ||
(i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) ||
(i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)))
return NOT_ALLOWED;
if (flags & HOSTAPD_CHAN_NO_IR)
no_ir = 1;
}
if (no_ir)
return NO_IR;
return ALLOWED;
}
enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class,
u8 channel, u8 bw)
{
unsigned int flag = 0;
enum chan_allowed res, res2;
res2 = res = allow_channel(mode, op_class, channel, &flag);
if (bw == BW40MINUS || (bw == BW40 && (((channel - 1) / 4) % 2))) {
if (!(flag & HOSTAPD_CHAN_HT40MINUS))
return NOT_ALLOWED;
res2 = allow_channel(mode, op_class, channel - 4, NULL);
- } else if (bw == BW40PLUS ||
- (bw == BW40 && !(((channel - 1) / 4) % 2))) {
+ } else if (bw == BW40PLUS) {
if (!(flag & HOSTAPD_CHAN_HT40PLUS))
return NOT_ALLOWED;
res2 = allow_channel(mode, op_class, channel + 4, NULL);
+ } else if (is_6ghz_op_class(op_class) && bw == BW40) {
+ if (get_6ghz_sec_channel(channel) < 0)
+ res2 = allow_channel(mode, op_class, channel - 4, NULL);
+ else
+ res2 = allow_channel(mode, op_class, channel + 4, NULL);
} else if (bw == BW80) {
/*
* channel is a center channel and as such, not necessarily a
* valid 20 MHz channels. Override earlier allow_channel()
* result and use only the 80 MHz specific version.
*/
res2 = res = verify_80mhz(mode, op_class, channel);
} else if (bw == BW160) {
/*
* channel is a center channel and as such, not necessarily a
* valid 20 MHz channels. Override earlier allow_channel()
* result and use only the 160 MHz specific version.
*/
res2 = res = verify_160mhz(mode, op_class, channel);
} else if (bw == BW80P80) {
/*
* channel is a center channel and as such, not necessarily a
* valid 20 MHz channels. Override earlier allow_channel()
* result and use only the 80 MHz specific version.
*/
res2 = res = verify_80mhz(mode, op_class, channel);
}
if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
return NOT_ALLOWED;
if (res == NO_IR || res2 == NO_IR)
return NO_IR;
return ALLOWED;
}
static int wpas_op_class_supported(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
const struct oper_class_map *op_class)
{
int chan;
size_t i;
struct hostapd_hw_modes *mode;
int found;
int z;
int freq2 = 0;
int freq5 = 0;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode,
is_6ghz_op_class(op_class->op_class));
if (!mode)
return 0;
/* If we are configured to disable certain things, take that into
* account here. */
if (ssid && ssid->freq_list && ssid->freq_list[0]) {
for (z = 0; ; z++) {
int f = ssid->freq_list[z];
if (f == 0)
break; /* end of list */
if (f > 4000 && f < 6000)
freq5 = 1;
else if (f > 2400 && f < 2500)
freq2 = 1;
}
} else {
/* No frequencies specified, can use anything hardware supports.
*/
freq2 = freq5 = 1;
}
if (op_class->op_class >= 115 && op_class->op_class <= 130 && !freq5)
return 0;
if (op_class->op_class >= 81 && op_class->op_class <= 84 && !freq2)
return 0;
#ifdef CONFIG_HT_OVERRIDES
if (ssid && ssid->disable_ht) {
switch (op_class->op_class) {
case 83:
case 84:
case 104:
case 105:
case 116:
case 117:
case 119:
case 120:
case 122:
case 123:
case 126:
case 127:
case 128:
case 129:
case 130:
/* Disable >= 40 MHz channels if HT is disabled */
return 0;
}
}
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
if (ssid && ssid->disable_vht) {
if (op_class->op_class >= 128 && op_class->op_class <= 130) {
/* Disable >= 80 MHz channels if VHT is disabled */
return 0;
}
}
#endif /* CONFIG_VHT_OVERRIDES */
if (op_class->op_class == 128) {
u8 channels[] = { 42, 58, 106, 122, 138, 155, 171 };
for (i = 0; i < ARRAY_SIZE(channels); i++) {
if (verify_channel(mode, op_class->op_class,
channels[i], op_class->bw) !=
NOT_ALLOWED)
return 1;
}
return 0;
}
if (op_class->op_class == 129) {
/* Check if either 160 MHz channels is allowed */
return verify_channel(mode, op_class->op_class, 50,
op_class->bw) != NOT_ALLOWED ||
verify_channel(mode, op_class->op_class, 114,
op_class->bw) != NOT_ALLOWED ||
verify_channel(mode, op_class->op_class, 163,
op_class->bw) != NOT_ALLOWED;
}
if (op_class->op_class == 130) {
/* Need at least two non-contiguous 80 MHz segments */
found = 0;
if (verify_channel(mode, op_class->op_class, 42,
op_class->bw) != NOT_ALLOWED ||
verify_channel(mode, op_class->op_class, 58,
op_class->bw) != NOT_ALLOWED)
found++;
if (verify_channel(mode, op_class->op_class, 106,
op_class->bw) != NOT_ALLOWED ||
verify_channel(mode, op_class->op_class, 122,
op_class->bw) != NOT_ALLOWED ||
verify_channel(mode, op_class->op_class, 138,
op_class->bw) != NOT_ALLOWED ||
verify_channel(mode, op_class->op_class, 155,
op_class->bw) != NOT_ALLOWED ||
verify_channel(mode, op_class->op_class, 171,
op_class->bw) != NOT_ALLOWED)
found++;
if (verify_channel(mode, op_class->op_class, 106,
op_class->bw) != NOT_ALLOWED &&
verify_channel(mode, op_class->op_class, 138,
op_class->bw) != NOT_ALLOWED)
found++;
if (verify_channel(mode, op_class->op_class, 122,
op_class->bw) != NOT_ALLOWED &&
verify_channel(mode, op_class->op_class, 155,
op_class->bw) != NOT_ALLOWED)
found++;
if (verify_channel(mode, op_class->op_class, 138,
op_class->bw) != NOT_ALLOWED &&
verify_channel(mode, op_class->op_class, 171,
op_class->bw) != NOT_ALLOWED)
found++;
if (found >= 2)
return 1;
return 0;
}
if (op_class->op_class == 135) {
/* Need at least two 80 MHz segments which do not fall under the
* same 160 MHz segment to support 80+80 in 6 GHz.
*/
int first_seg = 0;
int curr_seg = 0;
for (chan = op_class->min_chan; chan <= op_class->max_chan;
chan += op_class->inc) {
curr_seg++;
if (verify_channel(mode, op_class->op_class, chan,
op_class->bw) != NOT_ALLOWED) {
if (!first_seg) {
first_seg = curr_seg;
continue;
}
/* Supported if at least two non-consecutive 80
* MHz segments allowed.
*/
if ((curr_seg - first_seg) > 1)
return 1;
/* Supported even if the 80 MHz segments are
* consecutive when they do not fall under the
* same 160 MHz segment.
*/
if ((first_seg % 2) == 0)
return 1;
}
}
return 0;
}
found = 0;
for (chan = op_class->min_chan; chan <= op_class->max_chan;
chan += op_class->inc) {
if (verify_channel(mode, op_class->op_class, chan,
op_class->bw) != NOT_ALLOWED) {
found = 1;
break;
}
}
return found;
}
static int wpas_sta_secondary_channel_offset(struct wpa_bss *bss, u8 *current,
u8 *channel)
{
const u8 *ies;
u8 phy_type;
size_t ies_len;
if (!bss)
return -1;
ies = wpa_bss_ie_ptr(bss);
ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
return wpas_get_op_chan_phy(bss->freq, ies, ies_len, current,
channel, &phy_type);
}
size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct wpa_bss *bss, u8 *pos, size_t len)
{
struct wpabuf *buf;
u8 op, current, chan;
u8 *ie_len;
size_t res;
/*
* Determine the current operating class correct mode based on
* advertised BSS capabilities, if available. Fall back to a less
* accurate guess based on frequency if the needed IEs are not available
* or used.
*/
if (wpas_sta_secondary_channel_offset(bss, &current, &chan) < 0 &&
ieee80211_freq_to_channel_ext(bss->freq, 0, CHANWIDTH_USE_HT,
&current, &chan) == NUM_HOSTAPD_MODES)
return 0;
/*
* Need 3 bytes for EID, length, and current operating class, plus
* 1 byte for every other supported operating class.
*/
buf = wpabuf_alloc(global_op_class_size + 3);
if (!buf)
return 0;
wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES);
/* Will set the length later, putting a placeholder */
ie_len = wpabuf_put(buf, 1);
wpabuf_put_u8(buf, current);
for (op = 0; global_op_class[op].op_class; op++) {
if (wpas_op_class_supported(wpa_s, ssid, &global_op_class[op]))
wpabuf_put_u8(buf, global_op_class[op].op_class);
}
*ie_len = wpabuf_len(buf) - 2;
if (*ie_len < 2) {
wpa_printf(MSG_DEBUG,
"No supported operating classes IE to add");
res = 0;
} else if (wpabuf_len(buf) > len) {
wpa_printf(MSG_ERROR,
"Supported operating classes IE exceeds maximum buffer length");
res = 0;
} else {
os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf));
res = wpabuf_len(buf);
wpa_hexdump_buf(MSG_DEBUG,
"Added supported operating classes IE", buf);
}
wpabuf_free(buf);
return res;
}
int * wpas_supp_op_classes(struct wpa_supplicant *wpa_s)
{
int op;
unsigned int pos, max_num = 0;
int *classes;
for (op = 0; global_op_class[op].op_class; op++)
max_num++;
classes = os_zalloc((max_num + 1) * sizeof(int));
if (!classes)
return NULL;
for (op = 0, pos = 0; global_op_class[op].op_class; op++) {
if (wpas_op_class_supported(wpa_s, NULL, &global_op_class[op]))
classes[pos++] = global_op_class[op].op_class;
}
return classes;
}
diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.c b/contrib/wpa/wpa_supplicant/p2p_supplicant.c
index 62c9a26a3490..ce44dfb9e053 100644
--- a/contrib/wpa/wpa_supplicant/p2p_supplicant.c
+++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.c
@@ -1,10043 +1,10107 @@
/*
* wpa_supplicant - P2P
* Copyright (c) 2009-2010, Atheros Communications
* Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "eloop.h"
#include "common/ieee802_11_common.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "wps/wps_i.h"
#include "p2p/p2p.h"
#include "ap/hostapd.h"
#include "ap/ap_config.h"
#include "ap/sta_info.h"
#include "ap/ap_drv_ops.h"
#include "ap/wps_hostapd.h"
#include "ap/p2p_hostapd.h"
#include "ap/dfs.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "rsn_supp/wpa.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "ap.h"
#include "config_ssid.h"
#include "config.h"
#include "notify.h"
#include "scan.h"
#include "bss.h"
#include "offchannel.h"
#include "wps_supplicant.h"
#include "p2p_supplicant.h"
#include "wifi_display.h"
/*
* How many times to try to scan to find the GO before giving up on join
* request.
*/
#define P2P_MAX_JOIN_SCAN_ATTEMPTS 10
#define P2P_AUTO_PD_SCAN_ATTEMPTS 5
/**
* Defines time interval in seconds when a GO needs to evacuate a frequency that
* it is currently using, but is no longer valid for P2P use cases.
*/
#define P2P_GO_FREQ_CHANGE_TIME 5
/**
* Defines CSA parameters which are used when GO evacuates the no longer valid
* channel (and if the driver supports channel switch).
*/
#define P2P_GO_CSA_COUNT 7
#define P2P_GO_CSA_BLOCK_TX 0
#ifndef P2P_MAX_CLIENT_IDLE
/*
* How many seconds to try to reconnect to the GO when connection in P2P client
* role has been lost.
*/
#define P2P_MAX_CLIENT_IDLE 10
#endif /* P2P_MAX_CLIENT_IDLE */
#ifndef P2P_MAX_INITIAL_CONN_WAIT
/*
* How many seconds to wait for initial 4-way handshake to get completed after
* WPS provisioning step or after the re-invocation of a persistent group on a
* P2P Client.
*/
#define P2P_MAX_INITIAL_CONN_WAIT 10
#endif /* P2P_MAX_INITIAL_CONN_WAIT */
#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO
/*
* How many seconds to wait for initial 4-way handshake to get completed after
* WPS provisioning step on the GO. This controls the extra time the P2P
* operation is considered to be in progress (e.g., to delay other scans) after
* WPS provisioning has been completed on the GO during group formation.
*/
#define P2P_MAX_INITIAL_CONN_WAIT_GO 10
#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO */
#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE
/*
* How many seconds to wait for initial 4-way handshake to get completed after
* re-invocation of a persistent group on the GO when the client is expected
* to connect automatically (no user interaction).
*/
#define P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE 15
#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE */
#define P2P_MGMT_DEVICE_PREFIX "p2p-dev-"
/*
* How many seconds to wait to re-attempt to move GOs, in case previous attempt
* was not possible.
*/
#define P2P_RECONSIDER_GO_MOVE_DELAY 30
enum p2p_group_removal_reason {
P2P_GROUP_REMOVAL_UNKNOWN,
P2P_GROUP_REMOVAL_SILENT,
P2P_GROUP_REMOVAL_FORMATION_FAILED,
P2P_GROUP_REMOVAL_REQUESTED,
P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
P2P_GROUP_REMOVAL_UNAVAILABLE,
P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
P2P_GROUP_REMOVAL_PSK_FAILURE,
P2P_GROUP_REMOVAL_FREQ_CONFLICT,
P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL
};
static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
static struct wpa_supplicant *
wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
int go);
static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
const u8 *ssid, size_t ssid_len);
static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
int *force_freq, int *pref_freq, int go,
unsigned int *pref_freq_list,
unsigned int *num_pref_freq);
static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
const u8 *ssid, size_t ssid_len);
static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
const u8 *dev_addr, enum p2p_wps_method wps_method,
int auto_join, int freq,
const u8 *ssid, size_t ssid_len);
static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
void *timeout_ctx);
static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx);
static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
int group_added);
static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
static void wpas_stop_listen(void *ctx);
static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx);
static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
enum wpa_driver_if_type type);
static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
int already_deleted);
static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs,
unsigned int num);
static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx);
static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq);
static void
wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs, unsigned int num,
enum wpas_p2p_channel_update_trig trig);
static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx);
+static int wpas_get_6ghz_he_chwidth_capab(struct hostapd_hw_modes *mode)
+{
+ int he_capab = 0;
+
+ if (mode)
+ he_capab = mode->he_capab[WPAS_MODE_INFRA].phy_cap[
+ HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
+ return he_capab;
+}
+
+
/*
* Get the number of concurrent channels that the HW can operate, but that are
* currently not in use by any of the wpa_supplicant interfaces.
*/
static int wpas_p2p_num_unused_channels(struct wpa_supplicant *wpa_s)
{
int *freqs;
int num, unused;
freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
if (!freqs)
return -1;
num = get_shared_radio_freqs(wpa_s, freqs,
wpa_s->num_multichan_concurrent);
os_free(freqs);
unused = wpa_s->num_multichan_concurrent - num;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: num_unused_channels: %d", unused);
return unused;
}
/*
* Get the frequencies that are currently in use by one or more of the virtual
* interfaces, and that are also valid for P2P operation.
*/
static unsigned int
wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *p2p_freqs,
unsigned int len)
{
struct wpa_used_freq_data *freqs;
unsigned int num, i, j;
freqs = os_calloc(wpa_s->num_multichan_concurrent,
sizeof(struct wpa_used_freq_data));
if (!freqs)
return 0;
num = get_shared_radio_freqs_data(wpa_s, freqs,
wpa_s->num_multichan_concurrent);
os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len);
for (i = 0, j = 0; i < num && j < len; i++) {
if (p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq))
p2p_freqs[j++] = freqs[i];
}
os_free(freqs);
dump_freq_data(wpa_s, "valid for P2P", p2p_freqs, j);
return j;
}
static void wpas_p2p_set_own_freq_preference(struct wpa_supplicant *wpa_s,
int freq)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
/* Use the wpa_s used to control the P2P Device operation */
wpa_s = wpa_s->global->p2p_init_wpa_s;
if (wpa_s->conf->p2p_ignore_shared_freq &&
freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
wpas_p2p_num_unused_channels(wpa_s) > 0) {
wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration",
freq);
freq = 0;
}
p2p_set_own_freq_preference(wpa_s->global->p2p, freq);
}
static void wpas_p2p_scan_res_handled(struct wpa_supplicant *wpa_s)
{
unsigned int delay = wpas_p2p_search_delay(wpa_s);
/* In case of concurrent P2P and external scans, delay P2P search. */
if (external_scan_running(wpa_s->radio)) {
delay = wpa_s->conf->p2p_search_delay;
wpa_printf(MSG_DEBUG,
"P2P: Delay next P2P search by %d ms to let externally triggered scan complete",
delay);
}
p2p_scan_res_handled(wpa_s->global->p2p, delay);
}
static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res)
{
size_t i;
if (wpa_s->p2p_scan_work) {
struct wpa_radio_work *work = wpa_s->p2p_scan_work;
wpa_s->p2p_scan_work = NULL;
radio_work_done(work);
}
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS)",
(int) scan_res->num);
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *bss = scan_res->res[i];
struct os_reltime time_tmp_age, entry_ts;
const u8 *ies;
size_t ies_len;
time_tmp_age.sec = bss->age / 1000;
time_tmp_age.usec = (bss->age % 1000) * 1000;
os_reltime_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts);
ies = (const u8 *) (bss + 1);
ies_len = bss->ie_len;
if (bss->beacon_ie_len > 0 &&
!wpa_scan_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
wpa_scan_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
wpa_printf(MSG_DEBUG, "P2P: Use P2P IE(s) from Beacon frame since no P2P IE(s) in Probe Response frames received for "
MACSTR, MAC2STR(bss->bssid));
ies = ies + ies_len;
ies_len = bss->beacon_ie_len;
}
if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
bss->freq, &entry_ts, bss->level,
ies, ies_len) > 0)
break;
}
wpas_p2p_scan_res_handled(wpa_s);
}
static void wpas_p2p_scan_res_fail_handler(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_scan_work) {
struct wpa_radio_work *work = wpa_s->p2p_scan_work;
wpa_s->p2p_scan_work = NULL;
radio_work_done(work);
}
if (wpa_s->global->p2p_disabled || !wpa_s->global->p2p)
return;
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Failed to get scan results - try to continue");
wpas_p2p_scan_res_handled(wpa_s);
}
static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
struct wpa_driver_scan_params *params = work->ctx;
int ret;
if (deinit) {
if (!work->started) {
wpa_scan_free_params(params);
return;
}
wpa_s->p2p_scan_work = NULL;
return;
}
if (wpa_s->clear_driver_scan_cache) {
wpa_printf(MSG_DEBUG,
"Request driver to clear scan cache due to local BSS flush");
params->only_new_results = 1;
}
if (!params->p2p_include_6ghz && !params->freqs) {
wpa_printf(MSG_DEBUG,
"P2P: Exclude 6 GHz channels - update the scan frequency list");
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
0);
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
0);
}
ret = wpa_drv_scan(wpa_s, params);
if (ret == 0)
wpa_s->curr_scan_cookie = params->scan_cookie;
wpa_scan_free_params(params);
work->ctx = NULL;
if (ret) {
radio_work_done(work);
p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret);
return;
}
p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret);
os_get_reltime(&wpa_s->scan_trigger_time);
wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
wpa_s->scan_res_fail_handler = wpas_p2p_scan_res_fail_handler;
wpa_s->own_scan_requested = 1;
wpa_s->clear_driver_scan_cache = 0;
wpa_s->p2p_scan_work = work;
}
static int wpas_p2p_search_social_channel(struct wpa_supplicant *wpa_s,
int freq)
{
if (wpa_s->global->p2p_24ghz_social_channels &&
(freq == 2412 || freq == 2437 || freq == 2462)) {
/*
* Search all social channels regardless of whether these have
* been disabled for P2P operating channel use to avoid missing
* peers.
*/
return 1;
}
return p2p_supported_freq(wpa_s->global->p2p, freq);
}
static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
unsigned int num_req_dev_types,
const u8 *req_dev_types, const u8 *dev_id, u16 pw_id,
bool include_6ghz)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_driver_scan_params *params = NULL;
struct wpabuf *wps_ie, *ies;
unsigned int num_channels = 0;
int social_channels_freq[] = { 2412, 2437, 2462, 60480 };
size_t ielen;
u8 *n, i;
unsigned int bands;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
if (wpa_s->p2p_scan_work) {
wpa_dbg(wpa_s, MSG_INFO, "P2P: Reject scan trigger since one is already pending");
return -1;
}
params = os_zalloc(sizeof(*params));
if (params == NULL)
return -1;
/* P2P Wildcard SSID */
params->num_ssids = 1;
n = os_malloc(P2P_WILDCARD_SSID_LEN);
if (n == NULL)
goto fail;
os_memcpy(n, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
params->ssids[0].ssid = n;
params->ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
wpa_s->wps->dev.p2p = 1;
wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
wpa_s->wps->uuid, WPS_REQ_ENROLLEE,
num_req_dev_types, req_dev_types);
if (wps_ie == NULL)
goto fail;
if (!wpa_s->conf->p2p_6ghz_disable)
params->p2p_include_6ghz = include_6ghz;
switch (type) {
case P2P_SCAN_SOCIAL:
params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 1,
sizeof(int));
if (params->freqs == NULL)
goto fail;
for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) {
if (wpas_p2p_search_social_channel(
wpa_s, social_channels_freq[i]))
params->freqs[num_channels++] =
social_channels_freq[i];
}
params->freqs[num_channels++] = 0;
break;
case P2P_SCAN_FULL:
break;
case P2P_SCAN_SPECIFIC:
params->freqs = os_calloc(2, sizeof(int));
if (params->freqs == NULL)
goto fail;
params->freqs[0] = freq;
params->freqs[1] = 0;
break;
case P2P_SCAN_SOCIAL_PLUS_ONE:
params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 2,
sizeof(int));
if (params->freqs == NULL)
goto fail;
for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) {
if (wpas_p2p_search_social_channel(
wpa_s, social_channels_freq[i]))
params->freqs[num_channels++] =
social_channels_freq[i];
}
if (p2p_supported_freq(wpa_s->global->p2p, freq))
params->freqs[num_channels++] = freq;
params->freqs[num_channels++] = 0;
break;
}
ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
if (ies == NULL) {
wpabuf_free(wps_ie);
goto fail;
}
wpabuf_put_buf(ies, wps_ie);
wpabuf_free(wps_ie);
bands = wpas_get_bands(wpa_s, params->freqs);
p2p_scan_ie(wpa_s->global->p2p, ies, dev_id, bands);
params->p2p_probe = 1;
n = os_malloc(wpabuf_len(ies));
if (n == NULL) {
wpabuf_free(ies);
goto fail;
}
os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies));
params->extra_ies = n;
params->extra_ies_len = wpabuf_len(ies);
wpabuf_free(ies);
radio_remove_works(wpa_s, "p2p-scan", 0);
if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb,
params) < 0)
goto fail;
return 0;
fail:
wpa_scan_free_params(params);
return -1;
}
static enum wpa_driver_if_type wpas_p2p_if_type(int p2p_group_interface)
{
switch (p2p_group_interface) {
case P2P_GROUP_INTERFACE_PENDING:
return WPA_IF_P2P_GROUP;
case P2P_GROUP_INTERFACE_GO:
return WPA_IF_P2P_GO;
case P2P_GROUP_INTERFACE_CLIENT:
return WPA_IF_P2P_CLIENT;
}
return WPA_IF_P2P_GROUP;
}
static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s,
const u8 *ssid,
size_t ssid_len, int *go)
{
struct wpa_ssid *s;
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
for (s = wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled != 0 || !s->p2p_group ||
s->ssid_len != ssid_len ||
os_memcmp(ssid, s->ssid, ssid_len) != 0)
continue;
if (s->mode == WPAS_MODE_P2P_GO &&
s != wpa_s->current_ssid)
continue;
if (go)
*go = s->mode == WPAS_MODE_P2P_GO;
return wpa_s;
}
}
return NULL;
}
static void run_wpas_p2p_disconnect(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_printf(MSG_DEBUG,
"P2P: Complete previously requested removal of %s",
wpa_s->ifname);
wpas_p2p_disconnect(wpa_s);
}
static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s,
struct wpa_supplicant *calling_wpa_s)
{
if (calling_wpa_s == wpa_s && wpa_s &&
wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
/*
* The calling wpa_s instance is going to be removed. Do that
* from an eloop callback to keep the instance available until
* the caller has returned. This may be needed, e.g., to provide
* control interface responses on the per-interface socket.
*/
if (eloop_register_timeout(0, 0, run_wpas_p2p_disconnect,
wpa_s, NULL) < 0)
return -1;
return 0;
}
return wpas_p2p_disconnect(wpa_s);
}
/* Determine total number of clients in active groups where we are the GO */
static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s)
{
unsigned int count = 0;
struct wpa_ssid *s;
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
for (s = wpa_s->conf->ssid; s; s = s->next) {
wpa_printf(MSG_DEBUG,
"P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
wpa_s, s, s->disabled, s->p2p_group,
s->mode);
if (!s->disabled && s->p2p_group &&
s->mode == WPAS_MODE_P2P_GO) {
count += p2p_get_group_num_members(
wpa_s->p2p_group);
}
}
}
return count;
}
static unsigned int p2p_is_active_persistent_group(struct wpa_supplicant *wpa_s)
{
return !wpa_s->p2p_mgmt && wpa_s->current_ssid &&
!wpa_s->current_ssid->disabled &&
wpa_s->current_ssid->p2p_group &&
wpa_s->current_ssid->p2p_persistent_group;
}
static unsigned int p2p_is_active_persistent_go(struct wpa_supplicant *wpa_s)
{
return p2p_is_active_persistent_group(wpa_s) &&
wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO;
}
/* Find an interface for a P2P group where we are the GO */
static struct wpa_supplicant *
wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
{
struct wpa_supplicant *save = NULL;
if (!wpa_s)
return NULL;
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (!p2p_is_active_persistent_go(wpa_s))
continue;
/* Prefer a group with connected clients */
if (p2p_get_group_num_members(wpa_s->p2p_group))
return wpa_s;
save = wpa_s;
}
/* No group with connected clients, so pick the one without (if any) */
return save;
}
static unsigned int p2p_is_active_persistent_cli(struct wpa_supplicant *wpa_s)
{
return p2p_is_active_persistent_group(wpa_s) &&
wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
}
/* Find an interface for a P2P group where we are the P2P Client */
static struct wpa_supplicant *
wpas_p2p_get_cli_group(struct wpa_supplicant *wpa_s)
{
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (p2p_is_active_persistent_cli(wpa_s))
return wpa_s;
}
return NULL;
}
/* Find a persistent group where we are the GO */
static struct wpa_ssid *
wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *s;
for (s = wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled == 2 && s->mode == WPAS_MODE_P2P_GO)
return s;
}
return NULL;
}
static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role,
unsigned int *force_freq,
unsigned int *pref_freq)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *s;
u8 conncap = P2PS_SETUP_NONE;
unsigned int owned_members = 0;
struct wpa_supplicant *go_wpa_s, *cli_wpa_s;
struct wpa_ssid *persistent_go;
int p2p_no_group_iface;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
if (force_freq)
*force_freq = 0;
if (pref_freq)
*pref_freq = 0;
size = P2P_MAX_PREF_CHANNELS;
if (force_freq && pref_freq &&
!wpas_p2p_setup_freqs(wpa_s, 0, (int *) force_freq,
(int *) pref_freq, 0, pref_freq_list, &size))
wpas_p2p_set_own_freq_preference(wpa_s,
*force_freq ? *force_freq :
*pref_freq);
/*
* For non-concurrent capable devices:
* If persistent_go, then no new.
* If GO, then no client.
* If client, then no GO.
*/
go_wpa_s = wpas_p2p_get_go_group(wpa_s);
if (go_wpa_s)
owned_members = p2p_get_group_num_members(go_wpa_s->p2p_group);
persistent_go = wpas_p2p_get_persistent_go(wpa_s);
p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s);
cli_wpa_s = wpas_p2p_get_cli_group(wpa_s);
wpa_printf(MSG_DEBUG,
"P2P: GO(iface)=%p members=%u CLI(iface)=%p persistent(ssid)=%p",
go_wpa_s, owned_members, cli_wpa_s, persistent_go);
/* If not concurrent, restrict our choices */
if (p2p_no_group_iface) {
wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
if (cli_wpa_s)
return P2PS_SETUP_NONE;
if (go_wpa_s) {
if (role == P2PS_SETUP_CLIENT ||
incoming == P2PS_SETUP_GROUP_OWNER ||
p2p_client_limit_reached(go_wpa_s->p2p_group))
return P2PS_SETUP_NONE;
return P2PS_SETUP_GROUP_OWNER;
}
if (persistent_go) {
if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) {
if (!incoming)
return P2PS_SETUP_GROUP_OWNER |
P2PS_SETUP_CLIENT;
if (incoming == P2PS_SETUP_NEW) {
u8 r;
if (os_get_random(&r, sizeof(r)) < 0 ||
(r & 1))
return P2PS_SETUP_CLIENT;
return P2PS_SETUP_GROUP_OWNER;
}
}
}
}
/* If a required role has been specified, handle it here */
if (role && role != P2PS_SETUP_NEW) {
switch (incoming) {
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
/*
* Peer has an active GO, so if the role allows it and
* we do not have any active roles, become client.
*/
if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s &&
!cli_wpa_s)
return P2PS_SETUP_CLIENT;
/* fall through */
case P2PS_SETUP_NONE:
case P2PS_SETUP_NEW:
conncap = role;
goto grp_owner;
case P2PS_SETUP_GROUP_OWNER:
/*
* Must be a complimentary role - cannot be a client to
* more than one peer.
*/
if (incoming == role || cli_wpa_s)
return P2PS_SETUP_NONE;
return P2PS_SETUP_CLIENT;
case P2PS_SETUP_CLIENT:
/* Must be a complimentary role */
if (incoming != role) {
conncap = P2PS_SETUP_GROUP_OWNER;
goto grp_owner;
}
/* fall through */
default:
return P2PS_SETUP_NONE;
}
}
/*
* For now, we only will support ownership of one group, and being a
* client of one group. Therefore, if we have either an existing GO
* group, or an existing client group, we will not do a new GO
* negotiation, but rather try to re-use the existing groups.
*/
switch (incoming) {
case P2PS_SETUP_NONE:
case P2PS_SETUP_NEW:
if (cli_wpa_s)
conncap = P2PS_SETUP_GROUP_OWNER;
else if (!owned_members)
conncap = P2PS_SETUP_NEW;
else if (incoming == P2PS_SETUP_NONE)
conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT;
else
conncap = P2PS_SETUP_CLIENT;
break;
case P2PS_SETUP_CLIENT:
conncap = P2PS_SETUP_GROUP_OWNER;
break;
case P2PS_SETUP_GROUP_OWNER:
if (!cli_wpa_s)
conncap = P2PS_SETUP_CLIENT;
break;
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
if (cli_wpa_s)
conncap = P2PS_SETUP_GROUP_OWNER;
else {
u8 r;
if (os_get_random(&r, sizeof(r)) < 0 ||
(r & 1))
conncap = P2PS_SETUP_CLIENT;
else
conncap = P2PS_SETUP_GROUP_OWNER;
}
break;
default:
return P2PS_SETUP_NONE;
}
grp_owner:
if ((conncap & P2PS_SETUP_GROUP_OWNER) ||
(!incoming && (conncap & P2PS_SETUP_NEW))) {
if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
conncap &= ~P2PS_SETUP_GROUP_OWNER;
s = wpas_p2p_get_persistent_go(wpa_s);
if (!s && !go_wpa_s && p2p_no_group_iface) {
p2p_set_intended_addr(wpa_s->global->p2p,
wpa_s->p2p_mgmt ?
wpa_s->parent->own_addr :
wpa_s->own_addr);
} else if (!s && !go_wpa_s) {
if (wpas_p2p_add_group_interface(wpa_s,
WPA_IF_P2P_GROUP) < 0) {
wpa_printf(MSG_ERROR,
"P2P: Failed to allocate a new interface for the group");
return P2PS_SETUP_NONE;
}
wpa_s->global->pending_group_iface_for_p2ps = 1;
p2p_set_intended_addr(wpa_s->global->p2p,
wpa_s->pending_interface_addr);
}
}
return conncap;
}
static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
enum p2p_group_removal_reason removal_reason)
{
struct wpa_ssid *ssid;
char *gtype;
const char *reason;
ssid = wpa_s->current_ssid;
if (ssid == NULL) {
/*
* The current SSID was not known, but there may still be a
* pending P2P group interface waiting for provisioning or a
* P2P group that is trying to reconnect.
*/
ssid = wpa_s->conf->ssid;
while (ssid) {
if (ssid->p2p_group && ssid->disabled != 2)
break;
ssid = ssid->next;
}
if (ssid == NULL &&
wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)
{
wpa_printf(MSG_ERROR, "P2P: P2P group interface "
"not found");
return -1;
}
}
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO)
gtype = "GO";
else if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
(ssid && ssid->mode == WPAS_MODE_INFRA)) {
wpa_s->reassociate = 0;
wpa_s->disconnected = 1;
gtype = "client";
} else
gtype = "GO";
if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
if (os_strcmp(gtype, "client") == 0) {
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
if (eloop_is_timeout_registered(wpas_p2p_psk_failure_removal,
wpa_s, NULL)) {
wpa_printf(MSG_DEBUG,
"P2P: PSK failure removal was scheduled, so use PSK failure as reason for group removal");
removal_reason = P2P_GROUP_REMOVAL_PSK_FAILURE;
eloop_cancel_timeout(wpas_p2p_psk_failure_removal,
wpa_s, NULL);
}
}
if (wpa_s->cross_connect_in_use) {
wpa_s->cross_connect_in_use = 0;
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
wpa_s->ifname, wpa_s->cross_connect_uplink);
}
switch (removal_reason) {
case P2P_GROUP_REMOVAL_REQUESTED:
reason = " reason=REQUESTED";
break;
case P2P_GROUP_REMOVAL_FORMATION_FAILED:
reason = " reason=FORMATION_FAILED";
break;
case P2P_GROUP_REMOVAL_IDLE_TIMEOUT:
reason = " reason=IDLE";
break;
case P2P_GROUP_REMOVAL_UNAVAILABLE:
reason = " reason=UNAVAILABLE";
break;
case P2P_GROUP_REMOVAL_GO_ENDING_SESSION:
reason = " reason=GO_ENDING_SESSION";
break;
case P2P_GROUP_REMOVAL_PSK_FAILURE:
reason = " reason=PSK_FAILURE";
break;
case P2P_GROUP_REMOVAL_FREQ_CONFLICT:
reason = " reason=FREQ_CONFLICT";
break;
default:
reason = "";
break;
}
if (removal_reason != P2P_GROUP_REMOVAL_SILENT) {
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_GROUP_REMOVED "%s %s%s",
wpa_s->ifname, gtype, reason);
}
if (eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL) > 0)
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group freq_conflict timeout");
if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL) > 0) {
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
"timeout");
wpa_s->p2p_in_provisioning = 0;
wpas_p2p_group_formation_failed(wpa_s, 1);
}
wpa_s->p2p_in_invitation = 0;
eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
/*
* Make sure wait for the first client does not remain active after the
* group has been removed.
*/
wpa_s->global->p2p_go_wait_client.sec = 0;
if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
struct wpa_global *global;
char *ifname;
enum wpa_driver_if_type type;
wpa_printf(MSG_DEBUG, "P2P: Remove group interface %s",
wpa_s->ifname);
global = wpa_s->global;
ifname = os_strdup(wpa_s->ifname);
type = wpas_p2p_if_type(wpa_s->p2p_group_interface);
eloop_cancel_timeout(run_wpas_p2p_disconnect, wpa_s, NULL);
wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
wpa_s = global->ifaces;
if (wpa_s && ifname)
wpa_drv_if_remove(wpa_s, type, ifname);
os_free(ifname);
return 1;
}
/*
* The primary interface was used for P2P group operations, so
* need to reset its p2pdev.
*/
wpa_s->p2pdev = wpa_s->parent;
if (!wpa_s->p2p_go_group_formation_completed) {
wpa_s->global->p2p_group_formation = NULL;
wpa_s->p2p_in_provisioning = 0;
}
wpa_s->show_group_started = 0;
os_free(wpa_s->go_params);
wpa_s->go_params = NULL;
os_free(wpa_s->p2p_group_common_freqs);
wpa_s->p2p_group_common_freqs = NULL;
wpa_s->p2p_group_common_freqs_num = 0;
wpa_s->p2p_go_do_acs = 0;
+ wpa_s->p2p_go_allow_dfs = 0;
wpa_s->waiting_presence_resp = 0;
wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
if (ssid && (ssid->p2p_group ||
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
(ssid->key_mgmt & WPA_KEY_MGMT_WPS))) {
int id = ssid->id;
if (ssid == wpa_s->current_ssid) {
wpa_sm_set_config(wpa_s->wpa, NULL);
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_s->current_ssid = NULL;
}
/*
* Networks objects created during any P2P activities are not
* exposed out as they might/will confuse certain non-P2P aware
* applications since these network objects won't behave like
* regular ones.
*
* Likewise, we don't send out network removed signals for such
* network objects.
*/
wpa_config_remove_network(wpa_s->conf, id);
wpa_supplicant_clear_status(wpa_s);
wpa_supplicant_cancel_sched_scan(wpa_s);
} else {
wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
"found");
}
if (wpa_s->ap_iface)
wpa_supplicant_ap_deinit(wpa_s);
else
wpa_drv_deinit_p2p_cli(wpa_s);
os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
return 0;
}
static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
u8 *go_dev_addr,
const u8 *ssid, size_t ssid_len)
{
struct wpa_bss *bss;
const u8 *bssid;
struct wpabuf *p2p;
u8 group_capab;
const u8 *addr;
if (wpa_s->go_params)
bssid = wpa_s->go_params->peer_interface_addr;
else
bssid = wpa_s->bssid;
bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
if (bss == NULL && wpa_s->go_params &&
!is_zero_ether_addr(wpa_s->go_params->peer_device_addr))
bss = wpa_bss_get_p2p_dev_addr(
wpa_s, wpa_s->go_params->peer_device_addr);
if (bss == NULL) {
u8 iface_addr[ETH_ALEN];
if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
iface_addr) == 0)
bss = wpa_bss_get(wpa_s, iface_addr, ssid, ssid_len);
}
if (bss == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
"group is persistent - BSS " MACSTR " not found",
MAC2STR(bssid));
return 0;
}
p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
if (p2p == NULL)
p2p = wpa_bss_get_vendor_ie_multi_beacon(bss,
P2P_IE_VENDOR_TYPE);
if (p2p == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
"group is persistent - BSS " MACSTR
" did not include P2P IE", MAC2STR(bssid));
wpa_hexdump(MSG_DEBUG, "P2P: Probe Response IEs",
wpa_bss_ie_ptr(bss), bss->ie_len);
wpa_hexdump(MSG_DEBUG, "P2P: Beacon IEs",
wpa_bss_ie_ptr(bss) + bss->ie_len,
bss->beacon_ie_len);
return 0;
}
group_capab = p2p_get_group_capab(p2p);
addr = p2p_get_go_dev_addr(p2p);
wpa_printf(MSG_DEBUG, "P2P: Checking whether group is persistent: "
"group_capab=0x%x", group_capab);
if (addr) {
os_memcpy(go_dev_addr, addr, ETH_ALEN);
wpa_printf(MSG_DEBUG, "P2P: GO Device Address " MACSTR,
MAC2STR(addr));
} else
os_memset(go_dev_addr, 0, ETH_ALEN);
wpabuf_free(p2p);
wpa_printf(MSG_DEBUG, "P2P: BSS " MACSTR " group_capab=0x%x "
"go_dev_addr=" MACSTR,
MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr));
return !!(group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP);
}
static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
const u8 *go_dev_addr)
{
struct wpa_ssid *s;
int changed = 0;
wpa_printf(MSG_DEBUG, "P2P: Storing credentials for a persistent "
"group (GO Dev Addr " MACSTR ")", MAC2STR(go_dev_addr));
for (s = wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled == 2 &&
os_memcmp(go_dev_addr, s->bssid, ETH_ALEN) == 0 &&
s->ssid_len == ssid->ssid_len &&
os_memcmp(ssid->ssid, s->ssid, ssid->ssid_len) == 0)
break;
}
if (s) {
wpa_printf(MSG_DEBUG, "P2P: Update existing persistent group "
"entry");
if (ssid->passphrase && !s->passphrase)
changed = 1;
else if (ssid->passphrase && s->passphrase &&
os_strcmp(ssid->passphrase, s->passphrase) != 0)
changed = 1;
} else {
wpa_printf(MSG_DEBUG, "P2P: Create a new persistent group "
"entry");
changed = 1;
s = wpa_config_add_network(wpa_s->conf);
if (s == NULL)
return -1;
/*
* Instead of network_added we emit persistent_group_added
* notification. Also to keep the defense checks in
* persistent_group obj registration method, we set the
* relevant flags in s to designate it as a persistent group.
*/
s->p2p_group = 1;
s->p2p_persistent_group = 1;
wpas_notify_persistent_group_added(wpa_s, s);
wpa_config_set_network_defaults(s);
}
s->p2p_group = 1;
s->p2p_persistent_group = 1;
s->disabled = 2;
s->bssid_set = 1;
os_memcpy(s->bssid, go_dev_addr, ETH_ALEN);
s->mode = ssid->mode;
s->auth_alg = WPA_AUTH_ALG_OPEN;
s->key_mgmt = WPA_KEY_MGMT_PSK;
s->proto = WPA_PROTO_RSN;
s->pbss = ssid->pbss;
s->pairwise_cipher = ssid->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
s->export_keys = 1;
if (ssid->passphrase) {
os_free(s->passphrase);
s->passphrase = os_strdup(ssid->passphrase);
}
if (ssid->psk_set) {
s->psk_set = 1;
os_memcpy(s->psk, ssid->psk, 32);
}
if (s->passphrase && !s->psk_set)
wpa_config_update_psk(s);
if (s->ssid == NULL || s->ssid_len < ssid->ssid_len) {
os_free(s->ssid);
s->ssid = os_malloc(ssid->ssid_len);
}
if (s->ssid) {
s->ssid_len = ssid->ssid_len;
os_memcpy(s->ssid, ssid->ssid, s->ssid_len);
}
if (ssid->mode == WPAS_MODE_P2P_GO && wpa_s->global->add_psk) {
dl_list_add(&s->psk_list, &wpa_s->global->add_psk->list);
wpa_s->global->add_psk = NULL;
changed = 1;
}
if (changed && wpa_s->conf->update_config &&
wpa_config_write(wpa_s->confname, wpa_s->conf)) {
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
}
return s->id;
}
static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
const u8 *addr)
{
struct wpa_ssid *ssid, *s;
u8 *n;
size_t i;
int found = 0;
struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
ssid = wpa_s->current_ssid;
if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
!ssid->p2p_persistent_group)
return;
for (s = p2p_wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
continue;
if (s->ssid_len == ssid->ssid_len &&
os_memcmp(s->ssid, ssid->ssid, s->ssid_len) == 0)
break;
}
if (s == NULL)
return;
for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr,
ETH_ALEN) != 0)
continue;
if (i == s->num_p2p_clients - 1)
return; /* already the most recent entry */
/* move the entry to mark it most recent */
os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
(s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
os_memcpy(s->p2p_client_list +
(s->num_p2p_clients - 1) * 2 * ETH_ALEN, addr,
ETH_ALEN);
os_memset(s->p2p_client_list +
(s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
0xff, ETH_ALEN);
found = 1;
break;
}
if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) {
n = os_realloc_array(s->p2p_client_list,
s->num_p2p_clients + 1, 2 * ETH_ALEN);
if (n == NULL)
return;
os_memcpy(n + s->num_p2p_clients * 2 * ETH_ALEN, addr,
ETH_ALEN);
os_memset(n + s->num_p2p_clients * 2 * ETH_ALEN + ETH_ALEN,
0xff, ETH_ALEN);
s->p2p_client_list = n;
s->num_p2p_clients++;
} else if (!found && s->p2p_client_list) {
/* Not enough room for an additional entry - drop the oldest
* entry */
os_memmove(s->p2p_client_list,
s->p2p_client_list + 2 * ETH_ALEN,
(s->num_p2p_clients - 1) * 2 * ETH_ALEN);
os_memcpy(s->p2p_client_list +
(s->num_p2p_clients - 1) * 2 * ETH_ALEN,
addr, ETH_ALEN);
os_memset(s->p2p_client_list +
(s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
0xff, ETH_ALEN);
}
if (p2p_wpa_s->conf->update_config &&
wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
}
static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s,
int go, struct wpa_ssid *ssid, int freq,
const u8 *psk, const char *passphrase,
const u8 *go_dev_addr, int persistent,
const char *extra)
{
const char *ssid_txt;
char psk_txt[65];
if (psk)
wpa_snprintf_hex(psk_txt, sizeof(psk_txt), psk, 32);
else
psk_txt[0] = '\0';
if (ssid)
ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
else
ssid_txt = "";
if (passphrase && passphrase[0] == '\0')
passphrase = NULL;
/*
* Include PSK/passphrase only in the control interface message and
* leave it out from the debug log entry.
*/
wpa_msg_global_ctrl(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_GROUP_STARTED
"%s %s ssid=\"%s\" freq=%d%s%s%s%s%s go_dev_addr="
MACSTR "%s%s",
wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq,
psk ? " psk=" : "", psk_txt,
passphrase ? " passphrase=\"" : "",
passphrase ? passphrase : "",
passphrase ? "\"" : "",
MAC2STR(go_dev_addr),
persistent ? " [PERSISTENT]" : "", extra);
wpa_printf(MSG_INFO, P2P_EVENT_GROUP_STARTED
"%s %s ssid=\"%s\" freq=%d go_dev_addr=" MACSTR "%s%s",
wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq,
MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : "",
extra);
}
static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
int success, int already_deleted)
{
struct wpa_ssid *ssid;
int client;
int persistent;
u8 go_dev_addr[ETH_ALEN];
/*
* This callback is likely called for the main interface. Update wpa_s
* to use the group interface if a new interface was created for the
* group.
*/
if (wpa_s->global->p2p_group_formation)
wpa_s = wpa_s->global->p2p_group_formation;
if (wpa_s->p2p_go_group_formation_completed) {
wpa_s->global->p2p_group_formation = NULL;
wpa_s->p2p_in_provisioning = 0;
} else if (wpa_s->p2p_in_provisioning && !success) {
wpa_msg(wpa_s, MSG_DEBUG,
"P2P: Stop provisioning state due to failure");
wpa_s->p2p_in_provisioning = 0;
}
wpa_s->p2p_in_invitation = 0;
wpa_s->group_formation_reported = 1;
if (!success) {
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_GROUP_FORMATION_FAILURE);
wpas_notify_p2p_group_formation_failure(wpa_s, "");
if (already_deleted)
return;
wpas_p2p_group_delete(wpa_s,
P2P_GROUP_REMOVAL_FORMATION_FAILED);
return;
}
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_GROUP_FORMATION_SUCCESS);
ssid = wpa_s->current_ssid;
if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
ssid->mode = WPAS_MODE_P2P_GO;
p2p_group_notif_formation_done(wpa_s->p2p_group);
wpa_supplicant_ap_mac_addr_filter(wpa_s, NULL);
}
persistent = 0;
if (ssid) {
client = ssid->mode == WPAS_MODE_INFRA;
if (ssid->mode == WPAS_MODE_P2P_GO) {
persistent = ssid->p2p_persistent_group;
os_memcpy(go_dev_addr, wpa_s->global->p2p_dev_addr,
ETH_ALEN);
} else
persistent = wpas_p2p_persistent_group(wpa_s,
go_dev_addr,
ssid->ssid,
ssid->ssid_len);
} else {
client = wpa_s->p2p_group_interface ==
P2P_GROUP_INTERFACE_CLIENT;
os_memset(go_dev_addr, 0, ETH_ALEN);
}
wpa_s->show_group_started = 0;
if (client) {
/*
* Indicate event only after successfully completed 4-way
* handshake, i.e., when the interface is ready for data
* packets.
*/
wpa_s->show_group_started = 1;
} else {
wpas_p2p_group_started(wpa_s, 1, ssid,
ssid ? ssid->frequency : 0,
ssid && ssid->passphrase == NULL &&
ssid->psk_set ? ssid->psk : NULL,
ssid ? ssid->passphrase : NULL,
go_dev_addr, persistent, "");
wpas_p2p_cross_connect_setup(wpa_s);
wpas_p2p_set_group_idle_timeout(wpa_s);
}
if (persistent)
wpas_p2p_store_persistent_group(wpa_s->p2pdev,
ssid, go_dev_addr);
else {
os_free(wpa_s->global->add_psk);
wpa_s->global->add_psk = NULL;
}
if (!client) {
wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0, NULL);
os_get_reltime(&wpa_s->global->p2p_go_wait_client);
}
}
struct send_action_work {
unsigned int freq;
u8 dst[ETH_ALEN];
u8 src[ETH_ALEN];
u8 bssid[ETH_ALEN];
size_t len;
unsigned int wait_time;
u8 buf[0];
};
static void wpas_p2p_free_send_action_work(struct wpa_supplicant *wpa_s)
{
struct send_action_work *awork = wpa_s->p2p_send_action_work->ctx;
wpa_printf(MSG_DEBUG,
"P2P: Free Action frame radio work @%p (freq=%u dst="
MACSTR " src=" MACSTR " bssid=" MACSTR " wait_time=%u)",
wpa_s->p2p_send_action_work, awork->freq,
MAC2STR(awork->dst), MAC2STR(awork->src),
MAC2STR(awork->bssid), awork->wait_time);
wpa_hexdump(MSG_DEBUG, "P2P: Freeing pending Action frame",
awork->buf, awork->len);
os_free(awork);
wpa_s->p2p_send_action_work->ctx = NULL;
radio_work_done(wpa_s->p2p_send_action_work);
wpa_s->p2p_send_action_work = NULL;
}
static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (!wpa_s->p2p_send_action_work)
return;
wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out");
wpas_p2p_free_send_action_work(wpa_s);
}
static void wpas_p2p_action_tx_clear(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_send_action_work) {
struct send_action_work *awork;
awork = wpa_s->p2p_send_action_work->ctx;
wpa_printf(MSG_DEBUG,
"P2P: Clear Action TX work @%p (wait_time=%u)",
wpa_s->p2p_send_action_work, awork->wait_time);
if (awork->wait_time == 0) {
wpas_p2p_free_send_action_work(wpa_s);
} else {
/*
* In theory, this should not be needed, but number of
* places in the P2P code is still using non-zero wait
* time for the last Action frame in the sequence and
* some of these do not call send_action_done().
*/
eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
wpa_s, NULL);
eloop_register_timeout(
0, awork->wait_time * 1000,
wpas_p2p_send_action_work_timeout,
wpa_s, NULL);
}
}
}
static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
unsigned int freq,
const u8 *dst, const u8 *src,
const u8 *bssid,
const u8 *data, size_t data_len,
enum offchannel_send_action_result
result)
{
enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
wpas_p2p_action_tx_clear(wpa_s);
if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
return;
switch (result) {
case OFFCHANNEL_SEND_ACTION_SUCCESS:
res = P2P_SEND_ACTION_SUCCESS;
break;
case OFFCHANNEL_SEND_ACTION_NO_ACK:
res = P2P_SEND_ACTION_NO_ACK;
break;
case OFFCHANNEL_SEND_ACTION_FAILED:
res = P2P_SEND_ACTION_FAILED;
break;
}
p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res);
if (result != OFFCHANNEL_SEND_ACTION_SUCCESS &&
wpa_s->pending_pd_before_join &&
(os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0) &&
wpa_s->p2p_fallback_to_go_neg) {
wpa_s->pending_pd_before_join = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req "
"during p2p_connect-auto");
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_FALLBACK_TO_GO_NEG
"reason=no-ACK-to-PD-Req");
wpas_p2p_fallback_to_go_neg(wpa_s, 0);
return;
}
}
static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
struct send_action_work *awork = work->ctx;
if (deinit) {
if (work->started) {
eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
wpa_s, NULL);
wpa_s->p2p_send_action_work = NULL;
offchannel_send_action_done(wpa_s);
}
os_free(awork);
return;
}
if (offchannel_send_action(wpa_s, awork->freq, awork->dst, awork->src,
awork->bssid, awork->buf, awork->len,
awork->wait_time,
wpas_p2p_send_action_tx_status, 1) < 0) {
os_free(awork);
radio_work_done(work);
return;
}
wpa_s->p2p_send_action_work = work;
}
static int wpas_send_action_work(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid, const u8 *buf,
size_t len, unsigned int wait_time)
{
struct send_action_work *awork;
if (radio_work_pending(wpa_s, "p2p-send-action")) {
wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending");
return -1;
}
awork = os_zalloc(sizeof(*awork) + len);
if (awork == NULL)
return -1;
awork->freq = freq;
os_memcpy(awork->dst, dst, ETH_ALEN);
os_memcpy(awork->src, src, ETH_ALEN);
os_memcpy(awork->bssid, bssid, ETH_ALEN);
awork->len = len;
awork->wait_time = wait_time;
os_memcpy(awork->buf, buf, len);
if (radio_add_work(wpa_s, freq, "p2p-send-action", 1,
wpas_send_action_cb, awork) < 0) {
os_free(awork);
return -1;
}
return 0;
}
static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid, const u8 *buf,
size_t len, unsigned int wait_time, int *scheduled)
{
struct wpa_supplicant *wpa_s = ctx;
int listen_freq = -1, send_freq = -1;
if (scheduled)
*scheduled = 0;
if (wpa_s->p2p_listen_work)
listen_freq = wpa_s->p2p_listen_work->freq;
if (wpa_s->p2p_send_action_work)
send_freq = wpa_s->p2p_send_action_work->freq;
if (listen_freq != (int) freq && send_freq != (int) freq) {
int res;
wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d freq=%u)",
listen_freq, send_freq, freq);
res = wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf,
len, wait_time);
if (res == 0 && scheduled)
*scheduled = 1;
return res;
}
wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX");
return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len,
wait_time,
wpas_p2p_send_action_tx_status, 1);
}
static void wpas_send_action_done(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->p2p_send_action_work) {
eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
wpa_s, NULL);
os_free(wpa_s->p2p_send_action_work->ctx);
radio_work_done(wpa_s->p2p_send_action_work);
wpa_s->p2p_send_action_work = NULL;
}
offchannel_send_action_done(wpa_s);
}
static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params)
{
if (wpa_s->go_params == NULL) {
wpa_s->go_params = os_malloc(sizeof(*params));
if (wpa_s->go_params == NULL)
return -1;
}
os_memcpy(wpa_s->go_params, params, sizeof(*params));
return 0;
}
static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *res)
{
wpa_s->group_formation_reported = 0;
wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR
" dev_addr " MACSTR " wps_method %d",
MAC2STR(res->peer_interface_addr),
MAC2STR(res->peer_device_addr), res->wps_method);
wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID",
res->ssid, res->ssid_len);
wpa_supplicant_ap_deinit(wpa_s);
wpas_copy_go_neg_results(wpa_s, res);
if (res->wps_method == WPS_PBC) {
wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1, 0);
#ifdef CONFIG_WPS_NFC
} else if (res->wps_method == WPS_NFC) {
wpas_wps_start_nfc(wpa_s, res->peer_device_addr,
res->peer_interface_addr,
wpa_s->p2pdev->p2p_oob_dev_pw,
wpa_s->p2pdev->p2p_oob_dev_pw_id, 1,
wpa_s->p2pdev->p2p_oob_dev_pw_id ==
DEV_PW_NFC_CONNECTION_HANDOVER ?
wpa_s->p2pdev->p2p_peer_oob_pubkey_hash :
NULL,
NULL, 0, 0);
#endif /* CONFIG_WPS_NFC */
} else {
u16 dev_pw_id = DEV_PW_DEFAULT;
if (wpa_s->p2p_wps_method == WPS_P2PS)
dev_pw_id = DEV_PW_P2PS_DEFAULT;
if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
wpa_s->p2p_pin, 1, dev_pw_id);
}
}
static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct wpa_ssid *persistent;
struct psk_list_entry *psk;
struct hostapd_data *hapd;
if (!wpa_s->ap_iface)
return;
persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, NULL, ssid->ssid,
ssid->ssid_len);
if (persistent == NULL)
return;
hapd = wpa_s->ap_iface->bss[0];
dl_list_for_each(psk, &persistent->psk_list, struct psk_list_entry,
list) {
struct hostapd_wpa_psk *hpsk;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add persistent group PSK entry for "
MACSTR " psk=%d",
MAC2STR(psk->addr), psk->p2p);
hpsk = os_zalloc(sizeof(*hpsk));
if (hpsk == NULL)
break;
os_memcpy(hpsk->psk, psk->psk, PMK_LEN);
if (psk->p2p)
os_memcpy(hpsk->p2p_dev_addr, psk->addr, ETH_ALEN);
else
os_memcpy(hpsk->addr, psk->addr, ETH_ALEN);
hpsk->next = hapd->conf->ssid.wpa_psk;
hapd->conf->ssid.wpa_psk = hpsk;
}
}
static void p2p_go_dump_common_freqs(struct wpa_supplicant *wpa_s)
{
char buf[20 + P2P_MAX_CHANNELS * 6];
char *pos, *end;
unsigned int i;
int res;
pos = buf;
end = pos + sizeof(buf);
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
res = os_snprintf(pos, end - pos, " %d",
wpa_s->p2p_group_common_freqs[i]);
if (os_snprintf_error(end - pos, res))
break;
pos += res;
}
*pos = '\0';
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies:%s", buf);
}
static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params)
{
unsigned int i, len = int_array_len(wpa_s->go_params->freq_list);
wpa_s->p2p_group_common_freqs_num = 0;
os_free(wpa_s->p2p_group_common_freqs);
wpa_s->p2p_group_common_freqs = os_calloc(len, sizeof(int));
if (!wpa_s->p2p_group_common_freqs)
return;
for (i = 0; i < len; i++) {
if (!wpa_s->go_params->freq_list[i])
break;
wpa_s->p2p_group_common_freqs[i] =
wpa_s->go_params->freq_list[i];
}
wpa_s->p2p_group_common_freqs_num = i;
}
static void p2p_config_write(struct wpa_supplicant *wpa_s)
{
#ifndef CONFIG_NO_CONFIG_WRITE
if (wpa_s->p2pdev->conf->update_config &&
wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf))
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
#endif /* CONFIG_NO_CONFIG_WRITE */
}
static void p2p_go_configured(void *ctx, void *data)
{
struct wpa_supplicant *wpa_s = ctx;
struct p2p_go_neg_results *params = data;
struct wpa_ssid *ssid;
wpa_s->ap_configured_cb = NULL;
wpa_s->ap_configured_cb_ctx = NULL;
wpa_s->ap_configured_cb_data = NULL;
if (!wpa_s->go_params) {
wpa_printf(MSG_ERROR,
"P2P: p2p_go_configured() called with wpa_s->go_params == NULL");
return;
}
p2p_go_save_group_common_freqs(wpa_s, params);
p2p_go_dump_common_freqs(wpa_s);
ssid = wpa_s->current_ssid;
if (ssid && ssid->mode == WPAS_MODE_P2P_GO) {
wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
if (wpa_s->global->p2p_group_formation == wpa_s)
wpa_s->global->p2p_group_formation = NULL;
wpas_p2p_group_started(wpa_s, 1, ssid, ssid->frequency,
params->passphrase[0] == '\0' ?
params->psk : NULL,
params->passphrase,
wpa_s->global->p2p_dev_addr,
params->persistent_group, "");
wpa_s->group_formation_reported = 1;
if (wpa_s->p2pdev->p2ps_method_config_any) {
if (is_zero_ether_addr(wpa_s->p2pdev->p2ps_join_addr)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2PS: Setting default PIN for ANY");
wpa_supplicant_ap_wps_pin(wpa_s, NULL,
"12345670", NULL, 0,
0);
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2PS: Setting default PIN for " MACSTR,
MAC2STR(wpa_s->p2pdev->p2ps_join_addr));
wpa_supplicant_ap_wps_pin(
wpa_s, wpa_s->p2pdev->p2ps_join_addr,
"12345670", NULL, 0, 0);
}
wpa_s->p2pdev->p2ps_method_config_any = 0;
}
os_get_reltime(&wpa_s->global->p2p_go_wait_client);
if (params->persistent_group) {
wpas_p2p_store_persistent_group(
wpa_s->p2pdev, ssid,
wpa_s->global->p2p_dev_addr);
wpas_p2p_add_psk_list(wpa_s, ssid);
}
wpas_notify_p2p_group_started(wpa_s, ssid,
params->persistent_group, 0,
NULL);
wpas_p2p_cross_connect_setup(wpa_s);
wpas_p2p_set_group_idle_timeout(wpa_s);
if (wpa_s->p2p_first_connection_timeout) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Start group formation timeout of %d seconds until first data connection on GO",
wpa_s->p2p_first_connection_timeout);
wpa_s->p2p_go_group_formation_completed = 0;
wpa_s->global->p2p_group_formation = wpa_s;
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
eloop_register_timeout(
wpa_s->p2p_first_connection_timeout, 0,
wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
}
return;
}
wpa_printf(MSG_DEBUG, "P2P: Setting up WPS for GO provisioning");
if (wpa_supplicant_ap_mac_addr_filter(wpa_s,
params->peer_interface_addr)) {
wpa_printf(MSG_DEBUG, "P2P: Failed to setup MAC address "
"filtering");
return;
}
if (params->wps_method == WPS_PBC) {
wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
params->peer_device_addr);
#ifdef CONFIG_WPS_NFC
} else if (params->wps_method == WPS_NFC) {
if (wpa_s->p2pdev->p2p_oob_dev_pw_id !=
DEV_PW_NFC_CONNECTION_HANDOVER &&
!wpa_s->p2pdev->p2p_oob_dev_pw) {
wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
return;
}
wpas_ap_wps_add_nfc_pw(
wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id,
wpa_s->p2pdev->p2p_oob_dev_pw,
wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ?
wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL);
#endif /* CONFIG_WPS_NFC */
} else if (wpa_s->p2p_pin[0])
wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
wpa_s->p2p_pin, NULL, 0, 0);
os_free(wpa_s->go_params);
wpa_s->go_params = NULL;
}
/**
* wpas_p2p_freq_to_edmg_channel - Convert frequency into EDMG channel
* @freq: Frequency (MHz) to convert
* @op_class: Buffer for returning operating class
* @op_edmg_channel: Buffer for returning channel number
* Returns: 0 on success, -1 on failure
*
* This can be used to find the highest channel bonding which includes the
* specified frequency.
*/
static int wpas_p2p_freq_to_edmg_channel(struct wpa_supplicant *wpa_s,
unsigned int freq,
u8 *op_class, u8 *op_edmg_channel)
{
struct hostapd_hw_modes *hwmode;
struct ieee80211_edmg_config edmg;
unsigned int i;
enum chan_width chanwidth[] = {
CHAN_WIDTH_8640,
CHAN_WIDTH_6480,
CHAN_WIDTH_4320,
};
if (!wpa_s->hw.modes)
return -1;
hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
HOSTAPD_MODE_IEEE80211AD, false);
if (!hwmode) {
wpa_printf(MSG_ERROR,
"Unsupported AP mode: HOSTAPD_MODE_IEEE80211AD");
return -1;
}
/* Find the highest EDMG channel bandwidth to start the P2P GO */
for (i = 0; i < ARRAY_SIZE(chanwidth); i++) {
if (ieee80211_chaninfo_to_channel(freq, chanwidth[i], 0,
op_class,
op_edmg_channel) < 0)
continue;
hostapd_encode_edmg_chan(1, *op_edmg_channel, 0, &edmg);
if (edmg.channels &&
ieee802_edmg_is_allowed(hwmode->edmg, edmg)) {
wpa_printf(MSG_DEBUG,
"Freq %u to EDMG channel %u at opclass %u",
freq, *op_edmg_channel, *op_class);
return 0;
}
}
return -1;
}
int wpas_p2p_try_edmg_channel(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params)
{
u8 op_channel, op_class;
int freq;
/* Try social channel as primary channel frequency */
freq = (!params->freq) ? 58320 + 1 * 2160 : params->freq;
if (wpas_p2p_freq_to_edmg_channel(wpa_s, freq, &op_class,
&op_channel) == 0) {
wpa_printf(MSG_DEBUG,
"Freq %d will be used to set an EDMG connection (channel=%u opclass=%u)",
freq, op_channel, op_class);
params->freq = freq;
return 0;
}
return -1;
}
static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params,
int group_formation)
{
struct wpa_ssid *ssid;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Starting GO");
if (wpas_copy_go_neg_results(wpa_s, params) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not copy GO Negotiation "
"results");
return;
}
ssid = wpa_config_add_network(wpa_s->conf);
if (ssid == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not add network for GO");
return;
}
wpa_s->show_group_started = 0;
wpa_s->p2p_go_group_formation_completed = 0;
wpa_s->group_formation_reported = 0;
os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
wpa_config_set_network_defaults(ssid);
ssid->temporary = 1;
ssid->p2p_group = 1;
ssid->p2p_persistent_group = !!params->persistent_group;
ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION :
WPAS_MODE_P2P_GO;
ssid->frequency = params->freq;
ssid->ht40 = params->ht40;
ssid->vht = params->vht;
ssid->max_oper_chwidth = params->max_oper_chwidth;
ssid->vht_center_freq2 = params->vht_center_freq2;
ssid->he = params->he;
if (params->edmg) {
u8 op_channel, op_class;
if (!wpas_p2p_freq_to_edmg_channel(wpa_s, params->freq,
&op_class, &op_channel)) {
ssid->edmg_channel = op_channel;
ssid->enable_edmg = params->edmg;
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Could not match EDMG channel, freq %d, for GO",
params->freq);
}
}
ssid->ssid = os_zalloc(params->ssid_len + 1);
if (ssid->ssid) {
os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
ssid->ssid_len = params->ssid_len;
}
ssid->auth_alg = WPA_AUTH_ALG_OPEN;
ssid->key_mgmt = WPA_KEY_MGMT_PSK;
if (is_6ghz_freq(ssid->frequency) &&
is_p2p_6ghz_capable(wpa_s->global->p2p)) {
ssid->auth_alg |= WPA_AUTH_ALG_SAE;
ssid->key_mgmt = WPA_KEY_MGMT_SAE;
+ ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
+ ssid->sae_pwe = 1;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use SAE auth_alg and key_mgmt");
} else {
p2p_set_6ghz_dev_capab(wpa_s->global->p2p, false);
}
ssid->proto = WPA_PROTO_RSN;
ssid->pairwise_cipher = WPA_CIPHER_CCMP;
ssid->group_cipher = WPA_CIPHER_CCMP;
if (params->freq > 56160) {
/*
* Enable GCMP instead of CCMP as pairwise_cipher and
* group_cipher in 60 GHz.
*/
ssid->pairwise_cipher = WPA_CIPHER_GCMP;
ssid->group_cipher = WPA_CIPHER_GCMP;
/* P2P GO in 60 GHz is always a PCP (PBSS) */
ssid->pbss = 1;
}
if (os_strlen(params->passphrase) > 0) {
ssid->passphrase = os_strdup(params->passphrase);
if (ssid->passphrase == NULL) {
wpa_msg_global(wpa_s, MSG_ERROR,
"P2P: Failed to copy passphrase for GO");
wpa_config_remove_network(wpa_s->conf, ssid->id);
return;
}
} else
ssid->passphrase = NULL;
ssid->psk_set = params->psk_set;
if (ssid->psk_set)
os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk));
else if (ssid->passphrase)
wpa_config_update_psk(ssid);
ssid->ap_max_inactivity = wpa_s->p2pdev->conf->p2p_go_max_inactivity;
wpa_s->ap_configured_cb = p2p_go_configured;
wpa_s->ap_configured_cb_ctx = wpa_s;
wpa_s->ap_configured_cb_data = wpa_s->go_params;
wpa_s->scan_req = NORMAL_SCAN_REQ;
wpa_s->connect_without_scan = ssid;
wpa_s->reassociate = 1;
wpa_s->disconnected = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to "
"start GO)");
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
const struct wpa_supplicant *src)
{
struct wpa_config *d;
const struct wpa_config *s;
d = dst->conf;
s = src->conf;
#define C(n) \
do { \
if (s->n && !d->n) \
d->n = os_strdup(s->n); \
} while (0)
C(device_name);
C(manufacturer);
C(model_name);
C(model_number);
C(serial_number);
C(config_methods);
#undef C
os_memcpy(d->device_type, s->device_type, WPS_DEV_TYPE_LEN);
os_memcpy(d->sec_device_type, s->sec_device_type,
sizeof(d->sec_device_type));
d->num_sec_device_types = s->num_sec_device_types;
d->p2p_group_idle = s->p2p_group_idle;
d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy;
d->p2p_intra_bss = s->p2p_intra_bss;
d->persistent_reconnect = s->persistent_reconnect;
d->max_num_sta = s->max_num_sta;
d->pbc_in_m1 = s->pbc_in_m1;
d->ignore_old_scan_res = s->ignore_old_scan_res;
d->beacon_int = s->beacon_int;
d->dtim_period = s->dtim_period;
d->p2p_go_ctwindow = s->p2p_go_ctwindow;
d->disassoc_low_ack = s->disassoc_low_ack;
d->disable_scan_offload = s->disable_scan_offload;
d->passive_scan = s->passive_scan;
+ d->pmf = s->pmf;
+ d->p2p_6ghz_disable = s->p2p_6ghz_disable;
if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey &&
!d->wps_nfc_pw_from_config) {
wpabuf_free(d->wps_nfc_dh_privkey);
wpabuf_free(d->wps_nfc_dh_pubkey);
d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
}
d->p2p_cli_probe = s->p2p_cli_probe;
d->go_interworking = s->go_interworking;
d->go_access_network_type = s->go_access_network_type;
d->go_internet = s->go_internet;
d->go_venue_group = s->go_venue_group;
d->go_venue_type = s->go_venue_type;
d->p2p_add_cli_chan = s->p2p_add_cli_chan;
}
static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s,
char *ifname, size_t len)
{
char *ifname_ptr = wpa_s->ifname;
if (os_strncmp(wpa_s->ifname, P2P_MGMT_DEVICE_PREFIX,
os_strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) {
ifname_ptr = os_strrchr(wpa_s->ifname, '-') + 1;
}
os_snprintf(ifname, len, "p2p-%s-%d", ifname_ptr, wpa_s->p2p_group_idx);
if (os_strlen(ifname) >= IFNAMSIZ &&
os_strlen(wpa_s->ifname) < IFNAMSIZ) {
int res;
/* Try to avoid going over the IFNAMSIZ length limit */
res = os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx);
if (os_snprintf_error(len, res) && len)
ifname[len - 1] = '\0';
}
}
static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
enum wpa_driver_if_type type)
{
char ifname[120], force_ifname[120];
if (wpa_s->pending_interface_name[0]) {
wpa_printf(MSG_DEBUG, "P2P: Pending virtual interface exists "
"- skip creation of a new one");
if (is_zero_ether_addr(wpa_s->pending_interface_addr)) {
wpa_printf(MSG_DEBUG, "P2P: Pending virtual address "
"unknown?! ifname='%s'",
wpa_s->pending_interface_name);
return -1;
}
return 0;
}
wpas_p2p_get_group_ifname(wpa_s, ifname, sizeof(ifname));
force_ifname[0] = '\0';
wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group",
ifname);
wpa_s->p2p_group_idx++;
wpa_s->pending_interface_type = type;
if (wpa_drv_if_add(wpa_s, type, ifname, NULL, NULL, force_ifname,
wpa_s->pending_interface_addr, NULL) < 0) {
wpa_printf(MSG_ERROR, "P2P: Failed to create new group "
"interface");
return -1;
}
if (wpa_s->conf->p2p_interface_random_mac_addr) {
random_mac_addr(wpa_s->pending_interface_addr);
wpa_printf(MSG_DEBUG, "P2P: Generate random MAC address " MACSTR
" for the group",
MAC2STR(wpa_s->pending_interface_addr));
}
if (force_ifname[0]) {
wpa_printf(MSG_DEBUG, "P2P: Driver forced interface name %s",
force_ifname);
os_strlcpy(wpa_s->pending_interface_name, force_ifname,
sizeof(wpa_s->pending_interface_name));
} else
os_strlcpy(wpa_s->pending_interface_name, ifname,
sizeof(wpa_s->pending_interface_name));
wpa_printf(MSG_DEBUG, "P2P: Created pending virtual interface %s addr "
MACSTR, wpa_s->pending_interface_name,
MAC2STR(wpa_s->pending_interface_addr));
return 0;
}
static void wpas_p2p_remove_pending_group_interface(
struct wpa_supplicant *wpa_s)
{
if (!wpa_s->pending_interface_name[0] ||
is_zero_ether_addr(wpa_s->pending_interface_addr))
return; /* No pending virtual interface */
wpa_printf(MSG_DEBUG, "P2P: Removing pending group interface %s",
wpa_s->pending_interface_name);
wpa_drv_if_remove(wpa_s, wpa_s->pending_interface_type,
wpa_s->pending_interface_name);
os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
wpa_s->pending_interface_name[0] = '\0';
wpa_s->global->pending_group_iface_for_p2ps = 0;
}
static struct wpa_supplicant *
wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go)
{
struct wpa_interface iface;
struct wpa_supplicant *group_wpa_s;
if (!wpa_s->pending_interface_name[0]) {
wpa_printf(MSG_ERROR, "P2P: No pending group interface");
if (!wpas_p2p_create_iface(wpa_s))
return NULL;
/*
* Something has forced us to remove the pending interface; try
* to create a new one and hope for the best that we will get
* the same local address.
*/
if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
WPA_IF_P2P_CLIENT) < 0)
return NULL;
}
os_memset(&iface, 0, sizeof(iface));
iface.ifname = wpa_s->pending_interface_name;
iface.driver = wpa_s->driver->name;
if (wpa_s->conf->ctrl_interface == NULL &&
wpa_s->parent != wpa_s &&
wpa_s->p2p_mgmt &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE))
iface.ctrl_interface = wpa_s->parent->conf->ctrl_interface;
else
iface.ctrl_interface = wpa_s->conf->ctrl_interface;
iface.driver_param = wpa_s->conf->driver_param;
group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
if (group_wpa_s == NULL) {
wpa_printf(MSG_ERROR, "P2P: Failed to create new "
"wpa_supplicant interface");
return NULL;
}
wpa_s->pending_interface_name[0] = '\0';
group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
P2P_GROUP_INTERFACE_CLIENT;
wpa_s->global->p2p_group_formation = group_wpa_s;
wpa_s->global->pending_group_iface_for_p2ps = 0;
wpas_p2p_clone_config(group_wpa_s, wpa_s);
if (wpa_s->conf->p2p_interface_random_mac_addr) {
if (wpa_drv_set_mac_addr(group_wpa_s,
wpa_s->pending_interface_addr) < 0) {
wpa_msg(group_wpa_s, MSG_INFO,
"Failed to set random MAC address");
wpa_supplicant_remove_iface(wpa_s->global, group_wpa_s,
0);
return NULL;
}
if (wpa_supplicant_update_mac_addr(group_wpa_s) < 0) {
wpa_msg(group_wpa_s, MSG_INFO,
"Could not update MAC address information");
wpa_supplicant_remove_iface(wpa_s->global, group_wpa_s,
0);
return NULL;
}
wpa_printf(MSG_DEBUG, "P2P: Using random MAC address " MACSTR
" for the group",
MAC2STR(wpa_s->pending_interface_addr));
}
return group_wpa_s;
}
static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
wpas_p2p_group_formation_failed(wpa_s, 0);
}
static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
int already_deleted)
{
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
if (wpa_s->global->p2p)
p2p_group_formation_failed(wpa_s->global->p2p);
wpas_group_formation_completed(wpa_s, 0, already_deleted);
}
static void wpas_p2p_grpform_fail_after_wps(struct wpa_supplicant *wpa_s)
{
wpa_printf(MSG_DEBUG, "P2P: Reject group formation due to WPS provisioning failure");
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
wpa_s->global->p2p_fail_on_wps_complete = 0;
}
void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
{
if (wpa_s->global->p2p_group_formation != wpa_s)
return;
/* Speed up group formation timeout since this cannot succeed */
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
}
static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_supplicant *group_wpa_s;
if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
wpa_drv_cancel_remain_on_channel(wpa_s);
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = 0;
}
if (res->status) {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_GO_NEG_FAILURE "status=%d",
res->status);
wpas_notify_p2p_go_neg_completed(wpa_s, res);
wpas_p2p_remove_pending_group_interface(wpa_s);
return;
}
if (!res->role_go) {
/* Inform driver of the operating channel of GO. */
wpa_drv_set_prob_oper_freq(wpa_s, res->freq);
}
if (wpa_s->p2p_go_ht40)
res->ht40 = 1;
if (wpa_s->p2p_go_vht)
res->vht = 1;
if (wpa_s->p2p_go_he)
res->he = 1;
if (wpa_s->p2p_go_edmg)
res->edmg = 1;
res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth;
res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s "
"freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR
" wps_method=%s",
res->role_go ? "GO" : "client", res->freq, res->ht40,
MAC2STR(res->peer_device_addr),
MAC2STR(res->peer_interface_addr),
p2p_wps_method_text(res->wps_method));
wpas_notify_p2p_go_neg_completed(wpa_s, res);
if (res->role_go && wpa_s->p2p_persistent_id >= 0) {
struct wpa_ssid *ssid;
ssid = wpa_config_get_network(wpa_s->conf,
wpa_s->p2p_persistent_id);
if (ssid && ssid->disabled == 2 &&
ssid->mode == WPAS_MODE_P2P_GO && ssid->passphrase) {
size_t len = os_strlen(ssid->passphrase);
wpa_printf(MSG_DEBUG, "P2P: Override passphrase based "
"on requested persistent group");
os_memcpy(res->passphrase, ssid->passphrase, len);
res->passphrase[len] = '\0';
}
}
if (wpa_s->create_p2p_iface) {
group_wpa_s =
wpas_p2p_init_group_interface(wpa_s, res->role_go);
if (group_wpa_s == NULL) {
wpas_p2p_remove_pending_group_interface(wpa_s);
eloop_cancel_timeout(wpas_p2p_long_listen_timeout,
wpa_s, NULL);
wpas_p2p_group_formation_failed(wpa_s, 1);
return;
}
os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
wpa_s->pending_interface_name[0] = '\0';
} else {
group_wpa_s = wpa_s->parent;
wpa_s->global->p2p_group_formation = group_wpa_s;
if (group_wpa_s != wpa_s)
wpas_p2p_clone_config(group_wpa_s, wpa_s);
}
group_wpa_s->p2p_in_provisioning = 1;
group_wpa_s->p2pdev = wpa_s;
if (group_wpa_s != wpa_s) {
os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
sizeof(group_wpa_s->p2p_pin));
group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method;
}
if (res->role_go) {
wpas_start_wps_go(group_wpa_s, res, 1);
} else {
os_get_reltime(&group_wpa_s->scan_min_time);
wpas_start_wps_enrollee(group_wpa_s, res);
}
wpa_s->global->p2p_long_listen = 0;
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
eloop_register_timeout(15 + res->peer_config_timeout / 100,
(res->peer_config_timeout % 100) * 10000,
wpas_p2p_group_formation_timeout, wpa_s, NULL);
}
static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id,
u8 go_intent)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
" dev_passwd_id=%u go_intent=%u", MAC2STR(src),
dev_passwd_id, go_intent);
wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
}
static void wpas_dev_found(void *ctx, const u8 *addr,
const struct p2p_peer_info *info,
int new_device)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
struct wpa_supplicant *wpa_s = ctx;
char devtype[WPS_DEV_TYPE_BUFSIZE];
char *wfd_dev_info_hex = NULL;
#ifdef CONFIG_WIFI_DISPLAY
wfd_dev_info_hex = wifi_display_subelem_hex(info->wfd_subelems,
WFD_SUBELEM_DEVICE_INFO);
#endif /* CONFIG_WIFI_DISPLAY */
if (info->p2ps_instance) {
char str[256];
const u8 *buf = wpabuf_head(info->p2ps_instance);
size_t len = wpabuf_len(info->p2ps_instance);
while (len) {
u32 id;
u16 methods;
u8 str_len;
if (len < 4 + 2 + 1)
break;
id = WPA_GET_LE32(buf);
buf += sizeof(u32);
methods = WPA_GET_BE16(buf);
buf += sizeof(u16);
str_len = *buf++;
if (str_len > len - 4 - 2 - 1)
break;
os_memcpy(str, buf, str_len);
str[str_len] = '\0';
buf += str_len;
len -= str_len + sizeof(u32) + sizeof(u16) + sizeof(u8);
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_DEVICE_FOUND MACSTR
" p2p_dev_addr=" MACSTR
" pri_dev_type=%s name='%s'"
" config_methods=0x%x"
" dev_capab=0x%x"
" group_capab=0x%x"
" adv_id=%x asp_svc=%s%s",
MAC2STR(addr),
MAC2STR(info->p2p_device_addr),
wps_dev_type_bin2str(
info->pri_dev_type,
devtype, sizeof(devtype)),
info->device_name, methods,
info->dev_capab, info->group_capab,
id, str,
info->vendor_elems ?
" vendor_elems=1" : "");
}
goto done;
}
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
" p2p_dev_addr=" MACSTR
" pri_dev_type=%s name='%s' config_methods=0x%x "
"dev_capab=0x%x group_capab=0x%x%s%s%s new=%d",
MAC2STR(addr), MAC2STR(info->p2p_device_addr),
wps_dev_type_bin2str(info->pri_dev_type, devtype,
sizeof(devtype)),
info->device_name, info->config_methods,
info->dev_capab, info->group_capab,
wfd_dev_info_hex ? " wfd_dev_info=0x" : "",
wfd_dev_info_hex ? wfd_dev_info_hex : "",
info->vendor_elems ? " vendor_elems=1" : "",
new_device);
done:
os_free(wfd_dev_info_hex);
#endif /* CONFIG_NO_STDOUT_DEBUG */
wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
}
static void wpas_dev_lost(void *ctx, const u8 *dev_addr)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_LOST
"p2p_dev_addr=" MACSTR, MAC2STR(dev_addr));
wpas_notify_p2p_device_lost(wpa_s, dev_addr);
}
static void wpas_find_stopped(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->p2p_scan_work && wpas_abort_ongoing_scan(wpa_s) < 0)
wpa_printf(MSG_DEBUG, "P2P: Abort ongoing scan failed");
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED);
wpas_notify_p2p_find_stopped(wpa_s);
}
struct wpas_p2p_listen_work {
unsigned int freq;
unsigned int duration;
struct wpabuf *probe_resp_ie;
};
static void wpas_p2p_listen_work_free(struct wpas_p2p_listen_work *lwork)
{
if (lwork == NULL)
return;
wpabuf_free(lwork->probe_resp_ie);
os_free(lwork);
}
static void wpas_p2p_listen_work_done(struct wpa_supplicant *wpa_s)
{
struct wpas_p2p_listen_work *lwork;
if (!wpa_s->p2p_listen_work)
return;
lwork = wpa_s->p2p_listen_work->ctx;
wpas_p2p_listen_work_free(lwork);
radio_work_done(wpa_s->p2p_listen_work);
wpa_s->p2p_listen_work = NULL;
}
static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
struct wpas_p2p_listen_work *lwork = work->ctx;
unsigned int duration;
if (deinit) {
if (work->started) {
wpa_s->p2p_listen_work = NULL;
wpas_stop_listen(wpa_s);
}
wpas_p2p_listen_work_free(lwork);
return;
}
wpa_s->p2p_listen_work = work;
wpa_drv_set_ap_wps_ie(wpa_s, NULL, lwork->probe_resp_ie, NULL);
if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
"report received Probe Request frames");
wpas_p2p_listen_work_done(wpa_s);
return;
}
wpa_s->pending_listen_freq = lwork->freq;
wpa_s->pending_listen_duration = lwork->duration;
duration = lwork->duration;
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->extra_roc_dur) {
wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
duration, duration + wpa_s->extra_roc_dur);
duration += wpa_s->extra_roc_dur;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
"to remain on channel (%u MHz) for Listen "
"state", lwork->freq);
wpas_p2p_listen_work_done(wpa_s);
wpa_s->pending_listen_freq = 0;
return;
}
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = lwork->freq;
}
static int wpas_start_listen(void *ctx, unsigned int freq,
unsigned int duration,
const struct wpabuf *probe_resp_ie)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpas_p2p_listen_work *lwork;
if (wpa_s->p2p_listen_work) {
wpa_printf(MSG_DEBUG, "P2P: Reject start_listen since p2p_listen_work already exists");
return -1;
}
lwork = os_zalloc(sizeof(*lwork));
if (lwork == NULL)
return -1;
lwork->freq = freq;
lwork->duration = duration;
if (probe_resp_ie) {
lwork->probe_resp_ie = wpabuf_dup(probe_resp_ie);
if (lwork->probe_resp_ie == NULL) {
wpas_p2p_listen_work_free(lwork);
return -1;
}
}
if (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb,
lwork) < 0) {
wpas_p2p_listen_work_free(lwork);
return -1;
}
return 0;
}
static void wpas_stop_listen(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
wpa_drv_cancel_remain_on_channel(wpa_s);
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = 0;
}
wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
/*
* Don't cancel Probe Request RX reporting for a connected P2P Client
* handling Probe Request frames.
*/
if (!wpa_s->p2p_cli_probe)
wpa_drv_probe_req_report(wpa_s, 0);
wpas_p2p_listen_work_done(wpa_s);
}
static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf,
unsigned int freq)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
freq, 0);
}
static void wpas_prov_disc_local_display(struct wpa_supplicant *wpa_s,
const u8 *peer, const char *params,
unsigned int generated_pin)
{
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_SHOW_PIN MACSTR
" %08d%s", MAC2STR(peer), generated_pin, params);
}
static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s,
const u8 *peer, const char *params)
{
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_ENTER_PIN MACSTR
"%s", MAC2STR(peer), params);
}
static void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
const u8 *dev_addr, const u8 *pri_dev_type,
const char *dev_name, u16 supp_config_methods,
u8 dev_capab, u8 group_capab, const u8 *group_id,
size_t group_id_len)
{
struct wpa_supplicant *wpa_s = ctx;
char devtype[WPS_DEV_TYPE_BUFSIZE];
char params[300];
u8 empty_dev_type[8];
unsigned int generated_pin = 0;
struct wpa_supplicant *group = NULL;
int res;
if (group_id) {
for (group = wpa_s->global->ifaces; group; group = group->next)
{
struct wpa_ssid *s = group->current_ssid;
if (s != NULL &&
s->mode == WPAS_MODE_P2P_GO &&
group_id_len - ETH_ALEN == s->ssid_len &&
os_memcmp(group_id + ETH_ALEN, s->ssid,
s->ssid_len) == 0)
break;
}
}
if (pri_dev_type == NULL) {
os_memset(empty_dev_type, 0, sizeof(empty_dev_type));
pri_dev_type = empty_dev_type;
}
res = os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
" pri_dev_type=%s name='%s' config_methods=0x%x "
"dev_capab=0x%x group_capab=0x%x%s%s",
MAC2STR(dev_addr),
wps_dev_type_bin2str(pri_dev_type, devtype,
sizeof(devtype)),
dev_name, supp_config_methods, dev_capab, group_capab,
group ? " group=" : "",
group ? group->ifname : "");
if (os_snprintf_error(sizeof(params), res))
wpa_printf(MSG_DEBUG, "P2P: PD Request event truncated");
params[sizeof(params) - 1] = '\0';
if (config_methods & WPS_CONFIG_DISPLAY) {
if (wps_generate_pin(&generated_pin) < 0) {
wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN");
wpas_notify_p2p_provision_discovery(
wpa_s, peer, 0 /* response */,
P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0);
return;
}
wpas_prov_disc_local_display(wpa_s, peer, params,
generated_pin);
} else if (config_methods & WPS_CONFIG_KEYPAD)
wpas_prov_disc_local_keypad(wpa_s, peer, params);
else if (config_methods & WPS_CONFIG_PUSHBUTTON)
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_REQ
MACSTR "%s", MAC2STR(peer), params);
wpas_notify_p2p_provision_discovery(wpa_s, peer, 1 /* request */,
P2P_PROV_DISC_SUCCESS,
config_methods, generated_pin);
}
static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
{
struct wpa_supplicant *wpa_s = ctx;
unsigned int generated_pin = 0;
char params[20];
if (wpa_s->pending_pd_before_join &&
(os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
os_memcmp(peer, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
wpa_s->pending_pd_before_join = 0;
wpa_printf(MSG_DEBUG, "P2P: Starting pending "
"join-existing-group operation");
wpas_p2p_join_start(wpa_s, 0, NULL, 0);
return;
}
if (wpa_s->pending_pd_use == AUTO_PD_JOIN ||
wpa_s->pending_pd_use == AUTO_PD_GO_NEG) {
int res;
res = os_snprintf(params, sizeof(params), " peer_go=%d",
wpa_s->pending_pd_use == AUTO_PD_JOIN);
if (os_snprintf_error(sizeof(params), res))
params[sizeof(params) - 1] = '\0';
} else
params[0] = '\0';
if (config_methods & WPS_CONFIG_DISPLAY)
wpas_prov_disc_local_keypad(wpa_s, peer, params);
else if (config_methods & WPS_CONFIG_KEYPAD) {
if (wps_generate_pin(&generated_pin) < 0) {
wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN");
wpas_notify_p2p_provision_discovery(
wpa_s, peer, 0 /* response */,
P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0);
return;
}
wpas_prov_disc_local_display(wpa_s, peer, params,
generated_pin);
} else if (config_methods & WPS_CONFIG_PUSHBUTTON)
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP
MACSTR "%s", MAC2STR(peer), params);
wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
P2P_PROV_DISC_SUCCESS,
config_methods, generated_pin);
}
static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
enum p2p_prov_disc_status status,
u32 adv_id, const u8 *adv_mac,
const char *deferred_session_resp)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->p2p_fallback_to_go_neg) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto "
"failed - fall back to GO Negotiation");
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_FALLBACK_TO_GO_NEG
"reason=PD-failed");
wpas_p2p_fallback_to_go_neg(wpa_s, 0);
return;
}
if (status == P2P_PROV_DISC_TIMEOUT_JOIN) {
wpa_s->pending_pd_before_join = 0;
wpa_printf(MSG_DEBUG, "P2P: Starting pending "
"join-existing-group operation (no ACK for PD "
"Req attempts)");
wpas_p2p_join_start(wpa_s, 0, NULL, 0);
return;
}
if (adv_id && adv_mac && deferred_session_resp) {
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
" p2p_dev_addr=" MACSTR " status=%d adv_id=%x"
" deferred_session_resp='%s'",
MAC2STR(peer), status, adv_id,
deferred_session_resp);
} else if (adv_id && adv_mac) {
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
" p2p_dev_addr=" MACSTR " status=%d adv_id=%x",
MAC2STR(peer), status, adv_id);
} else {
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
" p2p_dev_addr=" MACSTR " status=%d",
MAC2STR(peer), status);
}
wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
status, 0, 0);
}
static int freq_included(struct wpa_supplicant *wpa_s,
const struct p2p_channels *channels,
unsigned int freq)
{
if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) &&
wpas_p2p_go_is_peer_freq(wpa_s, freq))
return 1;
return 0;
}
static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s)
{
unsigned int num = P2P_MAX_CHANNELS;
int *common_freqs;
int ret;
p2p_go_dump_common_freqs(wpa_s);
common_freqs = os_calloc(num, sizeof(int));
if (!common_freqs)
return;
ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num);
if (ret < 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Failed to get group common freqs");
os_free(common_freqs);
return;
}
os_free(wpa_s->p2p_group_common_freqs);
wpa_s->p2p_group_common_freqs = common_freqs;
wpa_s->p2p_group_common_freqs_num = num;
p2p_go_dump_common_freqs(wpa_s);
}
/*
* Check if the given frequency is one of the possible operating frequencies
* set after the completion of the GO Negotiation.
*/
static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq)
{
unsigned int i;
p2p_go_dump_common_freqs(wpa_s);
/* assume no restrictions */
if (!wpa_s->p2p_group_common_freqs_num)
return 1;
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
if (wpa_s->p2p_group_common_freqs[i] == freq)
return 1;
}
return 0;
}
static int wpas_sta_check_ecsa(struct hostapd_data *hapd,
struct sta_info *sta, void *ctx)
{
int *ecsa_support = ctx;
*ecsa_support &= sta->ecsa_supported;
return 0;
}
/* Check if all the peers support eCSA */
static int wpas_p2p_go_clients_support_ecsa(struct wpa_supplicant *wpa_s)
{
int ecsa_support = 1;
ap_for_each_sta(wpa_s->ap_iface->bss[0], wpas_sta_check_ecsa,
&ecsa_support);
return ecsa_support;
}
/**
* Pick the best frequency to use from all the currently used frequencies.
*/
static int wpas_p2p_pick_best_used_freq(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs,
unsigned int num)
{
unsigned int i, c;
/* find a candidate freq that is supported by P2P */
for (c = 0; c < num; c++)
if (p2p_supported_freq(wpa_s->global->p2p, freqs[c].freq))
break;
if (c == num)
return 0;
/* once we have a candidate, try to find a 'better' one */
for (i = c + 1; i < num; i++) {
if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq))
continue;
/*
* 1. Infrastructure station interfaces have higher preference.
* 2. P2P Clients have higher preference.
* 3. All others.
*/
if (freqs[i].flags & WPA_FREQ_USED_BY_INFRA_STATION) {
c = i;
break;
}
if ((freqs[i].flags & WPA_FREQ_USED_BY_P2P_CLIENT))
c = i;
}
return freqs[c].freq;
}
static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
const u8 *go_dev_addr, const u8 *ssid,
size_t ssid_len, int *go, u8 *group_bssid,
int *force_freq, int persistent_group,
const struct p2p_channels *channels,
int dev_pw_id)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *s;
struct wpa_used_freq_data *freqs;
struct wpa_supplicant *grp;
int best_freq;
if (!persistent_group) {
wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
" to join an active group (SSID: %s)",
MAC2STR(sa), wpa_ssid_txt(ssid, ssid_len));
if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
(os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN)
== 0 ||
os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) {
wpa_printf(MSG_DEBUG, "P2P: Accept previously "
"authorized invitation");
goto accept_inv;
}
#ifdef CONFIG_WPS_NFC
if (dev_pw_id >= 0 && wpa_s->p2p_nfc_tag_enabled &&
dev_pw_id == wpa_s->p2p_oob_dev_pw_id) {
wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag");
wpa_s->p2p_wps_method = WPS_NFC;
wpa_s->pending_join_wps_method = WPS_NFC;
os_memcpy(wpa_s->pending_join_dev_addr,
go_dev_addr, ETH_ALEN);
os_memcpy(wpa_s->pending_join_iface_addr,
bssid, ETH_ALEN);
goto accept_inv;
}
#endif /* CONFIG_WPS_NFC */
/*
* Do not accept the invitation automatically; notify user and
* request approval.
*/
return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
}
grp = wpas_get_p2p_group(wpa_s, ssid, ssid_len, go);
if (grp) {
wpa_printf(MSG_DEBUG, "P2P: Accept invitation to already "
"running persistent group");
if (*go)
os_memcpy(group_bssid, grp->own_addr, ETH_ALEN);
goto accept_inv;
}
if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG, "P2P: Accept previously initiated "
"invitation to re-invoke a persistent group");
os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
} else if (!wpa_s->conf->persistent_reconnect)
return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
for (s = wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled == 2 &&
os_memcmp(s->bssid, go_dev_addr, ETH_ALEN) == 0 &&
s->ssid_len == ssid_len &&
os_memcmp(ssid, s->ssid, ssid_len) == 0)
break;
}
if (!s) {
wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
" requested reinvocation of an unknown group",
MAC2STR(sa));
return P2P_SC_FAIL_UNKNOWN_GROUP;
}
if (s->mode == WPAS_MODE_P2P_GO && !wpas_p2p_create_iface(wpa_s)) {
*go = 1;
if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
wpa_printf(MSG_DEBUG, "P2P: The only available "
"interface is already in use - reject "
"invitation");
return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
}
if (wpa_s->p2p_mgmt)
os_memcpy(group_bssid, wpa_s->parent->own_addr,
ETH_ALEN);
else
os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN);
} else if (s->mode == WPAS_MODE_P2P_GO) {
*go = 1;
if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO) < 0)
{
wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
"interface address for the group");
return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
}
os_memcpy(group_bssid, wpa_s->pending_interface_addr,
ETH_ALEN);
}
accept_inv:
wpas_p2p_set_own_freq_preference(wpa_s, 0);
best_freq = 0;
freqs = os_calloc(wpa_s->num_multichan_concurrent,
sizeof(struct wpa_used_freq_data));
if (freqs) {
int num_channels = wpa_s->num_multichan_concurrent;
int num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, num_channels);
best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
os_free(freqs);
}
/* Get one of the frequencies currently in use */
if (best_freq > 0) {
wpa_printf(MSG_DEBUG, "P2P: Trying to prefer a channel already used by one of the interfaces");
wpas_p2p_set_own_freq_preference(wpa_s, best_freq);
if (wpa_s->num_multichan_concurrent < 2 ||
wpas_p2p_num_unused_channels(wpa_s) < 1) {
wpa_printf(MSG_DEBUG, "P2P: No extra channels available - trying to force channel to match a channel already used by one of the interfaces");
*force_freq = best_freq;
}
}
if (*force_freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
wpas_p2p_num_unused_channels(wpa_s) > 0) {
if (*go == 0) {
/* We are the client */
wpa_printf(MSG_DEBUG, "P2P: Peer was found to be "
"running a GO but we are capable of MCC, "
"figure out the best channel to use");
*force_freq = 0;
} else if (!freq_included(wpa_s, channels, *force_freq)) {
/* We are the GO, and *force_freq is not in the
* intersection */
wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
"in intersection but we are capable of MCC, "
"figure out the best channel to use",
*force_freq);
*force_freq = 0;
}
}
return P2P_SC_SUCCESS;
}
static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
const u8 *ssid, size_t ssid_len,
const u8 *go_dev_addr, u8 status,
int op_freq)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *s;
for (s = wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled == 2 &&
s->ssid_len == ssid_len &&
os_memcmp(ssid, s->ssid, ssid_len) == 0)
break;
}
if (status == P2P_SC_SUCCESS) {
wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
" was accepted; op_freq=%d MHz, SSID=%s",
MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len));
if (s) {
int go = s->mode == WPAS_MODE_P2P_GO;
if (go) {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_INVITATION_ACCEPTED
"sa=" MACSTR
" persistent=%d freq=%d",
MAC2STR(sa), s->id, op_freq);
} else {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_INVITATION_ACCEPTED
"sa=" MACSTR
" persistent=%d",
MAC2STR(sa), s->id);
}
wpas_p2p_group_add_persistent(
wpa_s, s, go, 0, op_freq, 0,
wpa_s->conf->p2p_go_ht40,
wpa_s->conf->p2p_go_vht,
0,
wpa_s->conf->p2p_go_he,
wpa_s->conf->p2p_go_edmg, NULL,
go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
1, is_p2p_allow_6ghz(wpa_s->global->p2p));
} else if (bssid) {
wpa_s->user_initiated_pd = 0;
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_INVITATION_ACCEPTED
"sa=" MACSTR " go_dev_addr=" MACSTR
" bssid=" MACSTR " unknown-network",
MAC2STR(sa), MAC2STR(go_dev_addr),
MAC2STR(bssid));
wpas_p2p_join(wpa_s, bssid, go_dev_addr,
wpa_s->p2p_wps_method, 0, op_freq,
ssid, ssid_len);
}
return;
}
if (status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
" was rejected (status %u)", MAC2STR(sa), status);
return;
}
if (!s) {
if (bssid) {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_INVITATION_RECEIVED
"sa=" MACSTR " go_dev_addr=" MACSTR
" bssid=" MACSTR " unknown-network",
MAC2STR(sa), MAC2STR(go_dev_addr),
MAC2STR(bssid));
} else {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_INVITATION_RECEIVED
"sa=" MACSTR " go_dev_addr=" MACSTR
" unknown-network",
MAC2STR(sa), MAC2STR(go_dev_addr));
}
wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr,
bssid, 0, op_freq);
return;
}
if (s->mode == WPAS_MODE_P2P_GO && op_freq) {
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
"sa=" MACSTR " persistent=%d freq=%d",
MAC2STR(sa), s->id, op_freq);
} else {
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED
"sa=" MACSTR " persistent=%d",
MAC2STR(sa), s->id);
}
wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
s->id, op_freq);
}
static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
const u8 *peer, int inv)
{
size_t i;
struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
if (ssid == NULL)
return;
for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) {
if (os_memcmp(ssid->p2p_client_list + i * 2 * ETH_ALEN, peer,
ETH_ALEN) == 0)
break;
}
if (i >= ssid->num_p2p_clients || !ssid->p2p_client_list) {
if (ssid->mode != WPAS_MODE_P2P_GO &&
os_memcmp(ssid->bssid, peer, ETH_ALEN) == 0) {
wpa_printf(MSG_DEBUG, "P2P: Remove persistent group %d "
"due to invitation result", ssid->id);
wpas_notify_network_removed(wpa_s, ssid);
wpa_config_remove_network(wpa_s->conf, ssid->id);
return;
}
return; /* Peer not found in client list */
}
wpa_printf(MSG_DEBUG, "P2P: Remove peer " MACSTR " from persistent "
"group %d client list%s",
MAC2STR(peer), ssid->id,
inv ? " due to invitation result" : "");
os_memmove(ssid->p2p_client_list + i * 2 * ETH_ALEN,
ssid->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
(ssid->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
ssid->num_p2p_clients--;
if (p2p_wpa_s->conf->update_config &&
wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
}
static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s,
const u8 *peer)
{
struct wpa_ssid *ssid;
wpa_s = wpa_s->global->p2p_invite_group;
if (wpa_s == NULL)
return; /* No known invitation group */
ssid = wpa_s->current_ssid;
if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
!ssid->p2p_persistent_group)
return; /* Not operating as a GO in persistent group */
ssid = wpas_p2p_get_persistent(wpa_s->p2pdev, peer,
ssid->ssid, ssid->ssid_len);
wpas_remove_persistent_peer(wpa_s, ssid, peer, 1);
}
static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
const struct p2p_channels *channels,
const u8 *peer, int neg_freq,
int peer_oper_freq)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *ssid;
int freq;
if (bssid) {
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
"status=%d " MACSTR,
status, MAC2STR(bssid));
} else {
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
"status=%d ", status);
}
wpas_notify_p2p_invitation_result(wpa_s, status, bssid);
wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR,
status, MAC2STR(peer));
if (wpa_s->pending_invite_ssid_id == -1) {
struct wpa_supplicant *group_if =
wpa_s->global->p2p_invite_group;
if (status == P2P_SC_FAIL_UNKNOWN_GROUP)
wpas_remove_persistent_client(wpa_s, peer);
/*
* Invitation to an active group. If this is successful and we
* are the GO, set the client wait to postpone some concurrent
* operations and to allow provisioning and connection to happen
* more quickly.
*/
if (status == P2P_SC_SUCCESS &&
group_if && group_if->current_ssid &&
group_if->current_ssid->mode == WPAS_MODE_P2P_GO) {
os_get_reltime(&wpa_s->global->p2p_go_wait_client);
#ifdef CONFIG_TESTING_OPTIONS
if (group_if->p2p_go_csa_on_inv) {
wpa_printf(MSG_DEBUG,
"Testing: force P2P GO CSA after invitation");
eloop_cancel_timeout(
wpas_p2p_reconsider_moving_go,
wpa_s, NULL);
eloop_register_timeout(
0, 50000,
wpas_p2p_reconsider_moving_go,
wpa_s, NULL);
}
#endif /* CONFIG_TESTING_OPTIONS */
}
return;
}
if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
wpa_printf(MSG_DEBUG, "P2P: Waiting for peer to start another "
"invitation exchange to indicate readiness for "
"re-invocation");
}
if (status != P2P_SC_SUCCESS) {
if (status == P2P_SC_FAIL_UNKNOWN_GROUP) {
ssid = wpa_config_get_network(
wpa_s->conf, wpa_s->pending_invite_ssid_id);
wpas_remove_persistent_peer(wpa_s, ssid, peer, 1);
}
wpas_p2p_remove_pending_group_interface(wpa_s);
return;
}
ssid = wpa_config_get_network(wpa_s->conf,
wpa_s->pending_invite_ssid_id);
if (ssid == NULL) {
wpa_printf(MSG_ERROR, "P2P: Could not find persistent group "
"data matching with invitation");
return;
}
/*
* The peer could have missed our ctrl::ack frame for Invitation
* Response and continue retransmitting the frame. To reduce the
* likelihood of the peer not getting successful TX status for the
* Invitation Response frame, wait a short time here before starting
* the persistent group so that we will remain on the current channel to
* acknowledge any possible retransmission from the peer.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before "
"starting persistent group");
os_sleep(0, 50000);
if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
freq_included(wpa_s, channels, neg_freq))
freq = neg_freq;
else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
freq_included(wpa_s, channels, peer_oper_freq))
freq = peer_oper_freq;
else
freq = 0;
wpa_printf(MSG_DEBUG, "P2P: Persistent group invitation success - op_freq=%d MHz SSID=%s",
freq, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
wpas_p2p_group_add_persistent(wpa_s, ssid,
ssid->mode == WPAS_MODE_P2P_GO,
wpa_s->p2p_persistent_go_freq,
freq,
wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
wpa_s->p2p_go_max_oper_chwidth,
wpa_s->p2p_go_he,
wpa_s->p2p_go_edmg,
channels,
ssid->mode == WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
0, 1,
is_p2p_allow_6ghz(wpa_s->global->p2p));
}
static int wpas_p2p_disallowed_freq(struct wpa_global *global,
unsigned int freq)
{
if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq))
return 1;
return freq_range_list_includes(&global->p2p_disallow_freq, freq);
}
static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan)
{
reg->channel[reg->channels] = chan;
reg->channels++;
}
static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
struct p2p_channels *chan,
struct p2p_channels *cli_chan)
{
int i, cla = 0;
wpa_s->global->p2p_24ghz_social_channels = 1;
os_memset(cli_chan, 0, sizeof(*cli_chan));
wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz "
"band");
/* Operating class 81 - 2.4 GHz band channels 1..13 */
chan->reg_class[cla].reg_class = 81;
chan->reg_class[cla].channels = 0;
for (i = 0; i < 11; i++) {
if (!wpas_p2p_disallowed_freq(wpa_s->global, 2412 + i * 5))
wpas_p2p_add_chan(&chan->reg_class[cla], i + 1);
}
if (chan->reg_class[cla].channels)
cla++;
wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz "
"band");
/* Operating class 115 - 5 GHz, channels 36-48 */
chan->reg_class[cla].reg_class = 115;
chan->reg_class[cla].channels = 0;
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 36 * 5))
wpas_p2p_add_chan(&chan->reg_class[cla], 36);
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 40 * 5))
wpas_p2p_add_chan(&chan->reg_class[cla], 40);
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 44 * 5))
wpas_p2p_add_chan(&chan->reg_class[cla], 44);
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 48 * 5))
wpas_p2p_add_chan(&chan->reg_class[cla], 48);
if (chan->reg_class[cla].channels)
cla++;
wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz "
"band");
/* Operating class 124 - 5 GHz, channels 149,153,157,161 */
chan->reg_class[cla].reg_class = 124;
chan->reg_class[cla].channels = 0;
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 149 * 5))
wpas_p2p_add_chan(&chan->reg_class[cla], 149);
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 153 * 5))
wpas_p2p_add_chan(&chan->reg_class[cla], 153);
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 156 * 5))
wpas_p2p_add_chan(&chan->reg_class[cla], 157);
if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 161 * 5))
wpas_p2p_add_chan(&chan->reg_class[cla], 161);
if (chan->reg_class[cla].channels)
cla++;
chan->reg_classes = cla;
return 0;
}
static enum chan_allowed has_channel(struct wpa_global *global,
struct hostapd_hw_modes *mode, u8 op_class,
u8 chan, int *flags)
{
int i;
unsigned int freq;
freq = ieee80211_chan_to_freq(NULL, op_class, chan);
if (wpas_p2p_disallowed_freq(global, freq))
return NOT_ALLOWED;
for (i = 0; i < mode->num_channels; i++) {
if ((unsigned int) mode->channels[i].freq == freq) {
if (flags)
*flags = mode->channels[i].flag;
- if (mode->channels[i].flag &
- (HOSTAPD_CHAN_DISABLED |
- HOSTAPD_CHAN_RADAR))
+ if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
return NOT_ALLOWED;
if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR)
return NO_IR;
+ if (mode->channels[i].flag & HOSTAPD_CHAN_RADAR)
+ return RADAR;
return ALLOWED;
}
}
return NOT_ALLOWED;
}
static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 channel, const u8 *center_channels,
size_t num_chan)
{
size_t i;
if (mode->mode != HOSTAPD_MODE_IEEE80211A)
return 0;
for (i = 0; i < num_chan; i++)
/*
* In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
* so the center channel is 6 channels away from the start/end.
*/
if (channel >= center_channels[i] - 6 &&
channel <= center_channels[i] + 6)
return center_channels[i];
return 0;
}
static const u8 center_channels_5ghz_80mhz[] = { 42, 58, 106, 122, 138,
155, 171 };
static const u8 center_channels_6ghz_80mhz[] = { 7, 23, 39, 55, 71, 87, 103,
119, 135, 151, 167, 183, 199,
215 };
static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 op_class, u8 channel, u8 bw)
{
u8 center_chan;
int i, flags;
enum chan_allowed res, ret = ALLOWED;
const u8 *chans;
size_t num_chans;
bool is_6ghz = is_6ghz_op_class(op_class);
if (is_6ghz) {
chans = center_channels_6ghz_80mhz;
num_chans = ARRAY_SIZE(center_channels_6ghz_80mhz);
} else {
chans = center_channels_5ghz_80mhz;
num_chans = ARRAY_SIZE(center_channels_5ghz_80mhz);
}
center_chan = wpas_p2p_get_center_80mhz(wpa_s, mode, channel,
chans, num_chans);
if (!center_chan)
return NOT_ALLOWED;
- if (!is_6ghz && center_chan >= 58 && center_chan <= 138)
+ if (!wpa_s->p2p_go_allow_dfs &&
+ !is_6ghz && center_chan >= 58 && center_chan <= 138)
return NOT_ALLOWED; /* Do not allow DFS channels for P2P */
/* check all the channels are available */
for (i = 0; i < 4; i++) {
int adj_chan = center_chan - 6 + i * 4;
res = has_channel(wpa_s->global, mode, op_class, adj_chan,
&flags);
if (res == NOT_ALLOWED)
return NOT_ALLOWED;
+ if (res == RADAR)
+ ret = RADAR;
if (res == NO_IR)
ret = NO_IR;
-
- if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70))
- return NOT_ALLOWED;
- if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50))
- return NOT_ALLOWED;
- if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30))
- return NOT_ALLOWED;
- if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))
+ if (!is_6ghz) {
+ if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70))
+ return NOT_ALLOWED;
+ if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50))
+ return NOT_ALLOWED;
+ if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30))
+ return NOT_ALLOWED;
+ if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))
+ return NOT_ALLOWED;
+ } else if (is_6ghz &&
+ (!(wpas_get_6ghz_he_chwidth_capab(mode) &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G))) {
return NOT_ALLOWED;
+ }
}
return ret;
}
static int wpas_p2p_get_center_160mhz(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 channel, const u8 *center_channels,
size_t num_chan)
{
unsigned int i;
if (mode->mode != HOSTAPD_MODE_IEEE80211A)
return 0;
for (i = 0; i < num_chan; i++)
/*
* In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
* so the center channel is 14 channels away from the start/end.
*/
if (channel >= center_channels[i] - 14 &&
channel <= center_channels[i] + 14)
return center_channels[i];
return 0;
}
static const u8 center_channels_5ghz_160mhz[] = { 50, 114, 163 };
static const u8 center_channels_6ghz_160mhz[] = { 15, 47, 79, 111, 143, 175,
207 };
static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 op_class, u8 channel, u8 bw)
{
u8 center_chan;
int i, flags;
enum chan_allowed res, ret = ALLOWED;
const u8 *chans;
size_t num_chans;
if (is_6ghz_op_class(op_class)) {
chans = center_channels_6ghz_160mhz;
num_chans = ARRAY_SIZE(center_channels_6ghz_160mhz);
} else {
chans = center_channels_5ghz_160mhz;
num_chans = ARRAY_SIZE(center_channels_5ghz_160mhz);
}
center_chan = wpas_p2p_get_center_160mhz(wpa_s, mode, channel,
chans, num_chans);
if (!center_chan)
return NOT_ALLOWED;
/* VHT 160 MHz uses DFS channels in most countries. */
/* Check all the channels are available */
for (i = 0; i < 8; i++) {
int adj_chan = center_chan - 14 + i * 4;
res = has_channel(wpa_s->global, mode, op_class, adj_chan,
&flags);
if (res == NOT_ALLOWED)
return NOT_ALLOWED;
+ if (res == RADAR)
+ ret = RADAR;
if (res == NO_IR)
ret = NO_IR;
- if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150))
- return NOT_ALLOWED;
- if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130))
- return NOT_ALLOWED;
- if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110))
- return NOT_ALLOWED;
- if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90))
- return NOT_ALLOWED;
- if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70))
- return NOT_ALLOWED;
- if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50))
- return NOT_ALLOWED;
- if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30))
- return NOT_ALLOWED;
- if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))
+ if (!is_6ghz_op_class(op_class)) {
+ if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150))
+ return NOT_ALLOWED;
+ if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130))
+ return NOT_ALLOWED;
+ if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110))
+ return NOT_ALLOWED;
+ if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90))
+ return NOT_ALLOWED;
+ if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70))
+ return NOT_ALLOWED;
+ if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50))
+ return NOT_ALLOWED;
+ if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30))
+ return NOT_ALLOWED;
+ if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))
+ return NOT_ALLOWED;
+ } else if (is_6ghz_op_class(op_class) &&
+ (!(wpas_get_6ghz_he_chwidth_capab(mode) &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G))) {
return NOT_ALLOWED;
+ }
}
return ret;
}
static enum chan_allowed wpas_p2p_verify_edmg(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 channel)
{
struct ieee80211_edmg_config edmg;
hostapd_encode_edmg_chan(1, channel, 0, &edmg);
if (edmg.channels && ieee802_edmg_is_allowed(mode->edmg, edmg))
return ALLOWED;
return NOT_ALLOWED;
}
static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 op_class, u8 channel, u8 bw)
{
int flag = 0;
enum chan_allowed res, res2;
res2 = res = has_channel(wpa_s->global, mode, op_class, channel, &flag);
if (bw == BW40MINUS) {
if (!(flag & HOSTAPD_CHAN_HT40MINUS))
return NOT_ALLOWED;
res2 = has_channel(wpa_s->global, mode, op_class, channel - 4,
NULL);
} else if (bw == BW40PLUS) {
if (!(flag & HOSTAPD_CHAN_HT40PLUS))
return NOT_ALLOWED;
res2 = has_channel(wpa_s->global, mode, op_class, channel + 4,
NULL);
+ } else if (is_6ghz_op_class(op_class) && bw == BW40) {
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return NOT_ALLOWED;
+ if (get_6ghz_sec_channel(channel) < 0)
+ res2 = has_channel(wpa_s->global, mode, op_class,
+ channel - 4, NULL);
+ else
+ res2 = has_channel(wpa_s->global, mode, op_class,
+ channel + 4, NULL);
} else if (bw == BW80) {
res2 = wpas_p2p_verify_80mhz(wpa_s, mode, op_class, channel,
bw);
} else if (bw == BW160) {
res2 = wpas_p2p_verify_160mhz(wpa_s, mode, op_class, channel,
bw);
} else if (bw == BW4320 || bw == BW6480 || bw == BW8640) {
return wpas_p2p_verify_edmg(wpa_s, mode, channel);
}
if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
return NOT_ALLOWED;
if (res == NO_IR || res2 == NO_IR)
return NO_IR;
+ if (res == RADAR || res2 == RADAR)
+ return RADAR;
return res;
}
static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
struct p2p_channels *chan,
struct p2p_channels *cli_chan,
bool p2p_disable_6ghz)
{
struct hostapd_hw_modes *mode;
int cla, op, cli_cla;
if (wpa_s->hw.modes == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
"of all supported channels; assume dualband "
"support");
return wpas_p2p_default_channels(wpa_s, chan, cli_chan);
}
cla = cli_cla = 0;
for (op = 0; global_op_class[op].op_class; op++) {
const struct oper_class_map *o = &global_op_class[op];
- u8 ch;
+ unsigned int ch;
struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
if (o->p2p == NO_P2P_SUPP ||
(is_6ghz_op_class(o->op_class) && p2p_disable_6ghz))
continue;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode,
is_6ghz_op_class(o->op_class));
if (mode == NULL)
continue;
if (mode->mode == HOSTAPD_MODE_IEEE80211G)
wpa_s->global->p2p_24ghz_social_channels = 1;
for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
enum chan_allowed res;
/* Check for non-continuous jump in channel index
* incrementation */
if ((o->op_class >= 128 && o->op_class <= 130) &&
ch < 149 && ch + o->inc > 149)
ch = 149;
res = wpas_p2p_verify_channel(wpa_s, mode, o->op_class,
ch, o->bw);
if (res == ALLOWED) {
if (reg == NULL) {
if (cla == P2P_MAX_REG_CLASSES)
continue;
wpa_printf(MSG_DEBUG, "P2P: Add operating class %u",
o->op_class);
reg = &chan->reg_class[cla];
cla++;
reg->reg_class = o->op_class;
}
if (reg->channels == P2P_MAX_REG_CLASS_CHANNELS)
continue;
reg->channel[reg->channels] = ch;
reg->channels++;
} else if (res == NO_IR &&
wpa_s->conf->p2p_add_cli_chan) {
if (cli_reg == NULL) {
if (cli_cla == P2P_MAX_REG_CLASSES)
continue;
wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)",
o->op_class);
cli_reg = &cli_chan->reg_class[cli_cla];
cli_cla++;
cli_reg->reg_class = o->op_class;
}
if (cli_reg->channels ==
P2P_MAX_REG_CLASS_CHANNELS)
continue;
cli_reg->channel[cli_reg->channels] = ch;
cli_reg->channels++;
}
}
if (reg) {
wpa_hexdump(MSG_DEBUG, "P2P: Channels",
reg->channel, reg->channels);
}
if (cli_reg) {
wpa_hexdump(MSG_DEBUG, "P2P: Channels (client only)",
cli_reg->channel, cli_reg->channels);
}
}
chan->reg_classes = cla;
cli_chan->reg_classes = cli_cla;
return 0;
}
-int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
- struct hostapd_hw_modes *mode, u8 channel)
+int wpas_p2p_get_sec_channel_offset_40mhz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel)
{
int op;
enum chan_allowed ret;
for (op = 0; global_op_class[op].op_class; op++) {
const struct oper_class_map *o = &global_op_class[op];
- u8 ch;
-
- if (o->p2p == NO_P2P_SUPP ||
+ u16 ch;
+ int chan = channel;
+
+ /* Allow DFS channels marked as NO_P2P_SUPP to be used with
+ * driver offloaded DFS. */
+ if ((o->p2p == NO_P2P_SUPP &&
+ (!is_dfs_global_op_class(o->op_class) ||
+ !wpa_s->p2p_go_allow_dfs)) ||
(is_6ghz_op_class(o->op_class) &&
wpa_s->conf->p2p_6ghz_disable))
continue;
+ if (is_6ghz_op_class(o->op_class) && o->bw == BW40 &&
+ get_6ghz_sec_channel(channel) < 0)
+ chan = channel - 4;
+
for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
if (o->mode != HOSTAPD_MODE_IEEE80211A ||
- (o->bw != BW40PLUS && o->bw != BW40MINUS) ||
- ch != channel)
+ (o->bw != BW40PLUS && o->bw != BW40MINUS &&
+ o->bw != BW40) ||
+ ch != chan)
continue;
ret = wpas_p2p_verify_channel(wpa_s, mode, o->op_class,
ch, o->bw);
- if (ret == ALLOWED)
+ if (ret == ALLOWED) {
+ if (is_6ghz_op_class(o->op_class) &&
+ o->bw == BW40)
+ return get_6ghz_sec_channel(channel);
+ return (o->bw == BW40MINUS) ? -1 : 1;
+ }
+ if (ret == RADAR && wpa_s->p2p_go_allow_dfs) {
+ /* Allow RADAR channels used for driver
+ * offloaded DFS */
return (o->bw == BW40MINUS) ? -1 : 1;
+ }
}
}
return 0;
}
int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel,
u8 op_class)
{
const u8 *chans;
size_t num_chans;
+ enum chan_allowed ret;
- if (!wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW80))
+ ret = wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW80);
+ if (!(ret == ALLOWED || (ret == RADAR && wpa_s->p2p_go_allow_dfs)))
return 0;
if (is_6ghz_op_class(op_class)) {
chans = center_channels_6ghz_80mhz;
num_chans = ARRAY_SIZE(center_channels_6ghz_80mhz);
} else {
chans = center_channels_5ghz_80mhz;
num_chans = ARRAY_SIZE(center_channels_5ghz_80mhz);
}
return wpas_p2p_get_center_80mhz(wpa_s, mode, channel,
chans, num_chans);
}
int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel,
u8 op_class)
{
const u8 *chans;
size_t num_chans;
+ enum chan_allowed ret;
- if (!wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW160))
+ ret = wpas_p2p_verify_channel(wpa_s, mode, op_class, channel, BW160);
+ if (!(ret == ALLOWED || (ret == RADAR && wpa_s->p2p_go_allow_dfs)))
return 0;
if (is_6ghz_op_class(op_class)) {
chans = center_channels_6ghz_160mhz;
num_chans = ARRAY_SIZE(center_channels_6ghz_160mhz);
} else {
chans = center_channels_5ghz_160mhz;
num_chans = ARRAY_SIZE(center_channels_5ghz_160mhz);
}
return wpas_p2p_get_center_160mhz(wpa_s, mode, channel,
chans, num_chans);
}
static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
size_t buf_len)
{
struct wpa_supplicant *wpa_s = ctx;
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_memcmp(wpa_s->own_addr, interface_addr, ETH_ALEN) == 0)
break;
}
if (wpa_s == NULL)
return -1;
return wpa_drv_get_noa(wpa_s, buf, buf_len);
}
struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
const u8 *ssid, size_t ssid_len)
{
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
struct wpa_ssid *s = wpa_s->current_ssid;
if (s == NULL)
continue;
if (s->mode != WPAS_MODE_P2P_GO &&
s->mode != WPAS_MODE_AP &&
s->mode != WPAS_MODE_P2P_GROUP_FORMATION)
continue;
if (s->ssid_len != ssid_len ||
os_memcmp(ssid, s->ssid, ssid_len) != 0)
continue;
return wpa_s;
}
return NULL;
}
struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
const u8 *peer_dev_addr)
{
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group))
continue;
if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0)
return wpa_s;
}
return NULL;
}
static int wpas_go_connected(void *ctx, const u8 *dev_addr)
{
struct wpa_supplicant *wpa_s = ctx;
return wpas_get_p2p_client_iface(wpa_s, dev_addr) != NULL;
}
static int wpas_is_concurrent_session_active(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_supplicant *ifs;
for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
if (ifs == wpa_s)
continue;
if (ifs->wpa_state > WPA_ASSOCIATED)
return 1;
}
return 0;
}
static void wpas_p2p_debug_print(void *ctx, int level, const char *msg)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_msg_global(wpa_s, level, "P2P: %s", msg);
}
int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
const char *conf_p2p_dev)
{
struct wpa_interface iface;
struct wpa_supplicant *p2pdev_wpa_s;
char ifname[100];
char force_name[100];
int ret;
const u8 *if_addr = NULL;
ret = os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s",
wpa_s->ifname);
if (os_snprintf_error(sizeof(ifname), ret))
return -1;
/* Cut length at the maximum size. Note that we don't need to ensure
* collision free names here as the created interface is not a netdev.
*/
ifname[IFNAMSIZ - 1] = '\0';
force_name[0] = '\0';
wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE;
if (wpa_s->conf->p2p_device_random_mac_addr == 2 &&
!is_zero_ether_addr(wpa_s->conf->p2p_device_persistent_mac_addr))
if_addr = wpa_s->conf->p2p_device_persistent_mac_addr;
ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, if_addr, NULL,
force_name, wpa_s->pending_interface_addr, NULL);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface");
return ret;
}
os_strlcpy(wpa_s->pending_interface_name, ifname,
sizeof(wpa_s->pending_interface_name));
os_memset(&iface, 0, sizeof(iface));
iface.p2p_mgmt = 1;
iface.ifname = wpa_s->pending_interface_name;
iface.driver = wpa_s->driver->name;
iface.driver_param = wpa_s->conf->driver_param;
/*
* If a P2P Device configuration file was given, use it as the interface
* configuration file (instead of using parent's configuration file.
*/
if (conf_p2p_dev) {
iface.confname = conf_p2p_dev;
iface.ctrl_interface = NULL;
} else {
iface.confname = wpa_s->confname;
iface.ctrl_interface = wpa_s->conf->ctrl_interface;
}
p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
if (!p2pdev_wpa_s) {
wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface");
return -1;
}
p2pdev_wpa_s->p2pdev = p2pdev_wpa_s;
wpa_s->pending_interface_name[0] = '\0';
return 0;
}
static void wpas_presence_resp(void *ctx, const u8 *src, u8 status,
const u8 *noa, size_t noa_len)
{
struct wpa_supplicant *wpa_s, *intf = ctx;
char hex[100];
for (wpa_s = intf->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (wpa_s->waiting_presence_resp)
break;
}
if (!wpa_s) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No group interface was waiting for presence response");
return;
}
wpa_s->waiting_presence_resp = 0;
wpa_snprintf_hex(hex, sizeof(hex), noa, noa_len);
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PRESENCE_RESPONSE "src=" MACSTR
" status=%u noa=%s", MAC2STR(src), status, hex);
}
static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid,
size_t ssid_len, u8 *go_dev_addr,
u8 *ret_ssid, size_t *ret_ssid_len,
u8 *intended_iface_addr)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *s;
s = wpas_p2p_get_persistent(wpa_s, addr, ssid, ssid_len);
if (s) {
os_memcpy(ret_ssid, s->ssid, s->ssid_len);
*ret_ssid_len = s->ssid_len;
os_memcpy(go_dev_addr, s->bssid, ETH_ALEN);
if (s->mode != WPAS_MODE_P2P_GO) {
os_memset(intended_iface_addr, 0, ETH_ALEN);
} else if (wpas_p2p_create_iface(wpa_s)) {
if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO))
return 0;
os_memcpy(intended_iface_addr,
wpa_s->pending_interface_addr, ETH_ALEN);
} else {
os_memcpy(intended_iface_addr, wpa_s->own_addr,
ETH_ALEN);
}
return 1;
}
return 0;
}
static int wpas_get_go_info(void *ctx, u8 *intended_addr,
u8 *ssid, size_t *ssid_len, int *group_iface,
unsigned int *freq)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_supplicant *go;
struct wpa_ssid *s;
/*
* group_iface will be set to 1 only if a dedicated interface for P2P
* role is required. First, we try to reuse an active GO. However,
* if it is not present, we will try to reactivate an existing
* persistent group and set group_iface to 1, so the caller will know
* that the pending interface should be used.
*/
*group_iface = 0;
if (freq)
*freq = 0;
go = wpas_p2p_get_go_group(wpa_s);
if (!go) {
s = wpas_p2p_get_persistent_go(wpa_s);
*group_iface = wpas_p2p_create_iface(wpa_s);
if (s)
os_memcpy(intended_addr, s->bssid, ETH_ALEN);
else
return 0;
} else {
s = go->current_ssid;
os_memcpy(intended_addr, go->own_addr, ETH_ALEN);
if (freq)
*freq = go->assoc_freq;
}
os_memcpy(ssid, s->ssid, s->ssid_len);
*ssid_len = s->ssid_len;
return 1;
}
static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go,
const u8 *ssid, size_t ssid_len)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *s;
int save_config = 0;
size_t i;
/* Start with our first choice of Persistent Groups */
while ((s = wpas_p2p_get_persistent(wpa_s, peer, NULL, 0))) {
if (go && ssid && ssid_len &&
s->ssid_len == ssid_len &&
os_memcmp(go, s->bssid, ETH_ALEN) == 0 &&
os_memcmp(ssid, s->ssid, ssid_len) == 0)
break;
/* Remove stale persistent group */
if (s->mode != WPAS_MODE_P2P_GO || s->num_p2p_clients <= 1) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Remove stale persistent group id=%d",
s->id);
wpas_notify_persistent_group_removed(wpa_s, s);
wpa_config_remove_network(wpa_s->conf, s->id);
save_config = 1;
continue;
}
for (i = 0; i < s->num_p2p_clients; i++) {
if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
peer, ETH_ALEN) != 0)
continue;
os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
(s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
break;
}
s->num_p2p_clients--;
save_config = 1;
}
if (save_config)
p2p_config_write(wpa_s);
/* Return TRUE if valid SSID remains */
return s != NULL;
}
static void wpas_p2ps_get_feat_cap_str(char *buf, size_t buf_len,
const u8 *feat_cap, size_t feat_cap_len)
{
static const char pref[] = " feature_cap=";
int ret;
buf[0] = '\0';
/*
* We expect a feature capability to contain at least one byte to be
* reported. The string buffer provided by the caller function is
* expected to be big enough to contain all bytes of the attribute for
* known specifications. This function truncates the reported bytes if
* the feature capability data exceeds the string buffer size.
*/
if (!feat_cap || !feat_cap_len || buf_len < sizeof(pref) + 2)
return;
os_memcpy(buf, pref, sizeof(pref));
ret = wpa_snprintf_hex(&buf[sizeof(pref) - 1],
buf_len - sizeof(pref) + 1,
feat_cap, feat_cap_len);
if (ret != (2 * (int) feat_cap_len))
wpa_printf(MSG_WARNING, "P2PS feature_cap bytes truncated");
}
static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
const u8 *adv_mac, const u8 *ses_mac,
const u8 *grp_mac, u32 adv_id, u32 ses_id,
u8 conncap, int passwd_id,
const u8 *persist_ssid,
size_t persist_ssid_size, int response_done,
int prov_start, const char *session_info,
const u8 *feat_cap, size_t feat_cap_len,
unsigned int freq,
const u8 *group_ssid, size_t group_ssid_len)
{
struct wpa_supplicant *wpa_s = ctx;
u8 mac[ETH_ALEN];
struct wpa_ssid *persistent_go, *stale, *s = NULL;
int save_config = 0;
struct wpa_supplicant *go_wpa_s;
char feat_cap_str[256];
if (!dev)
return;
os_memset(mac, 0, ETH_ALEN);
if (!adv_mac)
adv_mac = mac;
if (!ses_mac)
ses_mac = mac;
if (!grp_mac)
grp_mac = mac;
wpas_p2ps_get_feat_cap_str(feat_cap_str, sizeof(feat_cap_str),
feat_cap, feat_cap_len);
if (prov_start) {
if (session_info == NULL) {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_START MACSTR
" adv_id=%x conncap=%x"
" adv_mac=" MACSTR
" session=%x mac=" MACSTR
" dev_passwd_id=%d%s",
MAC2STR(dev), adv_id, conncap,
MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
passwd_id, feat_cap_str);
} else {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_START MACSTR
" adv_id=%x conncap=%x"
" adv_mac=" MACSTR
" session=%x mac=" MACSTR
" dev_passwd_id=%d info='%s'%s",
MAC2STR(dev), adv_id, conncap,
MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
passwd_id, session_info, feat_cap_str);
}
return;
}
go_wpa_s = wpas_p2p_get_go_group(wpa_s);
persistent_go = wpas_p2p_get_persistent_go(wpa_s);
if (status && status != P2P_SC_SUCCESS_DEFERRED) {
if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
if (persistent_go && !persistent_go->num_p2p_clients) {
/* remove empty persistent GO */
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Remove empty persistent group id=%d",
persistent_go->id);
wpas_notify_persistent_group_removed(wpa_s,
persistent_go);
wpa_config_remove_network(wpa_s->conf,
persistent_go->id);
}
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
" status=%d"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR "%s",
MAC2STR(dev), status,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac), feat_cap_str);
return;
}
/* Clean up stale persistent groups with this device */
if (persist_ssid && persist_ssid_size)
s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
persist_ssid_size);
if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO &&
is_zero_ether_addr(grp_mac)) {
wpa_dbg(wpa_s, MSG_ERROR,
"P2P: Peer device is a GO in a persistent group, but it did not provide the intended MAC address");
return;
}
for (;;) {
stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0);
if (!stale)
break;
if (s && s->ssid_len == stale->ssid_len &&
os_memcmp(stale->bssid, s->bssid, ETH_ALEN) == 0 &&
os_memcmp(stale->ssid, s->ssid, s->ssid_len) == 0)
break;
/* Remove stale persistent group */
if (stale->mode != WPAS_MODE_P2P_GO ||
stale->num_p2p_clients <= 1) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Remove stale persistent group id=%d",
stale->id);
wpas_notify_persistent_group_removed(wpa_s, stale);
wpa_config_remove_network(wpa_s->conf, stale->id);
} else {
size_t i;
for (i = 0; i < stale->num_p2p_clients; i++) {
if (os_memcmp(stale->p2p_client_list +
i * ETH_ALEN,
dev, ETH_ALEN) == 0) {
os_memmove(stale->p2p_client_list +
i * ETH_ALEN,
stale->p2p_client_list +
(i + 1) * ETH_ALEN,
(stale->num_p2p_clients -
i - 1) * ETH_ALEN);
break;
}
}
stale->num_p2p_clients--;
}
save_config = 1;
}
if (save_config)
p2p_config_write(wpa_s);
if (s) {
if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
if (persistent_go && s != persistent_go &&
!persistent_go->num_p2p_clients) {
/* remove empty persistent GO */
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Remove empty persistent group id=%d",
persistent_go->id);
wpas_notify_persistent_group_removed(wpa_s,
persistent_go);
wpa_config_remove_network(wpa_s->conf,
persistent_go->id);
/* Save config */
}
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
" status=%d"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
" persist=%d%s",
MAC2STR(dev), status,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac), s->id, feat_cap_str);
return;
}
wpa_s->global->pending_p2ps_group = 0;
wpa_s->global->pending_p2ps_group_freq = 0;
if (conncap == P2PS_SETUP_GROUP_OWNER) {
/*
* We need to copy the interface name. Simply saving a
* pointer isn't enough, since if we use pending_interface_name
* it will be overwritten when the group is added.
*/
char go_ifname[100];
go_ifname[0] = '\0';
if (!go_wpa_s) {
if (!response_done) {
wpa_s->global->pending_p2ps_group = 1;
wpa_s->global->pending_p2ps_group_freq = freq;
}
if (!wpas_p2p_create_iface(wpa_s))
os_memcpy(go_ifname, wpa_s->ifname,
sizeof(go_ifname));
else if (wpa_s->pending_interface_name[0])
os_memcpy(go_ifname,
wpa_s->pending_interface_name,
sizeof(go_ifname));
if (!go_ifname[0]) {
wpas_p2ps_prov_complete(
wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
dev, adv_mac, ses_mac,
grp_mac, adv_id, ses_id, 0, 0,
NULL, 0, 0, 0, NULL, NULL, 0, 0,
NULL, 0);
return;
}
/* If PD Resp complete, start up the GO */
if (response_done && persistent_go) {
wpas_p2p_group_add_persistent(
wpa_s, persistent_go,
0, 0, freq, 0, 0, 0, 0, 0, 0, NULL,
persistent_go->mode ==
WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
0, 0, false);
} else if (response_done) {
wpas_p2p_group_add(wpa_s, 1, freq,
0, 0, 0, 0, 0, 0, false);
}
if (passwd_id == DEV_PW_P2PS_DEFAULT) {
os_memcpy(wpa_s->p2ps_join_addr, grp_mac,
ETH_ALEN);
wpa_s->p2ps_method_config_any = 1;
}
} else if (passwd_id == DEV_PW_P2PS_DEFAULT) {
os_memcpy(go_ifname, go_wpa_s->ifname,
sizeof(go_ifname));
if (is_zero_ether_addr(grp_mac)) {
wpa_dbg(go_wpa_s, MSG_DEBUG,
"P2P: Setting PIN-1 for ANY");
wpa_supplicant_ap_wps_pin(go_wpa_s, NULL,
"12345670", NULL, 0,
0);
} else {
wpa_dbg(go_wpa_s, MSG_DEBUG,
"P2P: Setting PIN-1 for " MACSTR,
MAC2STR(grp_mac));
wpa_supplicant_ap_wps_pin(go_wpa_s, grp_mac,
"12345670", NULL, 0,
0);
}
os_memcpy(wpa_s->p2ps_join_addr, grp_mac, ETH_ALEN);
wpa_s->p2ps_method_config_any = 1;
}
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
" status=%d conncap=%x"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
" dev_passwd_id=%d go=%s%s",
MAC2STR(dev), status, conncap,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
passwd_id, go_ifname, feat_cap_str);
return;
}
if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
if (persistent_go && !persistent_go->num_p2p_clients) {
/* remove empty persistent GO */
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Remove empty persistent group id=%d",
persistent_go->id);
wpas_notify_persistent_group_removed(wpa_s, persistent_go);
wpa_config_remove_network(wpa_s->conf, persistent_go->id);
}
if (conncap == P2PS_SETUP_CLIENT) {
char ssid_hex[32 * 2 + 1];
if (group_ssid)
wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex),
group_ssid, group_ssid_len);
else
ssid_hex[0] = '\0';
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
" status=%d conncap=%x"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
" dev_passwd_id=%d join=" MACSTR "%s%s%s",
MAC2STR(dev), status, conncap,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
passwd_id, MAC2STR(grp_mac), feat_cap_str,
group_ssid ? " group_ssid=" : "", ssid_hex);
} else {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
" status=%d conncap=%x"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
" dev_passwd_id=%d%s",
MAC2STR(dev), status, conncap,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
passwd_id, feat_cap_str);
}
}
static int _wpas_p2p_in_progress(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
return wpas_p2p_in_progress(wpa_s);
}
static int wpas_prov_disc_resp_cb(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *persistent_go;
unsigned int freq;
if (!wpa_s->global->pending_p2ps_group)
return 0;
freq = wpa_s->global->pending_p2ps_group_freq;
wpa_s->global->pending_p2ps_group_freq = 0;
wpa_s->global->pending_p2ps_group = 0;
if (wpas_p2p_get_go_group(wpa_s))
return 0;
persistent_go = wpas_p2p_get_persistent_go(wpa_s);
if (persistent_go) {
wpas_p2p_group_add_persistent(
wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, 0, 0,
NULL,
persistent_go->mode == WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0,
is_p2p_allow_6ghz(wpa_s->global->p2p));
} else {
wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0, 0, 0,
is_p2p_allow_6ghz(wpa_s->global->p2p));
}
return 1;
}
static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
unsigned int *len,
unsigned int *freq_list)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO :
WPA_IF_P2P_CLIENT, len, freq_list);
}
int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s)
{
u8 addr[ETH_ALEN] = {0};
if (wpa_s->conf->p2p_device_random_mac_addr == 0)
return 0;
if (wpa_s->conf->p2p_device_random_mac_addr == 2) {
if (is_zero_ether_addr(
wpa_s->conf->p2p_device_persistent_mac_addr) &&
!is_zero_ether_addr(wpa_s->own_addr)) {
os_memcpy(wpa_s->conf->p2p_device_persistent_mac_addr,
wpa_s->own_addr, ETH_ALEN);
}
return 0;
}
if (!wpa_s->conf->ssid) {
if (random_mac_addr(addr) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Failed to generate random MAC address");
return -EINVAL;
}
/* Store generated MAC address. */
os_memcpy(wpa_s->conf->p2p_device_persistent_mac_addr, addr,
ETH_ALEN);
} else {
/* If there are existing saved groups, restore last MAC address.
* if there is no last used MAC address, the last one is
* factory MAC. */
if (is_zero_ether_addr(
wpa_s->conf->p2p_device_persistent_mac_addr))
return 0;
os_memcpy(addr, wpa_s->conf->p2p_device_persistent_mac_addr,
ETH_ALEN);
wpa_msg(wpa_s, MSG_DEBUG, "Restore last used MAC address.");
}
if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Failed to set random MAC address");
return -EINVAL;
}
if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Could not update MAC address information");
return -EINVAL;
}
wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
MAC2STR(addr));
return 0;
}
/**
* wpas_p2p_init - Initialize P2P module for %wpa_supplicant
* @global: Pointer to global data from wpa_supplicant_init()
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
* Returns: 0 on success, -1 on failure
*/
int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
{
struct p2p_config p2p;
int i;
if (wpa_s->conf->p2p_disabled)
return 0;
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
return 0;
if (global->p2p)
return 0;
if (wpas_p2p_mac_setup(wpa_s) < 0) {
wpa_msg(wpa_s, MSG_ERROR,
"Failed to initialize P2P random MAC address.");
return -1;
}
os_memset(&p2p, 0, sizeof(p2p));
p2p.cb_ctx = wpa_s;
p2p.debug_print = wpas_p2p_debug_print;
p2p.p2p_scan = wpas_p2p_scan;
p2p.send_action = wpas_send_action;
p2p.send_action_done = wpas_send_action_done;
p2p.go_neg_completed = wpas_go_neg_completed;
p2p.go_neg_req_rx = wpas_go_neg_req_rx;
p2p.dev_found = wpas_dev_found;
p2p.dev_lost = wpas_dev_lost;
p2p.find_stopped = wpas_find_stopped;
p2p.start_listen = wpas_start_listen;
p2p.stop_listen = wpas_stop_listen;
p2p.send_probe_resp = wpas_send_probe_resp;
p2p.sd_request = wpas_sd_request;
p2p.sd_response = wpas_sd_response;
p2p.prov_disc_req = wpas_prov_disc_req;
p2p.prov_disc_resp = wpas_prov_disc_resp;
p2p.prov_disc_fail = wpas_prov_disc_fail;
p2p.invitation_process = wpas_invitation_process;
p2p.invitation_received = wpas_invitation_received;
p2p.invitation_result = wpas_invitation_result;
p2p.get_noa = wpas_get_noa;
p2p.go_connected = wpas_go_connected;
p2p.presence_resp = wpas_presence_resp;
p2p.is_concurrent_session_active = wpas_is_concurrent_session_active;
p2p.is_p2p_in_progress = _wpas_p2p_in_progress;
p2p.get_persistent_group = wpas_get_persistent_group;
p2p.get_go_info = wpas_get_go_info;
p2p.remove_stale_groups = wpas_remove_stale_groups;
p2p.p2ps_prov_complete = wpas_p2ps_prov_complete;
p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb;
p2p.p2ps_group_capability = p2ps_group_capability;
p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list;
p2p.p2p_6ghz_disable = wpa_s->conf->p2p_6ghz_disable;
os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
p2p.dev_name = wpa_s->conf->device_name;
p2p.manufacturer = wpa_s->conf->manufacturer;
p2p.model_name = wpa_s->conf->model_name;
p2p.model_number = wpa_s->conf->model_number;
p2p.serial_number = wpa_s->conf->serial_number;
if (wpa_s->wps) {
os_memcpy(p2p.uuid, wpa_s->wps->uuid, 16);
p2p.config_methods = wpa_s->wps->config_methods;
}
if (wpas_p2p_setup_channels(wpa_s, &p2p.channels, &p2p.cli_channels,
p2p.p2p_6ghz_disable)) {
wpa_printf(MSG_ERROR,
"P2P: Failed to configure supported channel list");
return -1;
}
if (wpa_s->conf->p2p_listen_reg_class &&
wpa_s->conf->p2p_listen_channel) {
p2p.reg_class = wpa_s->conf->p2p_listen_reg_class;
p2p.channel = wpa_s->conf->p2p_listen_channel;
p2p.channel_forced = 1;
} else {
/*
* Pick one of the social channels randomly as the listen
* channel.
*/
if (p2p_config_get_random_social(&p2p, &p2p.reg_class,
&p2p.channel,
&global->p2p_go_avoid_freq,
&global->p2p_disallow_freq) !=
0) {
wpa_printf(MSG_INFO,
"P2P: No social channels supported by the driver - do not enable P2P");
return 0;
}
p2p.channel_forced = 0;
}
wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d:%d",
p2p.reg_class, p2p.channel);
if (wpa_s->conf->p2p_oper_reg_class &&
wpa_s->conf->p2p_oper_channel) {
p2p.op_reg_class = wpa_s->conf->p2p_oper_reg_class;
p2p.op_channel = wpa_s->conf->p2p_oper_channel;
p2p.cfg_op_channel = 1;
wpa_printf(MSG_DEBUG, "P2P: Configured operating channel: "
"%d:%d", p2p.op_reg_class, p2p.op_channel);
} else {
/*
* Use random operation channel from 2.4 GHz band social
* channels (1, 6, 11) or band 60 GHz social channel (2) if no
* other preference is indicated.
*/
if (p2p_config_get_random_social(&p2p, &p2p.op_reg_class,
&p2p.op_channel, NULL,
NULL) != 0) {
wpa_printf(MSG_INFO,
"P2P: Failed to select random social channel as operation channel");
p2p.op_reg_class = 0;
p2p.op_channel = 0;
/* This will be overridden during group setup in
* p2p_prepare_channel(), so allow setup to continue. */
}
p2p.cfg_op_channel = 0;
wpa_printf(MSG_DEBUG, "P2P: Random operating channel: "
"%d:%d", p2p.op_reg_class, p2p.op_channel);
}
if (wpa_s->conf->p2p_pref_chan && wpa_s->conf->num_p2p_pref_chan) {
p2p.pref_chan = wpa_s->conf->p2p_pref_chan;
p2p.num_pref_chan = wpa_s->conf->num_p2p_pref_chan;
}
if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
os_memcpy(p2p.country, wpa_s->conf->country, 2);
p2p.country[2] = 0x04;
} else
os_memcpy(p2p.country, "XX\x04", 3);
os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type,
WPS_DEV_TYPE_LEN);
p2p.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
os_memcpy(p2p.sec_dev_type, wpa_s->conf->sec_device_type,
p2p.num_sec_dev_types * WPS_DEV_TYPE_LEN);
p2p.concurrent_operations = !!(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_P2P_CONCURRENT);
p2p.max_peers = 100;
if (wpa_s->conf->p2p_ssid_postfix) {
p2p.ssid_postfix_len =
os_strlen(wpa_s->conf->p2p_ssid_postfix);
if (p2p.ssid_postfix_len > sizeof(p2p.ssid_postfix))
p2p.ssid_postfix_len = sizeof(p2p.ssid_postfix);
os_memcpy(p2p.ssid_postfix, wpa_s->conf->p2p_ssid_postfix,
p2p.ssid_postfix_len);
}
p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss;
p2p.max_listen = wpa_s->max_remain_on_chan;
if (wpa_s->conf->p2p_passphrase_len >= 8 &&
wpa_s->conf->p2p_passphrase_len <= 63)
p2p.passphrase_len = wpa_s->conf->p2p_passphrase_len;
else
p2p.passphrase_len = 8;
global->p2p = p2p_init(&p2p);
if (global->p2p == NULL)
return -1;
global->p2p_init_wpa_s = wpa_s;
for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
if (wpa_s->conf->wps_vendor_ext[i] == NULL)
continue;
p2p_add_wps_vendor_extension(
global->p2p, wpa_s->conf->wps_vendor_ext[i]);
}
p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq);
return 0;
}
/**
* wpas_p2p_deinit - Deinitialize per-interface P2P data
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
*
* This function deinitialize per-interface P2P data.
*/
void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
{
if (wpa_s->driver && wpa_s->drv_priv)
wpa_drv_probe_req_report(wpa_s, 0);
if (wpa_s->go_params) {
/* Clear any stored provisioning info */
p2p_clear_provisioning_info(
wpa_s->global->p2p,
wpa_s->go_params->peer_device_addr);
}
os_free(wpa_s->go_params);
wpa_s->go_params = NULL;
eloop_cancel_timeout(wpas_p2p_psk_failure_removal, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
wpa_s->global->p2p_long_listen = 0;
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
wpas_p2p_remove_pending_group_interface(wpa_s);
eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
wpas_p2p_listen_work_done(wpa_s);
if (wpa_s->p2p_send_action_work) {
os_free(wpa_s->p2p_send_action_work->ctx);
radio_work_done(wpa_s->p2p_send_action_work);
wpa_s->p2p_send_action_work = NULL;
}
eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL);
wpabuf_free(wpa_s->p2p_oob_dev_pw);
wpa_s->p2p_oob_dev_pw = NULL;
os_free(wpa_s->p2p_group_common_freqs);
wpa_s->p2p_group_common_freqs = NULL;
wpa_s->p2p_group_common_freqs_num = 0;
/* TODO: remove group interface from the driver if this wpa_s instance
* is on top of a P2P group interface */
}
/**
* wpas_p2p_deinit_global - Deinitialize global P2P module
* @global: Pointer to global data from wpa_supplicant_init()
*
* This function deinitializes the global (per device) P2P module.
*/
static void wpas_p2p_deinit_global(struct wpa_global *global)
{
struct wpa_supplicant *wpa_s, *tmp;
wpa_s = global->ifaces;
wpas_p2p_service_flush(global->p2p_init_wpa_s);
/* Remove remaining P2P group interfaces */
while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
wpa_s = wpa_s->next;
while (wpa_s) {
tmp = global->ifaces;
while (tmp &&
(tmp == wpa_s ||
tmp->p2p_group_interface == NOT_P2P_GROUP_INTERFACE)) {
tmp = tmp->next;
}
if (tmp == NULL)
break;
/* Disconnect from the P2P group and deinit the interface */
wpas_p2p_disconnect(tmp);
}
/*
* Deinit GO data on any possibly remaining interface (if main
* interface is used as GO).
*/
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (wpa_s->ap_iface)
wpas_p2p_group_deinit(wpa_s);
}
p2p_deinit(global->p2p);
global->p2p = NULL;
global->p2p_init_wpa_s = NULL;
}
static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s)
{
if (wpa_s->conf->p2p_no_group_iface)
return 0; /* separate interface disabled per configuration */
if (wpa_s->drv_flags &
(WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE |
WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P))
return 1; /* P2P group requires a new interface in every case
*/
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CONCURRENT))
return 0; /* driver does not support concurrent operations */
if (wpa_s->global->ifaces->next)
return 1; /* more that one interface already in use */
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
return 1; /* this interface is already in use */
return 0;
}
static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
const u8 *peer_addr,
enum p2p_wps_method wps_method,
int go_intent, const u8 *own_interface_addr,
unsigned int force_freq, int persistent_group,
struct wpa_ssid *ssid, unsigned int pref_freq)
{
if (persistent_group && wpa_s->conf->persistent_reconnect)
persistent_group = 2;
/*
* Increase GO config timeout if HT40 is used since it takes some time
* to scan channels for coex purposes before the BSS can be started.
*/
p2p_set_config_timeout(wpa_s->global->p2p,
wpa_s->p2p_go_ht40 ? 255 : 100, 20);
return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method,
go_intent, own_interface_addr, force_freq,
persistent_group, ssid ? ssid->ssid : NULL,
ssid ? ssid->ssid_len : 0,
wpa_s->p2p_pd_before_go_neg, pref_freq,
wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
0);
}
static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
const u8 *peer_addr,
enum p2p_wps_method wps_method,
int go_intent, const u8 *own_interface_addr,
unsigned int force_freq, int persistent_group,
struct wpa_ssid *ssid, unsigned int pref_freq)
{
if (persistent_group && wpa_s->conf->persistent_reconnect)
persistent_group = 2;
return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
go_intent, own_interface_addr, force_freq,
persistent_group, ssid ? ssid->ssid : NULL,
ssid ? ssid->ssid_len : 0, pref_freq,
wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
0);
}
static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s)
{
wpa_s->p2p_join_scan_count++;
wpa_printf(MSG_DEBUG, "P2P: Join scan attempt %d",
wpa_s->p2p_join_scan_count);
if (wpa_s->p2p_join_scan_count > P2P_MAX_JOIN_SCAN_ATTEMPTS) {
wpa_printf(MSG_DEBUG, "P2P: Failed to find GO " MACSTR
" for join operationg - stop join attempt",
MAC2STR(wpa_s->pending_join_iface_addr));
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
if (wpa_s->p2p_auto_pd) {
wpa_s->p2p_auto_pd = 0;
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_PROV_DISC_FAILURE
" p2p_dev_addr=" MACSTR " status=N/A",
MAC2STR(wpa_s->pending_join_dev_addr));
return;
}
if (wpa_s->p2p_fallback_to_go_neg) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Join operation failed - fall back to GO Negotiation");
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_FALLBACK_TO_GO_NEG
"reason=join-failed");
wpas_p2p_fallback_to_go_neg(wpa_s, 0);
return;
}
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_GROUP_FORMATION_FAILURE);
wpas_notify_p2p_group_formation_failure(wpa_s, "");
}
}
static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq)
{
int res;
unsigned int num, i;
struct wpa_used_freq_data *freqs;
if (wpas_p2p_num_unused_channels(wpa_s) > 0) {
/* Multiple channels are supported and not all are in use */
return 0;
}
freqs = os_calloc(wpa_s->num_multichan_concurrent,
sizeof(struct wpa_used_freq_data));
if (!freqs)
return 1;
num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
wpa_s->num_multichan_concurrent);
for (i = 0; i < num; i++) {
if (freqs[i].freq == freq) {
wpa_printf(MSG_DEBUG, "P2P: Frequency %d MHz in use by another virtual interface and can be used",
freq);
res = 0;
goto exit_free;
}
}
wpa_printf(MSG_DEBUG, "P2P: No valid operating frequencies");
res = 1;
exit_free:
os_free(freqs);
return res;
}
static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s,
const u8 *peer_dev_addr)
{
struct wpa_bss *bss;
int updated;
bss = wpa_bss_get_p2p_dev_addr(wpa_s, peer_dev_addr);
if (bss == NULL)
return -1;
if (bss->last_update_idx < wpa_s->bss_update_idx) {
wpa_printf(MSG_DEBUG, "P2P: Peer BSS entry not updated in the "
"last scan");
return 0;
}
updated = os_reltime_before(&wpa_s->p2p_auto_started,
&bss->last_update);
wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at "
"%ld.%06ld (%supdated in last scan)",
bss->last_update.sec, bss->last_update.usec,
updated ? "": "not ");
return updated;
}
static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res)
{
struct wpa_bss *bss = NULL;
int freq;
u8 iface_addr[ETH_ALEN];
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
if (wpa_s->global->p2p_disabled)
return;
wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for %sjoin",
scan_res ? (int) scan_res->num : -1,
wpa_s->p2p_auto_join ? "auto_" : "");
if (scan_res)
wpas_p2p_scan_res_handler(wpa_s, scan_res);
if (wpa_s->p2p_auto_pd) {
int join = wpas_p2p_peer_go(wpa_s,
wpa_s->pending_join_dev_addr);
if (join == 0 &&
wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) {
wpa_s->auto_pd_scan_retry++;
bss = wpa_bss_get_bssid_latest(
wpa_s, wpa_s->pending_join_dev_addr);
if (bss) {
freq = bss->freq;
wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for "
"the peer " MACSTR " at %d MHz",
wpa_s->auto_pd_scan_retry,
MAC2STR(wpa_s->
pending_join_dev_addr),
freq);
wpas_p2p_join_scan_req(wpa_s, freq, NULL, 0);
return;
}
}
if (join < 0)
join = 0;
wpa_s->p2p_auto_pd = 0;
wpa_s->pending_pd_use = join ? AUTO_PD_JOIN : AUTO_PD_GO_NEG;
wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d",
MAC2STR(wpa_s->pending_join_dev_addr), join);
if (p2p_prov_disc_req(wpa_s->global->p2p,
wpa_s->pending_join_dev_addr, NULL,
wpa_s->pending_pd_config_methods, join,
0, wpa_s->user_initiated_pd) < 0) {
wpa_s->p2p_auto_pd = 0;
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_PROV_DISC_FAILURE
" p2p_dev_addr=" MACSTR " status=N/A",
MAC2STR(wpa_s->pending_join_dev_addr));
}
return;
}
if (wpa_s->p2p_auto_join) {
int join = wpas_p2p_peer_go(wpa_s,
wpa_s->pending_join_dev_addr);
if (join < 0) {
wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be "
"running a GO -> use GO Negotiation");
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_FALLBACK_TO_GO_NEG
"reason=peer-not-running-GO");
wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
wpa_s->p2p_pin, wpa_s->p2p_wps_method,
wpa_s->p2p_persistent_group, 0, 0, 0,
wpa_s->p2p_go_intent,
wpa_s->p2p_connect_freq,
wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_persistent_id,
wpa_s->p2p_pd_before_go_neg,
wpa_s->p2p_go_ht40,
wpa_s->p2p_go_vht,
wpa_s->p2p_go_max_oper_chwidth,
wpa_s->p2p_go_he,
wpa_s->p2p_go_edmg,
NULL, 0,
is_p2p_allow_6ghz(wpa_s->global->p2p));
return;
}
wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> "
"try to join the group", join ? "" :
" in older scan");
if (!join) {
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED);
wpa_s->p2p_fallback_to_go_neg = 1;
}
}
freq = p2p_get_oper_freq(wpa_s->global->p2p,
wpa_s->pending_join_iface_addr);
if (freq < 0 &&
p2p_get_interface_addr(wpa_s->global->p2p,
wpa_s->pending_join_dev_addr,
iface_addr) == 0 &&
os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0
&& !wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr)) {
wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface "
"address for join from " MACSTR " to " MACSTR
" based on newly discovered P2P peer entry",
MAC2STR(wpa_s->pending_join_iface_addr),
MAC2STR(iface_addr));
os_memcpy(wpa_s->pending_join_iface_addr, iface_addr,
ETH_ALEN);
freq = p2p_get_oper_freq(wpa_s->global->p2p,
wpa_s->pending_join_iface_addr);
}
if (freq >= 0) {
wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
"from P2P peer table: %d MHz", freq);
}
if (wpa_s->p2p_join_ssid_len) {
wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
MACSTR " and SSID %s",
MAC2STR(wpa_s->pending_join_iface_addr),
wpa_ssid_txt(wpa_s->p2p_join_ssid,
wpa_s->p2p_join_ssid_len));
bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
wpa_s->p2p_join_ssid,
wpa_s->p2p_join_ssid_len);
} else if (!bss) {
wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
MACSTR, MAC2STR(wpa_s->pending_join_iface_addr));
bss = wpa_bss_get_bssid_latest(wpa_s,
wpa_s->pending_join_iface_addr);
}
if (bss) {
u8 dev_addr[ETH_ALEN];
freq = bss->freq;
wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
"from BSS table: %d MHz (SSID %s)", freq,
wpa_ssid_txt(bss->ssid, bss->ssid_len));
if (p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len,
dev_addr) == 0 &&
os_memcmp(wpa_s->pending_join_dev_addr,
wpa_s->pending_join_iface_addr, ETH_ALEN) == 0 &&
os_memcmp(dev_addr, wpa_s->pending_join_dev_addr,
ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG,
"P2P: Update target GO device address based on BSS entry: " MACSTR " (was " MACSTR ")",
MAC2STR(dev_addr),
MAC2STR(wpa_s->pending_join_dev_addr));
os_memcpy(wpa_s->pending_join_dev_addr, dev_addr,
ETH_ALEN);
}
}
if (freq > 0) {
u16 method;
if (wpas_check_freq_conflict(wpa_s, freq) > 0) {
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_GROUP_FORMATION_FAILURE
"reason=FREQ_CONFLICT");
wpas_notify_p2p_group_formation_failure(
wpa_s, "FREQ_CONFLICT");
return;
}
wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
"prior to joining an existing group (GO " MACSTR
" freq=%u MHz)",
MAC2STR(wpa_s->pending_join_dev_addr), freq);
wpa_s->pending_pd_before_join = 1;
switch (wpa_s->pending_join_wps_method) {
case WPS_PIN_DISPLAY:
method = WPS_CONFIG_KEYPAD;
break;
case WPS_PIN_KEYPAD:
method = WPS_CONFIG_DISPLAY;
break;
case WPS_PBC:
method = WPS_CONFIG_PUSHBUTTON;
break;
case WPS_P2PS:
method = WPS_CONFIG_P2PS;
break;
default:
method = 0;
break;
}
if ((p2p_get_provisioning_info(wpa_s->global->p2p,
wpa_s->pending_join_dev_addr) ==
method)) {
/*
* We have already performed provision discovery for
* joining the group. Proceed directly to join
* operation without duplicated provision discovery. */
wpa_printf(MSG_DEBUG, "P2P: Provision discovery "
"with " MACSTR " already done - proceed to "
"join",
MAC2STR(wpa_s->pending_join_dev_addr));
wpa_s->pending_pd_before_join = 0;
goto start;
}
if (p2p_prov_disc_req(wpa_s->global->p2p,
wpa_s->pending_join_dev_addr,
NULL, method, 1,
freq, wpa_s->user_initiated_pd) < 0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
"Discovery Request before joining an "
"existing group");
wpa_s->pending_pd_before_join = 0;
goto start;
}
return;
}
wpa_printf(MSG_DEBUG, "P2P: Failed to find BSS/GO - try again later");
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
wpas_p2p_check_join_scan_limit(wpa_s);
return;
start:
/* Start join operation immediately */
wpas_p2p_join_start(wpa_s, 0, wpa_s->p2p_join_ssid,
wpa_s->p2p_join_ssid_len);
}
static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
const u8 *ssid, size_t ssid_len)
{
int ret;
struct wpa_driver_scan_params params;
struct wpabuf *wps_ie, *ies;
size_t ielen;
int freqs[2] = { 0, 0 };
unsigned int bands;
os_memset(&params, 0, sizeof(params));
/* P2P Wildcard SSID */
params.num_ssids = 1;
if (ssid && ssid_len) {
params.ssids[0].ssid = ssid;
params.ssids[0].ssid_len = ssid_len;
os_memcpy(wpa_s->p2p_join_ssid, ssid, ssid_len);
wpa_s->p2p_join_ssid_len = ssid_len;
} else {
params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
wpa_s->p2p_join_ssid_len = 0;
}
wpa_s->wps->dev.p2p = 1;
wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev,
wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0,
NULL);
if (wps_ie == NULL) {
wpas_p2p_scan_res_join(wpa_s, NULL);
return;
}
if (!freq) {
int oper_freq;
/*
* If freq is not provided, check the operating freq of the GO
* and use a single channel scan on if possible.
*/
oper_freq = p2p_get_oper_freq(wpa_s->global->p2p,
wpa_s->pending_join_iface_addr);
if (oper_freq > 0)
freq = oper_freq;
}
if (freq > 0) {
freqs[0] = freq;
params.freqs = freqs;
} else if (wpa_s->conf->p2p_6ghz_disable ||
!is_p2p_allow_6ghz(wpa_s->global->p2p)) {
wpa_printf(MSG_DEBUG,
"P2P: 6 GHz disabled - update the scan frequency list");
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, &params,
0);
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, &params,
0);
}
ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
if (ies == NULL) {
wpabuf_free(wps_ie);
wpas_p2p_scan_res_join(wpa_s, NULL);
return;
}
wpabuf_put_buf(ies, wps_ie);
wpabuf_free(wps_ie);
bands = wpas_get_bands(wpa_s, freqs);
p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands);
params.p2p_probe = 1;
params.extra_ies = wpabuf_head(ies);
params.extra_ies_len = wpabuf_len(ies);
if (wpa_s->clear_driver_scan_cache) {
wpa_printf(MSG_DEBUG,
"Request driver to clear scan cache due to local BSS flush");
params.only_new_results = 1;
}
/*
* Run a scan to update BSS table and start Provision Discovery once
* the new scan results become available.
*/
ret = wpa_drv_scan(wpa_s, &params);
if (params.freqs != freqs)
os_free(params.freqs);
if (!ret) {
os_get_reltime(&wpa_s->scan_trigger_time);
wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
wpa_s->own_scan_requested = 1;
wpa_s->clear_driver_scan_cache = 0;
}
wpabuf_free(ies);
if (ret) {
wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - "
"try again later");
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
wpas_p2p_check_join_scan_limit(wpa_s);
}
}
static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpas_p2p_join_scan_req(wpa_s, 0, NULL, 0);
}
static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
const u8 *dev_addr, enum p2p_wps_method wps_method,
int auto_join, int op_freq,
const u8 *ssid, size_t ssid_len)
{
wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
MACSTR " dev " MACSTR " op_freq=%d)%s",
MAC2STR(iface_addr), MAC2STR(dev_addr), op_freq,
auto_join ? " (auto_join)" : "");
if (ssid && ssid_len) {
wpa_printf(MSG_DEBUG, "P2P: Group SSID specified: %s",
wpa_ssid_txt(ssid, ssid_len));
}
wpa_s->p2p_auto_pd = 0;
wpa_s->p2p_auto_join = !!auto_join;
os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
wpa_s->pending_join_wps_method = wps_method;
/* Make sure we are not running find during connection establishment */
wpas_p2p_stop_find(wpa_s);
wpa_s->p2p_join_scan_count = 0;
wpas_p2p_join_scan_req(wpa_s, op_freq, ssid, ssid_len);
return 0;
}
static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
const u8 *ssid, size_t ssid_len)
{
struct wpa_supplicant *group;
struct p2p_go_neg_results res;
struct wpa_bss *bss;
group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
if (group == NULL)
return -1;
if (group != wpa_s) {
os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
sizeof(group->p2p_pin));
group->p2p_wps_method = wpa_s->p2p_wps_method;
}
/*
* Need to mark the current interface for p2p_group_formation
* when a separate group interface is not used. This is needed
* to allow p2p_cancel stop a pending p2p_connect-join.
* wpas_p2p_init_group_interface() addresses this for the case
* where a separate group interface is used.
*/
if (group == wpa_s->parent)
wpa_s->global->p2p_group_formation = group;
group->p2p_in_provisioning = 1;
group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
os_memset(&res, 0, sizeof(res));
os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN);
os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
ETH_ALEN);
res.wps_method = wpa_s->pending_join_wps_method;
if (freq && ssid && ssid_len) {
res.freq = freq;
res.ssid_len = ssid_len;
os_memcpy(res.ssid, ssid, ssid_len);
} else {
if (ssid && ssid_len) {
bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
ssid, ssid_len);
} else {
bss = wpa_bss_get_bssid_latest(
wpa_s, wpa_s->pending_join_iface_addr);
}
if (bss) {
res.freq = bss->freq;
res.ssid_len = bss->ssid_len;
os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)",
bss->freq,
wpa_ssid_txt(bss->ssid, bss->ssid_len));
} else if (ssid && ssid_len) {
res.ssid_len = ssid_len;
os_memcpy(res.ssid, ssid, ssid_len);
wpa_printf(MSG_DEBUG, "P2P: Join target GO (SSID %s)",
wpa_ssid_txt(ssid, ssid_len));
}
}
if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to "
"starting client");
wpa_drv_cancel_remain_on_channel(wpa_s);
wpa_s->off_channel_freq = 0;
wpa_s->roc_waiting_drv_freq = 0;
}
wpas_start_wps_enrollee(group, &res);
/*
* Allow a longer timeout for join-a-running-group than normal 15
* second group formation timeout since the GO may not have authorized
* our connection yet.
*/
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout,
wpa_s, NULL);
return 0;
}
static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
int *force_freq, int *pref_freq, int go,
unsigned int *pref_freq_list,
unsigned int *num_pref_freq)
{
struct wpa_used_freq_data *freqs;
int res, best_freq, num_unused;
unsigned int freq_in_use = 0, num, i, max_pref_freq;
max_pref_freq = *num_pref_freq;
*num_pref_freq = 0;
freqs = os_calloc(wpa_s->num_multichan_concurrent,
sizeof(struct wpa_used_freq_data));
if (!freqs)
return -1;
num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
wpa_s->num_multichan_concurrent);
/*
* It is possible that the total number of used frequencies is bigger
* than the number of frequencies used for P2P, so get the system wide
* number of unused frequencies.
*/
num_unused = wpas_p2p_num_unused_channels(wpa_s);
wpa_printf(MSG_DEBUG,
"P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u num_unused=%d",
freq, wpa_s->num_multichan_concurrent, num, num_unused);
if (freq > 0) {
int ret;
if (go)
ret = p2p_supported_freq(wpa_s->global->p2p, freq);
else
ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq);
if (!ret) {
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
ieee80211_is_dfs(freq, wpa_s->hw.modes,
wpa_s->hw.num_modes)) {
/*
* If freq is a DFS channel and DFS is offloaded
* to the driver, allow P2P GO to use it.
*/
wpa_printf(MSG_DEBUG,
"P2P: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to the driver",
freq);
} else {
wpa_printf(MSG_DEBUG,
"P2P: The forced channel (%u MHz) is not supported for P2P uses",
freq);
res = -3;
goto exit_free;
}
}
for (i = 0; i < num; i++) {
if (freqs[i].freq == freq)
freq_in_use = 1;
}
if (num_unused <= 0 && !freq_in_use) {
wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz as there are no available channels",
freq);
res = -2;
goto exit_free;
}
wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
"requested channel (%u MHz)", freq);
*force_freq = freq;
goto exit_ok;
}
best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) {
enum wpa_driver_if_type iface_type;
if (go)
iface_type = WPA_IF_P2P_GO;
else
iface_type = WPA_IF_P2P_CLIENT;
wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d",
best_freq, go);
res = wpa_drv_get_pref_freq_list(wpa_s, iface_type,
&max_pref_freq,
pref_freq_list);
if (!is_p2p_allow_6ghz(wpa_s->global->p2p))
max_pref_freq = p2p_remove_6ghz_channels(pref_freq_list,
max_pref_freq);
if (!res && max_pref_freq > 0) {
*num_pref_freq = max_pref_freq;
i = 0;
while (i < *num_pref_freq &&
(!p2p_supported_freq(wpa_s->global->p2p,
pref_freq_list[i]) ||
wpas_p2p_disallowed_freq(wpa_s->global,
pref_freq_list[i]))) {
wpa_printf(MSG_DEBUG,
"P2P: preferred_freq_list[%d]=%d is disallowed",
i, pref_freq_list[i]);
i++;
}
if (i != *num_pref_freq) {
best_freq = pref_freq_list[i];
wpa_printf(MSG_DEBUG,
"P2P: Using preferred_freq_list[%d]=%d",
i, best_freq);
} else {
wpa_printf(MSG_DEBUG,
"P2P: All driver preferred frequencies are disallowed for P2P use");
*num_pref_freq = 0;
}
} else {
wpa_printf(MSG_DEBUG,
"P2P: No preferred frequency list available");
}
}
/* We have a candidate frequency to use */
if (best_freq > 0) {
if (*pref_freq == 0 && num_unused > 0) {
wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency (%u MHz) we are already using",
best_freq);
*pref_freq = best_freq;
} else {
wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use",
best_freq);
*force_freq = best_freq;
}
} else if (num_unused > 0) {
wpa_printf(MSG_DEBUG,
"P2P: Current operating channels are not available for P2P. Try to use another channel");
*force_freq = 0;
} else {
wpa_printf(MSG_DEBUG,
"P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group");
res = -2;
goto exit_free;
}
exit_ok:
res = 0;
exit_free:
os_free(freqs);
return res;
}
static bool is_p2p_6ghz_supported(struct wpa_supplicant *wpa_s,
const u8 *peer_addr)
{
if (wpa_s->conf->p2p_6ghz_disable ||
!get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
HOSTAPD_MODE_IEEE80211A, true))
return false;
if (!p2p_wfd_enabled(wpa_s->global->p2p))
return false;
if (peer_addr && !p2p_peer_wfd_enabled(wpa_s->global->p2p, peer_addr))
return false;
return true;
}
static int wpas_p2p_check_6ghz(struct wpa_supplicant *wpa_s,
const u8 *peer_addr, bool allow_6ghz, int freq)
{
if (allow_6ghz && is_p2p_6ghz_supported(wpa_s, peer_addr)) {
wpa_printf(MSG_DEBUG,
"P2P: Allow connection on 6 GHz channels");
p2p_set_6ghz_dev_capab(wpa_s->global->p2p, true);
} else {
if (is_6ghz_freq(freq))
return -2;
p2p_set_6ghz_dev_capab(wpa_s->global->p2p, false);
}
return 0;
}
/**
* wpas_p2p_connect - Request P2P Group Formation to be started
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
* @peer_addr: Address of the peer P2P Device
* @pin: PIN to use during provisioning or %NULL to indicate PBC mode
* @persistent_group: Whether to create a persistent group
* @auto_join: Whether to select join vs. GO Negotiation automatically
* @join: Whether to join an existing group (as a client) instead of starting
* Group Owner negotiation; @peer_addr is BSSID in that case
* @auth: Whether to only authorize the connection instead of doing that and
* initiating Group Owner negotiation
* @go_intent: GO Intent or -1 to use default
* @freq: Frequency for the group or 0 for auto-selection
* @freq2: Center frequency of segment 1 for the GO operating in VHT 80P80 mode
* @persistent_id: Persistent group credentials to use for forcing GO
* parameters or -1 to generate new values (SSID/passphrase)
* @pd: Whether to send Provision Discovery prior to GO Negotiation as an
* interoperability workaround when initiating group formation
* @ht40: Start GO with 40 MHz channel width
* @vht: Start GO with VHT support
* @vht_chwidth: Channel width supported by GO operating with VHT support
* (CHANWIDTH_*).
* @group_ssid: Specific Group SSID for join or %NULL if not set
* @group_ssid_len: Length of @group_ssid in octets
* @allow_6ghz: Allow P2P connection on 6 GHz channels
* Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
* failure, -2 on failure due to channel not currently available,
* -3 if forced channel is not supported
*/
int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *pin, enum p2p_wps_method wps_method,
int persistent_group, int auto_join, int join, int auth,
int go_intent, int freq, unsigned int vht_center_freq2,
int persistent_id, int pd, int ht40, int vht,
unsigned int vht_chwidth, int he, int edmg,
const u8 *group_ssid, size_t group_ssid_len,
bool allow_6ghz)
{
int force_freq = 0, pref_freq = 0;
int ret = 0, res;
enum wpa_driver_if_type iftype;
const u8 *if_addr;
struct wpa_ssid *ssid = NULL;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
if (persistent_id >= 0) {
ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
if (ssid == NULL || ssid->disabled != 2 ||
ssid->mode != WPAS_MODE_P2P_GO)
return -1;
}
if (wpas_p2p_check_6ghz(wpa_s, peer_addr, allow_6ghz, freq))
return -2;
os_free(wpa_s->global->add_psk);
wpa_s->global->add_psk = NULL;
wpa_s->global->p2p_fail_on_wps_complete = 0;
wpa_s->global->pending_p2ps_group = 0;
wpa_s->global->pending_p2ps_group_freq = 0;
wpa_s->p2ps_method_config_any = 0;
if (go_intent < 0)
go_intent = wpa_s->conf->p2p_go_intent;
if (!auth)
wpa_s->global->p2p_long_listen = 0;
wpa_s->p2p_wps_method = wps_method;
wpa_s->p2p_persistent_group = !!persistent_group;
wpa_s->p2p_persistent_id = persistent_id;
wpa_s->p2p_go_intent = go_intent;
wpa_s->p2p_connect_freq = freq;
wpa_s->p2p_fallback_to_go_neg = 0;
wpa_s->p2p_pd_before_go_neg = !!pd;
wpa_s->p2p_go_ht40 = !!ht40;
wpa_s->p2p_go_vht = !!vht;
wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
wpa_s->p2p_go_max_oper_chwidth = vht_chwidth;
wpa_s->p2p_go_he = !!he;
wpa_s->p2p_go_edmg = !!edmg;
if (pin)
os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
else if (wps_method == WPS_PIN_DISPLAY) {
if (wps_generate_pin((unsigned int *) &ret) < 0)
return -1;
res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin),
"%08d", ret);
if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res))
wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0';
wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
wpa_s->p2p_pin);
} else if (wps_method == WPS_P2PS) {
/* Force the P2Ps default PIN to be used */
os_strlcpy(wpa_s->p2p_pin, "12345670", sizeof(wpa_s->p2p_pin));
} else
wpa_s->p2p_pin[0] = '\0';
if (join || auto_join) {
u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
if (auth) {
wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
"connect a running group from " MACSTR,
MAC2STR(peer_addr));
os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
return ret;
}
os_memcpy(dev_addr, peer_addr, ETH_ALEN);
if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
iface_addr) < 0) {
os_memcpy(iface_addr, peer_addr, ETH_ALEN);
p2p_get_dev_addr(wpa_s->global->p2p, peer_addr,
dev_addr);
}
if (auto_join) {
os_get_reltime(&wpa_s->p2p_auto_started);
wpa_printf(MSG_DEBUG, "P2P: Auto join started at "
"%ld.%06ld",
wpa_s->p2p_auto_started.sec,
wpa_s->p2p_auto_started.usec);
}
wpa_s->user_initiated_pd = 1;
if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
auto_join, freq,
group_ssid, group_ssid_len) < 0)
return -1;
return ret;
}
size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
go_intent == 15, pref_freq_list, &size);
if (res)
return res;
wpas_p2p_set_own_freq_preference(wpa_s,
force_freq ? force_freq : pref_freq);
p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
if (wpa_s->create_p2p_iface) {
/* Prepare to add a new interface for the group */
iftype = WPA_IF_P2P_GROUP;
if (go_intent == 15)
iftype = WPA_IF_P2P_GO;
if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
"interface for the group");
return -1;
}
if_addr = wpa_s->pending_interface_addr;
} else {
if (wpa_s->p2p_mgmt)
if_addr = wpa_s->parent->own_addr;
else
if_addr = wpa_s->own_addr;
os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
}
if (auth) {
if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
go_intent, if_addr,
force_freq, persistent_group, ssid,
pref_freq) < 0)
return -1;
return ret;
}
if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
go_intent, if_addr, force_freq,
persistent_group, ssid, pref_freq) < 0) {
if (wpa_s->create_p2p_iface)
wpas_p2p_remove_pending_group_interface(wpa_s);
return -1;
}
return ret;
}
/**
* wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
* @freq: Frequency of the channel in MHz
* @duration: Duration of the stay on the channel in milliseconds
*
* This callback is called when the driver indicates that it has started the
* requested remain-on-channel duration.
*/
void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq, unsigned int duration)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
wpa_printf(MSG_DEBUG, "P2P: remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u duration=%u)",
wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
wpa_s->roc_waiting_drv_freq, freq, duration);
if (wpa_s->off_channel_freq &&
wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
wpa_s->pending_listen_duration);
wpa_s->pending_listen_freq = 0;
} else {
wpa_printf(MSG_DEBUG, "P2P: Ignore remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d freq=%u duration=%u)",
wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
freq, duration);
}
}
int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout)
{
/* Limit maximum Listen state time based on driver limitation. */
if (timeout > wpa_s->max_remain_on_chan)
timeout = wpa_s->max_remain_on_chan;
return p2p_listen(wpa_s->global->p2p, timeout);
}
/**
* wpas_p2p_cancel_remain_on_channel_cb - Remain-on-channel timeout
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
* @freq: Frequency of the channel in MHz
*
* This callback is called when the driver indicates that a remain-on-channel
* operation has been completed, i.e., the duration on the requested channel
* has timed out.
*/
void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq)
{
wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback "
"(p2p_long_listen=%d ms pending_action_tx=%p)",
wpa_s->global->p2p_long_listen,
offchannel_pending_action_tx(wpa_s));
wpas_p2p_listen_work_done(wpa_s);
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
if (wpa_s->global->p2p_long_listen > 0)
wpa_s->global->p2p_long_listen -= wpa_s->max_remain_on_chan;
if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
return; /* P2P module started a new operation */
if (offchannel_pending_action_tx(wpa_s))
return;
if (wpa_s->global->p2p_long_listen > 0) {
wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
wpas_p2p_listen_start(wpa_s, wpa_s->global->p2p_long_listen);
} else {
/*
* When listen duration is over, stop listen & update p2p_state
* to IDLE.
*/
p2p_stop_listen(wpa_s->global->p2p);
}
}
/**
* wpas_p2p_group_remove - Remove a P2P group
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
* @ifname: Network interface name of the group interface or "*" to remove all
* groups
* Returns: 0 on success, -1 on failure
*
* This function is used to remove a P2P group. This can be used to disconnect
* from a group in which the local end is a P2P Client or to end a P2P Group in
* case the local end is the Group Owner. If a virtual network interface was
* created for this group, that interface will be removed. Otherwise, only the
* configured P2P group network will be removed from the interface.
*/
int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
{
struct wpa_global *global = wpa_s->global;
struct wpa_supplicant *calling_wpa_s = wpa_s;
if (os_strcmp(ifname, "*") == 0) {
struct wpa_supplicant *prev;
bool calling_wpa_s_group_removed = false;
wpa_s = global->ifaces;
while (wpa_s) {
prev = wpa_s;
wpa_s = wpa_s->next;
if (prev->p2p_group_interface !=
NOT_P2P_GROUP_INTERFACE ||
(prev->current_ssid &&
prev->current_ssid->p2p_group)) {
wpas_p2p_disconnect_safely(prev, calling_wpa_s);
if (prev == calling_wpa_s)
calling_wpa_s_group_removed = true;
}
}
if (!calling_wpa_s_group_removed &&
(calling_wpa_s->p2p_group_interface !=
NOT_P2P_GROUP_INTERFACE ||
(calling_wpa_s->current_ssid &&
calling_wpa_s->current_ssid->p2p_group))) {
wpa_printf(MSG_DEBUG, "Remove calling_wpa_s P2P group");
wpas_p2p_disconnect_safely(calling_wpa_s,
calling_wpa_s);
}
return 0;
}
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_strcmp(wpa_s->ifname, ifname) == 0)
break;
}
return wpas_p2p_disconnect_safely(wpa_s, calling_wpa_s);
}
static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
{
unsigned int r;
if (!wpa_s->conf->num_p2p_pref_chan && !freq) {
unsigned int i, size = P2P_MAX_PREF_CHANNELS;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
int res;
res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO,
&size, pref_freq_list);
if (!is_p2p_allow_6ghz(wpa_s->global->p2p))
size = p2p_remove_6ghz_channels(pref_freq_list, size);
if (!res && size > 0) {
i = 0;
while (i < size &&
(!p2p_supported_freq(wpa_s->global->p2p,
pref_freq_list[i]) ||
wpas_p2p_disallowed_freq(wpa_s->global,
pref_freq_list[i]))) {
wpa_printf(MSG_DEBUG,
"P2P: preferred_freq_list[%d]=%d is disallowed",
i, pref_freq_list[i]);
i++;
}
if (i != size) {
freq = pref_freq_list[i];
wpa_printf(MSG_DEBUG,
"P2P: Using preferred_freq_list[%d]=%d",
i, freq);
} else {
wpa_printf(MSG_DEBUG,
"P2P: All driver preferred frequencies are disallowed for P2P use");
}
} else {
wpa_printf(MSG_DEBUG,
"P2P: No preferred frequency list available");
}
}
if (freq == 2) {
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
"band");
if (wpa_s->best_24_freq > 0 &&
p2p_supported_freq_go(wpa_s->global->p2p,
wpa_s->best_24_freq)) {
freq = wpa_s->best_24_freq;
wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
"channel: %d MHz", freq);
} else {
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
return -1;
freq = 2412 + (r % 3) * 25;
wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band "
"channel: %d MHz", freq);
}
}
if (freq == 5) {
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
"band");
if (wpa_s->best_5_freq > 0 &&
p2p_supported_freq_go(wpa_s->global->p2p,
wpa_s->best_5_freq)) {
freq = wpa_s->best_5_freq;
wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
"channel: %d MHz", freq);
} else {
const int freqs[] = {
/* operating class 115 */
5180, 5200, 5220, 5240,
/* operating class 124 */
5745, 5765, 5785, 5805,
};
unsigned int i, num_freqs = ARRAY_SIZE(freqs);
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
return -1;
/*
* Most of the 5 GHz channels require DFS. Only
* operating classes 115 and 124 are available possibly
* without that requirement. Check these for
* availability starting from a randomly picked
* position.
*/
for (i = 0; i < num_freqs; i++, r++) {
freq = freqs[r % num_freqs];
if (p2p_supported_freq_go(wpa_s->global->p2p,
freq))
break;
}
if (i >= num_freqs) {
wpa_printf(MSG_DEBUG, "P2P: Could not select "
"5 GHz channel for P2P group");
return -1;
}
wpa_printf(MSG_DEBUG, "P2P: Use random 5 GHz band "
"channel: %d MHz", freq);
}
}
if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
ieee80211_is_dfs(freq, wpa_s->hw.modes,
wpa_s->hw.num_modes)) {
/*
* If freq is a DFS channel and DFS is offloaded to the
* driver, allow P2P GO to use it.
*/
wpa_printf(MSG_DEBUG, "P2P: "
"%s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded",
__func__, freq);
return freq;
}
wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
"(%u MHz) is not supported for P2P uses",
freq);
return -1;
}
return freq;
}
static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s,
const struct p2p_channels *channels,
int freq)
{
if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) &&
p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
freq_included(wpa_s, channels, freq))
return 1;
return 0;
}
static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params,
const struct p2p_channels *channels)
{
unsigned int i, r;
/* try all channels in operating class 115 */
for (i = 0; i < 4; i++) {
params->freq = 5180 + i * 20;
- if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(wpa_s, channels, params->freq) &&
- p2p_supported_freq(wpa_s->global->p2p, params->freq))
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
goto out;
}
/* try all channels in operating class 124 */
for (i = 0; i < 4; i++) {
params->freq = 5745 + i * 20;
- if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(wpa_s, channels, params->freq) &&
- p2p_supported_freq(wpa_s->global->p2p, params->freq))
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
goto out;
}
/* try social channel class 180 channel 2 */
params->freq = 58320 + 1 * 2160;
- if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(wpa_s, channels, params->freq) &&
- p2p_supported_freq(wpa_s->global->p2p, params->freq))
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
goto out;
/* try all channels in reg. class 180 */
for (i = 0; i < 4; i++) {
params->freq = 58320 + i * 2160;
- if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(wpa_s, channels, params->freq) &&
- p2p_supported_freq(wpa_s->global->p2p, params->freq))
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
goto out;
}
/* try some random selection of the social channels */
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
return;
for (i = 0; i < 3; i++) {
params->freq = 2412 + ((r + i) % 3) * 25;
if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
goto out;
}
/* try all other channels in operating class 81 */
for (i = 0; i < 11; i++) {
params->freq = 2412 + i * 5;
/* skip social channels; covered in the previous loop */
if (params->freq == 2412 ||
params->freq == 2437 ||
params->freq == 2462)
continue;
if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
goto out;
}
params->freq = 0;
wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
return;
out:
wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
params->freq);
}
static int wpas_same_band(int freq1, int freq2)
{
enum hostapd_hw_mode mode1, mode2;
u8 chan1, chan2;
mode1 = ieee80211_freq_to_chan(freq1, &chan1);
mode2 = ieee80211_freq_to_chan(freq2, &chan2);
if (mode1 == NUM_HOSTAPD_MODES)
return 0;
return mode1 == mode2;
}
static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params,
int freq, int vht_center_freq2, int ht40,
int vht, int max_oper_chwidth, int he,
int edmg,
const struct p2p_channels *channels)
{
struct wpa_used_freq_data *freqs;
unsigned int cand;
unsigned int num, i;
int ignore_no_freqs = 0;
int unused_channels = wpas_p2p_num_unused_channels(wpa_s) > 0;
os_memset(params, 0, sizeof(*params));
params->role_go = 1;
params->ht40 = ht40;
params->vht = vht;
params->he = he;
params->max_oper_chwidth = max_oper_chwidth;
params->vht_center_freq2 = vht_center_freq2;
params->edmg = edmg;
freqs = os_calloc(wpa_s->num_multichan_concurrent,
sizeof(struct wpa_used_freq_data));
if (!freqs)
return -1;
num = get_shared_radio_freqs_data(wpa_s, freqs,
wpa_s->num_multichan_concurrent);
if (wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO &&
wpa_s->wpa_state == WPA_COMPLETED) {
wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO",
__func__);
/*
* If the frequency selection is done for an active P2P GO that
* is not sharing a frequency, allow to select a new frequency
* even if there are no unused frequencies as we are about to
* move the P2P GO so its frequency can be re-used.
*/
for (i = 0; i < num; i++) {
if (freqs[i].freq == wpa_s->current_ssid->frequency &&
freqs[i].flags == 0) {
ignore_no_freqs = 1;
break;
}
}
}
/* Try to use EDMG channel */
if (params->edmg) {
if (wpas_p2p_try_edmg_channel(wpa_s, params) == 0)
goto success;
params->edmg = 0;
}
/* try using the forced freq */
if (freq) {
if (wpas_p2p_disallowed_freq(wpa_s->global, freq) ||
!freq_included(wpa_s, channels, freq)) {
wpa_printf(MSG_DEBUG,
"P2P: Forced GO freq %d MHz disallowed",
freq);
goto fail;
}
if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
ieee80211_is_dfs(freq, wpa_s->hw.modes,
wpa_s->hw.num_modes)) {
/*
* If freq is a DFS channel and DFS is offloaded
* to the driver, allow P2P GO to use it.
*/
wpa_printf(MSG_DEBUG,
"P2P: %s: The forced channel for GO (%u MHz) requires DFS and DFS is offloaded",
__func__, freq);
} else {
wpa_printf(MSG_DEBUG,
"P2P: The forced channel for GO (%u MHz) is not supported for P2P uses",
freq);
goto fail;
}
}
for (i = 0; i < num; i++) {
if (freqs[i].freq == freq) {
wpa_printf(MSG_DEBUG,
"P2P: forced freq (%d MHz) is also shared",
freq);
params->freq = freq;
goto success;
}
}
if (!ignore_no_freqs && !unused_channels) {
wpa_printf(MSG_DEBUG,
"P2P: Cannot force GO on freq (%d MHz) as all the channels are in use",
freq);
goto fail;
}
wpa_printf(MSG_DEBUG,
"P2P: force GO freq (%d MHz) on a free channel",
freq);
params->freq = freq;
goto success;
}
/* consider using one of the shared frequencies */
if (num &&
(!wpa_s->conf->p2p_ignore_shared_freq || !unused_channels)) {
cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
wpa_printf(MSG_DEBUG,
"P2P: Use shared freq (%d MHz) for GO",
cand);
params->freq = cand;
goto success;
}
/* try using one of the shared freqs */
for (i = 0; i < num; i++) {
if (wpas_p2p_supported_freq_go(wpa_s, channels,
freqs[i].freq)) {
wpa_printf(MSG_DEBUG,
"P2P: Use shared freq (%d MHz) for GO",
freqs[i].freq);
params->freq = freqs[i].freq;
goto success;
}
}
}
if (!ignore_no_freqs && !unused_channels) {
wpa_printf(MSG_DEBUG,
"P2P: Cannot force GO on any of the channels we are already using");
goto fail;
}
/* try using the setting from the configuration file */
if (wpa_s->conf->p2p_oper_reg_class == 81 &&
wpa_s->conf->p2p_oper_channel >= 1 &&
wpa_s->conf->p2p_oper_channel <= 11 &&
wpas_p2p_supported_freq_go(
wpa_s, channels,
2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
"frequency %d MHz", params->freq);
goto success;
}
if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
wpa_s->conf->p2p_oper_reg_class == 116 ||
wpa_s->conf->p2p_oper_reg_class == 117 ||
wpa_s->conf->p2p_oper_reg_class == 124 ||
wpa_s->conf->p2p_oper_reg_class == 125 ||
wpa_s->conf->p2p_oper_reg_class == 126 ||
wpa_s->conf->p2p_oper_reg_class == 127) &&
wpas_p2p_supported_freq_go(wpa_s, channels,
5000 +
5 * wpa_s->conf->p2p_oper_channel)) {
params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
"frequency %d MHz", params->freq);
goto success;
}
/* Try using best channels */
if (wpa_s->conf->p2p_oper_channel == 0 &&
wpa_s->best_overall_freq > 0 &&
wpas_p2p_supported_freq_go(wpa_s, channels,
wpa_s->best_overall_freq)) {
params->freq = wpa_s->best_overall_freq;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
"channel %d MHz", params->freq);
goto success;
}
if (wpa_s->conf->p2p_oper_channel == 0 &&
wpa_s->best_24_freq > 0 &&
wpas_p2p_supported_freq_go(wpa_s, channels,
wpa_s->best_24_freq)) {
params->freq = wpa_s->best_24_freq;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
"channel %d MHz", params->freq);
goto success;
}
if (wpa_s->conf->p2p_oper_channel == 0 &&
wpa_s->best_5_freq > 0 &&
wpas_p2p_supported_freq_go(wpa_s, channels,
wpa_s->best_5_freq)) {
params->freq = wpa_s->best_5_freq;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
"channel %d MHz", params->freq);
goto success;
}
/* try using preferred channels */
cand = p2p_get_pref_freq(wpa_s->global->p2p, channels);
if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
params->freq = cand;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
"channels", params->freq);
goto success;
}
/* Try using a channel that allows VHT to be used with 80 MHz */
if (wpa_s->hw.modes && wpa_s->p2p_group_common_freqs) {
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
enum hostapd_hw_mode mode;
struct hostapd_hw_modes *hwmode;
u8 chan;
u8 op_class;
cand = wpa_s->p2p_group_common_freqs[i];
op_class = is_6ghz_freq(cand) ? 133 : 128;
mode = ieee80211_freq_to_chan(cand, &chan);
hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
mode, is_6ghz_freq(cand));
if (!hwmode ||
wpas_p2p_verify_channel(wpa_s, hwmode, op_class,
chan, BW80) != ALLOWED)
continue;
if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
params->freq = cand;
wpa_printf(MSG_DEBUG,
"P2P: Use freq %d MHz common with the peer and allowing VHT80",
params->freq);
goto success;
}
}
}
/* Try using a channel that allows HT to be used with 40 MHz on the same
* band so that CSA can be used */
if (wpa_s->current_ssid && wpa_s->hw.modes &&
wpa_s->p2p_group_common_freqs) {
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
enum hostapd_hw_mode mode;
struct hostapd_hw_modes *hwmode;
u8 chan, op_class;
bool is_6ghz, supported = false;
is_6ghz = is_6ghz_freq(cand);
cand = wpa_s->p2p_group_common_freqs[i];
mode = ieee80211_freq_to_chan(cand, &chan);
hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
mode, is_6ghz);
if (!wpas_same_band(wpa_s->current_ssid->frequency,
cand) ||
!hwmode)
continue;
if (is_6ghz &&
wpas_p2p_verify_channel(wpa_s, hwmode, 132, chan,
BW40) == ALLOWED)
supported = true;
if (!is_6ghz &&
ieee80211_freq_to_channel_ext(
cand, -1, CHANWIDTH_USE_HT, &op_class,
&chan) != NUM_HOSTAPD_MODES &&
wpas_p2p_verify_channel(
wpa_s, hwmode, op_class, chan,
BW40MINUS) == ALLOWED)
supported = true;
if (!supported && !is_6ghz &&
ieee80211_freq_to_channel_ext(
cand, 1, CHANWIDTH_USE_HT, &op_class,
&chan) != NUM_HOSTAPD_MODES &&
wpas_p2p_verify_channel(
wpa_s, hwmode, op_class, chan,
BW40PLUS) == ALLOWED)
supported = true;
if (!supported)
continue;
if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
params->freq = cand;
wpa_printf(MSG_DEBUG,
"P2P: Use freq %d MHz common with the peer, allowing HT40, and maintaining same band",
params->freq);
goto success;
}
}
}
/* Try using one of the group common freqs on the same band so that CSA
* can be used */
if (wpa_s->current_ssid && wpa_s->p2p_group_common_freqs) {
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
cand = wpa_s->p2p_group_common_freqs[i];
if (!wpas_same_band(wpa_s->current_ssid->frequency,
cand))
continue;
if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
params->freq = cand;
wpa_printf(MSG_DEBUG,
"P2P: Use freq %d MHz common with the peer and maintaining same band",
params->freq);
goto success;
}
}
}
/* Try using one of the group common freqs */
if (wpa_s->p2p_group_common_freqs) {
for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
cand = wpa_s->p2p_group_common_freqs[i];
if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
params->freq = cand;
wpa_printf(MSG_DEBUG,
"P2P: Use freq %d MHz common with the peer",
params->freq);
goto success;
}
}
}
/* no preference, select some channel */
wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels);
if (params->freq == 0) {
wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use");
goto fail;
}
success:
os_free(freqs);
return 0;
fail:
os_free(freqs);
return -1;
}
static struct wpa_supplicant *
wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
int go)
{
struct wpa_supplicant *group_wpa_s;
if (!wpas_p2p_create_iface(wpa_s)) {
if (wpa_s->p2p_mgmt) {
/*
* We may be called on the p2p_dev interface which
* cannot be used for group operations, so always use
* the primary interface.
*/
wpa_s->parent->p2pdev = wpa_s;
wpa_s = wpa_s->parent;
}
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Use primary interface for group operations");
wpa_s->p2p_first_connection_timeout = 0;
if (wpa_s != wpa_s->p2pdev)
wpas_p2p_clone_config(wpa_s, wpa_s->p2pdev);
return wpa_s;
}
if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
WPA_IF_P2P_CLIENT) < 0) {
wpa_msg_global(wpa_s, MSG_ERROR,
"P2P: Failed to add group interface");
return NULL;
}
group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
if (group_wpa_s == NULL) {
wpa_msg_global(wpa_s, MSG_ERROR,
"P2P: Failed to initialize group interface");
wpas_p2p_remove_pending_group_interface(wpa_s);
return NULL;
}
if (go && wpa_s->p2p_go_do_acs) {
group_wpa_s->p2p_go_do_acs = wpa_s->p2p_go_do_acs;
group_wpa_s->p2p_go_acs_band = wpa_s->p2p_go_acs_band;
wpa_s->p2p_go_do_acs = 0;
}
+ if (go && wpa_s->p2p_go_allow_dfs) {
+ group_wpa_s->p2p_go_allow_dfs = wpa_s->p2p_go_allow_dfs;
+ wpa_s->p2p_go_allow_dfs = 0;
+ }
+
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s",
group_wpa_s->ifname);
group_wpa_s->p2p_first_connection_timeout = 0;
return group_wpa_s;
}
/**
* wpas_p2p_group_add - Add a new P2P group with local end as Group Owner
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
* @persistent_group: Whether to create a persistent group
* @freq: Frequency for the group or 0 to indicate no hardcoding
* @vht_center_freq2: segment_1 center frequency for GO operating in VHT 80P80
* @ht40: Start GO with 40 MHz channel width
* @vht: Start GO with VHT support
* @vht_chwidth: channel bandwidth for GO operating with VHT support
* @edmg: Start GO with EDMG support
* @allow_6ghz: Allow P2P group creation on a 6 GHz channel
* Returns: 0 on success, -1 on failure
*
* This function creates a new P2P group with the local end as the Group Owner,
* i.e., without using Group Owner Negotiation.
*/
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
int freq, int vht_center_freq2, int ht40, int vht,
int max_oper_chwidth, int he, int edmg,
bool allow_6ghz)
{
struct p2p_go_neg_results params;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
if (wpas_p2p_check_6ghz(wpa_s, NULL, allow_6ghz, freq))
return -1;
os_free(wpa_s->global->add_psk);
wpa_s->global->add_psk = NULL;
/* Make sure we are not running find during connection establishment */
wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND");
wpas_p2p_stop_find_oper(wpa_s);
if (!wpa_s->p2p_go_do_acs) {
freq = wpas_p2p_select_go_freq(wpa_s, freq);
if (freq < 0)
return -1;
}
if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
ht40, vht, max_oper_chwidth, he, edmg,
NULL))
return -1;
p2p_go_params(wpa_s->global->p2p, &params);
params.persistent_group = persistent_group;
wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1);
if (wpa_s == NULL)
return -1;
wpas_start_wps_go(wpa_s, &params, 0);
return 0;
}
static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
struct wpa_ssid *params, int addr_allocated,
int freq, int force_scan)
{
struct wpa_ssid *ssid;
wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
if (wpa_s == NULL)
return -1;
if (force_scan)
os_get_reltime(&wpa_s->scan_min_time);
wpa_s->p2p_last_4way_hs_fail = NULL;
wpa_supplicant_ap_deinit(wpa_s);
ssid = wpa_config_add_network(wpa_s->conf);
if (ssid == NULL)
return -1;
os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
wpa_config_set_network_defaults(ssid);
ssid->temporary = 1;
ssid->proto = WPA_PROTO_RSN;
ssid->pbss = params->pbss;
ssid->pairwise_cipher = params->pbss ? WPA_CIPHER_GCMP :
WPA_CIPHER_CCMP;
ssid->group_cipher = params->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
ssid->key_mgmt = WPA_KEY_MGMT_PSK;
ssid->ssid = os_malloc(params->ssid_len);
if (ssid->ssid == NULL) {
wpa_config_remove_network(wpa_s->conf, ssid->id);
return -1;
}
os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
ssid->ssid_len = params->ssid_len;
ssid->p2p_group = 1;
ssid->export_keys = 1;
if (params->psk_set) {
os_memcpy(ssid->psk, params->psk, 32);
ssid->psk_set = 1;
}
if (params->passphrase)
ssid->passphrase = os_strdup(params->passphrase);
wpa_s->show_group_started = 1;
wpa_s->p2p_in_invitation = 1;
wpa_s->p2p_invite_go_freq = freq;
wpa_s->p2p_go_group_formation_completed = 0;
wpa_s->global->p2p_group_formation = wpa_s;
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev,
NULL);
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
wpa_supplicant_select_network(wpa_s, ssid);
return 0;
}
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int addr_allocated,
int force_freq, int neg_freq,
int vht_center_freq2, int ht40,
int vht, int max_oper_chwidth, int he,
int edmg,
const struct p2p_channels *channels,
int connection_timeout, int force_scan,
bool allow_6ghz)
{
struct p2p_go_neg_results params;
int go = 0, freq;
if (ssid->disabled != 2 || ssid->ssid == NULL)
return -1;
if (wpas_get_p2p_group(wpa_s, ssid->ssid, ssid->ssid_len, &go) &&
go == (ssid->mode == WPAS_MODE_P2P_GO)) {
wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is "
"already running");
if (go == 0 &&
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL)) {
/*
* This can happen if Invitation Response frame was lost
* and the peer (GO of a persistent group) tries to
* invite us again. Reschedule the timeout to avoid
* terminating the wait for the connection too early
* since we now know that the peer is still trying to
* invite us instead of having already started the GO.
*/
wpa_printf(MSG_DEBUG,
"P2P: Reschedule group formation timeout since peer is still trying to invite us");
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
}
return 0;
}
os_free(wpa_s->global->add_psk);
wpa_s->global->add_psk = NULL;
/* Make sure we are not running find during connection establishment */
wpas_p2p_stop_find_oper(wpa_s);
wpa_s->p2p_fallback_to_go_neg = 0;
if (ssid->mode == WPAS_MODE_P2P_GO) {
if (force_freq > 0) {
freq = wpas_p2p_select_go_freq(wpa_s, force_freq);
if (freq < 0)
return -1;
} else {
freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
if (freq < 0 ||
(freq > 0 && !freq_included(wpa_s, channels, freq)))
freq = 0;
}
} else if (ssid->mode == WPAS_MODE_INFRA) {
freq = neg_freq;
if (freq <= 0 || !freq_included(wpa_s, channels, freq)) {
struct os_reltime now;
struct wpa_bss *bss =
wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid);
os_get_reltime(&now);
if (bss &&
!os_reltime_expired(&now, &bss->last_update, 5) &&
freq_included(wpa_s, channels, bss->freq))
freq = bss->freq;
else
freq = 0;
}
return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq,
force_scan);
} else {
return -1;
}
if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
ht40, vht, max_oper_chwidth, he, edmg,
channels))
return -1;
params.role_go = 1;
params.psk_set = ssid->psk_set;
if (params.psk_set)
os_memcpy(params.psk, ssid->psk, sizeof(params.psk));
if (ssid->passphrase) {
if (os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
wpa_printf(MSG_ERROR, "P2P: Invalid passphrase in "
"persistent group");
return -1;
}
os_strlcpy(params.passphrase, ssid->passphrase,
sizeof(params.passphrase));
}
os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len);
params.ssid_len = ssid->ssid_len;
params.persistent_group = 1;
wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 1);
if (wpa_s == NULL)
return -1;
p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS);
wpa_s->p2p_first_connection_timeout = connection_timeout;
wpas_start_wps_go(wpa_s, &params, 0);
return 0;
}
static void wpas_p2p_ie_update(void *ctx, struct wpabuf *beacon_ies,
struct wpabuf *proberesp_ies)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->ap_iface) {
struct hostapd_data *hapd = wpa_s->ap_iface->bss[0];
if (!(hapd->conf->p2p & P2P_GROUP_OWNER)) {
wpabuf_free(beacon_ies);
wpabuf_free(proberesp_ies);
return;
}
if (beacon_ies) {
wpabuf_free(hapd->p2p_beacon_ie);
hapd->p2p_beacon_ie = beacon_ies;
}
wpabuf_free(hapd->p2p_probe_resp_ie);
hapd->p2p_probe_resp_ie = proberesp_ies;
} else {
wpabuf_free(beacon_ies);
wpabuf_free(proberesp_ies);
}
wpa_supplicant_ap_update_beacon(wpa_s);
}
static void wpas_p2p_idle_update(void *ctx, int idle)
{
struct wpa_supplicant *wpa_s = ctx;
if (!wpa_s->ap_iface)
return;
wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
if (idle) {
if (wpa_s->global->p2p_fail_on_wps_complete &&
wpa_s->p2p_in_provisioning) {
wpas_p2p_grpform_fail_after_wps(wpa_s);
return;
}
wpas_p2p_set_group_idle_timeout(wpa_s);
} else
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
}
struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct p2p_group *group;
struct p2p_group_config *cfg;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
!ssid->p2p_group)
return NULL;
cfg = os_zalloc(sizeof(*cfg));
if (cfg == NULL)
return NULL;
if (ssid->p2p_persistent_group && wpa_s->conf->persistent_reconnect)
cfg->persistent_group = 2;
else if (ssid->p2p_persistent_group)
cfg->persistent_group = 1;
os_memcpy(cfg->interface_addr, wpa_s->own_addr, ETH_ALEN);
if (wpa_s->max_stations &&
wpa_s->max_stations < wpa_s->conf->max_num_sta)
cfg->max_clients = wpa_s->max_stations;
else
cfg->max_clients = wpa_s->conf->max_num_sta;
os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len);
cfg->ssid_len = ssid->ssid_len;
cfg->freq = ssid->frequency;
cfg->cb_ctx = wpa_s;
cfg->ie_update = wpas_p2p_ie_update;
cfg->idle_update = wpas_p2p_idle_update;
cfg->ip_addr_alloc = WPA_GET_BE32(wpa_s->p2pdev->conf->ip_addr_start)
!= 0;
group = p2p_group_init(wpa_s->global->p2p, cfg);
if (group == NULL)
os_free(cfg);
if (ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)
p2p_group_notif_formation_done(group);
wpa_s->p2p_group = group;
return group;
}
void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
int registrar)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (!wpa_s->p2p_in_provisioning) {
wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P "
"provisioning not in progress");
return;
}
if (ssid && ssid->mode == WPAS_MODE_INFRA) {
u8 go_dev_addr[ETH_ALEN];
os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN);
wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
ssid->ssid_len);
/* Clear any stored provisioning info */
p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr);
}
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev,
NULL);
wpa_s->p2p_go_group_formation_completed = 1;
if (ssid && ssid->mode == WPAS_MODE_INFRA) {
/*
* Use a separate timeout for initial data connection to
* complete to allow the group to be removed automatically if
* something goes wrong in this step before the P2P group idle
* timeout mechanism is taken into use.
*/
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Re-start group formation timeout (%d seconds) as client for initial connection",
P2P_MAX_INITIAL_CONN_WAIT);
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
/* Complete group formation on successful data connection. */
wpa_s->p2p_go_group_formation_completed = 0;
} else if (ssid) {
/*
* Use a separate timeout for initial data connection to
* complete to allow the group to be removed automatically if
* the client does not complete data connection successfully.
*/
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Re-start group formation timeout (%d seconds) as GO for initial connection",
P2P_MAX_INITIAL_CONN_WAIT_GO);
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT_GO, 0,
wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
/*
* Complete group formation on first successful data connection
*/
wpa_s->p2p_go_group_formation_completed = 0;
}
if (wpa_s->global->p2p)
p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
wpas_group_formation_completed(wpa_s, 1, 0);
}
void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
struct wps_event_fail *fail)
{
if (!wpa_s->p2p_in_provisioning) {
wpa_printf(MSG_DEBUG, "P2P: Ignore WPS fail event - P2P "
"provisioning not in progress");
return;
}
if (wpa_s->go_params) {
p2p_clear_provisioning_info(
wpa_s->global->p2p,
wpa_s->go_params->peer_device_addr);
}
wpas_notify_p2p_wps_failed(wpa_s, fail);
if (wpa_s == wpa_s->global->p2p_group_formation) {
/*
* Allow some time for the failed WPS negotiation exchange to
* complete, but remove the group since group formation cannot
* succeed after provisioning failure.
*/
wpa_printf(MSG_DEBUG, "P2P: WPS step failed during group formation - reject connection from timeout");
wpa_s->global->p2p_fail_on_wps_complete = 1;
eloop_deplete_timeout(0, 50000,
wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
}
}
int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
{
if (!wpa_s->global->p2p_fail_on_wps_complete ||
!wpa_s->p2p_in_provisioning)
return 0;
wpas_p2p_grpform_fail_after_wps(wpa_s);
return 1;
}
int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *config_method,
enum wpas_p2p_prov_disc_use use,
struct p2ps_provision *p2ps_prov)
{
u16 config_methods;
wpa_s->global->pending_p2ps_group = 0;
wpa_s->global->pending_p2ps_group_freq = 0;
wpa_s->p2p_fallback_to_go_neg = 0;
wpa_s->pending_pd_use = NORMAL_PD;
if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
p2ps_prov->conncap = p2ps_group_capability(
wpa_s, P2PS_SETUP_NONE, p2ps_prov->role,
&p2ps_prov->force_freq, &p2ps_prov->pref_freq);
wpa_printf(MSG_DEBUG,
"P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
__func__, p2ps_prov->conncap,
p2ps_prov->adv_id, p2ps_prov->conncap,
p2ps_prov->status, p2ps_prov->info);
config_methods = 0;
} else if (os_strncmp(config_method, "display", 7) == 0)
config_methods = WPS_CONFIG_DISPLAY;
else if (os_strncmp(config_method, "keypad", 6) == 0)
config_methods = WPS_CONFIG_KEYPAD;
else if (os_strncmp(config_method, "pbc", 3) == 0 ||
os_strncmp(config_method, "pushbutton", 10) == 0)
config_methods = WPS_CONFIG_PUSHBUTTON;
else {
wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
os_free(p2ps_prov);
return -1;
}
if (use == WPAS_P2P_PD_AUTO) {
os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN);
wpa_s->pending_pd_config_methods = config_methods;
wpa_s->p2p_auto_pd = 1;
wpa_s->p2p_auto_join = 0;
wpa_s->pending_pd_before_join = 0;
wpa_s->auto_pd_scan_retry = 0;
wpas_p2p_stop_find(wpa_s);
wpa_s->p2p_join_scan_count = 0;
os_get_reltime(&wpa_s->p2p_auto_started);
wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld",
wpa_s->p2p_auto_started.sec,
wpa_s->p2p_auto_started.usec);
wpas_p2p_join_scan(wpa_s, NULL);
return 0;
}
if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
os_free(p2ps_prov);
return -1;
}
return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov,
config_methods, use == WPAS_P2P_PD_FOR_JOIN,
0, 1);
}
int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
char *end)
{
return p2p_scan_result_text(ies, ies_len, buf, end);
}
static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
{
if (!offchannel_pending_action_tx(wpa_s))
return;
if (wpa_s->p2p_send_action_work) {
wpas_p2p_free_send_action_work(wpa_s);
eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
wpa_s, NULL);
offchannel_send_action_done(wpa_s);
}
wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
"operation request");
offchannel_clear_pending_action_tx(wpa_s);
}
int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
enum p2p_discovery_type type,
unsigned int num_req_dev_types, const u8 *req_dev_types,
const u8 *dev_id, unsigned int search_delay,
u8 seek_cnt, const char **seek_string, int freq,
bool include_6ghz)
{
wpas_p2p_clear_pending_action_tx(wpa_s);
wpa_s->global->p2p_long_listen = 0;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
wpa_s->p2p_in_provisioning) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Reject p2p_find operation%s%s",
(wpa_s->global->p2p_disabled || !wpa_s->global->p2p) ?
" (P2P disabled)" : "",
wpa_s->p2p_in_provisioning ?
" (p2p_in_provisioning)" : "");
return -1;
}
wpa_supplicant_cancel_sched_scan(wpa_s);
return p2p_find(wpa_s->global->p2p, timeout, type,
num_req_dev_types, req_dev_types, dev_id,
search_delay, seek_cnt, seek_string, freq,
include_6ghz);
}
static void wpas_p2p_scan_res_ignore_search(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res)
{
wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
if (wpa_s->p2p_scan_work) {
struct wpa_radio_work *work = wpa_s->p2p_scan_work;
wpa_s->p2p_scan_work = NULL;
radio_work_done(work);
}
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
/*
* Indicate that results have been processed so that the P2P module can
* continue pending tasks.
*/
wpas_p2p_scan_res_handled(wpa_s);
}
static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s)
{
wpas_p2p_clear_pending_action_tx(wpa_s);
wpa_s->global->p2p_long_listen = 0;
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
if (wpa_s->global->p2p)
p2p_stop_find(wpa_s->global->p2p);
if (wpa_s->scan_res_handler == wpas_p2p_scan_res_handler) {
wpa_printf(MSG_DEBUG,
"P2P: Do not consider the scan results after stop_find");
wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore_search;
}
}
void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
{
wpas_p2p_stop_find_oper(wpa_s);
if (!wpa_s->global->pending_group_iface_for_p2ps)
wpas_p2p_remove_pending_group_interface(wpa_s);
}
static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_s->global->p2p_long_listen = 0;
}
int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
{
int res;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
if (wpa_s->p2p_lo_started) {
wpa_printf(MSG_DEBUG,
"P2P: Cannot start P2P listen, it is offloaded");
return -1;
}
wpa_supplicant_cancel_sched_scan(wpa_s);
wpas_p2p_clear_pending_action_tx(wpa_s);
if (timeout == 0) {
/*
* This is a request for unlimited Listen state. However, at
* least for now, this is mapped to a Listen state for one
* hour.
*/
timeout = 3600;
}
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
wpa_s->global->p2p_long_listen = 0;
/*
* Stop previous find/listen operation to avoid trying to request a new
* remain-on-channel operation while the driver is still running the
* previous one.
*/
if (wpa_s->global->p2p)
p2p_stop_find(wpa_s->global->p2p);
res = wpas_p2p_listen_start(wpa_s, timeout * 1000);
if (res == 0 && timeout * 1000 > wpa_s->max_remain_on_chan) {
wpa_s->global->p2p_long_listen = timeout * 1000;
eloop_register_timeout(timeout, 0,
wpas_p2p_long_listen_timeout,
wpa_s, NULL);
}
return res;
}
int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
u8 *buf, size_t len, int p2p_group)
{
struct wpabuf *p2p_ie;
int ret;
if (wpa_s->global->p2p_disabled)
return -1;
/*
* Advertize mandatory cross connection capability even on
* p2p_disabled=1 interface when associating with a P2P Manager WLAN AP.
*/
if (wpa_s->conf->p2p_disabled && p2p_group)
return -1;
if (wpa_s->global->p2p == NULL)
return -1;
if (bss == NULL)
return -1;
p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len,
p2p_group, p2p_ie);
wpabuf_free(p2p_ie);
return ret;
}
int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
const u8 *dst, const u8 *bssid,
const u8 *ie, size_t ie_len,
unsigned int rx_freq, int ssi_signal)
{
if (wpa_s->global->p2p_disabled)
return 0;
if (wpa_s->global->p2p == NULL)
return 0;
switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
ie, ie_len, rx_freq, wpa_s->p2p_lo_started)) {
case P2P_PREQ_NOT_P2P:
wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
ssi_signal);
/* fall through */
case P2P_PREQ_MALFORMED:
case P2P_PREQ_NOT_LISTEN:
case P2P_PREQ_NOT_PROCESSED:
default: /* make gcc happy */
return 0;
case P2P_PREQ_PROCESSED:
return 1;
}
}
void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
const u8 *sa, const u8 *bssid,
u8 category, const u8 *data, size_t len, int freq)
{
if (wpa_s->global->p2p_disabled)
return;
if (wpa_s->global->p2p == NULL)
return;
p2p_rx_action(wpa_s->global->p2p, da, sa, bssid, category, data, len,
freq);
}
void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies)
{
unsigned int bands;
if (wpa_s->global->p2p_disabled)
return;
if (wpa_s->global->p2p == NULL)
return;
bands = wpas_get_bands(wpa_s, NULL);
p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands);
}
static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
{
p2p_group_deinit(wpa_s->p2p_group);
wpa_s->p2p_group = NULL;
wpa_s->ap_configured_cb = NULL;
wpa_s->ap_configured_cb_ctx = NULL;
wpa_s->ap_configured_cb_data = NULL;
wpa_s->connect_without_scan = NULL;
}
int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
{
wpa_s->global->p2p_long_listen = 0;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
return p2p_reject(wpa_s->global->p2p, addr);
}
/* Invite to reinvoke a persistent group */
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
int vht_center_freq2, int ht40, int vht, int max_chwidth,
int pref_freq, int he, int edmg, bool allow_6ghz)
{
enum p2p_invite_role role;
u8 *bssid = NULL;
int force_freq = 0;
int res;
int no_pref_freq_given = pref_freq == 0;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
if (wpas_p2p_check_6ghz(wpa_s, NULL, allow_6ghz, freq))
return -1;
wpa_s->global->p2p_invite_group = NULL;
if (peer_addr)
os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
else
os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
wpa_s->p2p_persistent_go_freq = freq;
wpa_s->p2p_go_ht40 = !!ht40;
wpa_s->p2p_go_vht = !!vht;
wpa_s->p2p_go_he = !!he;
wpa_s->p2p_go_max_oper_chwidth = max_chwidth;
wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
wpa_s->p2p_go_edmg = !!edmg;
if (ssid->mode == WPAS_MODE_P2P_GO) {
role = P2P_INVITE_ROLE_GO;
if (peer_addr == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Missing peer "
"address in invitation command");
return -1;
}
if (wpas_p2p_create_iface(wpa_s)) {
if (wpas_p2p_add_group_interface(wpa_s,
WPA_IF_P2P_GO) < 0) {
wpa_printf(MSG_ERROR, "P2P: Failed to "
"allocate a new interface for the "
"group");
return -1;
}
bssid = wpa_s->pending_interface_addr;
} else if (wpa_s->p2p_mgmt)
bssid = wpa_s->parent->own_addr;
else
bssid = wpa_s->own_addr;
} else {
role = P2P_INVITE_ROLE_CLIENT;
peer_addr = ssid->bssid;
}
wpa_s->pending_invite_ssid_id = ssid->id;
size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
role == P2P_INVITE_ROLE_GO,
pref_freq_list, &size);
if (res)
return res;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
no_pref_freq_given && pref_freq > 0 &&
wpa_s->num_multichan_concurrent > 1 &&
wpas_p2p_num_unused_channels(wpa_s) > 0) {
wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz for invitation due to p2p_ignore_shared_freq=1 configuration",
pref_freq);
pref_freq = 0;
}
/*
* Stop any find/listen operations before invitation and possibly
* connection establishment.
*/
wpas_p2p_stop_find_oper(wpa_s);
return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
1, pref_freq, -1);
}
/* Invite to join an active group */
int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
const u8 *peer_addr, const u8 *go_dev_addr,
bool allow_6ghz)
{
struct wpa_global *global = wpa_s->global;
enum p2p_invite_role role;
u8 *bssid = NULL;
struct wpa_ssid *ssid;
int persistent;
int freq = 0, force_freq = 0, pref_freq = 0;
int res;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
wpa_s->p2p_persistent_go_freq = 0;
wpa_s->p2p_go_ht40 = 0;
wpa_s->p2p_go_vht = 0;
wpa_s->p2p_go_vht_center_freq2 = 0;
wpa_s->p2p_go_max_oper_chwidth = 0;
wpa_s->p2p_go_edmg = 0;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_strcmp(wpa_s->ifname, ifname) == 0)
break;
}
if (wpa_s == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Interface '%s' not found", ifname);
return -1;
}
ssid = wpa_s->current_ssid;
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "P2P: No current SSID to use for "
"invitation");
return -1;
}
wpa_s->global->p2p_invite_group = wpa_s;
persistent = ssid->p2p_persistent_group &&
wpas_p2p_get_persistent(wpa_s->p2pdev, peer_addr,
ssid->ssid, ssid->ssid_len);
if (ssid->mode == WPAS_MODE_P2P_GO) {
role = P2P_INVITE_ROLE_ACTIVE_GO;
bssid = wpa_s->own_addr;
if (go_dev_addr == NULL)
go_dev_addr = wpa_s->global->p2p_dev_addr;
freq = ssid->frequency;
} else {
role = P2P_INVITE_ROLE_CLIENT;
if (wpa_s->wpa_state < WPA_ASSOCIATED) {
wpa_printf(MSG_DEBUG, "P2P: Not associated - cannot "
"invite to current group");
return -1;
}
bssid = wpa_s->bssid;
if (go_dev_addr == NULL &&
!is_zero_ether_addr(wpa_s->go_dev_addr))
go_dev_addr = wpa_s->go_dev_addr;
freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
(int) wpa_s->assoc_freq;
}
wpa_s->p2pdev->pending_invite_ssid_id = -1;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
if (wpas_p2p_check_6ghz(wpa_s, peer_addr, allow_6ghz, freq))
return -1;
size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
role == P2P_INVITE_ROLE_ACTIVE_GO,
pref_freq_list, &size);
if (res)
return res;
wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
ssid->ssid, ssid->ssid_len, force_freq,
go_dev_addr, persistent, pref_freq, -1);
}
void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
u8 go_dev_addr[ETH_ALEN];
int persistent;
int freq;
u8 ip[3 * 4], *ip_ptr = NULL;
char ip_addr[100];
if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) {
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
}
if (!wpa_s->show_group_started || !ssid)
return;
wpa_s->show_group_started = 0;
if (!wpa_s->p2p_go_group_formation_completed &&
wpa_s->global->p2p_group_formation == wpa_s) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Marking group formation completed on client on data connection");
wpa_s->p2p_go_group_formation_completed = 1;
wpa_s->global->p2p_group_formation = NULL;
wpa_s->p2p_in_provisioning = 0;
wpa_s->p2p_in_invitation = 0;
}
os_memset(go_dev_addr, 0, ETH_ALEN);
if (ssid->bssid_set)
os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
persistent = wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
ssid->ssid_len);
os_memcpy(wpa_s->go_dev_addr, go_dev_addr, ETH_ALEN);
if (wpa_s->global->p2p_group_formation == wpa_s)
wpa_s->global->p2p_group_formation = NULL;
freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
(int) wpa_s->assoc_freq;
ip_addr[0] = '\0';
if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) {
int res;
res = os_snprintf(ip_addr, sizeof(ip_addr),
" ip_addr=%u.%u.%u.%u "
"ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u",
ip[0], ip[1], ip[2], ip[3],
ip[4], ip[5], ip[6], ip[7],
ip[8], ip[9], ip[10], ip[11]);
if (os_snprintf_error(sizeof(ip_addr), res))
ip_addr[0] = '\0';
ip_ptr = ip;
}
wpas_p2p_group_started(wpa_s, 0, ssid, freq,
ssid->passphrase == NULL && ssid->psk_set ?
ssid->psk : NULL,
ssid->passphrase, go_dev_addr, persistent,
ip_addr);
if (persistent)
wpas_p2p_store_persistent_group(wpa_s->p2pdev,
ssid, go_dev_addr);
wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip_ptr);
}
int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
u32 interval1, u32 duration2, u32 interval2)
{
int ret;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
if (wpa_s->wpa_state < WPA_ASSOCIATED ||
wpa_s->current_ssid == NULL ||
wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
return -1;
ret = p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
wpa_s->own_addr, wpa_s->assoc_freq,
duration1, interval1, duration2, interval2);
if (ret == 0)
wpa_s->waiting_presence_resp = 1;
return ret;
}
int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
unsigned int interval)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
return p2p_ext_listen(wpa_s->global->p2p, period, interval);
}
static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s)
{
if (wpa_s->current_ssid == NULL) {
/*
* current_ssid can be cleared when P2P client interface gets
* disconnected, so assume this interface was used as P2P
* client.
*/
return 1;
}
return wpa_s->current_ssid->p2p_group &&
wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
}
static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (wpa_s->conf->p2p_group_idle == 0 && !wpas_p2p_is_client(wpa_s)) {
wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - "
"disabled");
return;
}
wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
"group");
wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_IDLE_TIMEOUT);
}
static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
{
int timeout;
if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
return;
timeout = wpa_s->conf->p2p_group_idle;
if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
(timeout == 0 || timeout > P2P_MAX_CLIENT_IDLE))
timeout = P2P_MAX_CLIENT_IDLE;
if (timeout == 0)
return;
if (timeout < 0) {
if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA)
timeout = 0; /* special client mode no-timeout */
else
return;
}
if (wpa_s->p2p_in_provisioning) {
/*
* Use the normal group formation timeout during the
* provisioning phase to avoid terminating this process too
* early due to group idle timeout.
*/
wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
"during provisioning");
return;
}
if (wpa_s->show_group_started) {
/*
* Use the normal group formation timeout between the end of
* the provisioning phase and completion of 4-way handshake to
* avoid terminating this process too early due to group idle
* timeout.
*/
wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
"while waiting for initial 4-way handshake to "
"complete");
return;
}
wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
timeout);
eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout,
wpa_s, NULL);
}
/* Returns 1 if the interface was removed */
int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
u16 reason_code, const u8 *ie, size_t ie_len,
int locally_generated)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return 0;
if (!locally_generated)
p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
ie_len);
if (reason_code == WLAN_REASON_DEAUTH_LEAVING && !locally_generated &&
wpa_s->current_ssid &&
wpa_s->current_ssid->p2p_group &&
wpa_s->current_ssid->mode == WPAS_MODE_INFRA) {
wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group "
"session is ending");
if (wpas_p2p_group_delete(wpa_s,
P2P_GROUP_REMOVAL_GO_ENDING_SESSION)
> 0)
return 1;
}
return 0;
}
void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
u16 reason_code, const u8 *ie, size_t ie_len,
int locally_generated)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
if (!locally_generated)
p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie,
ie_len);
}
void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
{
struct p2p_data *p2p = wpa_s->global->p2p;
if (p2p == NULL)
return;
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
return;
if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_NAME)
p2p_set_dev_name(p2p, wpa_s->conf->device_name);
if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
p2p_set_pri_dev_type(p2p, wpa_s->conf->device_type);
if (wpa_s->wps &&
(wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS))
p2p_set_config_methods(p2p, wpa_s->wps->config_methods);
if (wpa_s->wps && (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID))
p2p_set_uuid(p2p, wpa_s->wps->uuid);
if (wpa_s->conf->changed_parameters & CFG_CHANGED_WPS_STRING) {
p2p_set_manufacturer(p2p, wpa_s->conf->manufacturer);
p2p_set_model_name(p2p, wpa_s->conf->model_name);
p2p_set_model_number(p2p, wpa_s->conf->model_number);
p2p_set_serial_number(p2p, wpa_s->conf->serial_number);
}
if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE)
p2p_set_sec_dev_types(p2p,
(void *) wpa_s->conf->sec_device_type,
wpa_s->conf->num_sec_device_types);
if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION) {
int i;
p2p_remove_wps_vendor_extensions(p2p);
for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) {
if (wpa_s->conf->wps_vendor_ext[i] == NULL)
continue;
p2p_add_wps_vendor_extension(
p2p, wpa_s->conf->wps_vendor_ext[i]);
}
}
if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
char country[3];
country[0] = wpa_s->conf->country[0];
country[1] = wpa_s->conf->country[1];
country[2] = 0x04;
p2p_set_country(p2p, country);
}
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_SSID_POSTFIX) {
p2p_set_ssid_postfix(p2p, (u8 *) wpa_s->conf->p2p_ssid_postfix,
wpa_s->conf->p2p_ssid_postfix ?
os_strlen(wpa_s->conf->p2p_ssid_postfix) :
0);
}
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_INTRA_BSS)
p2p_set_intra_bss_dist(p2p, wpa_s->conf->p2p_intra_bss);
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_LISTEN_CHANNEL) {
u8 reg_class, channel;
int ret;
unsigned int r;
u8 channel_forced;
if (wpa_s->conf->p2p_listen_reg_class &&
wpa_s->conf->p2p_listen_channel) {
reg_class = wpa_s->conf->p2p_listen_reg_class;
channel = wpa_s->conf->p2p_listen_channel;
channel_forced = 1;
} else {
reg_class = 81;
/*
* Pick one of the social channels randomly as the
* listen channel.
*/
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
channel = 1;
else
channel = 1 + (r % 3) * 5;
channel_forced = 0;
}
ret = p2p_set_listen_channel(p2p, reg_class, channel,
channel_forced);
if (ret)
wpa_printf(MSG_ERROR, "P2P: Own listen channel update "
"failed: %d", ret);
}
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_OPER_CHANNEL) {
u8 op_reg_class, op_channel, cfg_op_channel;
int ret = 0;
unsigned int r;
if (wpa_s->conf->p2p_oper_reg_class &&
wpa_s->conf->p2p_oper_channel) {
op_reg_class = wpa_s->conf->p2p_oper_reg_class;
op_channel = wpa_s->conf->p2p_oper_channel;
cfg_op_channel = 1;
} else {
op_reg_class = 81;
/*
* Use random operation channel from (1, 6, 11)
*if no other preference is indicated.
*/
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
op_channel = 1;
else
op_channel = 1 + (r % 3) * 5;
cfg_op_channel = 0;
}
ret = p2p_set_oper_channel(p2p, op_reg_class, op_channel,
cfg_op_channel);
if (ret)
wpa_printf(MSG_ERROR, "P2P: Own oper channel update "
"failed: %d", ret);
}
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) {
if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan,
wpa_s->conf->p2p_pref_chan) < 0) {
wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
"update failed");
}
if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) {
wpa_printf(MSG_ERROR, "P2P: No GO channel list "
"update failed");
}
}
if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PASSPHRASE_LEN)
p2p_set_passphrase_len(p2p, wpa_s->conf->p2p_passphrase_len);
}
int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
int duration)
{
if (!wpa_s->ap_iface)
return -1;
return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start,
duration);
}
int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
wpa_s->global->cross_connection = enabled;
p2p_set_cross_connect(wpa_s->global->p2p, enabled);
if (!enabled) {
struct wpa_supplicant *iface;
for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
{
if (iface->cross_connect_enabled == 0)
continue;
iface->cross_connect_enabled = 0;
iface->cross_connect_in_use = 0;
wpa_msg_global(iface->p2pdev, MSG_INFO,
P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
iface->ifname,
iface->cross_connect_uplink);
}
}
return 0;
}
static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink)
{
struct wpa_supplicant *iface;
if (!uplink->global->cross_connection)
return;
for (iface = uplink->global->ifaces; iface; iface = iface->next) {
if (!iface->cross_connect_enabled)
continue;
if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
0)
continue;
if (iface->ap_iface == NULL)
continue;
if (iface->cross_connect_in_use)
continue;
iface->cross_connect_in_use = 1;
wpa_msg_global(iface->p2pdev, MSG_INFO,
P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
iface->ifname, iface->cross_connect_uplink);
}
}
static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink)
{
struct wpa_supplicant *iface;
for (iface = uplink->global->ifaces; iface; iface = iface->next) {
if (!iface->cross_connect_enabled)
continue;
if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
0)
continue;
if (!iface->cross_connect_in_use)
continue;
wpa_msg_global(iface->p2pdev, MSG_INFO,
P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
iface->ifname, iface->cross_connect_uplink);
iface->cross_connect_in_use = 0;
}
}
void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
{
if (wpa_s->ap_iface || wpa_s->current_ssid == NULL ||
wpa_s->current_ssid->mode != WPAS_MODE_INFRA ||
wpa_s->cross_connect_disallowed)
wpas_p2p_disable_cross_connect(wpa_s);
else
wpas_p2p_enable_cross_connect(wpa_s);
if (!wpa_s->ap_iface &&
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
}
void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
{
wpas_p2p_disable_cross_connect(wpa_s);
if (!wpa_s->ap_iface &&
!eloop_is_timeout_registered(wpas_p2p_group_idle_timeout,
wpa_s, NULL))
wpas_p2p_set_group_idle_timeout(wpa_s);
}
static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s)
{
struct wpa_supplicant *iface;
if (!wpa_s->global->cross_connection)
return;
for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
if (iface == wpa_s)
continue;
if (iface->drv_flags &
WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
continue;
if ((iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) &&
iface != wpa_s->parent)
continue;
wpa_s->cross_connect_enabled = 1;
os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname,
sizeof(wpa_s->cross_connect_uplink));
wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from "
"%s to %s whenever uplink is available",
wpa_s->ifname, wpa_s->cross_connect_uplink);
if (iface->ap_iface || iface->current_ssid == NULL ||
iface->current_ssid->mode != WPAS_MODE_INFRA ||
iface->cross_connect_disallowed ||
iface->wpa_state != WPA_COMPLETED)
break;
wpa_s->cross_connect_in_use = 1;
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
wpa_s->ifname, wpa_s->cross_connect_uplink);
break;
}
}
int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
{
if (wpa_s->p2p_group_interface != P2P_GROUP_INTERFACE_CLIENT &&
!wpa_s->p2p_in_provisioning)
return 0; /* not P2P client operation */
wpa_printf(MSG_DEBUG, "P2P: Terminate connection due to WPS PBC "
"session overlap");
if (wpa_s != wpa_s->p2pdev)
wpa_msg_ctrl(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_OVERLAP);
wpas_p2p_group_formation_failed(wpa_s, 0);
return 1;
}
void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpas_p2p_notif_pbc_overlap(wpa_s);
}
void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
enum wpas_p2p_channel_update_trig trig)
{
struct p2p_channels chan, cli_chan;
struct wpa_used_freq_data *freqs = NULL;
unsigned int num = wpa_s->num_multichan_concurrent;
if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
return;
freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
if (!freqs)
return;
num = get_shared_radio_freqs_data(wpa_s, freqs, num);
os_memset(&chan, 0, sizeof(chan));
os_memset(&cli_chan, 0, sizeof(cli_chan));
if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan,
is_p2p_6ghz_disabled(wpa_s->global->p2p))) {
wpa_printf(MSG_ERROR, "P2P: Failed to update supported "
"channel list");
return;
}
p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
/*
* The used frequencies map changed, so it is possible that a GO is
* using a channel that is no longer valid for P2P use. It is also
* possible that due to policy consideration, it would be preferable to
* move it to a frequency already used by other station interfaces.
*/
wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig);
os_free(freqs);
}
static void wpas_p2p_scan_res_ignore(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res)
{
wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
}
int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
{
struct wpa_global *global = wpa_s->global;
int found = 0;
const u8 *peer;
if (global->p2p == NULL)
return -1;
wpa_printf(MSG_DEBUG, "P2P: Request to cancel group formation");
if (wpa_s->pending_interface_name[0] &&
!is_zero_ether_addr(wpa_s->pending_interface_addr))
found = 1;
peer = p2p_get_go_neg_peer(global->p2p);
if (peer) {
wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer "
MACSTR, MAC2STR(peer));
p2p_unauthorize(global->p2p, peer);
found = 1;
}
if (wpa_s->scan_res_handler == wpas_p2p_scan_res_join) {
wpa_printf(MSG_DEBUG, "P2P: Stop pending scan for join");
wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore;
found = 1;
}
if (wpa_s->pending_pd_before_join) {
wpa_printf(MSG_DEBUG, "P2P: Stop pending PD before join");
wpa_s->pending_pd_before_join = 0;
found = 1;
}
wpas_p2p_stop_find(wpa_s);
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (wpa_s == global->p2p_group_formation &&
(wpa_s->p2p_in_provisioning ||
wpa_s->parent->pending_interface_type ==
WPA_IF_P2P_CLIENT)) {
wpa_printf(MSG_DEBUG, "P2P: Interface %s in group "
"formation found - cancelling",
wpa_s->ifname);
found = 1;
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
if (wpa_s->p2p_in_provisioning) {
wpas_group_formation_completed(wpa_s, 0, 0);
break;
}
wpas_p2p_group_delete(wpa_s,
P2P_GROUP_REMOVAL_REQUESTED);
break;
} else if (wpa_s->p2p_in_invitation) {
wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling",
wpa_s->ifname);
found = 1;
wpas_p2p_group_formation_failed(wpa_s, 0);
break;
}
}
if (!found) {
wpa_printf(MSG_DEBUG, "P2P: No ongoing group formation found");
return -1;
}
return 0;
}
void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
{
if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
return;
wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not "
"being available anymore");
wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_UNAVAILABLE);
}
void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
int freq_24, int freq_5, int freq_overall)
{
struct p2p_data *p2p = wpa_s->global->p2p;
if (p2p == NULL)
return;
p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall);
}
int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr)
{
u8 peer[ETH_ALEN];
struct p2p_data *p2p = wpa_s->global->p2p;
if (p2p == NULL)
return -1;
if (hwaddr_aton(addr, peer))
return -1;
return p2p_unauthorize(p2p, peer);
}
/**
* wpas_p2p_disconnect - Disconnect from a P2P Group
* @wpa_s: Pointer to wpa_supplicant data
* Returns: 0 on success, -1 on failure
*
* This can be used to disconnect from a group in which the local end is a P2P
* Client or to end a P2P Group in case the local end is the Group Owner. If a
* virtual network interface was created for this group, that interface will be
* removed. Otherwise, only the configured P2P group network will be removed
* from the interface.
*/
int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s)
{
if (wpa_s == NULL)
return -1;
return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED) < 0 ?
-1 : 0;
}
int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
{
int ret;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return 0;
ret = p2p_in_progress(wpa_s->global->p2p);
if (ret == 0) {
/*
* Check whether there is an ongoing WPS provisioning step (or
* other parts of group formation) on another interface since
* p2p_in_progress() does not report this to avoid issues for
* scans during such provisioning step.
*/
if (wpa_s->global->p2p_group_formation &&
wpa_s->global->p2p_group_formation != wpa_s) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Another interface (%s) "
"in group formation",
wpa_s->global->p2p_group_formation->ifname);
ret = 1;
}
}
if (!ret && wpa_s->global->p2p_go_wait_client.sec) {
struct os_reltime now;
os_get_reltime(&now);
if (os_reltime_expired(&now, &wpa_s->global->p2p_go_wait_client,
P2P_MAX_INITIAL_CONN_WAIT_GO)) {
/* Wait for the first client has expired */
wpa_s->global->p2p_go_wait_client.sec = 0;
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Waiting for initial client connection during group formation");
ret = 1;
}
}
return ret;
}
void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (wpa_s->p2p_in_provisioning && ssid->p2p_group &&
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL) > 0) {
/**
* Remove the network by scheduling the group formation
* timeout to happen immediately. The teardown code
* needs to be scheduled to run asynch later so that we
* don't delete data from under ourselves unexpectedly.
* Calling wpas_p2p_group_formation_timeout directly
* causes a series of crashes in WPS failure scenarios.
*/
wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to "
"P2P group network getting removed");
eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL);
}
}
struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
const u8 *addr, const u8 *ssid,
size_t ssid_len)
{
struct wpa_ssid *s;
size_t i;
for (s = wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled != 2)
continue;
if (ssid &&
(ssid_len != s->ssid_len ||
os_memcmp(ssid, s->ssid, ssid_len) != 0))
continue;
if (addr == NULL) {
if (s->mode == WPAS_MODE_P2P_GO)
return s;
continue;
}
if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0)
return s; /* peer is GO in the persistent group */
if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
continue;
for (i = 0; i < s->num_p2p_clients; i++) {
if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
addr, ETH_ALEN) == 0)
return s; /* peer is P2P client in persistent
* group */
}
}
return NULL;
}
void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
const u8 *addr)
{
if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->p2pdev, NULL) > 0) {
/*
* This can happen if WPS provisioning step is not terminated
* cleanly (e.g., P2P Client does not send WSC_Done). Since the
* peer was able to connect, there is no need to time out group
* formation after this, though. In addition, this is used with
* the initial connection wait on the GO as a separate formation
* timeout and as such, expected to be hit after the initial WPS
* provisioning step.
*/
wpa_printf(MSG_DEBUG, "P2P: Canceled P2P group formation timeout on data connection");
if (!wpa_s->p2p_go_group_formation_completed &&
!wpa_s->group_formation_reported) {
/*
* GO has not yet notified group formation success since
* the WPS step was not completed cleanly. Do that
* notification now since the P2P Client was able to
* connect and as such, must have received the
* credential from the WPS step.
*/
if (wpa_s->global->p2p)
p2p_wps_success_cb(wpa_s->global->p2p, addr);
wpas_group_formation_completed(wpa_s, 1, 0);
}
}
if (!wpa_s->p2p_go_group_formation_completed) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on GO on first data connection");
wpa_s->p2p_go_group_formation_completed = 1;
wpa_s->global->p2p_group_formation = NULL;
wpa_s->p2p_in_provisioning = 0;
wpa_s->p2p_in_invitation = 0;
}
wpa_s->global->p2p_go_wait_client.sec = 0;
if (addr == NULL)
return;
wpas_p2p_add_persistent_group_client(wpa_s, addr);
}
static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
int group_added)
{
struct wpa_supplicant *group = wpa_s;
int ret = 0;
if (wpa_s->global->p2p_group_formation)
group = wpa_s->global->p2p_group_formation;
wpa_s = wpa_s->global->p2p_init_wpa_s;
offchannel_send_action_done(wpa_s);
if (group_added)
ret = wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT);
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation");
wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_persistent_id,
wpa_s->p2p_pd_before_go_neg,
wpa_s->p2p_go_ht40,
wpa_s->p2p_go_vht,
wpa_s->p2p_go_max_oper_chwidth,
wpa_s->p2p_go_he,
wpa_s->p2p_go_edmg,
NULL, 0, is_p2p_allow_6ghz(wpa_s->global->p2p));
return ret;
}
int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s)
{
int res;
if (!wpa_s->p2p_fallback_to_go_neg ||
wpa_s->p2p_in_provisioning <= 5)
return 0;
if (wpas_p2p_peer_go(wpa_s, wpa_s->pending_join_dev_addr) > 0)
return 0; /* peer operating as a GO */
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - "
"fallback to GO Negotiation");
wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG
"reason=GO-not-found");
res = wpas_p2p_fallback_to_go_neg(wpa_s, 1);
return res == 1 ? 2 : 1;
}
unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s)
{
struct wpa_supplicant *ifs;
if (wpa_s->wpa_state > WPA_SCANNING) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to "
"concurrent operation",
wpa_s->conf->p2p_search_delay);
return wpa_s->conf->p2p_search_delay;
}
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
radio_list) {
if (ifs != wpa_s && ifs->wpa_state > WPA_SCANNING) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search "
"delay due to concurrent operation on "
"interface %s",
wpa_s->conf->p2p_search_delay,
ifs->ifname);
return wpa_s->conf->p2p_search_delay;
}
}
return 0;
}
static int wpas_p2p_remove_psk_entry(struct wpa_supplicant *wpa_s,
struct wpa_ssid *s, const u8 *addr,
int iface_addr)
{
struct psk_list_entry *psk, *tmp;
int changed = 0;
dl_list_for_each_safe(psk, tmp, &s->psk_list, struct psk_list_entry,
list) {
if ((iface_addr && !psk->p2p &&
os_memcmp(addr, psk->addr, ETH_ALEN) == 0) ||
(!iface_addr && psk->p2p &&
os_memcmp(addr, psk->addr, ETH_ALEN) == 0)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Remove persistent group PSK list entry for "
MACSTR " p2p=%u",
MAC2STR(psk->addr), psk->p2p);
dl_list_del(&psk->list);
os_free(psk);
changed++;
}
}
return changed;
}
void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
const u8 *p2p_dev_addr,
const u8 *psk, size_t psk_len)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
struct wpa_ssid *persistent;
struct psk_list_entry *p, *last;
if (psk_len != sizeof(p->psk))
return;
if (p2p_dev_addr) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR
" p2p_dev_addr=" MACSTR,
MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
if (is_zero_ether_addr(p2p_dev_addr))
p2p_dev_addr = NULL;
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR,
MAC2STR(mac_addr));
}
if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: new_psk_cb during group formation");
/* To be added to persistent group once created */
if (wpa_s->global->add_psk == NULL) {
wpa_s->global->add_psk = os_zalloc(sizeof(*p));
if (wpa_s->global->add_psk == NULL)
return;
}
p = wpa_s->global->add_psk;
if (p2p_dev_addr) {
p->p2p = 1;
os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN);
} else {
p->p2p = 0;
os_memcpy(p->addr, mac_addr, ETH_ALEN);
}
os_memcpy(p->psk, psk, psk_len);
return;
}
if (ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Ignore new_psk_cb on not-persistent GO");
return;
}
persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, NULL, ssid->ssid,
ssid->ssid_len);
if (!persistent) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not find persistent group information to store the new PSK");
return;
}
p = os_zalloc(sizeof(*p));
if (p == NULL)
return;
if (p2p_dev_addr) {
p->p2p = 1;
os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN);
} else {
p->p2p = 0;
os_memcpy(p->addr, mac_addr, ETH_ALEN);
}
os_memcpy(p->psk, psk, psk_len);
if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS &&
(last = dl_list_last(&persistent->psk_list,
struct psk_list_entry, list))) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove oldest PSK entry for "
MACSTR " (p2p=%u) to make room for a new one",
MAC2STR(last->addr), last->p2p);
dl_list_del(&last->list);
os_free(last);
}
wpas_p2p_remove_psk_entry(wpa_s->p2pdev, persistent,
p2p_dev_addr ? p2p_dev_addr : mac_addr,
p2p_dev_addr == NULL);
if (p2p_dev_addr) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for p2p_dev_addr="
MACSTR, MAC2STR(p2p_dev_addr));
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for addr=" MACSTR,
MAC2STR(mac_addr));
}
dl_list_add(&persistent->psk_list, &p->list);
if (wpa_s->p2pdev->conf->update_config &&
wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf))
wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
}
static void wpas_p2p_remove_psk(struct wpa_supplicant *wpa_s,
struct wpa_ssid *s, const u8 *addr,
int iface_addr)
{
int res;
res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr);
if (res > 0 && wpa_s->conf->update_config &&
wpa_config_write(wpa_s->confname, wpa_s->conf))
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Failed to update configuration");
}
static void wpas_p2p_remove_client_go(struct wpa_supplicant *wpa_s,
const u8 *peer, int iface_addr)
{
struct hostapd_data *hapd;
struct hostapd_wpa_psk *psk, *prev, *rem;
struct sta_info *sta;
if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL ||
wpa_s->current_ssid->mode != WPAS_MODE_P2P_GO)
return;
/* Remove per-station PSK entry */
hapd = wpa_s->ap_iface->bss[0];
prev = NULL;
psk = hapd->conf->ssid.wpa_psk;
while (psk) {
if ((iface_addr && os_memcmp(peer, psk->addr, ETH_ALEN) == 0) ||
(!iface_addr &&
os_memcmp(peer, psk->p2p_dev_addr, ETH_ALEN) == 0)) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove operating group PSK entry for "
MACSTR " iface_addr=%d",
MAC2STR(peer), iface_addr);
if (prev)
prev->next = psk->next;
else
hapd->conf->ssid.wpa_psk = psk->next;
rem = psk;
psk = psk->next;
os_free(rem);
} else {
prev = psk;
psk = psk->next;
}
}
/* Disconnect from group */
if (iface_addr)
sta = ap_get_sta(hapd, peer);
else
sta = ap_get_sta_p2p(hapd, peer);
if (sta) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disconnect peer " MACSTR
" (iface_addr=%d) from group",
MAC2STR(peer), iface_addr);
hostapd_drv_sta_deauth(hapd, sta->addr,
WLAN_REASON_DEAUTH_LEAVING);
ap_sta_deauthenticate(hapd, sta, WLAN_REASON_DEAUTH_LEAVING);
}
}
void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
int iface_addr)
{
struct wpa_ssid *s;
struct wpa_supplicant *w;
struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer));
/* Remove from any persistent group */
for (s = p2p_wpa_s->conf->ssid; s; s = s->next) {
if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
continue;
if (!iface_addr)
wpas_remove_persistent_peer(p2p_wpa_s, s, peer, 0);
wpas_p2p_remove_psk(p2p_wpa_s, s, peer, iface_addr);
}
/* Remove from any operating group */
for (w = wpa_s->global->ifaces; w; w = w->next)
wpas_p2p_remove_client_go(w, peer, iface_addr);
}
static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_PSK_FAILURE);
}
static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - terminate group");
wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FREQ_CONFLICT);
}
int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq,
struct wpa_ssid *ssid)
{
struct wpa_supplicant *iface;
for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
if (!iface->current_ssid ||
iface->current_ssid->frequency == freq ||
(iface->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
!iface->current_ssid->p2p_group))
continue;
/* Remove the connection with least priority */
if (!wpas_is_p2p_prioritized(iface)) {
/* STA connection has priority over existing
* P2P connection, so remove the interface. */
wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to single channel concurrent mode frequency conflict");
eloop_register_timeout(0, 0,
wpas_p2p_group_freq_conflict,
iface, NULL);
/* If connection in progress is P2P connection, do not
* proceed for the connection. */
if (wpa_s == iface)
return -1;
else
return 0;
} else {
/* P2P connection has priority, disable the STA network
*/
wpa_supplicant_disable_network(wpa_s->global->ifaces,
ssid);
wpa_msg(wpa_s->global->ifaces, MSG_INFO,
WPA_EVENT_FREQ_CONFLICT " id=%d", ssid->id);
os_memset(wpa_s->global->ifaces->pending_bssid, 0,
ETH_ALEN);
/* If P2P connection is in progress, continue
* connecting...*/
if (wpa_s == iface)
return 0;
else
return -1;
}
}
return 0;
}
int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
if (ssid == NULL || !ssid->p2p_group)
return 0;
if (wpa_s->p2p_last_4way_hs_fail &&
wpa_s->p2p_last_4way_hs_fail == ssid) {
u8 go_dev_addr[ETH_ALEN];
struct wpa_ssid *persistent;
if (wpas_p2p_persistent_group(wpa_s, go_dev_addr,
ssid->ssid,
ssid->ssid_len) <= 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not determine whether 4-way handshake failures were for a persistent group");
goto disconnect;
}
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Two 4-way handshake failures for a P2P group - go_dev_addr="
MACSTR, MAC2STR(go_dev_addr));
persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, go_dev_addr,
ssid->ssid,
ssid->ssid_len);
if (persistent == NULL || persistent->mode != WPAS_MODE_INFRA) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No matching persistent group stored");
goto disconnect;
}
wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
P2P_EVENT_PERSISTENT_PSK_FAIL "%d",
persistent->id);
disconnect:
wpa_s->p2p_last_4way_hs_fail = NULL;
/*
* Remove the group from a timeout to avoid issues with caller
* continuing to use the interface if this is on a P2P group
* interface.
*/
eloop_register_timeout(0, 0, wpas_p2p_psk_failure_removal,
wpa_s, NULL);
return 1;
}
wpa_s->p2p_last_4way_hs_fail = ssid;
return 0;
}
#ifdef CONFIG_WPS_NFC
static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc,
struct wpabuf *p2p)
{
struct wpabuf *ret;
size_t wsc_len;
if (p2p == NULL) {
wpabuf_free(wsc);
wpa_printf(MSG_DEBUG, "P2P: No p2p buffer for handover");
return NULL;
}
wsc_len = wsc ? wpabuf_len(wsc) : 0;
ret = wpabuf_alloc(2 + wsc_len + 2 + wpabuf_len(p2p));
if (ret == NULL) {
wpabuf_free(wsc);
wpabuf_free(p2p);
return NULL;
}
wpabuf_put_be16(ret, wsc_len);
if (wsc)
wpabuf_put_buf(ret, wsc);
wpabuf_put_be16(ret, wpabuf_len(p2p));
wpabuf_put_buf(ret, p2p);
wpabuf_free(wsc);
wpabuf_free(p2p);
wpa_hexdump_buf(MSG_DEBUG,
"P2P: Generated NFC connection handover message", ret);
if (ndef && ret) {
struct wpabuf *tmp;
tmp = ndef_build_p2p(ret);
wpabuf_free(ret);
if (tmp == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Failed to NDEF encapsulate handover request");
return NULL;
}
ret = tmp;
}
return ret;
}
static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s,
struct wpa_ssid **ssid, u8 *go_dev_addr)
{
struct wpa_supplicant *iface;
if (go_dev_addr)
os_memset(go_dev_addr, 0, ETH_ALEN);
if (ssid)
*ssid = NULL;
for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
if (iface->wpa_state < WPA_ASSOCIATING ||
iface->current_ssid == NULL || iface->assoc_freq == 0 ||
!iface->current_ssid->p2p_group ||
iface->current_ssid->mode != WPAS_MODE_INFRA)
continue;
if (ssid)
*ssid = iface->current_ssid;
if (go_dev_addr)
os_memcpy(go_dev_addr, iface->go_dev_addr, ETH_ALEN);
return iface->assoc_freq;
}
return 0;
}
struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
int ndef)
{
struct wpabuf *wsc, *p2p;
struct wpa_ssid *ssid;
u8 go_dev_addr[ETH_ALEN];
int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
wpa_printf(MSG_DEBUG, "P2P: P2P disabled - cannot build handover request");
return NULL;
}
if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
&wpa_s->conf->wps_nfc_dh_privkey) < 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No DH key available for handover request");
return NULL;
}
if (cli_freq == 0) {
wsc = wps_build_nfc_handover_req_p2p(
wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey);
} else
wsc = NULL;
p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p, cli_freq,
go_dev_addr, ssid ? ssid->ssid : NULL,
ssid ? ssid->ssid_len : 0);
return wpas_p2p_nfc_handover(ndef, wsc, p2p);
}
struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
int ndef, int tag)
{
struct wpabuf *wsc, *p2p;
struct wpa_ssid *ssid;
u8 go_dev_addr[ETH_ALEN];
int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return NULL;
if (!tag && wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
&wpa_s->conf->wps_nfc_dh_privkey) < 0)
return NULL;
if (cli_freq == 0) {
wsc = wps_build_nfc_handover_sel_p2p(
wpa_s->parent->wps,
tag ? wpa_s->conf->wps_nfc_dev_pw_id :
DEV_PW_NFC_CONNECTION_HANDOVER,
wpa_s->conf->wps_nfc_dh_pubkey,
tag ? wpa_s->conf->wps_nfc_dev_pw : NULL);
} else
wsc = NULL;
p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p, cli_freq,
go_dev_addr, ssid ? ssid->ssid : NULL,
ssid ? ssid->ssid_len : 0);
return wpas_p2p_nfc_handover(ndef, wsc, p2p);
}
static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s,
struct p2p_nfc_params *params)
{
wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC "
"connection handover (freq=%d)",
params->go_freq);
if (params->go_freq && params->go_ssid_len) {
wpa_s->p2p_wps_method = WPS_NFC;
wpa_s->pending_join_wps_method = WPS_NFC;
os_memset(wpa_s->pending_join_iface_addr, 0, ETH_ALEN);
os_memcpy(wpa_s->pending_join_dev_addr, params->go_dev_addr,
ETH_ALEN);
return wpas_p2p_join_start(wpa_s, params->go_freq,
params->go_ssid,
params->go_ssid_len);
}
return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
params->go_freq, wpa_s->p2p_go_vht_center_freq2,
-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
params->go_ssid_len ? params->go_ssid : NULL,
params->go_ssid_len, false);
}
static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s,
struct p2p_nfc_params *params, int tag)
{
int res, persistent;
struct wpa_ssid *ssid;
wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC "
"connection handover");
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
ssid = wpa_s->current_ssid;
if (ssid == NULL)
continue;
if (ssid->mode != WPAS_MODE_P2P_GO)
continue;
if (wpa_s->ap_iface == NULL)
continue;
break;
}
if (wpa_s == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface");
return -1;
}
if (wpa_s->p2pdev->p2p_oob_dev_pw_id !=
DEV_PW_NFC_CONNECTION_HANDOVER &&
!wpa_s->p2pdev->p2p_oob_dev_pw) {
wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
return -1;
}
res = wpas_ap_wps_add_nfc_pw(
wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id,
wpa_s->p2pdev->p2p_oob_dev_pw,
wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ?
wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL);
if (res)
return res;
if (!tag) {
wpa_printf(MSG_DEBUG, "P2P: Negotiated handover - wait for peer to join without invitation");
return 0;
}
if (!params->peer ||
!(params->peer->dev_capab & P2P_DEV_CAPAB_INVITATION_PROCEDURE))
return 0;
wpa_printf(MSG_DEBUG, "P2P: Static handover - invite peer " MACSTR
" to join", MAC2STR(params->peer->p2p_device_addr));
wpa_s->global->p2p_invite_group = wpa_s;
persistent = ssid->p2p_persistent_group &&
wpas_p2p_get_persistent(wpa_s->p2pdev,
params->peer->p2p_device_addr,
ssid->ssid, ssid->ssid_len);
wpa_s->p2pdev->pending_invite_ssid_id = -1;
return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr,
P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr,
ssid->ssid, ssid->ssid_len, ssid->frequency,
wpa_s->global->p2p_dev_addr, persistent, 0,
wpa_s->p2pdev->p2p_oob_dev_pw_id);
}
static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s,
struct p2p_nfc_params *params,
int forced_freq)
{
wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC "
"connection handover");
return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
forced_freq, wpa_s->p2p_go_vht_center_freq2,
-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
NULL, 0, false);
}
static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
struct p2p_nfc_params *params,
int forced_freq)
{
int res;
wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC "
"connection handover");
res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
forced_freq, wpa_s->p2p_go_vht_center_freq2,
-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
wpa_s->p2p_go_he, wpa_s->p2p_go_edmg,
NULL, 0, false);
if (res)
return res;
res = wpas_p2p_listen(wpa_s, 60);
if (res) {
p2p_unauthorize(wpa_s->global->p2p,
params->peer->p2p_device_addr);
}
return res;
}
static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
const struct wpabuf *data,
int sel, int tag, int forced_freq)
{
const u8 *pos, *end;
u16 len, id;
struct p2p_nfc_params params;
int res;
os_memset(&params, 0, sizeof(params));
params.sel = sel;
wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data);
pos = wpabuf_head(data);
end = pos + wpabuf_len(data);
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC "
"attributes");
return -1;
}
len = WPA_GET_BE16(pos);
pos += 2;
if (len > end - pos) {
wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC "
"attributes");
return -1;
}
params.wsc_attr = pos;
params.wsc_len = len;
pos += len;
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P "
"attributes");
return -1;
}
len = WPA_GET_BE16(pos);
pos += 2;
if (len > end - pos) {
wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P "
"attributes");
return -1;
}
params.p2p_attr = pos;
params.p2p_len = len;
pos += len;
wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes",
params.wsc_attr, params.wsc_len);
wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes",
params.p2p_attr, params.p2p_len);
if (pos < end) {
wpa_hexdump(MSG_DEBUG,
"P2P: Ignored extra data after P2P attributes",
pos, end - pos);
}
res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, &params);
if (res)
return res;
if (params.next_step == NO_ACTION)
return 0;
if (params.next_step == BOTH_GO) {
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_BOTH_GO "peer=" MACSTR,
MAC2STR(params.peer->p2p_device_addr));
return 0;
}
if (params.next_step == PEER_CLIENT) {
if (!is_zero_ether_addr(params.go_dev_addr)) {
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
"peer=" MACSTR " freq=%d go_dev_addr=" MACSTR
" ssid=\"%s\"",
MAC2STR(params.peer->p2p_device_addr),
params.go_freq,
MAC2STR(params.go_dev_addr),
wpa_ssid_txt(params.go_ssid,
params.go_ssid_len));
} else {
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
"peer=" MACSTR " freq=%d",
MAC2STR(params.peer->p2p_device_addr),
params.go_freq);
}
return 0;
}
if (wpas_p2p_cli_freq(wpa_s, NULL, NULL)) {
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_WHILE_CLIENT "peer="
MACSTR, MAC2STR(params.peer->p2p_device_addr));
return 0;
}
wpabuf_free(wpa_s->p2p_oob_dev_pw);
wpa_s->p2p_oob_dev_pw = NULL;
if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw "
"received");
return -1;
}
id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN);
wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id);
wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash",
params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash,
params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
wpa_s->p2p_peer_oob_pk_hash_known = 1;
if (tag) {
if (id < 0x10) {
wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid "
"peer OOB Device Password Id %u", id);
return -1;
}
wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB "
"Device Password Id %u", id);
wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password",
params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
params.oob_dev_pw_len -
WPS_OOB_PUBKEY_HASH_LEN - 2);
wpa_s->p2p_oob_dev_pw_id = id;
wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy(
params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
params.oob_dev_pw_len -
WPS_OOB_PUBKEY_HASH_LEN - 2);
if (wpa_s->p2p_oob_dev_pw == NULL)
return -1;
if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
&wpa_s->conf->wps_nfc_dh_privkey) < 0)
return -1;
} else {
wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake "
"without Device Password");
wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
}
switch (params.next_step) {
case NO_ACTION:
case BOTH_GO:
case PEER_CLIENT:
/* already covered above */
return 0;
case JOIN_GROUP:
return wpas_p2p_nfc_join_group(wpa_s, &params);
case AUTH_JOIN:
return wpas_p2p_nfc_auth_join(wpa_s, &params, tag);
case INIT_GO_NEG:
return wpas_p2p_nfc_init_go_neg(wpa_s, &params, forced_freq);
case RESP_GO_NEG:
/* TODO: use own OOB Dev Pw */
return wpas_p2p_nfc_resp_go_neg(wpa_s, &params, forced_freq);
}
return -1;
}
int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
const struct wpabuf *data, int forced_freq)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1, forced_freq);
}
int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
const struct wpabuf *req,
const struct wpabuf *sel, int forced_freq)
{
struct wpabuf *tmp;
int ret;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported");
wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req",
wpabuf_head(req), wpabuf_len(req));
wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel",
wpabuf_head(sel), wpabuf_len(sel));
if (forced_freq)
wpa_printf(MSG_DEBUG, "NFC: Forced freq %d", forced_freq);
tmp = ndef_parse_p2p(init ? sel : req);
if (tmp == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF");
return -1;
}
ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0,
forced_freq);
wpabuf_free(tmp);
return ret;
}
int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled)
{
const u8 *if_addr;
int go_intent = wpa_s->conf->p2p_go_intent;
struct wpa_supplicant *iface;
if (wpa_s->global->p2p == NULL)
return -1;
if (!enabled) {
wpa_printf(MSG_DEBUG, "P2P: Disable use of own NFC Tag");
for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
{
if (!iface->ap_iface)
continue;
hostapd_wps_nfc_token_disable(iface->ap_iface->bss[0]);
}
p2p_set_authorized_oob_dev_pw_id(wpa_s->global->p2p, 0,
0, NULL);
if (wpa_s->p2p_nfc_tag_enabled)
wpas_p2p_remove_pending_group_interface(wpa_s);
wpa_s->p2p_nfc_tag_enabled = 0;
return 0;
}
if (wpa_s->global->p2p_disabled)
return -1;
if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
wpa_s->conf->wps_nfc_dh_privkey == NULL ||
wpa_s->conf->wps_nfc_dev_pw == NULL ||
wpa_s->conf->wps_nfc_dev_pw_id < 0x10) {
wpa_printf(MSG_DEBUG, "P2P: NFC password token not configured "
"to allow static handover cases");
return -1;
}
wpa_printf(MSG_DEBUG, "P2P: Enable use of own NFC Tag");
wpa_s->p2p_oob_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
wpabuf_free(wpa_s->p2p_oob_dev_pw);
wpa_s->p2p_oob_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
if (wpa_s->p2p_oob_dev_pw == NULL)
return -1;
wpa_s->p2p_peer_oob_pk_hash_known = 0;
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO ||
wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) {
/*
* P2P Group Interface present and the command came on group
* interface, so enable the token for the current interface.
*/
wpa_s->create_p2p_iface = 0;
} else {
wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
}
if (wpa_s->create_p2p_iface) {
enum wpa_driver_if_type iftype;
/* Prepare to add a new interface for the group */
iftype = WPA_IF_P2P_GROUP;
if (go_intent == 15)
iftype = WPA_IF_P2P_GO;
if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
"interface for the group");
return -1;
}
if_addr = wpa_s->pending_interface_addr;
} else if (wpa_s->p2p_mgmt)
if_addr = wpa_s->parent->own_addr;
else
if_addr = wpa_s->own_addr;
wpa_s->p2p_nfc_tag_enabled = enabled;
for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
struct hostapd_data *hapd;
if (iface->ap_iface == NULL)
continue;
hapd = iface->ap_iface->bss[0];
wpabuf_free(hapd->conf->wps_nfc_dh_pubkey);
hapd->conf->wps_nfc_dh_pubkey =
wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
wpabuf_free(hapd->conf->wps_nfc_dh_privkey);
hapd->conf->wps_nfc_dh_privkey =
wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
wpabuf_free(hapd->conf->wps_nfc_dev_pw);
hapd->conf->wps_nfc_dev_pw =
wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
hapd->conf->wps_nfc_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
if (hostapd_wps_nfc_token_enable(iface->ap_iface->bss[0]) < 0) {
wpa_dbg(iface, MSG_DEBUG,
"P2P: Failed to enable NFC Tag for GO");
}
}
p2p_set_authorized_oob_dev_pw_id(
wpa_s->global->p2p, wpa_s->conf->wps_nfc_dev_pw_id, go_intent,
if_addr);
return 0;
}
#endif /* CONFIG_WPS_NFC */
static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs,
unsigned int num)
{
u8 curr_chan, cand, chan;
unsigned int i;
/*
* If possible, optimize the Listen channel to be a channel that is
* already used by one of the other interfaces.
*/
if (!wpa_s->conf->p2p_optimize_listen_chan)
return;
if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
return;
curr_chan = p2p_get_listen_channel(wpa_s->global->p2p);
for (i = 0, cand = 0; i < num; i++) {
ieee80211_freq_to_chan(freqs[i].freq, &chan);
if (curr_chan == chan) {
cand = 0;
break;
}
if (chan == 1 || chan == 6 || chan == 11)
cand = chan;
}
if (cand) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Update Listen channel to %u based on operating channel",
cand);
p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0);
}
}
static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
{
struct hostapd_config *conf;
struct p2p_go_neg_results params;
struct csa_settings csa_settings;
struct wpa_ssid *current_ssid = wpa_s->current_ssid;
int old_freq = current_ssid->frequency;
int ret;
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
return -1;
}
/*
* TODO: This function may not always work correctly. For example,
* when we have a running GO and a BSS on a DFS channel.
*/
if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, 0, 0, 0, 0,
NULL)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P CSA: Failed to select new frequency for GO");
return -1;
}
if (current_ssid->frequency == params.freq) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P CSA: Selected same frequency - not moving GO");
return 0;
}
conf = hostapd_config_defaults();
if (!conf) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P CSA: Failed to allocate default config");
return -1;
}
current_ssid->frequency = params.freq;
if (wpa_supplicant_conf_ap_ht(wpa_s, current_ssid, conf)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P CSA: Failed to create new GO config");
ret = -1;
goto out;
}
if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P CSA: CSA to a different band is not supported");
ret = -1;
goto out;
}
os_memset(&csa_settings, 0, sizeof(csa_settings));
csa_settings.cs_count = P2P_GO_CSA_COUNT;
csa_settings.block_tx = P2P_GO_CSA_BLOCK_TX;
csa_settings.freq_params.freq = params.freq;
csa_settings.freq_params.sec_channel_offset = conf->secondary_channel;
csa_settings.freq_params.ht_enabled = conf->ieee80211n;
csa_settings.freq_params.bandwidth = conf->secondary_channel ? 40 : 20;
if (conf->ieee80211ac) {
int freq1 = 0, freq2 = 0;
u8 chan, opclass;
if (ieee80211_freq_to_channel_ext(params.freq,
conf->secondary_channel,
conf->vht_oper_chwidth,
&opclass, &chan) ==
NUM_HOSTAPD_MODES) {
wpa_printf(MSG_ERROR, "P2P CSA: Bad freq");
ret = -1;
goto out;
}
if (conf->vht_oper_centr_freq_seg0_idx)
freq1 = ieee80211_chan_to_freq(
NULL, opclass,
conf->vht_oper_centr_freq_seg0_idx);
if (conf->vht_oper_centr_freq_seg1_idx)
freq2 = ieee80211_chan_to_freq(
NULL, opclass,
conf->vht_oper_centr_freq_seg1_idx);
if (freq1 < 0 || freq2 < 0) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P CSA: Selected invalid VHT center freqs");
ret = -1;
goto out;
}
csa_settings.freq_params.vht_enabled = conf->ieee80211ac;
csa_settings.freq_params.center_freq1 = freq1;
csa_settings.freq_params.center_freq2 = freq2;
switch (conf->vht_oper_chwidth) {
case CHANWIDTH_80MHZ:
case CHANWIDTH_80P80MHZ:
csa_settings.freq_params.bandwidth = 80;
break;
case CHANWIDTH_160MHZ:
csa_settings.freq_params.bandwidth = 160;
break;
}
}
ret = ap_switch_channel(wpa_s, &csa_settings);
out:
current_ssid->frequency = old_freq;
hostapd_config_free(conf);
return ret;
}
static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s)
{
struct p2p_go_neg_results params;
struct wpa_ssid *current_ssid = wpa_s->current_ssid;
void (*ap_configured_cb)(void *ctx, void *data);
void *ap_configured_cb_ctx, *ap_configured_cb_data;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz",
current_ssid->frequency);
/* Stop the AP functionality */
/* TODO: Should do this in a way that does not indicated to possible
* P2P Clients in the group that the group is terminated. */
/* If this action occurs before a group is started, the callback should
* be preserved, or GROUP-STARTED event would be lost. If this action
* occurs after a group is started, these pointers are all NULL and
* harmless. */
ap_configured_cb = wpa_s->ap_configured_cb;
ap_configured_cb_ctx = wpa_s->ap_configured_cb_ctx;
ap_configured_cb_data = wpa_s->ap_configured_cb_data;
wpa_supplicant_ap_deinit(wpa_s);
/* Reselect the GO frequency */
if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, 0, 0, 0, 0,
NULL)) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq");
wpas_p2p_group_delete(wpa_s,
P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
return;
}
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New freq selected for the GO (%u MHz)",
params.freq);
if (params.freq &&
!p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
wpa_printf(MSG_DEBUG,
"P2P: Selected freq (%u MHz) is not valid for P2P",
params.freq);
wpas_p2p_group_delete(wpa_s,
P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
return;
}
/* Restore preserved callback parameters */
wpa_s->ap_configured_cb = ap_configured_cb;
wpa_s->ap_configured_cb_ctx = ap_configured_cb_ctx;
wpa_s->ap_configured_cb_data = ap_configured_cb_data;
/* Update the frequency */
current_ssid->frequency = params.freq;
wpa_s->connect_without_scan = current_ssid;
wpa_s->reassociate = 1;
wpa_s->disconnected = 0;
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (!wpa_s->ap_iface || !wpa_s->current_ssid)
return;
wpas_p2p_go_update_common_freqs(wpa_s);
/* Do not move GO in the middle of a CSA */
if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
wpa_printf(MSG_DEBUG,
"P2P: CSA is in progress - not moving GO");
return;
}
/*
* First, try a channel switch flow. If it is not supported or fails,
* take down the GO and bring it up again.
*/
if (wpas_p2p_move_go_csa(wpa_s) < 0)
wpas_p2p_move_go_no_csa(wpa_s);
}
static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_used_freq_data *freqs = NULL;
unsigned int num = wpa_s->num_multichan_concurrent;
freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
if (!freqs)
return;
num = get_shared_radio_freqs_data(wpa_s, freqs, num);
/* Previous attempt to move a GO was not possible -- try again. */
wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
WPAS_P2P_CHANNEL_UPDATE_ANY);
os_free(freqs);
}
/*
* Consider moving a GO from its currently used frequency:
* 1. It is possible that due to regulatory consideration the frequency
* can no longer be used and there is a need to evacuate the GO.
* 2. It is possible that due to MCC considerations, it would be preferable
* to move the GO to a channel that is currently used by some other
* station interface.
*
* In case a frequency that became invalid is once again valid, cancel a
* previously initiated GO frequency change.
*/
static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs,
unsigned int num)
{
unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
unsigned int timeout;
int freq;
int dfs_offload;
wpas_p2p_go_update_common_freqs(wpa_s);
freq = wpa_s->current_ssid->frequency;
dfs_offload = (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes);
for (i = 0, invalid_freq = 0; i < num; i++) {
if (freqs[i].freq == freq) {
flags = freqs[i].flags;
/* The channel is invalid, must change it */
if (!p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
!dfs_offload) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Freq=%d MHz no longer valid for GO",
freq);
invalid_freq = 1;
}
} else if (freqs[i].flags == 0) {
/* Freq is not used by any other station interface */
continue;
} else if (!p2p_supported_freq(wpa_s->global->p2p,
freqs[i].freq) && !dfs_offload) {
/* Freq is not valid for P2P use cases */
continue;
} else if (wpa_s->conf->p2p_go_freq_change_policy ==
P2P_GO_FREQ_MOVE_SCM) {
policy_move = 1;
} else if (wpa_s->conf->p2p_go_freq_change_policy ==
P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS &&
wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
policy_move = 1;
} else if ((wpa_s->conf->p2p_go_freq_change_policy ==
P2P_GO_FREQ_MOVE_SCM_ECSA) &&
wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
if (!p2p_get_group_num_members(wpa_s->p2p_group)) {
policy_move = 1;
} else if ((wpa_s->drv_flags &
WPA_DRIVER_FLAGS_AP_CSA) &&
wpas_p2p_go_clients_support_ecsa(wpa_s)) {
u8 chan;
/*
* We do not support CSA between bands, so move
* GO only within the same band.
*/
if (wpa_s->ap_iface->current_mode->mode ==
ieee80211_freq_to_chan(freqs[i].freq,
&chan))
policy_move = 1;
}
}
}
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: GO move: invalid_freq=%u, policy_move=%u, flags=0x%X",
invalid_freq, policy_move, flags);
/*
* The channel is valid, or we are going to have a policy move, so
* cancel timeout.
*/
if (!invalid_freq || policy_move) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Cancel a GO move from freq=%d MHz", freq);
eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
if (wpas_p2p_in_progress(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move");
eloop_cancel_timeout(wpas_p2p_reconsider_moving_go,
wpa_s, NULL);
eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
wpas_p2p_reconsider_moving_go,
wpa_s, NULL);
return;
}
}
if (!invalid_freq && (!policy_move || flags != 0)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Not initiating a GO frequency change");
return;
}
/*
* Do not consider moving GO if it is in the middle of a CSA. When the
* CSA is finished this flow should be retriggered.
*/
if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Not initiating a GO frequency change - CSA is in progress");
return;
}
if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq))
timeout = P2P_GO_FREQ_CHANGE_TIME;
else
timeout = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz in %d secs",
freq, timeout);
eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL);
}
static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs,
unsigned int num,
enum wpas_p2p_channel_update_trig trig)
{
struct wpa_supplicant *ifs;
eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX,
NULL);
/*
* Travers all the radio interfaces, and for each GO interface, check
* if there is a need to move the GO from the frequency it is using,
* or in case the frequency is valid again, cancel the evacuation flow.
*/
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
radio_list) {
if (ifs->current_ssid == NULL ||
ifs->current_ssid->mode != WPAS_MODE_P2P_GO)
continue;
/*
* The GO was just started or completed channel switch, no need
* to move it.
*/
if (wpa_s == ifs &&
(trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE ||
trig == WPAS_P2P_CHANNEL_UPDATE_CS)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: GO move - schedule re-consideration");
eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
wpas_p2p_reconsider_moving_go,
wpa_s, NULL);
continue;
}
wpas_p2p_consider_moving_one_go(ifs, freqs, num);
}
}
void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
wpas_p2p_update_channel_list(wpa_s,
WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE);
}
void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
{
if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
"the management interface is being removed");
wpas_p2p_deinit_global(wpa_s->global);
}
}
void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
{
if (wpa_s->ap_iface->bss)
wpa_s->ap_iface->bss[0]->p2p_group = NULL;
wpas_p2p_group_deinit(wpa_s);
}
int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq,
unsigned int period, unsigned int interval,
unsigned int count)
{
struct p2p_data *p2p = wpa_s->global->p2p;
u8 *device_types;
size_t dev_types_len;
struct wpabuf *buf;
int ret;
if (wpa_s->p2p_lo_started) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P Listen offload is already started");
return 0;
}
if (wpa_s->global->p2p == NULL ||
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) {
wpa_printf(MSG_DEBUG, "P2P: Listen offload not supported");
return -1;
}
if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
wpa_printf(MSG_ERROR, "P2P: Input channel not supported: %u",
freq);
return -1;
}
/* Get device type */
dev_types_len = (wpa_s->conf->num_sec_device_types + 1) *
WPS_DEV_TYPE_LEN;
device_types = os_malloc(dev_types_len);
if (!device_types)
return -1;
os_memcpy(device_types, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN);
os_memcpy(&device_types[WPS_DEV_TYPE_LEN], wpa_s->conf->sec_device_type,
wpa_s->conf->num_sec_device_types * WPS_DEV_TYPE_LEN);
/* Get Probe Response IE(s) */
buf = p2p_build_probe_resp_template(p2p, freq);
if (!buf) {
os_free(device_types);
return -1;
}
ret = wpa_drv_p2p_lo_start(wpa_s, freq, period, interval, count,
device_types, dev_types_len,
wpabuf_mhead_u8(buf), wpabuf_len(buf));
if (ret < 0)
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Failed to start P2P listen offload");
os_free(device_types);
wpabuf_free(buf);
if (ret == 0) {
wpa_s->p2p_lo_started = 1;
/* Stop current P2P listen if any */
wpas_stop_listen(wpa_s);
}
return ret;
}
int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s)
{
int ret;
if (!wpa_s->p2p_lo_started)
return 0;
ret = wpa_drv_p2p_lo_stop(wpa_s);
if (ret < 0)
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Failed to stop P2P listen offload");
wpa_s->p2p_lo_started = 0;
return ret;
}
diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.h b/contrib/wpa/wpa_supplicant/p2p_supplicant.h
index dee9c1bcc1e1..5a869e7309a3 100644
--- a/contrib/wpa/wpa_supplicant/p2p_supplicant.h
+++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.h
@@ -1,356 +1,357 @@
/*
* wpa_supplicant - P2P
* Copyright (c) 2009-2010, Atheros Communications
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef P2P_SUPPLICANT_H
#define P2P_SUPPLICANT_H
enum p2p_wps_method;
struct p2p_go_neg_results;
enum p2p_send_action_result;
struct p2p_peer_info;
struct p2p_channels;
struct wps_event_fail;
struct p2ps_provision;
enum wpas_p2p_channel_update_trig {
WPAS_P2P_CHANNEL_UPDATE_ANY,
WPAS_P2P_CHANNEL_UPDATE_DRIVER,
WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE,
WPAS_P2P_CHANNEL_UPDATE_AVOID,
WPAS_P2P_CHANNEL_UPDATE_DISALLOW,
WPAS_P2P_CHANNEL_UPDATE_CS,
};
int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
const char *conf_p2p_dev);
struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
const u8 *ssid, size_t ssid_len);
struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
const u8 *peer_dev_addr);
int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *pin, enum p2p_wps_method wps_method,
int persistent_group, int auto_join, int join, int auth,
int go_intent, int freq, unsigned int vht_center_freq2,
int persistent_id, int pd, int ht40, int vht,
unsigned int vht_chwidth, int he, int edmg,
const u8 *group_ssid, size_t group_ssid_len,
bool allow_6ghz);
int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
int freq, struct wpa_ssid *ssid);
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
int freq, int vht_center_freq2, int ht40, int vht,
int max_oper_chwidth, int he, int edmg, bool allow_6ghz);
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int addr_allocated,
int force_freq, int neg_freq,
int vht_center_freq2, int ht40, int vht,
int max_oper_chwidth, int he, int edmg,
const struct p2p_channels *channels,
int connection_timeout, int force_scan,
bool allow_6ghz);
struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
enum wpas_p2p_prov_disc_use {
WPAS_P2P_PD_FOR_GO_NEG,
WPAS_P2P_PD_FOR_JOIN,
WPAS_P2P_PD_AUTO,
WPAS_P2P_PD_FOR_ASP
};
int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *config_method,
enum wpas_p2p_prov_disc_use use,
struct p2ps_provision *p2ps_prov);
void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
const u8 *data, size_t data_len,
enum p2p_send_action_result result);
int wpas_p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
char *end);
enum p2p_discovery_type;
int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
enum p2p_discovery_type type,
unsigned int num_req_dev_types, const u8 *req_dev_types,
const u8 *dev_id, unsigned int search_delay,
u8 seek_cnt, const char **seek_string, int freq,
bool include_6ghz);
void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout);
int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
u8 *buf, size_t len, int p2p_group);
void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
const struct wpabuf *tlvs);
u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
const char *svc_str, const char *info_substr);
u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
u8 version, const char *query);
u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
const u8 *dst, const char *role);
int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req);
void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
const u8 *dst, u8 dialog_token,
const struct wpabuf *resp_tlvs);
void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s);
void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s);
int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
struct wpabuf *query, struct wpabuf *resp);
int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
const struct wpabuf *query);
int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
const char *service);
int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
const char *service);
int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept,
u32 adv_id, const char *adv_str, u8 svc_state,
u16 config_methods, const char *svc_info,
const u8 *cpt_priority);
int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id);
void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s);
int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id);
void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
u16 update_indic, const u8 *tlvs, size_t tlvs_len);
void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
const u8 *tlvs, size_t tlvs_len);
int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
int vht_center_freq2, int ht40, int vht, int max_chwidth,
int pref_freq, int he, int edmg, bool allow_6ghz);
int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
const u8 *peer_addr, const u8 *go_dev_addr,
bool allow_6ghz);
int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
u32 interval1, u32 duration2, u32 interval2);
int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
unsigned int interval);
int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
u16 reason_code, const u8 *ie, size_t ie_len,
int locally_generated);
void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
u16 reason_code, const u8 *ie, size_t ie_len,
int locally_generated);
int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
int duration);
int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled);
int wpas_p2p_cancel(struct wpa_supplicant *wpa_s);
int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr);
int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s);
struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
const u8 *addr, const u8 *ssid,
size_t ssid_len);
void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
const u8 *addr);
int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
-int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
- struct hostapd_hw_modes *mode, u8 channel);
+int wpas_p2p_get_sec_channel_offset_40mhz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel);
int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel,
u8 op_class);
int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel,
u8 op_class);
unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
const u8 *p2p_dev_addr,
const u8 *psk, size_t psk_len);
void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
int iface_addr);
struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
int ndef);
struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
int ndef, int tag);
int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
const struct wpabuf *data, int forced_freq);
int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
const struct wpabuf *req,
const struct wpabuf *sel, int forced_freq);
int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled);
void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx);
int wpas_p2p_try_edmg_channel(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params);
#ifdef CONFIG_P2P
int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
const u8 *dst, const u8 *bssid,
const u8 *ie, size_t ie_len,
unsigned int rx_freq, int ssi_signal);
void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
int registrar);
void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
enum wpas_p2p_channel_update_trig trig);
void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
int freq_24, int freq_5, int freq_overall);
void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
const u8 *sa, const u8 *bssid,
u8 category, const u8 *data, size_t len, int freq);
void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq, unsigned int duration);
void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq);
void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s);
void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s);
void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s);
void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s);
void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s);
void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s);
void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
struct wps_event_fail *fail);
int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq,
unsigned int period, unsigned int interval,
unsigned int count);
int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s);
int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s);
#else /* CONFIG_P2P */
static inline int
wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
{
return 0;
}
static inline void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
{
}
static inline void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
{
}
static inline void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
{
}
static inline int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s,
const u8 *addr,
const u8 *dst, const u8 *bssid,
const u8 *ie, size_t ie_len,
unsigned int rx_freq, int ssi_signal)
{
return 0;
}
static inline void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s,
const u8 *peer_addr, int registrar)
{
}
static inline void
wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
enum wpas_p2p_channel_update_trig trig)
{
}
static inline void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
int freq_24, int freq_5,
int freq_overall)
{
}
static inline void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s,
const u8 *da,
const u8 *sa, const u8 *bssid,
u8 category, const u8 *data, size_t len,
int freq)
{
}
static inline void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq,
unsigned int duration)
{
}
static inline void
wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq)
{
}
static inline void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
{
}
static inline void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
{
}
static inline void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
{
}
static inline int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
{
return 0;
}
static inline int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
{
return 0;
}
static inline void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
{
}
static inline void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
{
}
static inline void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
{
}
static inline void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
{
}
static inline void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
}
static inline int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
{
return 0;
}
static inline int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
{
return 0;
}
static inline void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
struct wps_event_fail *fail)
{
}
static inline int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s,
const char *ifname)
{
return 0;
}
#endif /* CONFIG_P2P */
#endif /* P2P_SUPPLICANT_H */
diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c b/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c
index f8675e68bec4..b400cbacae61 100644
--- a/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c
+++ b/contrib/wpa/wpa_supplicant/p2p_supplicant_sd.c
@@ -1,1273 +1,1273 @@
/*
* wpa_supplicant - P2P service discovery
* Copyright (c) 2009-2010, Atheros Communications
* Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "p2p/p2p.h"
#include "wpa_supplicant_i.h"
#include "notify.h"
#include "p2p_supplicant.h"
/*
* DNS Header section is used only to calculate compression pointers, so the
* contents of this data does not matter, but the length needs to be reserved
* in the virtual packet.
*/
#define DNS_HEADER_LEN 12
/*
* 27-octet in-memory packet from P2P specification containing two implied
* queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
*/
#define P2P_SD_IN_MEMORY_LEN 27
static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
u8 **spos, const u8 *end)
{
while (*spos < end) {
u8 val = ((*spos)[0] & 0xc0) >> 6;
int len;
if (val == 1 || val == 2) {
/* These are reserved values in RFC 1035 */
wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
"sequence starting with 0x%x", val);
return -1;
}
if (val == 3) {
u16 offset;
u8 *spos_tmp;
/* Offset */
if (end - *spos < 2) {
wpa_printf(MSG_DEBUG, "P2P: No room for full "
"DNS offset field");
return -1;
}
offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
if (offset >= *spos - start) {
wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
"pointer offset %u", offset);
return -1;
}
(*spos) += 2;
spos_tmp = start + offset;
return p2p_sd_dns_uncompress_label(upos, uend, start,
&spos_tmp,
*spos - 2);
}
/* Label */
len = (*spos)[0] & 0x3f;
if (len == 0)
return 0;
(*spos)++;
if (len > end - *spos) {
wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
"sequence - no room for label with length "
"%u", len);
return -1;
}
if (len + 2 > uend - *upos)
return -2;
os_memcpy(*upos, *spos, len);
*spos += len;
*upos += len;
(*upos)[0] = '.';
(*upos)++;
(*upos)[0] = '\0';
}
return 0;
}
/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
* Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
* not large enough */
static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
size_t msg_len, size_t offset)
{
/* 27-octet in-memory packet from P2P specification */
const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
"\x04_udp\xC0\x11\x00\x0C\x00\x01";
u8 *tmp, *end, *spos;
char *upos, *uend;
int ret = 0;
if (buf_len < 2)
return -1;
if (offset > msg_len)
return -1;
tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
if (tmp == NULL)
return -1;
spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
end = spos + msg_len;
spos += offset;
os_memset(tmp, 0, DNS_HEADER_LEN);
os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
upos = buf;
uend = buf + buf_len;
ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
if (ret) {
os_free(tmp);
return ret;
}
if (upos == buf) {
upos[0] = '.';
upos[1] = '\0';
} else if (upos[-1] == '.')
upos[-1] = '\0';
os_free(tmp);
return 0;
}
static struct p2p_srv_bonjour *
wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
const struct wpabuf *query)
{
struct p2p_srv_bonjour *bsrv;
size_t len;
len = wpabuf_len(query);
dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
struct p2p_srv_bonjour, list) {
if (len == wpabuf_len(bsrv->query) &&
os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
len) == 0)
return bsrv;
}
return NULL;
}
static struct p2p_srv_upnp *
wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
const char *service)
{
struct p2p_srv_upnp *usrv;
dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
struct p2p_srv_upnp, list) {
if (version == usrv->version &&
os_strcmp(service, usrv->service) == 0)
return usrv;
}
return NULL;
}
static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
u8 srv_trans_id, u8 status)
{
u8 *len_pos;
if (wpabuf_tailroom(resp) < 5)
return;
/* Length (to be filled) */
len_pos = wpabuf_put(resp, 2);
wpabuf_put_u8(resp, srv_proto);
wpabuf_put_u8(resp, srv_trans_id);
/* Status Code */
wpabuf_put_u8(resp, status);
/* Response Data: empty */
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
}
static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
u8 srv_trans_id)
{
wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
P2P_SD_PROTO_NOT_AVAILABLE);
}
static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto,
u8 srv_trans_id)
{
wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST);
}
static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto,
u8 srv_trans_id)
{
wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
}
static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
struct wpabuf *resp, u8 srv_trans_id)
{
struct p2p_srv_bonjour *bsrv;
u8 *len_pos;
wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
return;
}
dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
struct p2p_srv_bonjour, list) {
if (wpabuf_tailroom(resp) <
5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
return;
/* Length (to be filled) */
len_pos = wpabuf_put(resp, 2);
wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
wpabuf_put_u8(resp, srv_trans_id);
/* Status Code */
wpabuf_put_u8(resp, P2P_SD_SUCCESS);
wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
wpabuf_head(bsrv->resp),
wpabuf_len(bsrv->resp));
/* Response Data */
wpabuf_put_buf(resp, bsrv->query); /* Key */
wpabuf_put_buf(resp, bsrv->resp); /* Value */
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
2);
}
}
static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
size_t query_len)
{
char str_rx[256], str_srv[256];
if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
return 0; /* Too short to include DNS Type and Version */
if (os_memcmp(query + query_len - 3,
wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
3) != 0)
return 0; /* Mismatch in DNS Type or Version */
if (query_len == wpabuf_len(bsrv->query) &&
os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
return 1; /* Binary match */
if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
0))
return 0; /* Failed to uncompress query */
if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
wpabuf_head(bsrv->query),
wpabuf_len(bsrv->query) - 3, 0))
return 0; /* Failed to uncompress service */
return os_strcmp(str_rx, str_srv) == 0;
}
static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
struct wpabuf *resp, u8 srv_trans_id,
const u8 *query, size_t query_len)
{
struct p2p_srv_bonjour *bsrv;
u8 *len_pos;
int matches = 0;
wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
query, query_len);
if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
srv_trans_id);
return;
}
if (query_len == 0) {
wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
return;
}
dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
struct p2p_srv_bonjour, list) {
if (!match_bonjour_query(bsrv, query, query_len))
continue;
if (wpabuf_tailroom(resp) <
5 + query_len + wpabuf_len(bsrv->resp))
return;
matches++;
/* Length (to be filled) */
len_pos = wpabuf_put(resp, 2);
wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
wpabuf_put_u8(resp, srv_trans_id);
/* Status Code */
wpabuf_put_u8(resp, P2P_SD_SUCCESS);
wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
wpabuf_head(bsrv->resp),
wpabuf_len(bsrv->resp));
/* Response Data */
wpabuf_put_data(resp, query, query_len); /* Key */
wpabuf_put_buf(resp, bsrv->resp); /* Value */
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
}
if (matches == 0) {
wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
"available");
if (wpabuf_tailroom(resp) < 5)
return;
/* Length (to be filled) */
len_pos = wpabuf_put(resp, 2);
wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
wpabuf_put_u8(resp, srv_trans_id);
/* Status Code */
wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
/* Response Data: empty */
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
2);
}
}
static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
struct wpabuf *resp, u8 srv_trans_id)
{
struct p2p_srv_upnp *usrv;
u8 *len_pos;
wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
return;
}
dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
struct p2p_srv_upnp, list) {
if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
return;
/* Length (to be filled) */
len_pos = wpabuf_put(resp, 2);
wpabuf_put_u8(resp, P2P_SERV_UPNP);
wpabuf_put_u8(resp, srv_trans_id);
/* Status Code */
wpabuf_put_u8(resp, P2P_SD_SUCCESS);
/* Response Data */
wpabuf_put_u8(resp, usrv->version);
wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
usrv->service);
wpabuf_put_str(resp, usrv->service);
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
2);
}
}
static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
struct wpabuf *resp, u8 srv_trans_id,
const u8 *query, size_t query_len)
{
struct p2p_srv_upnp *usrv;
u8 *len_pos;
u8 version;
char *str;
int count = 0;
wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
query, query_len);
if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
srv_trans_id);
return;
}
if (query_len == 0) {
wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
return;
}
if (wpabuf_tailroom(resp) < 5)
return;
/* Length (to be filled) */
len_pos = wpabuf_put(resp, 2);
wpabuf_put_u8(resp, P2P_SERV_UPNP);
wpabuf_put_u8(resp, srv_trans_id);
version = query[0];
str = os_malloc(query_len);
if (str == NULL)
return;
os_memcpy(str, query + 1, query_len - 1);
str[query_len - 1] = '\0';
dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
struct p2p_srv_upnp, list) {
if (version != usrv->version)
continue;
if (os_strcmp(str, "ssdp:all") != 0 &&
os_strstr(usrv->service, str) == NULL)
continue;
if (wpabuf_tailroom(resp) < 2)
break;
if (count == 0) {
/* Status Code */
wpabuf_put_u8(resp, P2P_SD_SUCCESS);
/* Response Data */
wpabuf_put_u8(resp, version);
} else
wpabuf_put_u8(resp, ',');
count++;
wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
usrv->service);
if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
break;
wpabuf_put_str(resp, usrv->service);
}
os_free(str);
if (count == 0) {
wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
"available");
/* Status Code */
wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
/* Response Data: empty */
}
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
}
#ifdef CONFIG_WIFI_DISPLAY
static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
struct wpabuf *resp, u8 srv_trans_id,
const u8 *query, size_t query_len)
{
const u8 *pos;
u8 role;
u8 *len_pos;
wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
if (!wpa_s->global->wifi_display) {
wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
srv_trans_id);
return;
}
if (query_len < 1) {
wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
"Role");
return;
}
if (wpabuf_tailroom(resp) < 5)
return;
pos = query;
role = *pos++;
wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
/* TODO: role specific handling */
/* Length (to be filled) */
len_pos = wpabuf_put(resp, 2);
wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
wpabuf_put_u8(resp, srv_trans_id);
wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
while (pos < query + query_len) {
if (*pos < MAX_WFD_SUBELEMS &&
wpa_s->global->wfd_subelem[*pos] &&
wpabuf_tailroom(resp) >=
wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
"subelement %u", *pos);
wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
}
pos++;
}
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
}
#endif /* CONFIG_WIFI_DISPLAY */
static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
const u8 *needle, size_t needle_len)
{
const u8 *haystack = (const u8 *) adv_data->svc_info;
size_t haystack_len, i;
/* Allow search term to be empty */
if (!needle || !needle_len)
return 1;
if (!haystack)
return 0;
haystack_len = os_strlen(adv_data->svc_info);
for (i = 0; i < haystack_len; i++) {
if (haystack_len - i < needle_len)
break;
if (os_memcmp(haystack + i, needle, needle_len) == 0)
return 1;
}
return 0;
}
static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s,
struct wpabuf *resp, u8 srv_trans_id,
const u8 *query, size_t query_len)
{
struct p2ps_advertisement *adv_data;
const u8 *svc = &query[1];
const u8 *info = NULL;
size_t svc_len = query[0];
size_t info_len = 0;
int prefix = 0;
u8 *count_pos = NULL;
u8 *len_pos = NULL;
wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len);
if (!wpa_s->global->p2p) {
wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available");
wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id);
return;
}
/* Info block is optional */
if (svc_len + 1 < query_len) {
info = &svc[svc_len];
info_len = *info++;
}
/* Range check length of svc string and info block */
if (svc_len + (info_len ? info_len + 2 : 1) > query_len) {
wpa_printf(MSG_DEBUG, "P2P: ASP bad request");
wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id);
return;
}
/* Detect and correct for prefix search */
if (svc_len && svc[svc_len - 1] == '*') {
prefix = 1;
svc_len--;
}
for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p);
adv_data; adv_data = adv_data->next) {
/* If not a prefix match, reject length mismatches */
if (!prefix && svc_len != os_strlen(adv_data->svc_name))
continue;
/* Search each service for request */
if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 &&
find_p2ps_substr(adv_data, info, info_len)) {
size_t len = os_strlen(adv_data->svc_name);
size_t svc_info_len = 0;
if (adv_data->svc_info)
svc_info_len = os_strlen(adv_data->svc_info);
if (len > 0xff || svc_info_len > 0xffff)
return;
/* Length & Count to be filled as we go */
if (!len_pos && !count_pos) {
if (wpabuf_tailroom(resp) <
len + svc_info_len + 16)
return;
len_pos = wpabuf_put(resp, 2);
wpabuf_put_u8(resp, P2P_SERV_P2PS);
wpabuf_put_u8(resp, srv_trans_id);
/* Status Code */
wpabuf_put_u8(resp, P2P_SD_SUCCESS);
count_pos = wpabuf_put(resp, 1);
*count_pos = 0;
} else if (wpabuf_tailroom(resp) <
len + svc_info_len + 10)
return;
if (svc_info_len) {
wpa_printf(MSG_DEBUG,
"P2P: Add Svc: %s info: %s",
adv_data->svc_name,
adv_data->svc_info);
} else {
wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s",
adv_data->svc_name);
}
/* Advertisement ID */
wpabuf_put_le32(resp, adv_data->id);
/* Config Methods */
wpabuf_put_be16(resp, adv_data->config_methods);
/* Service Name */
wpabuf_put_u8(resp, (u8) len);
wpabuf_put_data(resp, adv_data->svc_name, len);
/* Service State */
wpabuf_put_u8(resp, adv_data->state);
/* Service Information */
wpabuf_put_le16(resp, (u16) svc_info_len);
wpabuf_put_data(resp, adv_data->svc_info, svc_info_len);
/* Update length and count */
(*count_pos)++;
WPA_PUT_LE16(len_pos,
(u8 *) wpabuf_put(resp, 0) - len_pos - 2);
}
}
/* Return error if no matching svc found */
if (count_pos == NULL) {
wpa_printf(MSG_DEBUG, "P2P: ASP service not found");
wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id);
}
}
static void wpas_sd_all_asp(struct wpa_supplicant *wpa_s,
struct wpabuf *resp, u8 srv_trans_id)
{
/* Query data to add all P2PS advertisements:
* - Service name length: 1
* - Service name: '*'
* - Service Information Request Length: 0
*/
const u8 q[] = { 1, (const u8) '*', 0 };
if (p2p_get_p2ps_adv_list(wpa_s->global->p2p))
wpas_sd_req_asp(wpa_s, resp, srv_trans_id, q, sizeof(q));
}
void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
u16 update_indic, const u8 *tlvs, size_t tlvs_len)
{
struct wpa_supplicant *wpa_s = ctx;
const u8 *pos = tlvs;
const u8 *end = tlvs + tlvs_len;
const u8 *tlv_end;
u16 slen;
struct wpabuf *resp;
u8 srv_proto, srv_trans_id;
size_t buf_len;
char *buf;
wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
tlvs, tlvs_len);
buf_len = 2 * tlvs_len + 1;
buf = os_malloc(buf_len);
if (buf) {
wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
MACSTR " %u %u %s",
freq, MAC2STR(sa), dialog_token, update_indic,
buf);
os_free(buf);
}
if (wpa_s->p2p_sd_over_ctrl_iface) {
wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
update_indic, tlvs, tlvs_len);
return; /* to be processed by an external program */
}
resp = wpabuf_alloc(10000);
if (resp == NULL)
return;
while (end - pos > 1) {
wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
slen = WPA_GET_LE16(pos);
pos += 2;
if (slen > end - pos || slen < 2) {
wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
"length");
wpabuf_free(resp);
return;
}
tlv_end = pos + slen;
srv_proto = *pos++;
wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
srv_proto);
srv_trans_id = *pos++;
wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
srv_trans_id);
wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
pos, tlv_end - pos);
if (wpa_s->force_long_sd) {
wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
"response");
wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
wpas_sd_all_asp(wpa_s, resp, srv_trans_id);
goto done;
}
switch (srv_proto) {
case P2P_SERV_ALL_SERVICES:
wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
"for all services");
if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
dl_list_empty(&wpa_s->global->p2p_srv_bonjour) &&
!p2p_get_p2ps_adv_list(wpa_s->global->p2p)) {
wpa_printf(MSG_DEBUG, "P2P: No service "
"discovery protocols available");
wpas_sd_add_proto_not_avail(
resp, P2P_SERV_ALL_SERVICES,
srv_trans_id);
break;
}
wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
wpas_sd_all_asp(wpa_s, resp, srv_trans_id);
break;
case P2P_SERV_BONJOUR:
wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
pos, tlv_end - pos);
break;
case P2P_SERV_UPNP:
wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
pos, tlv_end - pos);
break;
#ifdef CONFIG_WIFI_DISPLAY
case P2P_SERV_WIFI_DISPLAY:
wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
pos, tlv_end - pos);
break;
#endif /* CONFIG_WIFI_DISPLAY */
case P2P_SERV_P2PS:
wpas_sd_req_asp(wpa_s, resp, srv_trans_id,
pos, tlv_end - pos);
break;
default:
wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
"protocol %u", srv_proto);
wpas_sd_add_proto_not_avail(resp, srv_proto,
srv_trans_id);
break;
}
pos = tlv_end;
}
done:
wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
update_indic, tlvs, tlvs_len);
wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
wpabuf_free(resp);
}
static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s,
const u8 *sa, u8 srv_trans_id,
const u8 *pos, const u8 *tlv_end)
{
u8 left = *pos++;
u32 adv_id;
u8 svc_status;
u16 config_methods;
char svc_str[256];
while (left-- && pos < tlv_end) {
char *buf = NULL;
size_t buf_len;
u8 svc_len;
- /* Sanity check fixed length+svc_str */
+ /* Validity check fixed length+svc_str */
if (6 >= tlv_end - pos)
break;
svc_len = pos[6];
if (svc_len + 10 > tlv_end - pos)
break;
/* Advertisement ID */
adv_id = WPA_GET_LE32(pos);
pos += sizeof(u32);
/* Config Methods */
config_methods = WPA_GET_BE16(pos);
pos += sizeof(u16);
/* Service Name */
pos++; /* svc_len */
os_memcpy(svc_str, pos, svc_len);
svc_str[svc_len] = '\0';
pos += svc_len;
/* Service Status */
svc_status = *pos++;
/* Service Information Length */
buf_len = WPA_GET_LE16(pos);
pos += sizeof(u16);
- /* Sanity check buffer length */
+ /* Validity check buffer length */
if (buf_len > (unsigned int) (tlv_end - pos))
break;
if (buf_len) {
buf = os_zalloc(2 * buf_len + 1);
if (buf) {
utf8_escape((const char *) pos, buf_len, buf,
2 * buf_len + 1);
}
}
pos += buf_len;
if (buf) {
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
MACSTR " %x %x %x %x %s '%s'",
MAC2STR(sa), srv_trans_id, adv_id,
svc_status, config_methods, svc_str,
buf);
os_free(buf);
} else {
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
MACSTR " %x %x %x %x %s",
MAC2STR(sa), srv_trans_id, adv_id,
svc_status, config_methods, svc_str);
}
}
}
void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
const u8 *tlvs, size_t tlvs_len)
{
struct wpa_supplicant *wpa_s = ctx;
const u8 *pos = tlvs;
const u8 *end = tlvs + tlvs_len;
const u8 *tlv_end;
u16 slen;
size_t buf_len;
char *buf;
wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
tlvs, tlvs_len);
if (tlvs_len > 1500) {
/* TODO: better way for handling this */
wpa_msg_ctrl(wpa_s, MSG_INFO,
P2P_EVENT_SERV_DISC_RESP MACSTR
" %u <long response: %u bytes>",
MAC2STR(sa), update_indic,
(unsigned int) tlvs_len);
} else {
buf_len = 2 * tlvs_len + 1;
buf = os_malloc(buf_len);
if (buf) {
wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
wpa_msg_ctrl(wpa_s, MSG_INFO,
P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
MAC2STR(sa), update_indic, buf);
os_free(buf);
}
}
while (end - pos >= 2) {
u8 srv_proto, srv_trans_id, status;
wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
slen = WPA_GET_LE16(pos);
pos += 2;
if (slen > end - pos || slen < 3) {
wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
"length");
return;
}
tlv_end = pos + slen;
srv_proto = *pos++;
wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
srv_proto);
srv_trans_id = *pos++;
wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
srv_trans_id);
status = *pos++;
wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
status);
wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
pos, tlv_end - pos);
if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) {
wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id,
pos, tlv_end);
}
pos = tlv_end;
}
wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
}
u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
const struct wpabuf *tlvs)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return 0;
return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
}
u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
u8 version, const char *query)
{
struct wpabuf *tlvs;
u64 ret;
tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
if (tlvs == NULL)
return 0;
wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
wpabuf_put_u8(tlvs, version);
wpabuf_put_str(tlvs, query);
ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
wpabuf_free(tlvs);
return ret;
}
u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
const char *svc_str, const char *info_substr)
{
struct wpabuf *tlvs;
size_t plen, svc_len, substr_len = 0;
u64 ret;
svc_len = os_strlen(svc_str);
if (info_substr)
substr_len = os_strlen(info_substr);
if (svc_len > 0xff || substr_len > 0xff)
return 0;
plen = 1 + 1 + 1 + svc_len + 1 + substr_len;
tlvs = wpabuf_alloc(2 + plen);
if (tlvs == NULL)
return 0;
wpabuf_put_le16(tlvs, plen);
wpabuf_put_u8(tlvs, P2P_SERV_P2PS);
wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */
wpabuf_put_data(tlvs, svc_str, svc_len);
wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */
wpabuf_put_data(tlvs, info_substr, substr_len);
ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
wpabuf_free(tlvs);
return ret;
}
#ifdef CONFIG_WIFI_DISPLAY
static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
const struct wpabuf *tlvs)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return 0;
return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
}
#define MAX_WFD_SD_SUBELEMS 20
static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
const char *subelems)
{
u8 *len;
const char *pos;
int val;
int count = 0;
len = wpabuf_put(tlvs, 2);
wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
wpabuf_put_u8(tlvs, role);
pos = subelems;
while (*pos) {
val = atoi(pos);
if (val >= 0 && val < 256) {
wpabuf_put_u8(tlvs, val);
count++;
if (count == MAX_WFD_SD_SUBELEMS)
break;
}
pos = os_strchr(pos + 1, ',');
if (pos == NULL)
break;
pos++;
}
WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
}
u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
const u8 *dst, const char *role)
{
struct wpabuf *tlvs;
u64 ret;
const char *subelems;
u8 id = 1;
subelems = os_strchr(role, ' ');
if (subelems == NULL)
return 0;
subelems++;
tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
if (tlvs == NULL)
return 0;
if (os_strstr(role, "[source]"))
wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
if (os_strstr(role, "[pri-sink]"))
wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
if (os_strstr(role, "[sec-sink]"))
wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
if (os_strstr(role, "[source+sink]"))
wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
wpabuf_free(tlvs);
return ret;
}
#endif /* CONFIG_WIFI_DISPLAY */
int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
return p2p_sd_cancel_request(wpa_s->global->p2p,
(void *) (uintptr_t) req);
}
void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
const u8 *dst, u8 dialog_token,
const struct wpabuf *resp_tlvs)
{
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
resp_tlvs);
}
void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
{
if (wpa_s->global->p2p)
p2p_sd_service_update(wpa_s->global->p2p);
}
static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
{
dl_list_del(&bsrv->list);
wpabuf_free(bsrv->query);
wpabuf_free(bsrv->resp);
os_free(bsrv);
}
static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
{
dl_list_del(&usrv->list);
os_free(usrv->service);
os_free(usrv);
}
void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
{
struct p2p_srv_bonjour *bsrv, *bn;
struct p2p_srv_upnp *usrv, *un;
dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
struct p2p_srv_bonjour, list)
wpas_p2p_srv_bonjour_free(bsrv);
dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
struct p2p_srv_upnp, list)
wpas_p2p_srv_upnp_free(usrv);
wpas_p2p_service_flush_asp(wpa_s);
wpas_p2p_sd_service_update(wpa_s);
}
int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id)
{
if (adv_id == 0)
return 1;
if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id))
return 1;
return 0;
}
int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id)
{
int ret;
ret = p2p_service_del_asp(wpa_s->global->p2p, adv_id);
if (ret == 0)
wpas_p2p_sd_service_update(wpa_s);
return ret;
}
int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s,
int auto_accept, u32 adv_id,
const char *adv_str, u8 svc_state,
u16 config_methods, const char *svc_info,
const u8 *cpt_priority)
{
int ret;
ret = p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
adv_str, svc_state, config_methods,
svc_info, cpt_priority);
if (ret == 0)
wpas_p2p_sd_service_update(wpa_s);
return ret;
}
void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s)
{
p2p_service_flush_asp(wpa_s->global->p2p);
}
int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
struct wpabuf *query, struct wpabuf *resp)
{
struct p2p_srv_bonjour *bsrv;
bsrv = os_zalloc(sizeof(*bsrv));
if (bsrv == NULL)
return -1;
bsrv->query = query;
bsrv->resp = resp;
dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
wpas_p2p_sd_service_update(wpa_s);
return 0;
}
int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
const struct wpabuf *query)
{
struct p2p_srv_bonjour *bsrv;
bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
if (bsrv == NULL)
return -1;
wpas_p2p_srv_bonjour_free(bsrv);
wpas_p2p_sd_service_update(wpa_s);
return 0;
}
int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
const char *service)
{
struct p2p_srv_upnp *usrv;
if (wpas_p2p_service_get_upnp(wpa_s, version, service))
return 0; /* Already listed */
usrv = os_zalloc(sizeof(*usrv));
if (usrv == NULL)
return -1;
usrv->version = version;
usrv->service = os_strdup(service);
if (usrv->service == NULL) {
os_free(usrv);
return -1;
}
dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
wpas_p2p_sd_service_update(wpa_s);
return 0;
}
int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
const char *service)
{
struct p2p_srv_upnp *usrv;
usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
if (usrv == NULL)
return -1;
wpas_p2p_srv_upnp_free(usrv);
wpas_p2p_sd_service_update(wpa_s);
return 0;
}
diff --git a/contrib/wpa/wpa_supplicant/preauth_test.c b/contrib/wpa/wpa_supplicant/preauth_test.c
index 97c16fb80d27..31b55325f7f7 100644
--- a/contrib/wpa/wpa_supplicant/preauth_test.c
+++ b/contrib/wpa/wpa_supplicant/preauth_test.c
@@ -1,371 +1,371 @@
/*
* WPA Supplicant - test code for pre-authentication
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
* Not used in production version.
*/
#include "includes.h"
#include <assert.h>
#include "common.h"
#include "config.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eloop.h"
#include "rsn_supp/wpa.h"
#include "eap_peer/eap.h"
#include "wpa_supplicant_i.h"
#include "l2_packet/l2_packet.h"
#include "ctrl_iface.h"
#include "pcsc_funcs.h"
#include "rsn_supp/preauth.h"
#include "rsn_supp/pmksa_cache.h"
#include "drivers/driver.h"
const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
struct preauth_test_data {
int auth_timed_out;
};
static void _wpa_supplicant_deauthenticate(void *wpa_s, u16 reason_code)
{
wpa_supplicant_deauthenticate(wpa_s, reason_code);
}
static void _wpa_supplicant_reconnect(void *wpa_s)
{
wpa_supplicant_reconnect(wpa_s);
}
static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
const void *data, u16 data_len,
size_t *msg_len, void **data_pos)
{
struct ieee802_1x_hdr *hdr;
*msg_len = sizeof(*hdr) + data_len;
hdr = os_malloc(*msg_len);
if (hdr == NULL)
return NULL;
hdr->version = wpa_s->conf->eapol_version;
hdr->type = type;
hdr->length = htons(data_len);
if (data)
os_memcpy(hdr + 1, data, data_len);
else
os_memset(hdr + 1, 0, data_len);
if (data_pos)
*data_pos = hdr + 1;
return (u8 *) hdr;
}
static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type,
const void *data, u16 data_len,
size_t *msg_len, void **data_pos)
{
return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos);
}
static void _wpa_supplicant_set_state(void *ctx, enum wpa_states state)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_s->wpa_state = state;
}
static enum wpa_states _wpa_supplicant_get_state(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_s->wpa_state;
}
static int wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto,
const u8 *buf, size_t len)
{
printf("%s - not implemented\n", __func__);
return -1;
}
static void * wpa_supplicant_get_network_ctx(void *wpa_s)
{
return wpa_supplicant_get_ssid(wpa_s);
}
static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s)
{
wpa_supplicant_cancel_auth_timeout(wpa_s);
}
static int wpa_supplicant_get_beacon_ie(void *wpa_s)
{
printf("%s - not implemented\n", __func__);
return -1;
}
static int wpa_supplicant_get_bssid(void *wpa_s, u8 *bssid)
{
printf("%s - not implemented\n", __func__);
return -1;
}
static int wpa_supplicant_set_key(void *wpa_s, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len,
enum key_flag key_flag)
{
printf("%s - not implemented\n", __func__);
return -1;
}
static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
int protection_type,
int key_type)
{
printf("%s - not implemented\n", __func__);
return -1;
}
static int wpa_supplicant_add_pmkid(void *wpa_s, void *network_ctx,
const u8 *bssid, const u8 *pmkid,
const u8 *fils_cache_id,
const u8 *pmk, size_t pmk_len,
u32 pmk_lifetime, u8 pmk_reauth_threshold,
int akmp)
{
printf("%s - not implemented\n", __func__);
return -1;
}
static int wpa_supplicant_remove_pmkid(void *wpa_s, void *network_ctx,
const u8 *bssid, const u8 *pmkid,
const u8 *fils_cache_id)
{
printf("%s - not implemented\n", __func__);
return -1;
}
static void wpa_supplicant_set_config_blob(void *ctx,
struct wpa_config_blob *blob)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_config_set_blob(wpa_s->conf, blob);
}
static const struct wpa_config_blob *
wpa_supplicant_get_config_blob(void *ctx, const char *name)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_config_get_blob(wpa_s->conf, name);
}
static void test_eapol_clean(struct wpa_supplicant *wpa_s)
{
rsn_preauth_deinit(wpa_s->wpa);
pmksa_candidate_free(wpa_s->wpa);
wpa_sm_deinit(wpa_s->wpa);
scard_deinit(wpa_s->scard);
wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
wpa_s->ctrl_iface = NULL;
wpa_config_free(wpa_s->conf);
}
static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct preauth_test_data *p = eloop_ctx;
printf("EAPOL test timed out\n");
p->auth_timed_out = 1;
eloop_terminate();
}
static void eapol_test_poll(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (!rsn_preauth_in_progress(wpa_s->wpa))
eloop_terminate();
else {
eloop_register_timeout(0, 100000, eapol_test_poll, eloop_ctx,
timeout_ctx);
}
}
-static struct wpa_driver_ops dummy_driver;
+static struct wpa_driver_ops stub_driver;
static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname)
{
struct l2_packet_data *l2;
struct wpa_sm_ctx *ctx;
- os_memset(&dummy_driver, 0, sizeof(dummy_driver));
- wpa_s->driver = &dummy_driver;
+ os_memset(&stub_driver, 0, sizeof(stub_driver));
+ wpa_s->driver = &stub_driver;
ctx = os_zalloc(sizeof(*ctx));
assert(ctx != NULL);
ctx->ctx = wpa_s;
ctx->msg_ctx = wpa_s;
ctx->set_state = _wpa_supplicant_set_state;
ctx->get_state = _wpa_supplicant_get_state;
ctx->deauthenticate = _wpa_supplicant_deauthenticate;
ctx->set_key = wpa_supplicant_set_key;
ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
ctx->get_bssid = wpa_supplicant_get_bssid;
ctx->ether_send = wpa_ether_send;
ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie;
ctx->alloc_eapol = _wpa_alloc_eapol;
ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout;
ctx->add_pmkid = wpa_supplicant_add_pmkid;
ctx->remove_pmkid = wpa_supplicant_remove_pmkid;
ctx->set_config_blob = wpa_supplicant_set_config_blob;
ctx->get_config_blob = wpa_supplicant_get_config_blob;
ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection;
ctx->reconnect = _wpa_supplicant_reconnect;
wpa_s->wpa = wpa_sm_init(ctx);
assert(wpa_s->wpa != NULL);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, WPA_PROTO_RSN);
os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname, NULL);
l2 = l2_packet_init(wpa_s->ifname, NULL, ETH_P_RSN_PREAUTH, NULL,
NULL, 0);
assert(l2 != NULL);
if (l2_packet_get_own_addr(l2, wpa_s->own_addr)) {
wpa_printf(MSG_WARNING, "Failed to get own L2 address\n");
exit(-1);
}
l2_packet_deinit(l2);
wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
}
static void eapol_test_terminate(int sig, void *signal_ctx)
{
struct wpa_supplicant *wpa_s = signal_ctx;
wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig);
eloop_terminate();
}
int main(int argc, char *argv[])
{
struct wpa_supplicant wpa_s;
int ret = 1;
u8 bssid[ETH_ALEN];
struct preauth_test_data preauth_test;
if (os_program_init())
return -1;
os_memset(&preauth_test, 0, sizeof(preauth_test));
wpa_debug_level = 0;
wpa_debug_show_keys = 1;
if (argc != 4) {
printf("usage: preauth_test <conf> <target MAC address> "
"<ifname>\n");
return -1;
}
if (hwaddr_aton(argv[2], bssid)) {
printf("Failed to parse target address '%s'.\n", argv[2]);
return -1;
}
if (eap_register_methods()) {
wpa_printf(MSG_ERROR, "Failed to register EAP methods");
return -1;
}
if (eloop_init()) {
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
return -1;
}
os_memset(&wpa_s, 0, sizeof(wpa_s));
wpa_s.conf = wpa_config_read(argv[1], NULL);
if (wpa_s.conf == NULL) {
printf("Failed to parse configuration file '%s'.\n", argv[1]);
return -1;
}
if (wpa_s.conf->ssid == NULL) {
printf("No networks defined.\n");
return -1;
}
wpa_init_conf(&wpa_s, argv[3]);
wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
if (wpa_s.ctrl_iface == NULL) {
printf("Failed to initialize control interface '%s'.\n"
"You may have another preauth_test process already "
"running or the file was\n"
"left by an unclean termination of preauth_test in "
"which case you will need\n"
"to manually remove this file before starting "
"preauth_test again.\n",
wpa_s.conf->ctrl_interface);
return -1;
}
if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
return -1;
if (rsn_preauth_init(wpa_s.wpa, bssid, &wpa_s.conf->ssid->eap))
return -1;
eloop_register_timeout(30, 0, eapol_test_timeout, &preauth_test, NULL);
eloop_register_timeout(0, 100000, eapol_test_poll, &wpa_s, NULL);
eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
eloop_run();
if (preauth_test.auth_timed_out)
ret = -2;
else {
ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0,
NULL, 0) ? 0 : -3;
}
test_eapol_clean(&wpa_s);
eap_peer_unregister_methods();
eloop_destroy();
os_program_deinit();
return ret;
}
diff --git a/contrib/wpa/wpa_supplicant/robust_av.c b/contrib/wpa/wpa_supplicant/robust_av.c
index 39d282f0870d..770c8fcab189 100644
--- a/contrib/wpa/wpa_supplicant/robust_av.c
+++ b/contrib/wpa/wpa_supplicant/robust_av.c
@@ -1,155 +1,1487 @@
/*
* wpa_supplicant - Robust AV procedures
* Copyright (c) 2020, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/eloop.h"
#include "common/wpa_ctrl.h"
#include "common/ieee802_11_common.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "bss.h"
+#define SCS_RESP_TIMEOUT 1
+#define DSCP_REQ_TIMEOUT 5
+
+
void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
struct wpabuf *buf)
{
u8 *len, *len1;
/* MSCS descriptor element */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
len = wpabuf_put(buf, 1);
wpabuf_put_u8(buf, WLAN_EID_EXT_MSCS_DESCRIPTOR);
wpabuf_put_u8(buf, robust_av->request_type);
wpabuf_put_u8(buf, robust_av->up_bitmap);
wpabuf_put_u8(buf, robust_av->up_limit);
wpabuf_put_le32(buf, robust_av->stream_timeout);
if (robust_av->request_type != SCS_REQ_REMOVE) {
/* TCLAS mask element */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
len1 = wpabuf_put(buf, 1);
wpabuf_put_u8(buf, WLAN_EID_EXT_TCLAS_MASK);
/* Frame classifier */
wpabuf_put_data(buf, robust_av->frame_classifier,
robust_av->frame_classifier_len);
*len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
}
*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
}
+static int wpas_populate_type4_classifier(struct type4_params *type4_param,
+ struct wpabuf *buf)
+{
+ /* classifier parameters */
+ wpabuf_put_u8(buf, type4_param->classifier_mask);
+ if (type4_param->ip_version == IPV4) {
+ wpabuf_put_u8(buf, IPV4); /* IP version */
+ wpabuf_put_data(buf, &type4_param->ip_params.v4.src_ip.s_addr,
+ 4);
+ wpabuf_put_data(buf, &type4_param->ip_params.v4.dst_ip.s_addr,
+ 4);
+ wpabuf_put_be16(buf, type4_param->ip_params.v4.src_port);
+ wpabuf_put_be16(buf, type4_param->ip_params.v4.dst_port);
+ wpabuf_put_u8(buf, type4_param->ip_params.v4.dscp);
+ wpabuf_put_u8(buf, type4_param->ip_params.v4.protocol);
+ wpabuf_put_u8(buf, 0); /* Reserved octet */
+ } else {
+ wpabuf_put_u8(buf, IPV6);
+ wpabuf_put_data(buf, &type4_param->ip_params.v6.src_ip.s6_addr,
+ 16);
+ wpabuf_put_data(buf, &type4_param->ip_params.v6.dst_ip.s6_addr,
+ 16);
+ wpabuf_put_be16(buf, type4_param->ip_params.v6.src_port);
+ wpabuf_put_be16(buf, type4_param->ip_params.v6.dst_port);
+ wpabuf_put_u8(buf, type4_param->ip_params.v6.dscp);
+ wpabuf_put_u8(buf, type4_param->ip_params.v6.next_header);
+ wpabuf_put_data(buf, type4_param->ip_params.v6.flow_label, 3);
+ }
+
+ return 0;
+}
+
+
+static int wpas_populate_type10_classifier(struct type10_params *type10_param,
+ struct wpabuf *buf)
+{
+ /* classifier parameters */
+ wpabuf_put_u8(buf, type10_param->prot_instance);
+ wpabuf_put_u8(buf, type10_param->prot_number);
+ wpabuf_put_data(buf, type10_param->filter_value,
+ type10_param->filter_len);
+ wpabuf_put_data(buf, type10_param->filter_mask,
+ type10_param->filter_len);
+ return 0;
+}
+
+
+static int wpas_populate_scs_descriptor_ie(struct scs_desc_elem *desc_elem,
+ struct wpabuf *buf)
+{
+ u8 *len, *len1;
+ struct tclas_element *tclas_elem;
+ unsigned int i;
+
+ /* SCS Descriptor element */
+ wpabuf_put_u8(buf, WLAN_EID_SCS_DESCRIPTOR);
+ len = wpabuf_put(buf, 1);
+ wpabuf_put_u8(buf, desc_elem->scs_id);
+ wpabuf_put_u8(buf, desc_elem->request_type);
+ if (desc_elem->request_type == SCS_REQ_REMOVE)
+ goto end;
+
+ if (desc_elem->intra_access_priority || desc_elem->scs_up_avail) {
+ wpabuf_put_u8(buf, WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY);
+ wpabuf_put_u8(buf, 1);
+ wpabuf_put_u8(buf, desc_elem->intra_access_priority);
+ }
+
+ tclas_elem = desc_elem->tclas_elems;
+
+ if (!tclas_elem)
+ return -1;
+
+ for (i = 0; i < desc_elem->num_tclas_elem; i++, tclas_elem++) {
+ int ret;
+
+ /* TCLAS element */
+ wpabuf_put_u8(buf, WLAN_EID_TCLAS);
+ len1 = wpabuf_put(buf, 1);
+ wpabuf_put_u8(buf, 255); /* User Priority: not compared */
+ /* Frame Classifier */
+ wpabuf_put_u8(buf, tclas_elem->classifier_type);
+ /* Frame classifier parameters */
+ switch (tclas_elem->classifier_type) {
+ case 4:
+ ret = wpas_populate_type4_classifier(
+ &tclas_elem->frame_classifier.type4_param,
+ buf);
+ break;
+ case 10:
+ ret = wpas_populate_type10_classifier(
+ &tclas_elem->frame_classifier.type10_param,
+ buf);
+ break;
+ default:
+ return -1;
+ }
+
+ if (ret == -1) {
+ wpa_printf(MSG_ERROR,
+ "Failed to populate frame classifier");
+ return -1;
+ }
+
+ *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
+ }
+
+ if (desc_elem->num_tclas_elem > 1) {
+ /* TCLAS Processing element */
+ wpabuf_put_u8(buf, WLAN_EID_TCLAS_PROCESSING);
+ wpabuf_put_u8(buf, 1);
+ wpabuf_put_u8(buf, desc_elem->tclas_processing);
+ }
+
+end:
+ *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+ return 0;
+}
+
+
int wpas_send_mscs_req(struct wpa_supplicant *wpa_s)
{
struct wpabuf *buf;
size_t buf_len;
int ret;
if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
return 0;
if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_MSCS)) {
wpa_dbg(wpa_s, MSG_INFO,
"AP does not support MSCS - could not send MSCS Req");
return -1;
}
if (!wpa_s->mscs_setup_done &&
wpa_s->robust_av.request_type != SCS_REQ_ADD) {
wpa_msg(wpa_s, MSG_INFO,
"MSCS: Failed to send MSCS Request: request type invalid");
return -1;
}
buf_len = 3 + /* Action frame header */
3 + /* MSCS descriptor IE header */
1 + /* Request type */
2 + /* User priority control */
4 + /* Stream timeout */
3 + /* TCLAS Mask IE header */
wpa_s->robust_av.frame_classifier_len;
buf = wpabuf_alloc(buf_len);
if (!buf) {
wpa_printf(MSG_ERROR, "Failed to allocate MSCS req");
return -1;
}
wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
wpabuf_put_u8(buf, ROBUST_AV_MSCS_REQ);
wpa_s->robust_av.dialog_token++;
wpabuf_put_u8(buf, wpa_s->robust_av.dialog_token);
/* MSCS descriptor element */
wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, buf);
wpa_hexdump_buf(MSG_MSGDUMP, "MSCS Request", buf);
ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
wpa_s->own_addr, wpa_s->bssid,
wpabuf_head(buf), wpabuf_len(buf), 0);
if (ret < 0)
wpa_dbg(wpa_s, MSG_INFO, "MSCS: Failed to send MSCS Request");
wpabuf_free(buf);
return ret;
}
+static size_t tclas_elem_len(const struct tclas_element *elem)
+{
+ size_t buf_len = 0;
+
+ buf_len += 2 + /* TCLAS element header */
+ 1 + /* User Priority */
+ 1 ; /* Classifier Type */
+
+ if (elem->classifier_type == 4) {
+ enum ip_version ip_ver;
+
+ buf_len += 1 + /* Classifier mask */
+ 1 + /* IP version */
+ 1 + /* user priority */
+ 2 + /* src_port */
+ 2 + /* dst_port */
+ 1 ; /* dscp */
+ ip_ver = elem->frame_classifier.type4_param.ip_version;
+ if (ip_ver == IPV4) {
+ buf_len += 4 + /* src_ip */
+ 4 + /* dst_ip */
+ 1 + /* protocol */
+ 1 ; /* Reserved */
+ } else if (ip_ver == IPV6) {
+ buf_len += 16 + /* src_ip */
+ 16 + /* dst_ip */
+ 1 + /* next_header */
+ 3 ; /* flow_label */
+ } else {
+ wpa_printf(MSG_ERROR, "%s: Incorrect IP version %d",
+ __func__, ip_ver);
+ return 0;
+ }
+ } else if (elem->classifier_type == 10) {
+ buf_len += 1 + /* protocol instance */
+ 1 + /* protocol number */
+ 2 * elem->frame_classifier.type10_param.filter_len;
+ } else {
+ wpa_printf(MSG_ERROR, "%s: Incorrect classifier type %u",
+ __func__, elem->classifier_type);
+ return 0;
+ }
+
+ return buf_len;
+}
+
+
+static struct wpabuf * allocate_scs_buf(struct scs_desc_elem *desc_elem,
+ unsigned int num_scs_desc)
+{
+ struct wpabuf *buf;
+ size_t buf_len = 0;
+ unsigned int i, j;
+
+ buf_len = 3; /* Action frame header */
+
+ for (i = 0; i < num_scs_desc; i++, desc_elem++) {
+ struct tclas_element *tclas_elem;
+
+ buf_len += 2 + /* SCS descriptor IE header */
+ 1 + /* SCSID */
+ 1 ; /* Request type */
+
+ if (desc_elem->request_type == SCS_REQ_REMOVE)
+ continue;
+
+ if (desc_elem->intra_access_priority || desc_elem->scs_up_avail)
+ buf_len += 3;
+
+ tclas_elem = desc_elem->tclas_elems;
+ if (!tclas_elem) {
+ wpa_printf(MSG_ERROR, "%s: TCLAS element null",
+ __func__);
+ return NULL;
+ }
+
+ for (j = 0; j < desc_elem->num_tclas_elem; j++, tclas_elem++) {
+ size_t elen;
+
+ elen = tclas_elem_len(tclas_elem);
+ if (elen == 0)
+ return NULL;
+ buf_len += elen;
+ }
+
+ if (desc_elem->num_tclas_elem > 1) {
+ buf_len += 1 + /* TCLAS Processing eid */
+ 1 + /* length */
+ 1 ; /* processing */
+ }
+ }
+
+ buf = wpabuf_alloc(buf_len);
+ if (!buf) {
+ wpa_printf(MSG_ERROR, "Failed to allocate SCS req");
+ return NULL;
+ }
+
+ return buf;
+}
+
+
+static void scs_request_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct active_scs_elem *scs_desc, *prev;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
+ return;
+
+ /* Once timeout is over, remove all SCS descriptors with no response */
+ dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids,
+ struct active_scs_elem, list) {
+ u8 bssid[ETH_ALEN] = { 0 };
+ const u8 *src;
+
+ if (scs_desc->status == SCS_DESC_SUCCESS)
+ continue;
+
+ if (wpa_s->current_bss)
+ src = wpa_s->current_bss->bssid;
+ else
+ src = bssid;
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR
+ " SCSID=%u status_code=timedout", MAC2STR(src),
+ scs_desc->scs_id);
+
+ dl_list_del(&scs_desc->list);
+ wpa_printf(MSG_INFO, "%s: SCSID %d removed after timeout",
+ __func__, scs_desc->scs_id);
+ os_free(scs_desc);
+ }
+
+ eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
+ wpa_s->ongoing_scs_req = false;
+}
+
+
+int wpas_send_scs_req(struct wpa_supplicant *wpa_s)
+{
+ struct wpabuf *buf = NULL;
+ struct scs_desc_elem *desc_elem = NULL;
+ int ret = -1;
+ unsigned int i;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
+ return -1;
+
+ if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_SCS)) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "AP does not support SCS - could not send SCS Request");
+ return -1;
+ }
+
+ desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems;
+ if (!desc_elem)
+ return -1;
+
+ buf = allocate_scs_buf(desc_elem,
+ wpa_s->scs_robust_av_req.num_scs_desc);
+ if (!buf)
+ return -1;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
+ wpabuf_put_u8(buf, ROBUST_AV_SCS_REQ);
+ wpa_s->scs_dialog_token++;
+ if (wpa_s->scs_dialog_token == 0)
+ wpa_s->scs_dialog_token++;
+ wpabuf_put_u8(buf, wpa_s->scs_dialog_token);
+
+ for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
+ i++, desc_elem++) {
+ /* SCS Descriptor element */
+ if (wpas_populate_scs_descriptor_ie(desc_elem, buf) < 0)
+ goto end;
+ }
+
+ wpa_hexdump_buf(MSG_DEBUG, "SCS Request", buf);
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0);
+ if (ret < 0) {
+ wpa_dbg(wpa_s, MSG_ERROR, "SCS: Failed to send SCS Request");
+ wpa_s->scs_dialog_token--;
+ goto end;
+ }
+
+ desc_elem = wpa_s->scs_robust_av_req.scs_desc_elems;
+ for (i = 0; i < wpa_s->scs_robust_av_req.num_scs_desc;
+ i++, desc_elem++) {
+ struct active_scs_elem *active_scs_elem;
+
+ if (desc_elem->request_type != SCS_REQ_ADD)
+ continue;
+
+ active_scs_elem = os_malloc(sizeof(struct active_scs_elem));
+ if (!active_scs_elem)
+ break;
+ active_scs_elem->scs_id = desc_elem->scs_id;
+ active_scs_elem->status = SCS_DESC_SENT;
+ dl_list_add(&wpa_s->active_scs_ids, &active_scs_elem->list);
+ }
+
+ /*
+ * Register a timeout after which this request will be removed from
+ * the cache.
+ */
+ eloop_register_timeout(SCS_RESP_TIMEOUT, 0, scs_request_timer, wpa_s,
+ NULL);
+ wpa_s->ongoing_scs_req = true;
+
+end:
+ wpabuf_free(buf);
+ free_up_scs_desc(&wpa_s->scs_robust_av_req);
+
+ return ret;
+}
+
+
+void free_up_tclas_elem(struct scs_desc_elem *elem)
+{
+ struct tclas_element *tclas_elems = elem->tclas_elems;
+ unsigned int num_tclas_elem = elem->num_tclas_elem;
+ struct tclas_element *tclas_data;
+ unsigned int j;
+
+ elem->tclas_elems = NULL;
+ elem->num_tclas_elem = 0;
+
+ if (!tclas_elems)
+ return;
+
+ tclas_data = tclas_elems;
+ for (j = 0; j < num_tclas_elem; j++, tclas_data++) {
+ if (tclas_data->classifier_type != 10)
+ continue;
+
+ os_free(tclas_data->frame_classifier.type10_param.filter_value);
+ os_free(tclas_data->frame_classifier.type10_param.filter_mask);
+ }
+
+ os_free(tclas_elems);
+}
+
+
+void free_up_scs_desc(struct scs_robust_av_data *data)
+{
+ struct scs_desc_elem *desc_elems = data->scs_desc_elems;
+ unsigned int num_scs_desc = data->num_scs_desc;
+ struct scs_desc_elem *desc_data;
+ unsigned int i;
+
+ data->scs_desc_elems = NULL;
+ data->num_scs_desc = 0;
+
+ if (!desc_elems)
+ return;
+
+ desc_data = desc_elems;
+ for (i = 0; i < num_scs_desc; i++, desc_data++) {
+ if (desc_data->request_type == SCS_REQ_REMOVE ||
+ !desc_data->tclas_elems)
+ continue;
+
+ free_up_tclas_elem(desc_data);
+ }
+ os_free(desc_elems);
+}
+
+
void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
const u8 *src, const u8 *buf, size_t len)
{
u8 dialog_token;
u16 status_code;
if (len < 3)
return;
dialog_token = *buf++;
if (dialog_token != wpa_s->robust_av.dialog_token) {
wpa_printf(MSG_INFO,
"MSCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
dialog_token, wpa_s->robust_av.dialog_token);
return;
}
status_code = WPA_GET_LE16(buf);
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
" status_code=%u", MAC2STR(src), status_code);
wpa_s->mscs_setup_done = status_code == WLAN_STATUS_SUCCESS;
}
void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
const u8 *ies, size_t ies_len)
{
const u8 *mscs_desc_ie, *mscs_status;
u16 status;
/* Process optional MSCS Status subelement when MSCS IE is in
* (Re)Association Response frame */
if (!ies || ies_len == 0 || !wpa_s->robust_av.valid_config)
return;
mscs_desc_ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_MSCS_DESCRIPTOR);
if (!mscs_desc_ie || mscs_desc_ie[1] <= 8)
return;
/* Subelements start after (ie_id(1) + ie_len(1) + ext_id(1) +
* request type(1) + upc(2) + stream timeout(4) =) 10.
*/
mscs_status = get_ie(&mscs_desc_ie[10], mscs_desc_ie[1] - 8,
MCSC_SUBELEM_STATUS);
if (!mscs_status || mscs_status[1] < 2)
return;
status = WPA_GET_LE16(mscs_status + 2);
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
" status_code=%u", MAC2STR(bssid), status);
wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS;
}
+
+
+static void wpas_wait_for_dscp_req_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ /* Once timeout is over, reset wait flag and allow sending DSCP query */
+ wpa_printf(MSG_DEBUG,
+ "QM: Wait time over for sending DSCP request - allow DSCP query");
+ wpa_s->wait_for_dscp_req = 0;
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_wait end");
+}
+
+
+void wpas_handle_assoc_resp_qos_mgmt(struct wpa_supplicant *wpa_s,
+ const u8 *ies, size_t ies_len)
+{
+ const u8 *wfa_capa;
+
+ wpa_s->connection_dscp = 0;
+ if (wpa_s->wait_for_dscp_req)
+ eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
+
+ if (!ies || ies_len == 0 || !wpa_s->enable_dscp_policy_capa)
+ return;
+
+ wfa_capa = get_vendor_ie(ies, ies_len, WFA_CAPA_IE_VENDOR_TYPE);
+ if (!wfa_capa || wfa_capa[1] < 6 || wfa_capa[6] < 1 ||
+ !(wfa_capa[7] & WFA_CAPA_QM_DSCP_POLICY))
+ return; /* AP does not enable QM DSCP Policy */
+
+ wpa_s->connection_dscp = 1;
+ wpa_s->wait_for_dscp_req = !!(wfa_capa[7] &
+ WFA_CAPA_QM_UNSOLIC_DSCP);
+ if (!wpa_s->wait_for_dscp_req)
+ return;
+
+ /* Register a timeout after which dscp query can be sent to AP. */
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_wait start");
+ eloop_register_timeout(DSCP_REQ_TIMEOUT, 0,
+ wpas_wait_for_dscp_req_timer, wpa_s, NULL);
+}
+
+
+void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s,
+ const u8 *src, const u8 *buf,
+ size_t len)
+{
+ u8 dialog_token;
+ unsigned int i, count;
+ struct active_scs_elem *scs_desc, *prev;
+
+ if (len < 2)
+ return;
+ if (!wpa_s->ongoing_scs_req) {
+ wpa_printf(MSG_INFO,
+ "SCS: Drop received response due to no ongoing request");
+ return;
+ }
+
+ dialog_token = *buf++;
+ len--;
+ if (dialog_token != wpa_s->scs_dialog_token) {
+ wpa_printf(MSG_INFO,
+ "SCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
+ dialog_token, wpa_s->scs_dialog_token);
+ return;
+ }
+
+ /* This Count field does not exist in the IEEE Std 802.11-2020
+ * definition of the SCS Response frame. However, it was accepted to
+ * be added into REVme per REVme/D0.0 CC35 CID 49 (edits in document
+ * 11-21-0688-07). */
+ count = *buf++;
+ len--;
+ if (count == 0 || count * 3 > len) {
+ wpa_printf(MSG_INFO,
+ "SCS: Drop received frame due to invalid count: %u (remaining %zu octets)",
+ count, len);
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ u8 id;
+ u16 status;
+ bool scs_desc_found = false;
+
+ id = *buf++;
+ status = WPA_GET_LE16(buf);
+ buf += 2;
+ len -= 3;
+
+ dl_list_for_each(scs_desc, &wpa_s->active_scs_ids,
+ struct active_scs_elem, list) {
+ if (id == scs_desc->scs_id) {
+ scs_desc_found = true;
+ break;
+ }
+ }
+
+ if (!scs_desc_found) {
+ wpa_printf(MSG_INFO, "SCS: SCS ID invalid %u", id);
+ continue;
+ }
+
+ if (status != WLAN_STATUS_SUCCESS) {
+ dl_list_del(&scs_desc->list);
+ os_free(scs_desc);
+ } else if (status == WLAN_STATUS_SUCCESS) {
+ scs_desc->status = SCS_DESC_SUCCESS;
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCS_RESULT "bssid=" MACSTR
+ " SCSID=%u status_code=%u", MAC2STR(src), id, status);
+ }
+
+ eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
+ wpa_s->ongoing_scs_req = false;
+
+ dl_list_for_each_safe(scs_desc, prev, &wpa_s->active_scs_ids,
+ struct active_scs_elem, list) {
+ if (scs_desc->status != SCS_DESC_SUCCESS) {
+ wpa_msg(wpa_s, MSG_INFO,
+ WPA_EVENT_SCS_RESULT "bssid=" MACSTR
+ " SCSID=%u status_code=response_not_received",
+ MAC2STR(src), scs_desc->scs_id);
+ dl_list_del(&scs_desc->list);
+ os_free(scs_desc);
+ }
+ }
+}
+
+
+static void wpas_clear_active_scs_ids(struct wpa_supplicant *wpa_s)
+{
+ struct active_scs_elem *scs_elem;
+
+ while ((scs_elem = dl_list_first(&wpa_s->active_scs_ids,
+ struct active_scs_elem, list))) {
+ dl_list_del(&scs_elem->list);
+ os_free(scs_elem);
+ }
+}
+
+
+void wpas_scs_deinit(struct wpa_supplicant *wpa_s)
+{
+ free_up_scs_desc(&wpa_s->scs_robust_av_req);
+ wpa_s->scs_dialog_token = 0;
+ wpas_clear_active_scs_ids(wpa_s);
+ eloop_cancel_timeout(scs_request_timer, wpa_s, NULL);
+ wpa_s->ongoing_scs_req = false;
+}
+
+
+static int write_ipv4_info(char *pos, int total_len,
+ const struct ipv4_params *v4)
+{
+ int res, rem_len;
+ char addr[INET_ADDRSTRLEN];
+
+ rem_len = total_len;
+
+ if (v4->param_mask & BIT(1)) {
+ if (!inet_ntop(AF_INET, &v4->src_ip, addr, INET_ADDRSTRLEN)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to set IPv4 source address");
+ return -1;
+ }
+
+ res = os_snprintf(pos, rem_len, " src_ip=%s", addr);
+ if (os_snprintf_error(rem_len, res))
+ return -1;
+
+ pos += res;
+ rem_len -= res;
+ }
+
+ if (v4->param_mask & BIT(2)) {
+ if (!inet_ntop(AF_INET, &v4->dst_ip, addr, INET_ADDRSTRLEN)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to set IPv4 destination address");
+ return -1;
+ }
+
+ res = os_snprintf(pos, rem_len, " dst_ip=%s", addr);
+ if (os_snprintf_error(rem_len, res))
+ return -1;
+
+ pos += res;
+ rem_len -= res;
+ }
+
+ if (v4->param_mask & BIT(3)) {
+ res = os_snprintf(pos, rem_len, " src_port=%d", v4->src_port);
+ if (os_snprintf_error(rem_len, res))
+ return -1;
+
+ pos += res;
+ rem_len -= res;
+ }
+
+ if (v4->param_mask & BIT(4)) {
+ res = os_snprintf(pos, rem_len, " dst_port=%d", v4->dst_port);
+ if (os_snprintf_error(rem_len, res))
+ return -1;
+
+ pos += res;
+ rem_len -= res;
+ }
+
+ if (v4->param_mask & BIT(6)) {
+ res = os_snprintf(pos, rem_len, " protocol=%d", v4->protocol);
+ if (os_snprintf_error(rem_len, res))
+ return -1;
+
+ pos += res;
+ rem_len -= res;
+ }
+
+ return total_len - rem_len;
+}
+
+
+static int write_ipv6_info(char *pos, int total_len,
+ const struct ipv6_params *v6)
+{
+ int res, rem_len;
+ char addr[INET6_ADDRSTRLEN];
+
+ rem_len = total_len;
+
+ if (v6->param_mask & BIT(1)) {
+ if (!inet_ntop(AF_INET6, &v6->src_ip, addr, INET6_ADDRSTRLEN)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to set IPv6 source addr");
+ return -1;
+ }
+
+ res = os_snprintf(pos, rem_len, " src_ip=%s", addr);
+ if (os_snprintf_error(rem_len, res))
+ return -1;
+
+ pos += res;
+ rem_len -= res;
+ }
+
+ if (v6->param_mask & BIT(2)) {
+ if (!inet_ntop(AF_INET6, &v6->dst_ip, addr, INET6_ADDRSTRLEN)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to set IPv6 destination addr");
+ return -1;
+ }
+
+ res = os_snprintf(pos, rem_len, " dst_ip=%s", addr);
+ if (os_snprintf_error(rem_len, res))
+ return -1;
+
+ pos += res;
+ rem_len -= res;
+ }
+
+ if (v6->param_mask & BIT(3)) {
+ res = os_snprintf(pos, rem_len, " src_port=%d", v6->src_port);
+ if (os_snprintf_error(rem_len, res))
+ return -1;
+
+ pos += res;
+ rem_len -= res;
+ }
+
+ if (v6->param_mask & BIT(4)) {
+ res = os_snprintf(pos, rem_len, " dst_port=%d", v6->dst_port);
+ if (os_snprintf_error(rem_len, res))
+ return -1;
+
+ pos += res;
+ rem_len -= res;
+ }
+
+ if (v6->param_mask & BIT(6)) {
+ res = os_snprintf(pos, rem_len, " protocol=%d",
+ v6->next_header);
+ if (os_snprintf_error(rem_len, res))
+ return -1;
+
+ pos += res;
+ rem_len -= res;
+ }
+
+ return total_len - rem_len;
+}
+
+
+struct dscp_policy_data {
+ u8 policy_id;
+ u8 req_type;
+ u8 dscp;
+ bool dscp_info;
+ const u8 *frame_classifier;
+ u8 frame_classifier_len;
+ struct type4_params type4_param;
+ const u8 *domain_name;
+ u8 domain_name_len;
+ u16 start_port;
+ u16 end_port;
+ bool port_range_info;
+};
+
+
+static int set_frame_classifier_type4_ipv4(struct dscp_policy_data *policy)
+{
+ u8 classifier_mask;
+ const u8 *frame_classifier = policy->frame_classifier;
+ struct type4_params *type4_param = &policy->type4_param;
+
+ if (policy->frame_classifier_len < 18) {
+ wpa_printf(MSG_ERROR,
+ "QM: Received IPv4 frame classifier with insufficient length %d",
+ policy->frame_classifier_len);
+ return -1;
+ }
+
+ classifier_mask = frame_classifier[1];
+
+ /* Classifier Mask - bit 1 = Source IP Address */
+ if (classifier_mask & BIT(1)) {
+ type4_param->ip_params.v4.param_mask |= BIT(1);
+ os_memcpy(&type4_param->ip_params.v4.src_ip,
+ &frame_classifier[3], 4);
+ }
+
+ /* Classifier Mask - bit 2 = Destination IP Address */
+ if (classifier_mask & BIT(2)) {
+ if (policy->domain_name) {
+ wpa_printf(MSG_ERROR,
+ "QM: IPv4: Both domain name and destination IP address not expected");
+ return -1;
+ }
+
+ type4_param->ip_params.v4.param_mask |= BIT(2);
+ os_memcpy(&type4_param->ip_params.v4.dst_ip,
+ &frame_classifier[7], 4);
+ }
+
+ /* Classifier Mask - bit 3 = Source Port */
+ if (classifier_mask & BIT(3)) {
+ type4_param->ip_params.v4.param_mask |= BIT(3);
+ type4_param->ip_params.v4.src_port =
+ WPA_GET_BE16(&frame_classifier[11]);
+ }
+
+ /* Classifier Mask - bit 4 = Destination Port */
+ if (classifier_mask & BIT(4)) {
+ if (policy->port_range_info) {
+ wpa_printf(MSG_ERROR,
+ "QM: IPv4: Both port range and destination port not expected");
+ return -1;
+ }
+
+ type4_param->ip_params.v4.param_mask |= BIT(4);
+ type4_param->ip_params.v4.dst_port =
+ WPA_GET_BE16(&frame_classifier[13]);
+ }
+
+ /* Classifier Mask - bit 5 = DSCP (ignored) */
+
+ /* Classifier Mask - bit 6 = Protocol */
+ if (classifier_mask & BIT(6)) {
+ type4_param->ip_params.v4.param_mask |= BIT(6);
+ type4_param->ip_params.v4.protocol = frame_classifier[16];
+ }
+
+ return 0;
+}
+
+
+static int set_frame_classifier_type4_ipv6(struct dscp_policy_data *policy)
+{
+ u8 classifier_mask;
+ const u8 *frame_classifier = policy->frame_classifier;
+ struct type4_params *type4_param = &policy->type4_param;
+
+ if (policy->frame_classifier_len < 44) {
+ wpa_printf(MSG_ERROR,
+ "QM: Received IPv6 frame classifier with insufficient length %d",
+ policy->frame_classifier_len);
+ return -1;
+ }
+
+ classifier_mask = frame_classifier[1];
+
+ /* Classifier Mask - bit 1 = Source IP Address */
+ if (classifier_mask & BIT(1)) {
+ type4_param->ip_params.v6.param_mask |= BIT(1);
+ os_memcpy(&type4_param->ip_params.v6.src_ip,
+ &frame_classifier[3], 16);
+ }
+
+ /* Classifier Mask - bit 2 = Destination IP Address */
+ if (classifier_mask & BIT(2)) {
+ if (policy->domain_name) {
+ wpa_printf(MSG_ERROR,
+ "QM: IPv6: Both domain name and destination IP address not expected");
+ return -1;
+ }
+ type4_param->ip_params.v6.param_mask |= BIT(2);
+ os_memcpy(&type4_param->ip_params.v6.dst_ip,
+ &frame_classifier[19], 16);
+ }
+
+ /* Classifier Mask - bit 3 = Source Port */
+ if (classifier_mask & BIT(3)) {
+ type4_param->ip_params.v6.param_mask |= BIT(3);
+ type4_param->ip_params.v6.src_port =
+ WPA_GET_BE16(&frame_classifier[35]);
+ }
+
+ /* Classifier Mask - bit 4 = Destination Port */
+ if (classifier_mask & BIT(4)) {
+ if (policy->port_range_info) {
+ wpa_printf(MSG_ERROR,
+ "IPv6: Both port range and destination port not expected");
+ return -1;
+ }
+
+ type4_param->ip_params.v6.param_mask |= BIT(4);
+ type4_param->ip_params.v6.dst_port =
+ WPA_GET_BE16(&frame_classifier[37]);
+ }
+
+ /* Classifier Mask - bit 5 = DSCP (ignored) */
+
+ /* Classifier Mask - bit 6 = Next Header */
+ if (classifier_mask & BIT(6)) {
+ type4_param->ip_params.v6.param_mask |= BIT(6);
+ type4_param->ip_params.v6.next_header = frame_classifier[40];
+ }
+
+ return 0;
+}
+
+
+static int wpas_set_frame_classifier_params(struct dscp_policy_data *policy)
+{
+ const u8 *frame_classifier = policy->frame_classifier;
+ u8 frame_classifier_len = policy->frame_classifier_len;
+
+ if (frame_classifier_len < 3) {
+ wpa_printf(MSG_ERROR,
+ "QM: Received frame classifier with insufficient length %d",
+ frame_classifier_len);
+ return -1;
+ }
+
+ /* Only allowed Classifier Type: IP and higher layer parameters (4) */
+ if (frame_classifier[0] != 4) {
+ wpa_printf(MSG_ERROR,
+ "QM: Received frame classifier with invalid classifier type %d",
+ frame_classifier[0]);
+ return -1;
+ }
+
+ /* Classifier Mask - bit 0 = Version */
+ if (!(frame_classifier[1] & BIT(0))) {
+ wpa_printf(MSG_ERROR,
+ "QM: Received frame classifier without IP version");
+ return -1;
+ }
+
+ /* Version (4 or 6) */
+ if (frame_classifier[2] == 4) {
+ if (set_frame_classifier_type4_ipv4(policy)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to set IPv4 parameters");
+ return -1;
+ }
+
+ policy->type4_param.ip_version = IPV4;
+ } else if (frame_classifier[2] == 6) {
+ if (set_frame_classifier_type4_ipv6(policy)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to set IPv6 parameters");
+ return -1;
+ }
+
+ policy->type4_param.ip_version = IPV6;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "QM: Received unknown IP version %d",
+ frame_classifier[2]);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static bool dscp_valid_domain_name(const char *str)
+{
+ if (!str[0])
+ return false;
+
+ while (*str) {
+ if (is_ctrl_char(*str) || *str == ' ' || *str == '=')
+ return false;
+ str++;
+ }
+
+ return true;
+}
+
+
+static void wpas_add_dscp_policy(struct wpa_supplicant *wpa_s,
+ struct dscp_policy_data *policy)
+{
+ int ip_ver = 0, res;
+ char policy_str[1000], *pos;
+ int len;
+
+ if (!policy->frame_classifier && !policy->domain_name &&
+ !policy->port_range_info) {
+ wpa_printf(MSG_ERROR,
+ "QM: Invalid DSCP policy - no attributes present");
+ goto fail;
+ }
+
+ policy_str[0] = '\0';
+ pos = policy_str;
+ len = sizeof(policy_str);
+
+ if (policy->frame_classifier) {
+ struct type4_params *type4 = &policy->type4_param;
+
+ if (wpas_set_frame_classifier_params(policy)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to set frame classifier parameters");
+ goto fail;
+ }
+
+ if (type4->ip_version == IPV4)
+ res = write_ipv4_info(pos, len, &type4->ip_params.v4);
+ else
+ res = write_ipv6_info(pos, len, &type4->ip_params.v6);
+
+ if (res <= 0) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to write IP parameters");
+ goto fail;
+ }
+
+ ip_ver = type4->ip_version;
+
+ pos += res;
+ len -= res;
+ }
+
+ if (policy->port_range_info) {
+ res = os_snprintf(pos, len, " start_port=%u end_port=%u",
+ policy->start_port, policy->end_port);
+ if (os_snprintf_error(len, res)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to write port range attributes for policy id = %d",
+ policy->policy_id);
+ goto fail;
+ }
+
+ pos += res;
+ len -= res;
+ }
+
+ if (policy->domain_name) {
+ char domain_name_str[250];
+
+ if (policy->domain_name_len >= sizeof(domain_name_str)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Domain name length higher than max expected");
+ goto fail;
+ }
+ os_memcpy(domain_name_str, policy->domain_name,
+ policy->domain_name_len);
+ domain_name_str[policy->domain_name_len] = '\0';
+ if (!dscp_valid_domain_name(domain_name_str)) {
+ wpa_printf(MSG_ERROR, "QM: Invalid domain name string");
+ goto fail;
+ }
+ res = os_snprintf(pos, len, " domain_name=%s", domain_name_str);
+ if (os_snprintf_error(len, res)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to write domain name attribute for policy id = %d",
+ policy->policy_id);
+ goto fail;
+ }
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
+ "add policy_id=%u dscp=%u ip_version=%d%s",
+ policy->policy_id, policy->dscp, ip_ver, policy_str);
+ return;
+fail:
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "reject policy_id=%u",
+ policy->policy_id);
+}
+
+
+void wpas_dscp_deinit(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "QM: Clear all active DSCP policies");
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "clear_all");
+ wpa_s->dscp_req_dialog_token = 0;
+ wpa_s->dscp_query_dialog_token = 0;
+ wpa_s->connection_dscp = 0;
+ if (wpa_s->wait_for_dscp_req) {
+ wpa_s->wait_for_dscp_req = 0;
+ eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
+ }
+}
+
+
+static void wpas_fill_dscp_policy(struct dscp_policy_data *policy, u8 attr_id,
+ u8 attr_len, const u8 *attr_data)
+{
+ switch (attr_id) {
+ case QM_ATTR_PORT_RANGE:
+ if (attr_len < 4) {
+ wpa_printf(MSG_ERROR,
+ "QM: Received Port Range attribute with insufficient length %d",
+ attr_len);
+ break;
+ }
+ policy->start_port = WPA_GET_BE16(attr_data);
+ policy->end_port = WPA_GET_BE16(attr_data + 2);
+ policy->port_range_info = true;
+ break;
+ case QM_ATTR_DSCP_POLICY:
+ if (attr_len < 3) {
+ wpa_printf(MSG_ERROR,
+ "QM: Received DSCP Policy attribute with insufficient length %d",
+ attr_len);
+ return;
+ }
+ policy->policy_id = attr_data[0];
+ policy->req_type = attr_data[1];
+ policy->dscp = attr_data[2];
+ policy->dscp_info = true;
+ break;
+ case QM_ATTR_TCLAS:
+ if (attr_len < 1) {
+ wpa_printf(MSG_ERROR,
+ "QM: Received TCLAS attribute with insufficient length %d",
+ attr_len);
+ return;
+ }
+ policy->frame_classifier = attr_data;
+ policy->frame_classifier_len = attr_len;
+ break;
+ case QM_ATTR_DOMAIN_NAME:
+ if (attr_len < 1) {
+ wpa_printf(MSG_ERROR,
+ "QM: Received domain name attribute with insufficient length %d",
+ attr_len);
+ return;
+ }
+ policy->domain_name = attr_data;
+ policy->domain_name_len = attr_len;
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "QM: Received invalid QoS attribute %d",
+ attr_id);
+ break;
+ }
+}
+
+
+void wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant *wpa_s,
+ const u8 *src,
+ const u8 *buf, size_t len)
+{
+ int rem_len;
+ const u8 *qos_ie, *attr;
+ int more, reset;
+
+ if (!wpa_s->enable_dscp_policy_capa) {
+ wpa_printf(MSG_ERROR,
+ "QM: Ignore DSCP Policy frame since the capability is not enabled");
+ return;
+ }
+
+ if (!pmf_in_use(wpa_s, src)) {
+ wpa_printf(MSG_ERROR,
+ "QM: Ignore DSCP Policy frame since PMF is not in use");
+ return;
+ }
+
+ if (!wpa_s->connection_dscp) {
+ wpa_printf(MSG_DEBUG,
+ "QM: DSCP Policy capability not enabled for the current association - ignore QoS Management Action frames");
+ return;
+ }
+
+ if (len < 1)
+ return;
+
+ /* Handle only DSCP Policy Request frame */
+ if (buf[0] != QM_DSCP_POLICY_REQ) {
+ wpa_printf(MSG_ERROR, "QM: Received unexpected QoS action frame %d",
+ buf[0]);
+ return;
+ }
+
+ if (len < 3) {
+ wpa_printf(MSG_ERROR,
+ "Received QoS Management DSCP Policy Request frame with invalid length %zu",
+ len);
+ return;
+ }
+
+ /* Clear wait_for_dscp_req on receiving first DSCP request from AP */
+ if (wpa_s->wait_for_dscp_req) {
+ wpa_s->wait_for_dscp_req = 0;
+ eloop_cancel_timeout(wpas_wait_for_dscp_req_timer, wpa_s, NULL);
+ }
+
+ wpa_s->dscp_req_dialog_token = buf[1];
+ more = buf[2] & DSCP_POLICY_CTRL_MORE;
+ reset = buf[2] & DSCP_POLICY_CTRL_RESET;
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_start%s%s",
+ reset ? " clear_all" : "", more ? " more" : "");
+
+ qos_ie = buf + 3;
+ rem_len = len - 3;
+ while (rem_len > 2) {
+ struct dscp_policy_data policy;
+ int rem_attrs_len, ie_len;
+
+ ie_len = 2 + qos_ie[1];
+ if (rem_len < ie_len)
+ break;
+
+ if (rem_len < 6 || qos_ie[0] != WLAN_EID_VENDOR_SPECIFIC ||
+ qos_ie[1] < 4 ||
+ WPA_GET_BE32(&qos_ie[2]) != QM_IE_VENDOR_TYPE) {
+ rem_len -= ie_len;
+ qos_ie += ie_len;
+ continue;
+ }
+
+ os_memset(&policy, 0, sizeof(struct dscp_policy_data));
+ attr = qos_ie + 6;
+ rem_attrs_len = qos_ie[1] - 4;
+
+ while (rem_attrs_len > 2 && rem_attrs_len >= 2 + attr[1]) {
+ wpas_fill_dscp_policy(&policy, attr[0], attr[1],
+ &attr[2]);
+ rem_attrs_len -= 2 + attr[1];
+ attr += 2 + attr[1];
+ }
+
+ rem_len -= ie_len;
+ qos_ie += ie_len;
+
+ if (!policy.dscp_info) {
+ wpa_printf(MSG_ERROR,
+ "QM: Received QoS IE without DSCP Policy attribute");
+ continue;
+ }
+
+ if (policy.req_type == DSCP_POLICY_REQ_ADD)
+ wpas_add_dscp_policy(wpa_s, &policy);
+ else if (policy.req_type == DSCP_POLICY_REQ_REMOVE)
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
+ "remove policy_id=%u", policy.policy_id);
+ else
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY
+ "reject policy_id=%u", policy.policy_id);
+ }
+
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DSCP_POLICY "request_end");
+}
+
+
+int wpas_send_dscp_response(struct wpa_supplicant *wpa_s,
+ struct dscp_resp_data *resp_data)
+{
+ struct wpabuf *buf = NULL;
+ size_t buf_len;
+ int ret = -1, i;
+ u8 resp_control = 0;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to send DSCP response - not connected to AP");
+ return -1;
+ }
+
+ if (resp_data->solicited && !wpa_s->dscp_req_dialog_token) {
+ wpa_printf(MSG_ERROR, "QM: No ongoing DSCP request");
+ return -1;
+ }
+
+ if (!wpa_s->connection_dscp) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to send DSCP response - DSCP capability not enabled for the current association");
+ return -1;
+
+ }
+
+ buf_len = 1 + /* Category */
+ 3 + /* OUI */
+ 1 + /* OUI Type */
+ 1 + /* OUI Subtype */
+ 1 + /* Dialog Token */
+ 1 + /* Response Control */
+ 1 + /* Count */
+ 2 * resp_data->num_policies; /* Status list */
+ buf = wpabuf_alloc(buf_len);
+ if (!buf) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to allocate DSCP policy response");
+ return -1;
+ }
+
+ wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, QM_ACTION_OUI_TYPE);
+ wpabuf_put_u8(buf, QM_DSCP_POLICY_RESP);
+
+ wpabuf_put_u8(buf, resp_data->solicited ?
+ wpa_s->dscp_req_dialog_token : 0);
+
+ if (resp_data->more)
+ resp_control |= DSCP_POLICY_CTRL_MORE;
+ if (resp_data->reset)
+ resp_control |= DSCP_POLICY_CTRL_RESET;
+ wpabuf_put_u8(buf, resp_control);
+
+ wpabuf_put_u8(buf, resp_data->num_policies);
+ for (i = 0; i < resp_data->num_policies; i++) {
+ wpabuf_put_u8(buf, resp_data->policy[i].id);
+ wpabuf_put_u8(buf, resp_data->policy[i].status);
+ }
+
+ wpa_hexdump_buf(MSG_MSGDUMP, "DSCP response frame: ", buf);
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0);
+ if (ret < 0) {
+ wpa_msg(wpa_s, MSG_INFO, "QM: Failed to send DSCP response");
+ goto fail;
+ }
+
+ /*
+ * Mark DSCP request complete whether response sent is solicited or
+ * unsolicited
+ */
+ wpa_s->dscp_req_dialog_token = 0;
+
+fail:
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+int wpas_send_dscp_query(struct wpa_supplicant *wpa_s, const char *domain_name,
+ size_t domain_name_length)
+{
+ struct wpabuf *buf = NULL;
+ int ret, dscp_query_size;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
+ return -1;
+
+ if (!wpa_s->connection_dscp) {
+ wpa_printf(MSG_ERROR,
+ "QM: Failed to send DSCP query - DSCP capability not enabled for the current association");
+ return -1;
+ }
+
+ if (wpa_s->wait_for_dscp_req) {
+ wpa_printf(MSG_INFO, "QM: Wait until AP sends a DSCP request");
+ return -1;
+ }
+
+#define DOMAIN_NAME_OFFSET (4 /* OUI */ + 1 /* Attr Id */ + 1 /* Attr len */)
+
+ if (domain_name_length > 255 - DOMAIN_NAME_OFFSET) {
+ wpa_printf(MSG_ERROR, "QM: Too long domain name");
+ return -1;
+ }
+
+ dscp_query_size = 1 + /* Category */
+ 4 + /* OUI Type */
+ 1 + /* OUI subtype */
+ 1; /* Dialog Token */
+ if (domain_name && domain_name_length)
+ dscp_query_size += 1 + /* Element ID */
+ 1 + /* IE Length */
+ DOMAIN_NAME_OFFSET + domain_name_length;
+
+ buf = wpabuf_alloc(dscp_query_size);
+ if (!buf) {
+ wpa_printf(MSG_ERROR, "QM: Failed to allocate DSCP query");
+ return -1;
+ }
+
+ wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED);
+ wpabuf_put_be32(buf, QM_ACTION_VENDOR_TYPE);
+ wpabuf_put_u8(buf, QM_DSCP_POLICY_QUERY);
+ wpa_s->dscp_query_dialog_token++;
+ if (wpa_s->dscp_query_dialog_token == 0)
+ wpa_s->dscp_query_dialog_token++;
+ wpabuf_put_u8(buf, wpa_s->dscp_query_dialog_token);
+
+ if (domain_name && domain_name_length) {
+ /* Domain Name attribute */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, DOMAIN_NAME_OFFSET + domain_name_length);
+ wpabuf_put_be32(buf, QM_IE_VENDOR_TYPE);
+ wpabuf_put_u8(buf, QM_ATTR_DOMAIN_NAME);
+ wpabuf_put_u8(buf, domain_name_length);
+ wpabuf_put_data(buf, domain_name, domain_name_length);
+ }
+#undef DOMAIN_NAME_OFFSET
+
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0);
+ if (ret < 0) {
+ wpa_dbg(wpa_s, MSG_ERROR, "QM: Failed to send DSCP query");
+ wpa_s->dscp_query_dialog_token--;
+ }
+
+ wpabuf_free(buf);
+ return ret;
+}
diff --git a/contrib/wpa/wpa_supplicant/scan.c b/contrib/wpa/wpa_supplicant/scan.c
index 97a8d9a638d6..b0094ca6ca5b 100644
--- a/contrib/wpa/wpa_supplicant/scan.c
+++ b/contrib/wpa/wpa_supplicant/scan.c
@@ -1,3309 +1,3360 @@
/*
* WPA Supplicant - Scanning
* Copyright (c) 2003-2019, 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"
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "wps_supplicant.h"
#include "p2p_supplicant.h"
#include "p2p/p2p.h"
#include "hs20_supplicant.h"
#include "notify.h"
#include "bss.h"
#include "scan.h"
#include "mesh.h"
static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid;
union wpa_event_data data;
ssid = wpa_supplicant_get_ssid(wpa_s);
if (ssid == NULL)
return;
if (wpa_s->current_ssid == NULL) {
wpa_s->current_ssid = ssid;
wpas_notify_network_changed(wpa_s);
}
wpa_supplicant_initiate_eapol(wpa_s);
wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
"network - generating associated event");
os_memset(&data, 0, sizeof(data));
wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
}
#ifdef CONFIG_WPS
static int wpas_wps_in_use(struct wpa_supplicant *wpa_s,
enum wps_request_type *req_type)
{
struct wpa_ssid *ssid;
int wps = 0;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
continue;
wps = 1;
*req_type = wpas_wps_get_req_type(ssid);
if (ssid->eap.phase1 && os_strstr(ssid->eap.phase1, "pbc=1"))
return 2;
}
#ifdef CONFIG_P2P
if (!wpa_s->global->p2p_disabled && wpa_s->global->p2p &&
!wpa_s->conf->p2p_disabled) {
wpa_s->wps->dev.p2p = 1;
if (!wps) {
wps = 1;
*req_type = WPS_REQ_ENROLLEE_INFO;
}
}
#endif /* CONFIG_P2P */
return wps;
}
#endif /* CONFIG_WPS */
static int wpa_setup_mac_addr_rand_params(struct wpa_driver_scan_params *params,
const u8 *mac_addr)
{
u8 *tmp;
if (params->mac_addr) {
params->mac_addr_mask = NULL;
os_free(params->mac_addr);
params->mac_addr = NULL;
}
params->mac_addr_rand = 1;
if (!mac_addr)
return 0;
tmp = os_malloc(2 * ETH_ALEN);
if (!tmp)
return -1;
os_memcpy(tmp, mac_addr, 2 * ETH_ALEN);
params->mac_addr = tmp;
params->mac_addr_mask = tmp + ETH_ALEN;
return 0;
}
/**
* wpa_supplicant_enabled_networks - Check whether there are enabled networks
* @wpa_s: Pointer to wpa_supplicant data
* Returns: 0 if no networks are enabled, >0 if networks are enabled
*
* This function is used to figure out whether any networks (or Interworking
* with enabled credentials and auto_interworking) are present in the current
* configuration.
*/
int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid = wpa_s->conf->ssid;
int count = 0, disabled = 0;
if (wpa_s->p2p_mgmt)
return 0; /* no normal network profiles on p2p_mgmt interface */
while (ssid) {
if (!wpas_network_disabled(wpa_s, ssid))
count++;
else
disabled++;
ssid = ssid->next;
}
if (wpa_s->conf->cred && wpa_s->conf->interworking &&
wpa_s->conf->auto_interworking)
count++;
if (count == 0 && disabled > 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks (%d disabled "
"networks)", disabled);
}
return count;
}
static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
int min_temp_disabled = 0;
while (ssid) {
if (!wpas_network_disabled(wpa_s, ssid)) {
int temp_disabled = wpas_temp_disabled(wpa_s, ssid);
if (temp_disabled <= 0)
break;
if (!min_temp_disabled ||
temp_disabled < min_temp_disabled)
min_temp_disabled = temp_disabled;
}
ssid = ssid->next;
}
/* ap_scan=2 mode - try to associate with each SSID. */
if (ssid == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached "
"end of scan list - go back to beginning");
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
wpa_supplicant_req_scan(wpa_s, min_temp_disabled, 0);
return;
}
if (ssid->next) {
/* Continue from the next SSID on the next attempt. */
wpa_s->prev_scan_ssid = ssid;
} else {
/* Start from the beginning of the SSID list. */
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
}
wpa_supplicant_associate(wpa_s, NULL, ssid);
}
static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
struct wpa_driver_scan_params *params = work->ctx;
int ret;
if (deinit) {
if (!work->started) {
wpa_scan_free_params(params);
return;
}
wpa_supplicant_notify_scanning(wpa_s, 0);
wpas_notify_scan_done(wpa_s, 0);
wpa_s->scan_work = NULL;
return;
}
if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
wpa_s->wpa_state <= WPA_SCANNING)
wpa_setup_mac_addr_rand_params(params, wpa_s->mac_addr_scan);
if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Failed to assign random MAC address for a scan");
wpa_scan_free_params(params);
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
radio_work_done(work);
return;
}
wpa_supplicant_notify_scanning(wpa_s, 1);
if (wpa_s->clear_driver_scan_cache) {
wpa_printf(MSG_DEBUG,
"Request driver to clear scan cache due to local BSS flush");
params->only_new_results = 1;
}
ret = wpa_drv_scan(wpa_s, params);
/*
* Store the obtained vendor scan cookie (if any) in wpa_s context.
* The current design is to allow only one scan request on each
* interface, hence having this scan cookie stored in wpa_s context is
* fine for now.
*
* Revisit this logic if concurrent scan operations per interface
* is supported.
*/
if (ret == 0)
wpa_s->curr_scan_cookie = params->scan_cookie;
wpa_scan_free_params(params);
work->ctx = NULL;
if (ret) {
int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
!wpa_s->beacon_rep_data.token;
if (wpa_s->disconnected)
retry = 0;
/* do not retry if operation is not supported */
if (ret == -EOPNOTSUPP)
retry = 0;
wpa_supplicant_notify_scanning(wpa_s, 0);
wpas_notify_scan_done(wpa_s, 0);
if (wpa_s->wpa_state == WPA_SCANNING)
wpa_supplicant_set_state(wpa_s,
wpa_s->scan_prev_wpa_state);
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d%s",
ret, retry ? " retry=1" : "");
radio_work_done(work);
if (retry) {
/* Restore scan_req since we will try to scan again */
wpa_s->scan_req = wpa_s->last_scan_req;
wpa_supplicant_req_scan(wpa_s, 1, 0);
} else if (wpa_s->scan_res_handler) {
/* Clear the scan_res_handler */
wpa_s->scan_res_handler = NULL;
}
if (wpa_s->beacon_rep_data.token)
wpas_rrm_refuse_request(wpa_s);
return;
}
os_get_reltime(&wpa_s->scan_trigger_time);
wpa_s->scan_runs++;
wpa_s->normal_scans++;
wpa_s->own_scan_requested = 1;
wpa_s->clear_driver_scan_cache = 0;
wpa_s->scan_work = work;
}
/**
* wpa_supplicant_trigger_scan - Request driver to start a scan
* @wpa_s: Pointer to wpa_supplicant data
* @params: Scan parameters
* Returns: 0 on success, -1 on failure
*/
int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params)
{
struct wpa_driver_scan_params *ctx;
if (wpa_s->scan_work) {
wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
return -1;
}
ctx = wpa_scan_clone_params(params);
if (!ctx ||
radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
{
wpa_scan_free_params(ctx);
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
return -1;
}
return 0;
}
static void
wpa_supplicant_delayed_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_dbg(wpa_s, MSG_DEBUG, "Starting delayed sched scan");
if (wpa_supplicant_req_sched_scan(wpa_s))
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
static void
wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_dbg(wpa_s, MSG_DEBUG, "Sched scan timeout - stopping it");
wpa_s->sched_scan_timed_out = 1;
wpa_supplicant_cancel_sched_scan(wpa_s);
}
static int
wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params)
{
int ret;
wpa_supplicant_notify_scanning(wpa_s, 1);
ret = wpa_drv_sched_scan(wpa_s, params);
if (ret)
wpa_supplicant_notify_scanning(wpa_s, 0);
else
wpa_s->sched_scanning = 1;
return ret;
}
static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
{
int ret;
ret = wpa_drv_stop_sched_scan(wpa_s);
if (ret) {
wpa_dbg(wpa_s, MSG_DEBUG, "stopping sched_scan failed!");
/* TODO: what to do if stopping fails? */
return -1;
}
return ret;
}
static struct wpa_driver_scan_filter *
wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
{
struct wpa_driver_scan_filter *ssids;
struct wpa_ssid *ssid;
size_t count;
*num_ssids = 0;
if (!conf->filter_ssids)
return NULL;
for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) {
if (ssid->ssid && ssid->ssid_len)
count++;
}
if (count == 0)
return NULL;
ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter));
if (ssids == NULL)
return NULL;
for (ssid = conf->ssid; ssid; ssid = ssid->next) {
if (!ssid->ssid || !ssid->ssid_len)
continue;
os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len);
ssids[*num_ssids].ssid_len = ssid->ssid_len;
(*num_ssids)++;
}
return ssids;
}
+#ifdef CONFIG_P2P
+static bool is_6ghz_supported(struct wpa_supplicant *wpa_s)
+{
+ struct hostapd_channel_data *chnl;
+ int i, j;
+
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211A) {
+ chnl = wpa_s->hw.modes[i].channels;
+ for (j = 0; j < wpa_s->hw.modes[i].num_channels; j++) {
+ if (chnl[j].flag & HOSTAPD_CHAN_DISABLED)
+ continue;
+ if (is_6ghz_freq(chnl[j].freq))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+#endif /* CONFIG_P2P */
+
+
static void wpa_supplicant_optimize_freqs(
struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params)
{
#ifdef CONFIG_P2P
if (params->freqs == NULL && wpa_s->p2p_in_provisioning &&
wpa_s->go_params) {
/* Optimize provisioning state scan based on GO information */
if (wpa_s->p2p_in_provisioning < 5 &&
wpa_s->go_params->freq > 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
"preferred frequency %d MHz",
wpa_s->go_params->freq);
params->freqs = os_calloc(2, sizeof(int));
if (params->freqs)
params->freqs[0] = wpa_s->go_params->freq;
} else if (wpa_s->p2p_in_provisioning < 8 &&
wpa_s->go_params->freq_list[0]) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only common "
"channels");
int_array_concat(&params->freqs,
wpa_s->go_params->freq_list);
if (params->freqs)
int_array_sort_unique(params->freqs);
}
wpa_s->p2p_in_provisioning++;
}
if (params->freqs == NULL && wpa_s->p2p_in_invitation) {
/*
* Optimize scan based on GO information during persistent
* group reinvocation
*/
if (wpa_s->p2p_in_invitation < 5 &&
wpa_s->p2p_invite_go_freq > 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation",
wpa_s->p2p_invite_go_freq);
params->freqs = os_calloc(2, sizeof(int));
if (params->freqs)
params->freqs[0] = wpa_s->p2p_invite_go_freq;
}
wpa_s->p2p_in_invitation++;
if (wpa_s->p2p_in_invitation > 20) {
/*
* This should not really happen since the variable is
* cleared on group removal, but if it does happen, make
* sure we do not get stuck in special invitation scan
* mode.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Clear p2p_in_invitation");
wpa_s->p2p_in_invitation = 0;
}
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_WPS
if (params->freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
/*
* Optimize post-provisioning scan based on channel used
* during provisioning.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
"that was used during provisioning", wpa_s->wps_freq);
params->freqs = os_calloc(2, sizeof(int));
if (params->freqs)
params->freqs[0] = wpa_s->wps_freq;
wpa_s->after_wps--;
} else if (wpa_s->after_wps)
wpa_s->after_wps--;
if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq)
{
/* Optimize provisioning scan based on already known channel */
wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz",
wpa_s->wps_freq);
params->freqs = os_calloc(2, sizeof(int));
if (params->freqs)
params->freqs[0] = wpa_s->wps_freq;
wpa_s->known_wps_freq = 0; /* only do this once */
}
#endif /* CONFIG_WPS */
}
#ifdef CONFIG_INTERWORKING
static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
struct wpabuf *buf)
{
wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
1 + ETH_ALEN);
wpabuf_put_u8(buf, wpa_s->conf->access_network_type);
/* No Venue Info */
if (!is_zero_ether_addr(wpa_s->conf->hessid))
wpabuf_put_data(buf, wpa_s->conf->hessid, ETH_ALEN);
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_MBO
static void wpas_fils_req_param_add_max_channel(struct wpa_supplicant *wpa_s,
struct wpabuf **ie)
{
if (wpabuf_resize(ie, 5)) {
wpa_printf(MSG_DEBUG,
"Failed to allocate space for FILS Request Parameters element");
return;
}
/* FILS Request Parameters element */
wpabuf_put_u8(*ie, WLAN_EID_EXTENSION);
wpabuf_put_u8(*ie, 3); /* FILS Request attribute length */
wpabuf_put_u8(*ie, WLAN_EID_EXT_FILS_REQ_PARAMS);
/* Parameter control bitmap */
wpabuf_put_u8(*ie, 0);
/* Max Channel Time field - contains the value of MaxChannelTime
* parameter of the MLME-SCAN.request primitive represented in units of
* TUs, as an unsigned integer. A Max Channel Time field value of 255
* is used to indicate any duration of more than 254 TUs, or an
* unspecified or unknown duration. (IEEE Std 802.11ai-2016, 9.4.2.178)
*/
wpabuf_put_u8(*ie, 255);
}
#endif /* CONFIG_MBO */
void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s)
{
struct wpabuf *default_ies = NULL;
u8 ext_capab[18];
int ext_capab_len, frame_id;
enum wpa_driver_if_type type = WPA_IF_STATION;
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
type = WPA_IF_P2P_CLIENT;
#endif /* CONFIG_P2P */
wpa_drv_get_ext_capa(wpa_s, type);
ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
sizeof(ext_capab));
if (ext_capab_len > 0 &&
wpabuf_resize(&default_ies, ext_capab_len) == 0)
wpabuf_put_data(default_ies, ext_capab, ext_capab_len);
#ifdef CONFIG_MBO
if (wpa_s->enable_oce & OCE_STA)
wpas_fils_req_param_add_max_channel(wpa_s, &default_ies);
/* Send MBO and OCE capabilities */
if (wpabuf_resize(&default_ies, 12) == 0)
wpas_mbo_scan_ie(wpa_s, default_ies);
#endif /* CONFIG_MBO */
if (type == WPA_IF_P2P_CLIENT)
frame_id = VENDOR_ELEM_PROBE_REQ_P2P;
else
frame_id = VENDOR_ELEM_PROBE_REQ;
if (wpa_s->vendor_elem[frame_id]) {
size_t len;
len = wpabuf_len(wpa_s->vendor_elem[frame_id]);
if (len > 0 && wpabuf_resize(&default_ies, len) == 0)
wpabuf_put_buf(default_ies,
wpa_s->vendor_elem[frame_id]);
}
if (default_ies)
wpa_drv_set_default_scan_ies(wpa_s, wpabuf_head(default_ies),
wpabuf_len(default_ies));
wpabuf_free(default_ies);
}
static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
{
struct wpabuf *extra_ie = NULL;
u8 ext_capab[18];
int ext_capab_len;
#ifdef CONFIG_WPS
int wps = 0;
enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
else
#endif /* CONFIG_P2P */
wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
sizeof(ext_capab));
if (ext_capab_len > 0 &&
wpabuf_resize(&extra_ie, ext_capab_len) == 0)
wpabuf_put_data(extra_ie, ext_capab, ext_capab_len);
#ifdef CONFIG_INTERWORKING
if (wpa_s->conf->interworking &&
wpabuf_resize(&extra_ie, 100) == 0)
wpas_add_interworking_elements(wpa_s, extra_ie);
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_MBO
if (wpa_s->enable_oce & OCE_STA)
wpas_fils_req_param_add_max_channel(wpa_s, &extra_ie);
#endif /* CONFIG_MBO */
#ifdef CONFIG_WPS
wps = wpas_wps_in_use(wpa_s, &req_type);
if (wps) {
struct wpabuf *wps_ie;
wps_ie = wps_build_probe_req_ie(wps == 2 ? DEV_PW_PUSHBUTTON :
DEV_PW_DEFAULT,
&wpa_s->wps->dev,
wpa_s->wps->uuid, req_type,
0, NULL);
if (wps_ie) {
if (wpabuf_resize(&extra_ie, wpabuf_len(wps_ie)) == 0)
wpabuf_put_buf(extra_ie, wps_ie);
wpabuf_free(wps_ie);
}
}
#ifdef CONFIG_P2P
if (wps) {
size_t ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
if (wpabuf_resize(&extra_ie, ielen) == 0)
wpas_p2p_scan_ie(wpa_s, extra_ie);
}
#endif /* CONFIG_P2P */
wpa_supplicant_mesh_add_scan_ie(wpa_s, &extra_ie);
#endif /* CONFIG_WPS */
#ifdef CONFIG_HS20
if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 9) == 0)
wpas_hs20_add_indication(extra_ie, -1, 0);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_FST
if (wpa_s->fst_ies &&
wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0)
wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
#endif /* CONFIG_FST */
#ifdef CONFIG_MBO
/* Send MBO and OCE capabilities */
if (wpabuf_resize(&extra_ie, 12) == 0)
wpas_mbo_scan_ie(wpa_s, extra_ie);
#endif /* CONFIG_MBO */
if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) {
struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ];
if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0)
wpabuf_put_buf(extra_ie, buf);
}
return extra_ie;
}
#ifdef CONFIG_P2P
/*
* Check whether there are any enabled networks or credentials that could be
* used for a non-P2P connection.
*/
static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (wpas_network_disabled(wpa_s, ssid))
continue;
if (!ssid->p2p_group)
return 1;
}
if (wpa_s->conf->cred && wpa_s->conf->interworking &&
wpa_s->conf->auto_interworking)
return 1;
return 0;
}
#endif /* CONFIG_P2P */
int wpa_add_scan_freqs_list(struct wpa_supplicant *wpa_s,
enum hostapd_hw_mode band,
struct wpa_driver_scan_params *params, bool is_6ghz)
{
/* Include only supported channels for the specified band */
struct hostapd_hw_modes *mode;
int num_chans = 0;
int *freqs, i;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band, is_6ghz);
if (!mode)
return -1;
if (params->freqs) {
while (params->freqs[num_chans])
num_chans++;
}
freqs = os_realloc(params->freqs,
(num_chans + mode->num_channels + 1) * sizeof(int));
if (!freqs)
return -1;
params->freqs = freqs;
for (i = 0; i < mode->num_channels; i++) {
if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
continue;
params->freqs[num_chans++] = mode->channels[i].freq;
}
params->freqs[num_chans] = 0;
return 0;
}
static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params)
{
if (wpa_s->hw.modes == NULL)
return; /* unknown what channels the driver supports */
if (params->freqs)
return; /* already using a limited channel set */
if (wpa_s->setband_mask & WPA_SETBAND_5G)
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
- 0);
+ false);
if (wpa_s->setband_mask & WPA_SETBAND_2G)
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, params,
- 0);
+ false);
if (wpa_s->setband_mask & WPA_SETBAND_6G)
wpa_add_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, params,
- 1);
+ true);
}
static void wpa_add_scan_ssid(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params,
size_t max_ssids, const u8 *ssid, size_t ssid_len)
{
unsigned int j;
for (j = 0; j < params->num_ssids; j++) {
if (params->ssids[j].ssid_len == ssid_len &&
params->ssids[j].ssid &&
os_memcmp(params->ssids[j].ssid, ssid, ssid_len) == 0)
return; /* already in the list */
}
if (params->num_ssids + 1 > max_ssids) {
wpa_printf(MSG_DEBUG, "Over max scan SSIDs for manual request");
return;
}
wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
wpa_ssid_txt(ssid, ssid_len));
params->ssids[params->num_ssids].ssid = ssid;
params->ssids[params->num_ssids].ssid_len = ssid_len;
params->num_ssids++;
}
static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params,
struct wpa_ssid *ssid, size_t max_ssids)
{
#ifdef CONFIG_OWE
struct wpa_bss *bss;
if (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE))
return;
wpa_printf(MSG_DEBUG, "OWE: Look for transition mode AP. ssid=%s",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
const u8 *owe, *pos, *end;
const u8 *owe_ssid;
size_t owe_ssid_len;
if (bss->ssid_len != ssid->ssid_len ||
os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) != 0)
continue;
owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
if (!owe || owe[1] < 4)
continue;
pos = owe + 6;
end = owe + 2 + owe[1];
/* Must include BSSID and ssid_len */
if (end - pos < ETH_ALEN + 1)
return;
/* Skip BSSID */
pos += ETH_ALEN;
owe_ssid_len = *pos++;
owe_ssid = pos;
if ((size_t) (end - pos) < owe_ssid_len ||
owe_ssid_len > SSID_MAX_LEN)
return;
wpa_printf(MSG_DEBUG,
"OWE: scan_ssids: transition mode OWE ssid=%s",
wpa_ssid_txt(owe_ssid, owe_ssid_len));
wpa_add_scan_ssid(wpa_s, params, max_ssids,
owe_ssid, owe_ssid_len);
return;
}
#endif /* CONFIG_OWE */
}
static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params,
size_t max_ssids)
{
unsigned int i;
struct wpa_ssid *ssid;
/*
* For devices with max_ssids greater than 1, leave the last slot empty
* for adding the wildcard scan entry.
*/
max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids;
for (i = 0; i < wpa_s->scan_id_count; i++) {
ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]);
if (!ssid)
continue;
if (ssid->scan_ssid)
wpa_add_scan_ssid(wpa_s, params, max_ssids,
ssid->ssid, ssid->ssid_len);
/*
* Also add the SSID of the OWE BSS, to allow discovery of
* transition mode APs more quickly.
*/
wpa_add_owe_scan_ssid(wpa_s, params, ssid, max_ssids);
}
wpa_s->scan_id_count = 0;
}
static int wpa_set_ssids_from_scan_req(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params,
size_t max_ssids)
{
unsigned int i;
if (wpa_s->ssids_from_scan_req == NULL ||
wpa_s->num_ssids_from_scan_req == 0)
return 0;
if (wpa_s->num_ssids_from_scan_req > max_ssids) {
wpa_s->num_ssids_from_scan_req = max_ssids;
wpa_printf(MSG_DEBUG, "Over max scan SSIDs from scan req: %u",
(unsigned int) max_ssids);
}
for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) {
params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid;
params->ssids[i].ssid_len =
wpa_s->ssids_from_scan_req[i].ssid_len;
wpa_hexdump_ascii(MSG_DEBUG, "specific SSID",
params->ssids[i].ssid,
params->ssids[i].ssid_len);
}
params->num_ssids = wpa_s->num_ssids_from_scan_req;
wpa_s->num_ssids_from_scan_req = 0;
return 1;
}
static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_ssid *ssid;
int ret, p2p_in_prog;
struct wpabuf *extra_ie = NULL;
struct wpa_driver_scan_params params;
struct wpa_driver_scan_params *scan_params;
size_t max_ssids;
int connect_without_scan = 0;
wpa_s->ignore_post_flush_scan_res = 0;
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
return;
}
if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) {
wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan");
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
return;
}
if (wpa_s->scanning) {
/*
* If we are already in scanning state, we shall reschedule the
* the incoming scan request.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req");
wpa_supplicant_req_scan(wpa_s, 1, 0);
return;
}
if (!wpa_supplicant_enabled_networks(wpa_s) &&
wpa_s->scan_req == NORMAL_SCAN_REQ) {
wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
return;
}
if (wpa_s->conf->ap_scan != 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Using wired authentication - "
"overriding ap_scan configuration");
wpa_s->conf->ap_scan = 0;
wpas_notify_ap_scan_changed(wpa_s);
}
if (wpa_s->conf->ap_scan == 0) {
wpa_supplicant_gen_assoc_event(wpa_s);
return;
}
ssid = NULL;
if (wpa_s->scan_req != MANUAL_SCAN_REQ &&
wpa_s->connect_without_scan) {
connect_without_scan = 1;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (ssid == wpa_s->connect_without_scan)
break;
}
}
p2p_in_prog = wpas_p2p_in_progress(wpa_s);
if (p2p_in_prog && p2p_in_prog != 2 &&
(!ssid ||
(ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GO))) {
wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress");
wpa_supplicant_req_scan(wpa_s, 5, 0);
return;
}
/*
* Don't cancel the scan based on ongoing PNO; defer it. Some scans are
* used for changing modes inside wpa_supplicant (roaming,
* auto-reconnect, etc). Discarding the scan might hurt these processes.
* The normal use case for PNO is to suspend the host immediately after
* starting PNO, so the periodic 100 ms attempts to run the scan do not
* normally happen in practice multiple times, i.e., this is simply
* restarting scanning once the host is woken up and PNO stopped.
*/
if (wpa_s->pno || wpa_s->pno_sched_pending) {
wpa_dbg(wpa_s, MSG_DEBUG, "Defer scan - PNO is in progress");
wpa_supplicant_req_scan(wpa_s, 0, 100000);
return;
}
if (wpa_s->conf->ap_scan == 2)
max_ssids = 1;
else {
max_ssids = wpa_s->max_scan_ssids;
if (max_ssids > WPAS_MAX_SCAN_SSIDS)
max_ssids = WPAS_MAX_SCAN_SSIDS;
}
wpa_s->last_scan_req = wpa_s->scan_req;
wpa_s->scan_req = NORMAL_SCAN_REQ;
if (connect_without_scan) {
wpa_s->connect_without_scan = NULL;
if (ssid) {
wpa_printf(MSG_DEBUG, "Start a pre-selected network "
"without scan step");
wpa_supplicant_associate(wpa_s, NULL, ssid);
return;
}
}
os_memset(&params, 0, sizeof(params));
wpa_s->scan_prev_wpa_state = wpa_s->wpa_state;
if (wpa_s->wpa_state == WPA_DISCONNECTED ||
wpa_s->wpa_state == WPA_INACTIVE)
wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
/*
* If autoscan has set its own scanning parameters
*/
if (wpa_s->autoscan_params != NULL) {
scan_params = wpa_s->autoscan_params;
goto scan;
}
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
wpa_set_ssids_from_scan_req(wpa_s, &params, max_ssids)) {
wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command");
goto ssid_list_set;
}
#ifdef CONFIG_P2P
if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
wpa_s->go_params && !wpa_s->conf->passive_scan) {
wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)",
wpa_s->p2p_in_provisioning,
wpa_s->show_group_started);
params.ssids[0].ssid = wpa_s->go_params->ssid;
params.ssids[0].ssid_len = wpa_s->go_params->ssid_len;
params.num_ssids = 1;
goto ssid_list_set;
}
if (wpa_s->p2p_in_invitation) {
if (wpa_s->current_ssid) {
wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during invitation");
params.ssids[0].ssid = wpa_s->current_ssid->ssid;
params.ssids[0].ssid_len =
wpa_s->current_ssid->ssid_len;
params.num_ssids = 1;
} else {
wpa_printf(MSG_DEBUG, "P2P: No specific SSID known for scan during invitation");
}
goto ssid_list_set;
}
#endif /* CONFIG_P2P */
/* Find the starting point from which to continue scanning */
ssid = wpa_s->conf->ssid;
if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
while (ssid) {
if (ssid == wpa_s->prev_scan_ssid) {
ssid = ssid->next;
break;
}
ssid = ssid->next;
}
}
if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
#ifdef CONFIG_AP
!wpa_s->ap_iface &&
#endif /* CONFIG_AP */
wpa_s->conf->ap_scan == 2) {
wpa_s->connect_without_scan = NULL;
wpa_s->prev_scan_wildcard = 0;
wpa_supplicant_assoc_try(wpa_s, ssid);
return;
} else if (wpa_s->conf->ap_scan == 2) {
/*
* User-initiated scan request in ap_scan == 2; scan with
* wildcard SSID.
*/
ssid = NULL;
} else if (wpa_s->reattach && wpa_s->current_ssid != NULL) {
/*
* Perform single-channel single-SSID scan for
* reassociate-to-same-BSS operation.
*/
/* Setup SSID */
ssid = wpa_s->current_ssid;
wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
ssid->ssid, ssid->ssid_len);
params.ssids[0].ssid = ssid->ssid;
params.ssids[0].ssid_len = ssid->ssid_len;
params.num_ssids = 1;
/*
* Allocate memory for frequency array, allocate one extra
* slot for the zero-terminator.
*/
params.freqs = os_malloc(sizeof(int) * 2);
if (params.freqs) {
params.freqs[0] = wpa_s->assoc_freq;
params.freqs[1] = 0;
}
/*
* Reset the reattach flag so that we fall back to full scan if
* this scan fails.
*/
wpa_s->reattach = 0;
} else {
struct wpa_ssid *start = ssid, *tssid;
int freqs_set = 0;
if (ssid == NULL && max_ssids > 1)
ssid = wpa_s->conf->ssid;
while (ssid) {
if (!wpas_network_disabled(wpa_s, ssid) &&
ssid->scan_ssid) {
wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
ssid->ssid, ssid->ssid_len);
params.ssids[params.num_ssids].ssid =
ssid->ssid;
params.ssids[params.num_ssids].ssid_len =
ssid->ssid_len;
params.num_ssids++;
if (params.num_ssids + 1 >= max_ssids)
break;
}
if (!wpas_network_disabled(wpa_s, ssid)) {
/*
* Also add the SSID of the OWE BSS, to allow
* discovery of transition mode APs more
* quickly.
*/
wpa_add_owe_scan_ssid(wpa_s, &params, ssid,
max_ssids);
}
ssid = ssid->next;
if (ssid == start)
break;
if (ssid == NULL && max_ssids > 1 &&
start != wpa_s->conf->ssid)
ssid = wpa_s->conf->ssid;
}
if (wpa_s->scan_id_count &&
wpa_s->last_scan_req == MANUAL_SCAN_REQ)
wpa_set_scan_ssids(wpa_s, &params, max_ssids);
for (tssid = wpa_s->conf->ssid;
wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid;
tssid = tssid->next) {
if (wpas_network_disabled(wpa_s, tssid))
continue;
if (((params.freqs || !freqs_set) &&
tssid->scan_freq) &&
int_array_len(params.freqs) < 100) {
int_array_concat(&params.freqs,
tssid->scan_freq);
} else {
os_free(params.freqs);
params.freqs = NULL;
}
freqs_set = 1;
}
int_array_sort_unique(params.freqs);
}
if (ssid && max_ssids == 1) {
/*
* If the driver is limited to 1 SSID at a time interleave
* wildcard SSID scans with specific SSID scans to avoid
* waiting a long time for a wildcard scan.
*/
if (!wpa_s->prev_scan_wildcard) {
params.ssids[0].ssid = NULL;
params.ssids[0].ssid_len = 0;
wpa_s->prev_scan_wildcard = 1;
wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for "
"wildcard SSID (Interleave with specific)");
} else {
wpa_s->prev_scan_ssid = ssid;
wpa_s->prev_scan_wildcard = 0;
wpa_dbg(wpa_s, MSG_DEBUG,
"Starting AP scan for specific SSID: %s",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
}
} else if (ssid) {
/* max_ssids > 1 */
wpa_s->prev_scan_ssid = ssid;
wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
"the scan request");
params.num_ssids++;
} else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
wpa_s->manual_scan_passive && params.num_ssids == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request");
} else if (wpa_s->conf->passive_scan) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Use passive scan based on configuration");
} else {
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
params.num_ssids++;
wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
"SSID");
}
ssid_list_set:
wpa_supplicant_optimize_freqs(wpa_s, &params);
extra_ie = wpa_supplicant_extra_ies(wpa_s);
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
wpa_s->manual_scan_only_new) {
wpa_printf(MSG_DEBUG,
"Request driver to clear scan cache due to manual only_new=1 scan");
params.only_new_results = 1;
}
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL &&
wpa_s->manual_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels");
params.freqs = wpa_s->manual_scan_freqs;
wpa_s->manual_scan_freqs = NULL;
}
if (params.freqs == NULL && wpa_s->select_network_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Limit select_network scan to specified channels");
params.freqs = wpa_s->select_network_scan_freqs;
wpa_s->select_network_scan_freqs = NULL;
}
if (params.freqs == NULL && wpa_s->next_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
"generated frequency list");
params.freqs = wpa_s->next_scan_freqs;
} else
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL;
wpa_setband_scan_freqs(wpa_s, &params);
/* See if user specified frequencies. If so, scan only those. */
if (wpa_s->last_scan_req == INITIAL_SCAN_REQ &&
wpa_s->conf->initial_freq_list && !params.freqs) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Optimize scan based on conf->initial_freq_list");
int_array_concat(&params.freqs, wpa_s->conf->initial_freq_list);
} else if (wpa_s->conf->freq_list && !params.freqs) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Optimize scan based on conf->freq_list");
int_array_concat(&params.freqs, wpa_s->conf->freq_list);
}
/* Use current associated channel? */
if (wpa_s->conf->scan_cur_freq && !params.freqs) {
unsigned int num = wpa_s->num_multichan_concurrent;
params.freqs = os_calloc(num + 1, sizeof(int));
if (params.freqs) {
num = get_shared_radio_freqs(wpa_s, params.freqs, num);
if (num > 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the "
"current operating channels since "
"scan_cur_freq is enabled");
} else {
os_free(params.freqs);
params.freqs = NULL;
}
}
}
#ifdef CONFIG_MBO
if (wpa_s->enable_oce & OCE_STA)
params.oce_scan = 1;
#endif /* CONFIG_MBO */
params.filter_ssids = wpa_supplicant_build_filter_ssids(
wpa_s->conf, &params.num_filter_ssids);
if (extra_ie) {
params.extra_ies = wpabuf_head(extra_ie);
params.extra_ies_len = wpabuf_len(extra_ie);
}
#ifdef CONFIG_P2P
if (wpa_s->p2p_in_provisioning || wpa_s->p2p_in_invitation ||
(wpa_s->show_group_started && wpa_s->go_params)) {
/*
* The interface may not yet be in P2P mode, so we have to
* explicitly request P2P probe to disable CCK rates.
*/
params.p2p_probe = 1;
}
#endif /* CONFIG_P2P */
if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
wpa_s->wpa_state <= WPA_SCANNING)
wpa_setup_mac_addr_rand_params(&params, wpa_s->mac_addr_scan);
if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) {
struct wpa_bss *bss;
params.bssid = wpa_s->next_scan_bssid;
bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid);
if (!wpa_s->next_scan_bssid_wildcard_ssid &&
bss && bss->ssid_len && params.num_ssids == 1 &&
params.ssids[0].ssid_len == 0) {
params.ssids[0].ssid = bss->ssid;
params.ssids[0].ssid_len = bss->ssid_len;
wpa_dbg(wpa_s, MSG_DEBUG,
"Scan a previously specified BSSID " MACSTR
" and SSID %s",
MAC2STR(params.bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len));
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"Scan a previously specified BSSID " MACSTR,
MAC2STR(params.bssid));
}
}
scan_params = &params;
scan:
#ifdef CONFIG_P2P
/*
* If the driver does not support multi-channel concurrency and a
* virtual interface that shares the same radio with the wpa_s interface
* is operating there may not be need to scan other channels apart from
* the current operating channel on the other virtual interface. Filter
* out other channels in case we are trying to find a connection for a
* station interface when we are not configured to prefer station
* connection and a concurrent operation is already in process.
*/
if (wpa_s->scan_for_connection &&
wpa_s->last_scan_req == NORMAL_SCAN_REQ &&
!scan_params->freqs && !params.freqs &&
wpas_is_p2p_prioritized(wpa_s) &&
wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
non_p2p_network_enabled(wpa_s)) {
unsigned int num = wpa_s->num_multichan_concurrent;
params.freqs = os_calloc(num + 1, sizeof(int));
if (params.freqs) {
num = get_shared_radio_freqs(wpa_s, params.freqs, num);
if (num > 0 && num == wpa_s->num_multichan_concurrent) {
wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used");
} else {
os_free(params.freqs);
params.freqs = NULL;
}
}
}
+
+ if (!params.freqs &&
+ (wpa_s->p2p_in_invitation || wpa_s->p2p_in_provisioning) &&
+ !is_p2p_allow_6ghz(wpa_s->global->p2p) &&
+ is_6ghz_supported(wpa_s)) {
+ int i;
+
+ /* Exclude 5 GHz channels from the full scan for P2P connection
+ * since the 6 GHz band is disabled for P2P uses. */
+ wpa_printf(MSG_DEBUG,
+ "P2P: 6 GHz disabled - update the scan frequency list");
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].num_channels == 0)
+ continue;
+ if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211G)
+ wpa_add_scan_freqs_list(
+ wpa_s, HOSTAPD_MODE_IEEE80211G,
+ &params, false);
+ if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211A)
+ wpa_add_scan_freqs_list(
+ wpa_s, HOSTAPD_MODE_IEEE80211A,
+ &params, false);
+ if (wpa_s->hw.modes[i].mode == HOSTAPD_MODE_IEEE80211AD)
+ wpa_add_scan_freqs_list(
+ wpa_s, HOSTAPD_MODE_IEEE80211AD,
+ &params, false);
+ }
+ }
#endif /* CONFIG_P2P */
ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs &&
!wpa_s->manual_scan_freqs) {
/* Restore manual_scan_freqs for the next attempt */
wpa_s->manual_scan_freqs = params.freqs;
params.freqs = NULL;
}
wpabuf_free(extra_ie);
os_free(params.freqs);
os_free(params.filter_ssids);
os_free(params.mac_addr);
if (ret) {
wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
if (wpa_s->scan_prev_wpa_state != wpa_s->wpa_state)
wpa_supplicant_set_state(wpa_s,
wpa_s->scan_prev_wpa_state);
/* Restore scan_req since we will try to scan again */
wpa_s->scan_req = wpa_s->last_scan_req;
wpa_supplicant_req_scan(wpa_s, 1, 0);
} else {
wpa_s->scan_for_connection = 0;
#ifdef CONFIG_INTERWORKING
wpa_s->interworking_fast_assoc_tried = 0;
#endif /* CONFIG_INTERWORKING */
wpa_s->next_scan_bssid_wildcard_ssid = 0;
if (params.bssid)
os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
}
}
void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec)
{
struct os_reltime remaining, new_int;
int cancelled;
cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL,
&remaining);
new_int.sec = sec;
new_int.usec = 0;
if (cancelled && os_reltime_before(&remaining, &new_int)) {
new_int.sec = remaining.sec;
new_int.usec = remaining.usec;
}
if (cancelled) {
eloop_register_timeout(new_int.sec, new_int.usec,
wpa_supplicant_scan, wpa_s, NULL);
}
wpa_s->scan_interval = sec;
}
/**
* wpa_supplicant_req_scan - Schedule a scan for neighboring access points
* @wpa_s: Pointer to wpa_supplicant data
* @sec: Number of seconds after which to scan
* @usec: Number of microseconds after which to scan
*
* This function is used to schedule a scan for neighboring access points after
* the specified time.
*/
void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
{
int res;
if (wpa_s->p2p_mgmt) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Ignore scan request (%d.%06d sec) on p2p_mgmt interface",
sec, usec);
return;
}
res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
NULL);
if (res == 1) {
wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec",
sec, usec);
} else if (res == 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner",
sec, usec);
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec",
sec, usec);
eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
}
}
/**
* wpa_supplicant_delayed_sched_scan - Request a delayed scheduled scan
* @wpa_s: Pointer to wpa_supplicant data
* @sec: Number of seconds after which to scan
* @usec: Number of microseconds after which to scan
* Returns: 0 on success or -1 otherwise
*
* This function is used to schedule periodic scans for neighboring
* access points after the specified time.
*/
int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
int sec, int usec)
{
if (!wpa_s->sched_scan_supported)
return -1;
eloop_register_timeout(sec, usec,
wpa_supplicant_delayed_sched_scan_timeout,
wpa_s, NULL);
return 0;
}
static void
wpa_scan_set_relative_rssi_params(struct wpa_supplicant *wpa_s,
struct wpa_driver_scan_params *params)
{
if (wpa_s->wpa_state != WPA_COMPLETED ||
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI) ||
wpa_s->srp.relative_rssi_set == 0)
return;
params->relative_rssi_set = 1;
params->relative_rssi = wpa_s->srp.relative_rssi;
if (wpa_s->srp.relative_adjust_rssi == 0)
return;
params->relative_adjust_band = wpa_s->srp.relative_adjust_band;
params->relative_adjust_rssi = wpa_s->srp.relative_adjust_rssi;
}
/**
* wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
* @wpa_s: Pointer to wpa_supplicant data
* Returns: 0 is sched_scan was started or -1 otherwise
*
* This function is used to schedule periodic scans for neighboring
* access points repeating the scan continuously.
*/
int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
{
struct wpa_driver_scan_params params;
struct wpa_driver_scan_params *scan_params;
enum wpa_states prev_state;
struct wpa_ssid *ssid = NULL;
struct wpabuf *extra_ie = NULL;
int ret;
unsigned int max_sched_scan_ssids;
int wildcard = 0;
int need_ssids;
struct sched_scan_plan scan_plan;
if (!wpa_s->sched_scan_supported)
return -1;
if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
else
max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
return -1;
wpa_s->sched_scan_stop_req = 0;
if (wpa_s->sched_scanning) {
wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning");
return 0;
}
need_ssids = 0;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (!wpas_network_disabled(wpa_s, ssid) && !ssid->scan_ssid) {
/* Use wildcard SSID to find this network */
wildcard = 1;
} else if (!wpas_network_disabled(wpa_s, ssid) &&
ssid->ssid_len)
need_ssids++;
#ifdef CONFIG_WPS
if (!wpas_network_disabled(wpa_s, ssid) &&
ssid->key_mgmt == WPA_KEY_MGMT_WPS) {
/*
* Normal scan is more reliable and faster for WPS
* operations and since these are for short periods of
* time, the benefit of trying to use sched_scan would
* be limited.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
"sched_scan for WPS");
return -1;
}
#endif /* CONFIG_WPS */
}
if (wildcard)
need_ssids++;
if (wpa_s->normal_scans < 3 &&
(need_ssids <= wpa_s->max_scan_ssids ||
wpa_s->max_scan_ssids >= (int) max_sched_scan_ssids)) {
/*
* When normal scan can speed up operations, use that for the
* first operations before starting the sched_scan to allow
* user space sleep more. We do this only if the normal scan
* has functionality that is suitable for this or if the
* sched_scan does not have better support for multiple SSIDs.
*/
wpa_dbg(wpa_s, MSG_DEBUG, "Use normal scan instead of "
"sched_scan for initial scans (normal_scans=%d)",
wpa_s->normal_scans);
return -1;
}
os_memset(&params, 0, sizeof(params));
/* If we can't allocate space for the filters, we just don't filter */
params.filter_ssids = os_calloc(wpa_s->max_match_sets,
sizeof(struct wpa_driver_scan_filter));
prev_state = wpa_s->wpa_state;
if (wpa_s->wpa_state == WPA_DISCONNECTED ||
wpa_s->wpa_state == WPA_INACTIVE)
wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
if (wpa_s->autoscan_params != NULL) {
scan_params = wpa_s->autoscan_params;
goto scan;
}
/* Find the starting point from which to continue scanning */
ssid = wpa_s->conf->ssid;
if (wpa_s->prev_sched_ssid) {
while (ssid) {
if (ssid == wpa_s->prev_sched_ssid) {
ssid = ssid->next;
break;
}
ssid = ssid->next;
}
}
if (!ssid || !wpa_s->prev_sched_ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
wpa_s->first_sched_scan = 1;
ssid = wpa_s->conf->ssid;
wpa_s->prev_sched_ssid = ssid;
}
if (wildcard) {
wpa_dbg(wpa_s, MSG_DEBUG, "Add wildcard SSID to sched_scan");
params.num_ssids++;
}
while (ssid) {
if (wpas_network_disabled(wpa_s, ssid))
goto next;
if (params.num_filter_ssids < wpa_s->max_match_sets &&
params.filter_ssids && ssid->ssid && ssid->ssid_len) {
wpa_dbg(wpa_s, MSG_DEBUG, "add to filter ssid: %s",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
os_memcpy(params.filter_ssids[params.num_filter_ssids].ssid,
ssid->ssid, ssid->ssid_len);
params.filter_ssids[params.num_filter_ssids].ssid_len =
ssid->ssid_len;
params.num_filter_ssids++;
} else if (params.filter_ssids && ssid->ssid && ssid->ssid_len)
{
wpa_dbg(wpa_s, MSG_DEBUG, "Not enough room for SSID "
"filter for sched_scan - drop filter");
os_free(params.filter_ssids);
params.filter_ssids = NULL;
params.num_filter_ssids = 0;
}
if (ssid->scan_ssid && ssid->ssid && ssid->ssid_len) {
if (params.num_ssids == max_sched_scan_ssids)
break; /* only room for broadcast SSID */
wpa_dbg(wpa_s, MSG_DEBUG,
"add to active scan ssid: %s",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
params.ssids[params.num_ssids].ssid =
ssid->ssid;
params.ssids[params.num_ssids].ssid_len =
ssid->ssid_len;
params.num_ssids++;
if (params.num_ssids >= max_sched_scan_ssids) {
wpa_s->prev_sched_ssid = ssid;
do {
ssid = ssid->next;
} while (ssid &&
(wpas_network_disabled(wpa_s, ssid) ||
!ssid->scan_ssid));
break;
}
}
next:
wpa_s->prev_sched_ssid = ssid;
ssid = ssid->next;
}
if (params.num_filter_ssids == 0) {
os_free(params.filter_ssids);
params.filter_ssids = NULL;
}
extra_ie = wpa_supplicant_extra_ies(wpa_s);
if (extra_ie) {
params.extra_ies = wpabuf_head(extra_ie);
params.extra_ies_len = wpabuf_len(extra_ie);
}
if (wpa_s->conf->filter_rssi)
params.filter_rssi = wpa_s->conf->filter_rssi;
/* See if user specified frequencies. If so, scan only those. */
if (wpa_s->conf->freq_list && !params.freqs) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Optimize scan based on conf->freq_list");
int_array_concat(&params.freqs, wpa_s->conf->freq_list);
}
#ifdef CONFIG_MBO
if (wpa_s->enable_oce & OCE_STA)
params.oce_scan = 1;
#endif /* CONFIG_MBO */
scan_params = &params;
scan:
wpa_s->sched_scan_timed_out = 0;
/*
* We cannot support multiple scan plans if the scan request includes
* too many SSID's, so in this case use only the last scan plan and make
* it run infinitely. It will be stopped by the timeout.
*/
if (wpa_s->sched_scan_plans_num == 1 ||
(wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) {
params.sched_scan_plans = wpa_s->sched_scan_plans;
params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
} else if (wpa_s->sched_scan_plans_num > 1) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Too many SSIDs. Default to using single scheduled_scan plan");
params.sched_scan_plans =
&wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num -
1];
params.sched_scan_plans_num = 1;
} else {
if (wpa_s->conf->sched_scan_interval)
scan_plan.interval = wpa_s->conf->sched_scan_interval;
else
scan_plan.interval = 10;
if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) {
wpa_printf(MSG_WARNING,
"Scan interval too long(%u), use the maximum allowed(%u)",
scan_plan.interval,
wpa_s->max_sched_scan_plan_interval);
scan_plan.interval =
wpa_s->max_sched_scan_plan_interval;
}
scan_plan.iterations = 0;
params.sched_scan_plans = &scan_plan;
params.sched_scan_plans_num = 1;
}
params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
if (ssid || !wpa_s->first_sched_scan) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Starting sched scan after %u seconds: interval %u timeout %d",
params.sched_scan_start_delay,
params.sched_scan_plans[0].interval,
wpa_s->sched_scan_timeout);
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"Starting sched scan after %u seconds (no timeout)",
params.sched_scan_start_delay);
}
wpa_setband_scan_freqs(wpa_s, scan_params);
if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) &&
wpa_s->wpa_state <= WPA_SCANNING)
wpa_setup_mac_addr_rand_params(&params,
wpa_s->mac_addr_sched_scan);
wpa_scan_set_relative_rssi_params(wpa_s, scan_params);
ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
wpabuf_free(extra_ie);
os_free(params.filter_ssids);
os_free(params.mac_addr);
if (ret) {
wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate sched scan");
if (prev_state != wpa_s->wpa_state)
wpa_supplicant_set_state(wpa_s, prev_state);
return ret;
}
/* If we have more SSIDs to scan, add a timeout so we scan them too */
if (ssid || !wpa_s->first_sched_scan) {
wpa_s->sched_scan_timed_out = 0;
eloop_register_timeout(wpa_s->sched_scan_timeout, 0,
wpa_supplicant_sched_scan_timeout,
wpa_s, NULL);
wpa_s->first_sched_scan = 0;
wpa_s->sched_scan_timeout /= 2;
params.sched_scan_plans[0].interval *= 2;
if ((unsigned int) wpa_s->sched_scan_timeout <
params.sched_scan_plans[0].interval ||
params.sched_scan_plans[0].interval >
wpa_s->max_sched_scan_plan_interval) {
params.sched_scan_plans[0].interval = 10;
wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
}
}
/* If there is no more ssids, start next time from the beginning */
if (!ssid)
wpa_s->prev_sched_ssid = NULL;
return 0;
}
/**
* wpa_supplicant_cancel_scan - Cancel a scheduled scan request
* @wpa_s: Pointer to wpa_supplicant data
*
* This function is used to cancel a scan request scheduled with
* wpa_supplicant_req_scan().
*/
void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
{
wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
}
/**
* wpa_supplicant_cancel_delayed_sched_scan - Stop a delayed scheduled scan
* @wpa_s: Pointer to wpa_supplicant data
*
* This function is used to stop a delayed scheduled scan.
*/
void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s)
{
if (!wpa_s->sched_scan_supported)
return;
wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling delayed sched scan");
eloop_cancel_timeout(wpa_supplicant_delayed_sched_scan_timeout,
wpa_s, NULL);
}
/**
* wpa_supplicant_cancel_sched_scan - Stop running scheduled scans
* @wpa_s: Pointer to wpa_supplicant data
*
* This function is used to stop a periodic scheduled scan.
*/
void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s)
{
if (!wpa_s->sched_scanning)
return;
if (wpa_s->sched_scanning)
wpa_s->sched_scan_stop_req = 1;
wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
wpa_supplicant_stop_sched_scan(wpa_s);
}
/**
* wpa_supplicant_notify_scanning - Indicate possible scan state change
* @wpa_s: Pointer to wpa_supplicant data
* @scanning: Whether scanning is currently in progress
*
* This function is to generate scanning notifycations. It is called whenever
* there may have been a change in scanning (scan started, completed, stopped).
* wpas_notify_scanning() is called whenever the scanning state changed from the
* previously notified state.
*/
void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
int scanning)
{
if (wpa_s->scanning != scanning) {
wpa_s->scanning = scanning;
wpas_notify_scanning(wpa_s);
}
}
static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
{
int rate = 0;
const u8 *ie;
int i;
ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
for (i = 0; ie && i < ie[1]; i++) {
if ((ie[i + 2] & 0x7f) > rate)
rate = ie[i + 2] & 0x7f;
}
ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
for (i = 0; ie && i < ie[1]; i++) {
if ((ie[i + 2] & 0x7f) > rate)
rate = ie[i + 2] & 0x7f;
}
return rate;
}
/**
* wpa_scan_get_ie - Fetch a specified information element from a scan result
* @res: Scan result entry
* @ie: Information element identitifier (WLAN_EID_*)
* Returns: Pointer to the information element (id field) or %NULL if not found
*
* This function returns the first matching information element in the scan
* result.
*/
const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
{
size_t ie_len = res->ie_len;
/* Use the Beacon frame IEs if res->ie_len is not available */
if (!ie_len)
ie_len = res->beacon_ie_len;
return get_ie((const u8 *) (res + 1), ie_len, ie);
}
/**
* wpa_scan_get_vendor_ie - Fetch vendor information element from a scan result
* @res: Scan result entry
* @vendor_type: Vendor type (four octets starting the IE payload)
* Returns: Pointer to the information element (id field) or %NULL if not found
*
* This function returns the first matching information element in the scan
* result.
*/
const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
u32 vendor_type)
{
const u8 *ies;
const struct element *elem;
ies = (const u8 *) (res + 1);
for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, res->ie_len) {
if (elem->datalen >= 4 &&
vendor_type == WPA_GET_BE32(elem->data))
return &elem->id;
}
return NULL;
}
/**
* wpa_scan_get_vendor_ie_beacon - Fetch vendor information from a scan result
* @res: Scan result entry
* @vendor_type: Vendor type (four octets starting the IE payload)
* Returns: Pointer to the information element (id field) or %NULL if not found
*
* This function returns the first matching information element in the scan
* result.
*
* This function is like wpa_scan_get_vendor_ie(), but uses IE buffer only
* from Beacon frames instead of either Beacon or Probe Response frames.
*/
const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
u32 vendor_type)
{
const u8 *ies;
const struct element *elem;
if (res->beacon_ie_len == 0)
return NULL;
ies = (const u8 *) (res + 1);
ies += res->ie_len;
for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies,
res->beacon_ie_len) {
if (elem->datalen >= 4 &&
vendor_type == WPA_GET_BE32(elem->data))
return &elem->id;
}
return NULL;
}
/**
* wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result
* @res: Scan result entry
* @vendor_type: Vendor type (four octets starting the IE payload)
* Returns: Pointer to the information element payload or %NULL if not found
*
* This function returns concatenated payload of possibly fragmented vendor
* specific information elements in the scan result. The caller is responsible
* for freeing the returned buffer.
*/
struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
u32 vendor_type)
{
struct wpabuf *buf;
const u8 *end, *pos;
buf = wpabuf_alloc(res->ie_len);
if (buf == NULL)
return NULL;
pos = (const u8 *) (res + 1);
end = pos + res->ie_len;
while (end - pos > 1) {
u8 ie, len;
ie = pos[0];
len = pos[1];
if (len > end - pos - 2)
break;
pos += 2;
if (ie == WLAN_EID_VENDOR_SPECIFIC && len >= 4 &&
vendor_type == WPA_GET_BE32(pos))
wpabuf_put_data(buf, pos + 4, len - 4);
pos += len;
}
if (wpabuf_len(buf) == 0) {
wpabuf_free(buf);
buf = NULL;
}
return buf;
}
/* Compare function for sorting scan results. Return >0 if @b is considered
* better. */
static int wpa_scan_result_compar(const void *a, const void *b)
{
#define MIN(a,b) a < b ? a : b
struct wpa_scan_res **_wa = (void *) a;
struct wpa_scan_res **_wb = (void *) b;
struct wpa_scan_res *wa = *_wa;
struct wpa_scan_res *wb = *_wb;
int wpa_a, wpa_b;
int snr_a, snr_b, snr_a_full, snr_b_full;
/* WPA/WPA2 support preferred */
wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
if (wpa_b && !wpa_a)
return 1;
if (!wpa_b && wpa_a)
return -1;
/* privacy support preferred */
if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
(wb->caps & IEEE80211_CAP_PRIVACY))
return 1;
if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
(wb->caps & IEEE80211_CAP_PRIVACY) == 0)
return -1;
if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) {
snr_a_full = wa->snr;
snr_a = MIN(wa->snr, GREAT_SNR);
snr_b_full = wb->snr;
snr_b = MIN(wb->snr, GREAT_SNR);
} else {
/* Level is not in dBm, so we can't calculate
* SNR. Just use raw level (units unknown). */
snr_a = snr_a_full = wa->level;
snr_b = snr_b_full = wb->level;
}
/* If SNR is close, decide by max rate or frequency band. For cases
* involving the 6 GHz band, use the throughput estimate irrespective
* of the SNR difference since the LPI/VLP rules may result in
* significant differences in SNR for cases where the estimated
* throughput can be considerably higher with the lower SNR. */
if (snr_a && snr_b && (abs(snr_b - snr_a) < 7 ||
is_6ghz_freq(wa->freq) ||
is_6ghz_freq(wb->freq))) {
if (wa->est_throughput != wb->est_throughput)
return (int) wb->est_throughput -
(int) wa->est_throughput;
}
if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
(wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
if (is_6ghz_freq(wa->freq) ^ is_6ghz_freq(wb->freq))
return is_6ghz_freq(wa->freq) ? -1 : 1;
if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
return IS_5GHZ(wa->freq) ? -1 : 1;
}
/* all things being equal, use SNR; if SNRs are
* identical, use quality values since some drivers may only report
* that value and leave the signal level zero */
if (snr_b_full == snr_a_full)
return wb->qual - wa->qual;
return snr_b_full - snr_a_full;
#undef MIN
}
#ifdef CONFIG_WPS
/* Compare function for sorting scan results when searching a WPS AP for
* provisioning. Return >0 if @b is considered better. */
static int wpa_scan_result_wps_compar(const void *a, const void *b)
{
struct wpa_scan_res **_wa = (void *) a;
struct wpa_scan_res **_wb = (void *) b;
struct wpa_scan_res *wa = *_wa;
struct wpa_scan_res *wb = *_wb;
int uses_wps_a, uses_wps_b;
struct wpabuf *wps_a, *wps_b;
int res;
/* Optimization - check WPS IE existence before allocated memory and
* doing full reassembly. */
uses_wps_a = wpa_scan_get_vendor_ie(wa, WPS_IE_VENDOR_TYPE) != NULL;
uses_wps_b = wpa_scan_get_vendor_ie(wb, WPS_IE_VENDOR_TYPE) != NULL;
if (uses_wps_a && !uses_wps_b)
return -1;
if (!uses_wps_a && uses_wps_b)
return 1;
if (uses_wps_a && uses_wps_b) {
wps_a = wpa_scan_get_vendor_ie_multi(wa, WPS_IE_VENDOR_TYPE);
wps_b = wpa_scan_get_vendor_ie_multi(wb, WPS_IE_VENDOR_TYPE);
res = wps_ap_priority_compar(wps_a, wps_b);
wpabuf_free(wps_a);
wpabuf_free(wps_b);
if (res)
return res;
}
/*
* Do not use current AP security policy as a sorting criteria during
* WPS provisioning step since the AP may get reconfigured at the
* completion of provisioning.
*/
/* all things being equal, use signal level; if signal levels are
* identical, use quality values since some drivers may only report
* that value and leave the signal level zero */
if (wb->level == wa->level)
return wb->qual - wa->qual;
return wb->level - wa->level;
}
#endif /* CONFIG_WPS */
static void dump_scan_res(struct wpa_scan_results *scan_res)
{
#ifndef CONFIG_NO_STDOUT_DEBUG
size_t i;
if (scan_res->res == NULL || scan_res->num == 0)
return;
wpa_printf(MSG_EXCESSIVE, "Sorted scan results");
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *r = scan_res->res[i];
u8 *pos;
if (r->flags & WPA_SCAN_LEVEL_DBM) {
int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
"noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
MAC2STR(r->bssid), r->freq, r->qual,
r->noise, noise_valid ? "" : "~", r->level,
r->snr, r->snr >= GREAT_SNR ? "*" : "",
r->flags,
r->age, r->est_throughput);
} else {
wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
"noise=%d level=%d flags=0x%x age=%u est=%u",
MAC2STR(r->bssid), r->freq, r->qual,
r->noise, r->level, r->flags, r->age,
r->est_throughput);
}
pos = (u8 *) (r + 1);
if (r->ie_len)
wpa_hexdump(MSG_EXCESSIVE, "IEs", pos, r->ie_len);
pos += r->ie_len;
if (r->beacon_ie_len)
wpa_hexdump(MSG_EXCESSIVE, "Beacon IEs",
pos, r->beacon_ie_len);
}
#endif /* CONFIG_NO_STDOUT_DEBUG */
}
/**
* wpa_supplicant_filter_bssid_match - Is the specified BSSID allowed
* @wpa_s: Pointer to wpa_supplicant data
* @bssid: BSSID to check
* Returns: 0 if the BSSID is filtered or 1 if not
*
* This function is used to filter out specific BSSIDs from scan reslts mainly
* for testing purposes (SET bssid_filter ctrl_iface command).
*/
int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
const u8 *bssid)
{
size_t i;
if (wpa_s->bssid_filter == NULL)
return 1;
for (i = 0; i < wpa_s->bssid_filter_count; i++) {
if (os_memcmp(wpa_s->bssid_filter + i * ETH_ALEN, bssid,
ETH_ALEN) == 0)
return 1;
}
return 0;
}
void filter_scan_res(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *res)
{
size_t i, j;
if (wpa_s->bssid_filter == NULL)
return;
for (i = 0, j = 0; i < res->num; i++) {
if (wpa_supplicant_filter_bssid_match(wpa_s,
res->res[i]->bssid)) {
res->res[j++] = res->res[i];
} else {
os_free(res->res[i]);
res->res[i] = NULL;
}
}
if (res->num != j) {
wpa_printf(MSG_DEBUG, "Filtered out %d scan results",
(int) (res->num - j));
res->num = j;
}
}
void scan_snr(struct wpa_scan_res *res)
{
if (res->flags & WPA_SCAN_NOISE_INVALID) {
res->noise = is_6ghz_freq(res->freq) ?
DEFAULT_NOISE_FLOOR_6GHZ :
(IS_5GHZ(res->freq) ?
DEFAULT_NOISE_FLOOR_5GHZ : DEFAULT_NOISE_FLOOR_2GHZ);
}
if (res->flags & WPA_SCAN_LEVEL_DBM) {
res->snr = res->level - res->noise;
} else {
/* Level is not in dBm, so we can't calculate
* SNR. Just use raw level (units unknown). */
res->snr = res->level;
}
}
/* Minimum SNR required to achieve a certain bitrate. */
struct minsnr_bitrate_entry {
int minsnr;
unsigned int bitrate; /* in Mbps */
};
/* VHT needs to be enabled in order to achieve MCS8 and MCS9 rates. */
static const int vht_mcs = 8;
static const struct minsnr_bitrate_entry vht20_table[] = {
{ 0, 0 },
{ 2, 6500 }, /* HT20 MCS0 */
{ 5, 13000 }, /* HT20 MCS1 */
{ 9, 19500 }, /* HT20 MCS2 */
{ 11, 26000 }, /* HT20 MCS3 */
{ 15, 39000 }, /* HT20 MCS4 */
{ 18, 52000 }, /* HT20 MCS5 */
{ 20, 58500 }, /* HT20 MCS6 */
{ 25, 65000 }, /* HT20 MCS7 */
{ 29, 78000 }, /* VHT20 MCS8 */
{ -1, 78000 } /* SNR > 29 */
};
static const struct minsnr_bitrate_entry vht40_table[] = {
{ 0, 0 },
{ 5, 13500 }, /* HT40 MCS0 */
{ 8, 27000 }, /* HT40 MCS1 */
{ 12, 40500 }, /* HT40 MCS2 */
{ 14, 54000 }, /* HT40 MCS3 */
{ 18, 81000 }, /* HT40 MCS4 */
{ 21, 108000 }, /* HT40 MCS5 */
{ 23, 121500 }, /* HT40 MCS6 */
{ 28, 135000 }, /* HT40 MCS7 */
{ 32, 162000 }, /* VHT40 MCS8 */
{ 34, 180000 }, /* VHT40 MCS9 */
{ -1, 180000 } /* SNR > 34 */
};
static const struct minsnr_bitrate_entry vht80_table[] = {
{ 0, 0 },
{ 8, 29300 }, /* VHT80 MCS0 */
{ 11, 58500 }, /* VHT80 MCS1 */
{ 15, 87800 }, /* VHT80 MCS2 */
{ 17, 117000 }, /* VHT80 MCS3 */
{ 21, 175500 }, /* VHT80 MCS4 */
{ 24, 234000 }, /* VHT80 MCS5 */
{ 26, 263300 }, /* VHT80 MCS6 */
{ 31, 292500 }, /* VHT80 MCS7 */
{ 35, 351000 }, /* VHT80 MCS8 */
{ 37, 390000 }, /* VHT80 MCS9 */
{ -1, 390000 } /* SNR > 37 */
};
static const struct minsnr_bitrate_entry vht160_table[] = {
{ 0, 0 },
{ 11, 58500 }, /* VHT160 MCS0 */
{ 14, 117000 }, /* VHT160 MCS1 */
{ 18, 175500 }, /* VHT160 MCS2 */
{ 20, 234000 }, /* VHT160 MCS3 */
{ 24, 351000 }, /* VHT160 MCS4 */
{ 27, 468000 }, /* VHT160 MCS5 */
{ 29, 526500 }, /* VHT160 MCS6 */
{ 34, 585000 }, /* VHT160 MCS7 */
{ 38, 702000 }, /* VHT160 MCS8 */
{ 40, 780000 }, /* VHT160 MCS9 */
{ -1, 780000 } /* SNR > 37 */
};
static const struct minsnr_bitrate_entry he20_table[] = {
{ 0, 0 },
{ 2, 8600 }, /* HE20 MCS0 */
{ 5, 17200 }, /* HE20 MCS1 */
{ 9, 25800 }, /* HE20 MCS2 */
{ 11, 34400 }, /* HE20 MCS3 */
{ 15, 51600 }, /* HE20 MCS4 */
{ 18, 68800 }, /* HE20 MCS5 */
{ 20, 77400 }, /* HE20 MCS6 */
{ 25, 86000 }, /* HE20 MCS7 */
{ 29, 103200 }, /* HE20 MCS8 */
{ 31, 114700 }, /* HE20 MCS9 */
{ 34, 129000 }, /* HE20 MCS10 */
{ 36, 143400 }, /* HE20 MCS11 */
{ -1, 143400 } /* SNR > 29 */
};
static const struct minsnr_bitrate_entry he40_table[] = {
{ 0, 0 },
{ 5, 17200 }, /* HE40 MCS0 */
{ 8, 34400 }, /* HE40 MCS1 */
{ 12, 51600 }, /* HE40 MCS2 */
{ 14, 68800 }, /* HE40 MCS3 */
{ 18, 103200 }, /* HE40 MCS4 */
{ 21, 137600 }, /* HE40 MCS5 */
{ 23, 154900 }, /* HE40 MCS6 */
{ 28, 172100 }, /* HE40 MCS7 */
{ 32, 206500 }, /* HE40 MCS8 */
{ 34, 229400 }, /* HE40 MCS9 */
{ 37, 258100 }, /* HE40 MCS10 */
{ 39, 286800 }, /* HE40 MCS11 */
{ -1, 286800 } /* SNR > 34 */
};
static const struct minsnr_bitrate_entry he80_table[] = {
{ 0, 0 },
{ 8, 36000 }, /* HE80 MCS0 */
{ 11, 72100 }, /* HE80 MCS1 */
{ 15, 108100 }, /* HE80 MCS2 */
{ 17, 144100 }, /* HE80 MCS3 */
{ 21, 216200 }, /* HE80 MCS4 */
{ 24, 288200 }, /* HE80 MCS5 */
{ 26, 324300 }, /* HE80 MCS6 */
{ 31, 360300 }, /* HE80 MCS7 */
{ 35, 432400 }, /* HE80 MCS8 */
{ 37, 480400 }, /* HE80 MCS9 */
{ 40, 540400 }, /* HE80 MCS10 */
{ 42, 600500 }, /* HE80 MCS11 */
{ -1, 600500 } /* SNR > 37 */
};
static const struct minsnr_bitrate_entry he160_table[] = {
{ 0, 0 },
{ 11, 72100 }, /* HE160 MCS0 */
{ 14, 144100 }, /* HE160 MCS1 */
{ 18, 216200 }, /* HE160 MCS2 */
{ 20, 288200 }, /* HE160 MCS3 */
{ 24, 432400 }, /* HE160 MCS4 */
{ 27, 576500 }, /* HE160 MCS5 */
{ 29, 648500 }, /* HE160 MCS6 */
{ 34, 720600 }, /* HE160 MCS7 */
{ 38, 864700 }, /* HE160 MCS8 */
{ 40, 960800 }, /* HE160 MCS9 */
{ 43, 1080900 }, /* HE160 MCS10 */
{ 45, 1201000 }, /* HE160 MCS11 */
{ -1, 1201000 } /* SNR > 37 */
};
static unsigned int interpolate_rate(int snr, int snr0, int snr1,
int rate0, int rate1)
{
return rate0 + (snr - snr0) * (rate1 - rate0) / (snr1 - snr0);
}
static unsigned int max_rate(const struct minsnr_bitrate_entry table[],
int snr, bool vht)
{
const struct minsnr_bitrate_entry *prev, *entry = table;
while ((entry->minsnr != -1) &&
(snr >= entry->minsnr) &&
(vht || entry - table <= vht_mcs))
entry++;
if (entry == table)
return entry->bitrate;
prev = entry - 1;
if (entry->minsnr == -1 || (!vht && entry - table > vht_mcs))
return prev->bitrate;
return interpolate_rate(snr, prev->minsnr, entry->minsnr, prev->bitrate,
entry->bitrate);
}
static unsigned int max_ht20_rate(int snr, bool vht)
{
return max_rate(vht20_table, snr, vht);
}
static unsigned int max_ht40_rate(int snr, bool vht)
{
return max_rate(vht40_table, snr, vht);
}
static unsigned int max_vht80_rate(int snr)
{
return max_rate(vht80_table, snr, 1);
}
static unsigned int max_vht160_rate(int snr)
{
return max_rate(vht160_table, snr, 1);
}
static unsigned int max_he_rate(const struct minsnr_bitrate_entry table[],
int snr)
{
const struct minsnr_bitrate_entry *prev, *entry = table;
while (entry->minsnr != -1 && snr >= entry->minsnr)
entry++;
if (entry == table)
return 0;
prev = entry - 1;
if (entry->minsnr == -1)
return prev->bitrate;
return interpolate_rate(snr, prev->minsnr, entry->minsnr,
prev->bitrate, entry->bitrate);
}
unsigned int wpas_get_est_tpt(const struct wpa_supplicant *wpa_s,
const u8 *ies, size_t ies_len, int rate,
int snr, int freq)
{
struct hostapd_hw_modes *hw_mode;
unsigned int est, tmp;
const u8 *ie;
/* Limit based on estimated SNR */
if (rate > 1 * 2 && snr < 1)
rate = 1 * 2;
else if (rate > 2 * 2 && snr < 4)
rate = 2 * 2;
else if (rate > 6 * 2 && snr < 5)
rate = 6 * 2;
else if (rate > 9 * 2 && snr < 6)
rate = 9 * 2;
else if (rate > 12 * 2 && snr < 7)
rate = 12 * 2;
else if (rate > 12 * 2 && snr < 8)
rate = 14 * 2;
else if (rate > 12 * 2 && snr < 9)
rate = 16 * 2;
else if (rate > 18 * 2 && snr < 10)
rate = 18 * 2;
else if (rate > 24 * 2 && snr < 11)
rate = 24 * 2;
else if (rate > 24 * 2 && snr < 12)
rate = 27 * 2;
else if (rate > 24 * 2 && snr < 13)
rate = 30 * 2;
else if (rate > 24 * 2 && snr < 14)
rate = 33 * 2;
else if (rate > 36 * 2 && snr < 15)
rate = 36 * 2;
else if (rate > 36 * 2 && snr < 16)
rate = 39 * 2;
else if (rate > 36 * 2 && snr < 17)
rate = 42 * 2;
else if (rate > 36 * 2 && snr < 18)
rate = 45 * 2;
else if (rate > 48 * 2 && snr < 19)
rate = 48 * 2;
else if (rate > 48 * 2 && snr < 20)
rate = 51 * 2;
else if (rate > 54 * 2 && snr < 21)
rate = 54 * 2;
est = rate * 500;
hw_mode = get_mode_with_freq(wpa_s->hw.modes, wpa_s->hw.num_modes,
freq);
if (hw_mode && hw_mode->ht_capab) {
ie = get_ie(ies, ies_len, WLAN_EID_HT_CAP);
if (ie) {
tmp = max_ht20_rate(snr, false);
if (tmp > est)
est = tmp;
}
}
if (hw_mode &&
(hw_mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
if (ie && ie[1] >= 2 &&
(ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
tmp = max_ht40_rate(snr, false);
if (tmp > est)
est = tmp;
}
}
if (hw_mode && hw_mode->vht_capab) {
/* Use +1 to assume VHT is always faster than HT */
ie = get_ie(ies, ies_len, WLAN_EID_VHT_CAP);
if (ie) {
bool vht80 = false, vht160 = false;
tmp = max_ht20_rate(snr, true) + 1;
if (tmp > est)
est = tmp;
ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
if (ie && ie[1] >= 2 &&
(ie[3] &
HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
tmp = max_ht40_rate(snr, true) + 1;
if (tmp > est)
est = tmp;
}
/* Determine VHT BSS bandwidth based on IEEE Std
* 802.11-2020, Table 11-23 (VHT BSs bandwidth) */
ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
if (ie && ie[1] >= 3) {
u8 cw = ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK;
u8 seg0 = ie[3];
u8 seg1 = ie[4];
if (cw)
vht80 = true;
if (cw == 2 ||
(cw == 3 &&
(seg1 > 0 && abs(seg1 - seg0) == 16)))
vht160 = true;
if (cw == 1 &&
((seg1 > 0 && abs(seg1 - seg0) == 8) ||
(seg1 > 0 && abs(seg1 - seg0) == 16)))
vht160 = true;
}
if (vht80) {
tmp = max_vht80_rate(snr) + 1;
if (tmp > est)
est = tmp;
}
if (vht160 &&
(hw_mode->vht_capab &
(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
tmp = max_vht160_rate(snr) + 1;
if (tmp > est)
est = tmp;
}
}
}
if (hw_mode && hw_mode->he_capab[IEEE80211_MODE_INFRA].he_supported) {
/* Use +2 to assume HE is always faster than HT/VHT */
struct ieee80211_he_capabilities *he;
struct he_capabilities *own_he;
u8 cw;
ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_HE_CAPABILITIES);
if (!ie || (ie[1] < 1 + IEEE80211_HE_CAPAB_MIN_LEN))
return est;
he = (struct ieee80211_he_capabilities *) &ie[3];
own_he = &hw_mode->he_capab[IEEE80211_MODE_INFRA];
tmp = max_he_rate(he20_table, snr) + 2;
if (tmp > est)
est = tmp;
cw = he->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
own_he->phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
if (cw &
(IS_2P4GHZ(freq) ? HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G :
HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
tmp = max_he_rate(he40_table, snr) + 2;
if (tmp > est)
est = tmp;
}
if (!IS_2P4GHZ(freq) &&
(cw & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G)) {
tmp = max_he_rate(he80_table, snr) + 2;
if (tmp > est)
est = tmp;
}
if (!IS_2P4GHZ(freq) &&
(cw & (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))) {
tmp = max_he_rate(he160_table, snr) + 2;
if (tmp > est)
est = tmp;
}
}
return est;
}
void scan_est_throughput(struct wpa_supplicant *wpa_s,
struct wpa_scan_res *res)
{
int rate; /* max legacy rate in 500 kb/s units */
int snr = res->snr;
const u8 *ies = (const void *) (res + 1);
size_t ie_len = res->ie_len;
if (res->est_throughput)
return;
/* Get maximum legacy rate */
rate = wpa_scan_get_max_rate(res);
if (!ie_len)
ie_len = res->beacon_ie_len;
res->est_throughput =
wpas_get_est_tpt(wpa_s, ies, ie_len, rate, snr, res->freq);
/* TODO: channel utilization and AP load (e.g., from AP Beacon) */
}
/**
* wpa_supplicant_get_scan_results - Get scan results
* @wpa_s: Pointer to wpa_supplicant data
* @info: Information about what was scanned or %NULL if not available
* @new_scan: Whether a new scan was performed
* Returns: Scan results, %NULL on failure
*
* This function request the current scan results from the driver and updates
* the local BSS list wpa_s->bss. The caller is responsible for freeing the
* results with wpa_scan_results_free().
*/
struct wpa_scan_results *
wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
struct scan_info *info, int new_scan)
{
struct wpa_scan_results *scan_res;
size_t i;
int (*compar)(const void *, const void *) = wpa_scan_result_compar;
scan_res = wpa_drv_get_scan_results2(wpa_s);
if (scan_res == NULL) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results");
return NULL;
}
if (scan_res->fetch_time.sec == 0) {
/*
* Make sure we have a valid timestamp if the driver wrapper
* does not set this.
*/
os_get_reltime(&scan_res->fetch_time);
}
filter_scan_res(wpa_s, scan_res);
for (i = 0; i < scan_res->num; i++) {
struct wpa_scan_res *scan_res_item = scan_res->res[i];
scan_snr(scan_res_item);
scan_est_throughput(wpa_s, scan_res_item);
}
#ifdef CONFIG_WPS
if (wpas_wps_searching(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
"provisioning rules");
compar = wpa_scan_result_wps_compar;
}
#endif /* CONFIG_WPS */
if (scan_res->res) {
qsort(scan_res->res, scan_res->num,
sizeof(struct wpa_scan_res *), compar);
}
dump_scan_res(scan_res);
if (wpa_s->ignore_post_flush_scan_res) {
/* FLUSH command aborted an ongoing scan and these are the
* results from the aborted scan. Do not process the results to
* maintain flushed state. */
wpa_dbg(wpa_s, MSG_DEBUG,
"Do not update BSS table based on pending post-FLUSH scan results");
wpa_s->ignore_post_flush_scan_res = 0;
return scan_res;
}
wpa_bss_update_start(wpa_s);
for (i = 0; i < scan_res->num; i++)
wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
&scan_res->fetch_time);
wpa_bss_update_end(wpa_s, info, new_scan);
return scan_res;
}
/**
* wpa_supplicant_update_scan_results - Update scan results from the driver
* @wpa_s: Pointer to wpa_supplicant data
* Returns: 0 on success, -1 on failure
*
* This function updates the BSS table within wpa_supplicant based on the
* currently available scan results from the driver without requesting a new
* scan. This is used in cases where the driver indicates an association
* (including roaming within ESS) and wpa_supplicant does not yet have the
* needed information to complete the connection (e.g., to perform validation
* steps in 4-way handshake).
*/
int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
{
struct wpa_scan_results *scan_res;
scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
if (scan_res == NULL)
return -1;
wpa_scan_results_free(scan_res);
return 0;
}
/**
* scan_only_handler - Reports scan results
*/
void scan_only_handler(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res)
{
wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received");
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
wpa_s->manual_scan_id);
wpa_s->manual_scan_use_id = 0;
} else {
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
}
wpas_notify_scan_results(wpa_s);
wpas_notify_scan_done(wpa_s, 1);
if (wpa_s->scan_work) {
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
}
if (wpa_s->wpa_state == WPA_SCANNING)
wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
}
int wpas_scan_scheduled(struct wpa_supplicant *wpa_s)
{
return eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL);
}
struct wpa_driver_scan_params *
wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
{
struct wpa_driver_scan_params *params;
size_t i;
u8 *n;
params = os_zalloc(sizeof(*params));
if (params == NULL)
return NULL;
for (i = 0; i < src->num_ssids; i++) {
if (src->ssids[i].ssid) {
n = os_memdup(src->ssids[i].ssid,
src->ssids[i].ssid_len);
if (n == NULL)
goto failed;
params->ssids[i].ssid = n;
params->ssids[i].ssid_len = src->ssids[i].ssid_len;
}
}
params->num_ssids = src->num_ssids;
if (src->extra_ies) {
n = os_memdup(src->extra_ies, src->extra_ies_len);
if (n == NULL)
goto failed;
params->extra_ies = n;
params->extra_ies_len = src->extra_ies_len;
}
if (src->freqs) {
int len = int_array_len(src->freqs);
params->freqs = os_memdup(src->freqs, (len + 1) * sizeof(int));
if (params->freqs == NULL)
goto failed;
}
if (src->filter_ssids) {
params->filter_ssids = os_memdup(src->filter_ssids,
sizeof(*params->filter_ssids) *
src->num_filter_ssids);
if (params->filter_ssids == NULL)
goto failed;
params->num_filter_ssids = src->num_filter_ssids;
}
params->filter_rssi = src->filter_rssi;
params->p2p_probe = src->p2p_probe;
params->only_new_results = src->only_new_results;
params->low_priority = src->low_priority;
params->duration = src->duration;
params->duration_mandatory = src->duration_mandatory;
params->oce_scan = src->oce_scan;
if (src->sched_scan_plans_num > 0) {
params->sched_scan_plans =
os_memdup(src->sched_scan_plans,
sizeof(*src->sched_scan_plans) *
src->sched_scan_plans_num);
if (!params->sched_scan_plans)
goto failed;
params->sched_scan_plans_num = src->sched_scan_plans_num;
}
if (src->mac_addr_rand &&
wpa_setup_mac_addr_rand_params(params, src->mac_addr))
goto failed;
if (src->bssid) {
u8 *bssid;
bssid = os_memdup(src->bssid, ETH_ALEN);
if (!bssid)
goto failed;
params->bssid = bssid;
}
params->relative_rssi_set = src->relative_rssi_set;
params->relative_rssi = src->relative_rssi;
params->relative_adjust_band = src->relative_adjust_band;
params->relative_adjust_rssi = src->relative_adjust_rssi;
params->p2p_include_6ghz = src->p2p_include_6ghz;
return params;
failed:
wpa_scan_free_params(params);
return NULL;
}
void wpa_scan_free_params(struct wpa_driver_scan_params *params)
{
size_t i;
if (params == NULL)
return;
for (i = 0; i < params->num_ssids; i++)
os_free((u8 *) params->ssids[i].ssid);
os_free((u8 *) params->extra_ies);
os_free(params->freqs);
os_free(params->filter_ssids);
os_free(params->sched_scan_plans);
/*
* Note: params->mac_addr_mask points to same memory allocation and
* must not be freed separately.
*/
os_free((u8 *) params->mac_addr);
os_free((u8 *) params->bssid);
os_free(params);
}
int wpas_start_pno(struct wpa_supplicant *wpa_s)
{
int ret;
size_t prio, i, num_ssid, num_match_ssid;
struct wpa_ssid *ssid;
struct wpa_driver_scan_params params;
struct sched_scan_plan scan_plan;
unsigned int max_sched_scan_ssids;
if (!wpa_s->sched_scan_supported)
return -1;
if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
else
max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
if (max_sched_scan_ssids < 1)
return -1;
if (wpa_s->pno || wpa_s->pno_sched_pending)
return 0;
if ((wpa_s->wpa_state > WPA_SCANNING) &&
(wpa_s->wpa_state < WPA_COMPLETED)) {
wpa_printf(MSG_ERROR, "PNO: In assoc process");
return -EAGAIN;
}
if (wpa_s->wpa_state == WPA_SCANNING) {
wpa_supplicant_cancel_scan(wpa_s);
if (wpa_s->sched_scanning) {
wpa_printf(MSG_DEBUG, "Schedule PNO on completion of "
"ongoing sched scan");
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_s->pno_sched_pending = 1;
return 0;
}
}
if (wpa_s->sched_scan_stop_req) {
wpa_printf(MSG_DEBUG,
"Schedule PNO after previous sched scan has stopped");
wpa_s->pno_sched_pending = 1;
return 0;
}
os_memset(&params, 0, sizeof(params));
num_ssid = num_match_ssid = 0;
ssid = wpa_s->conf->ssid;
while (ssid) {
if (!wpas_network_disabled(wpa_s, ssid)) {
num_match_ssid++;
if (ssid->scan_ssid)
num_ssid++;
}
ssid = ssid->next;
}
if (num_match_ssid == 0) {
wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
return -1;
}
if (num_match_ssid > num_ssid) {
params.num_ssids++; /* wildcard */
num_ssid++;
}
if (num_ssid > max_sched_scan_ssids) {
wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
"%u", max_sched_scan_ssids, (unsigned int) num_ssid);
num_ssid = max_sched_scan_ssids;
}
if (num_match_ssid > wpa_s->max_match_sets) {
num_match_ssid = wpa_s->max_match_sets;
wpa_dbg(wpa_s, MSG_DEBUG, "PNO: Too many SSIDs to match");
}
params.filter_ssids = os_calloc(num_match_ssid,
sizeof(struct wpa_driver_scan_filter));
if (params.filter_ssids == NULL)
return -1;
i = 0;
prio = 0;
ssid = wpa_s->conf->pssid[prio];
while (ssid) {
if (!wpas_network_disabled(wpa_s, ssid)) {
if (ssid->scan_ssid && params.num_ssids < num_ssid) {
params.ssids[params.num_ssids].ssid =
ssid->ssid;
params.ssids[params.num_ssids].ssid_len =
ssid->ssid_len;
params.num_ssids++;
}
os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
ssid->ssid_len);
params.filter_ssids[i].ssid_len = ssid->ssid_len;
params.num_filter_ssids++;
i++;
if (i == num_match_ssid)
break;
}
if (ssid->pnext)
ssid = ssid->pnext;
else if (prio + 1 == wpa_s->conf->num_prio)
break;
else
ssid = wpa_s->conf->pssid[++prio];
}
if (wpa_s->conf->filter_rssi)
params.filter_rssi = wpa_s->conf->filter_rssi;
if (wpa_s->sched_scan_plans_num) {
params.sched_scan_plans = wpa_s->sched_scan_plans;
params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
} else {
/* Set one scan plan that will run infinitely */
if (wpa_s->conf->sched_scan_interval)
scan_plan.interval = wpa_s->conf->sched_scan_interval;
else
scan_plan.interval = 10;
scan_plan.iterations = 0;
params.sched_scan_plans = &scan_plan;
params.sched_scan_plans_num = 1;
}
params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay;
if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
params.freqs = wpa_s->manual_sched_scan_freqs;
}
if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) &&
wpa_s->wpa_state <= WPA_SCANNING)
wpa_setup_mac_addr_rand_params(&params, wpa_s->mac_addr_pno);
wpa_scan_set_relative_rssi_params(wpa_s, &params);
ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
os_free(params.filter_ssids);
os_free(params.mac_addr);
if (ret == 0)
wpa_s->pno = 1;
else
wpa_msg(wpa_s, MSG_ERROR, "Failed to schedule PNO");
return ret;
}
int wpas_stop_pno(struct wpa_supplicant *wpa_s)
{
int ret = 0;
if (!wpa_s->pno)
return 0;
ret = wpa_supplicant_stop_sched_scan(wpa_s);
wpa_s->sched_scan_stop_req = 1;
wpa_s->pno = 0;
wpa_s->pno_sched_pending = 0;
if (wpa_s->wpa_state == WPA_SCANNING)
wpa_supplicant_req_scan(wpa_s, 0, 0);
return ret;
}
void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
unsigned int type)
{
type &= MAC_ADDR_RAND_ALL;
wpa_s->mac_addr_rand_enable &= ~type;
if (type & MAC_ADDR_RAND_SCAN) {
os_free(wpa_s->mac_addr_scan);
wpa_s->mac_addr_scan = NULL;
}
if (type & MAC_ADDR_RAND_SCHED_SCAN) {
os_free(wpa_s->mac_addr_sched_scan);
wpa_s->mac_addr_sched_scan = NULL;
}
if (type & MAC_ADDR_RAND_PNO) {
os_free(wpa_s->mac_addr_pno);
wpa_s->mac_addr_pno = NULL;
}
}
int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
unsigned int type, const u8 *addr,
const u8 *mask)
{
u8 *tmp = NULL;
if ((wpa_s->mac_addr_rand_supported & type) != type ) {
wpa_printf(MSG_INFO,
"scan: MAC randomization type %u != supported=%u",
type, wpa_s->mac_addr_rand_supported);
return -1;
}
wpas_mac_addr_rand_scan_clear(wpa_s, type);
if (addr) {
tmp = os_malloc(2 * ETH_ALEN);
if (!tmp)
return -1;
os_memcpy(tmp, addr, ETH_ALEN);
os_memcpy(tmp + ETH_ALEN, mask, ETH_ALEN);
}
if (type == MAC_ADDR_RAND_SCAN) {
wpa_s->mac_addr_scan = tmp;
} else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
wpa_s->mac_addr_sched_scan = tmp;
} else if (type == MAC_ADDR_RAND_PNO) {
wpa_s->mac_addr_pno = tmp;
} else {
wpa_printf(MSG_INFO,
"scan: Invalid MAC randomization type=0x%x",
type);
os_free(tmp);
return -1;
}
wpa_s->mac_addr_rand_enable |= type;
return 0;
}
int wpas_mac_addr_rand_scan_get_mask(struct wpa_supplicant *wpa_s,
unsigned int type, u8 *mask)
{
const u8 *to_copy;
if ((wpa_s->mac_addr_rand_enable & type) != type)
return -1;
if (type == MAC_ADDR_RAND_SCAN) {
to_copy = wpa_s->mac_addr_scan;
} else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
to_copy = wpa_s->mac_addr_sched_scan;
} else if (type == MAC_ADDR_RAND_PNO) {
to_copy = wpa_s->mac_addr_pno;
} else {
wpa_printf(MSG_DEBUG,
"scan: Invalid MAC randomization type=0x%x",
type);
return -1;
}
os_memcpy(mask, to_copy + ETH_ALEN, ETH_ALEN);
return 0;
}
int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
{
struct wpa_radio_work *work;
struct wpa_radio *radio = wpa_s->radio;
dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
if (work->wpa_s != wpa_s || !work->started ||
(os_strcmp(work->type, "scan") != 0 &&
os_strcmp(work->type, "p2p-scan") != 0))
continue;
wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan");
return wpa_drv_abort_scan(wpa_s, wpa_s->curr_scan_cookie);
}
wpa_dbg(wpa_s, MSG_DEBUG, "No ongoing scan/p2p-scan found to abort");
return -1;
}
int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd)
{
struct sched_scan_plan *scan_plans = NULL;
const char *token, *context = NULL;
unsigned int num = 0;
if (!cmd)
return -1;
if (!cmd[0]) {
wpa_printf(MSG_DEBUG, "Clear sched scan plans");
os_free(wpa_s->sched_scan_plans);
wpa_s->sched_scan_plans = NULL;
wpa_s->sched_scan_plans_num = 0;
return 0;
}
while ((token = cstr_token(cmd, " ", &context))) {
int ret;
struct sched_scan_plan *scan_plan, *n;
n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans));
if (!n)
goto fail;
scan_plans = n;
scan_plan = &scan_plans[num];
num++;
ret = sscanf(token, "%u:%u", &scan_plan->interval,
&scan_plan->iterations);
if (ret <= 0 || ret > 2 || !scan_plan->interval) {
wpa_printf(MSG_ERROR,
"Invalid sched scan plan input: %s", token);
goto fail;
}
if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
wpa_printf(MSG_WARNING,
"scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)",
num, scan_plan->interval,
wpa_s->max_sched_scan_plan_interval);
scan_plan->interval =
wpa_s->max_sched_scan_plan_interval;
}
if (ret == 1) {
scan_plan->iterations = 0;
break;
}
if (!scan_plan->iterations) {
wpa_printf(MSG_ERROR,
"scan plan %u: Number of iterations cannot be zero",
num);
goto fail;
}
if (scan_plan->iterations >
wpa_s->max_sched_scan_plan_iterations) {
wpa_printf(MSG_WARNING,
"scan plan %u: Too many iterations(%u), use the maximum allowed(%u)",
num, scan_plan->iterations,
wpa_s->max_sched_scan_plan_iterations);
scan_plan->iterations =
wpa_s->max_sched_scan_plan_iterations;
}
wpa_printf(MSG_DEBUG,
"scan plan %u: interval=%u iterations=%u",
num, scan_plan->interval, scan_plan->iterations);
}
if (!scan_plans) {
wpa_printf(MSG_ERROR, "Invalid scan plans entry");
goto fail;
}
if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) {
wpa_printf(MSG_ERROR,
"All scan plans but the last must specify a number of iterations");
goto fail;
}
wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u",
num, scan_plans[num - 1].interval);
if (num > wpa_s->max_sched_scan_plans) {
wpa_printf(MSG_WARNING,
"Too many scheduled scan plans (only %u supported)",
wpa_s->max_sched_scan_plans);
wpa_printf(MSG_WARNING,
"Use only the first %u scan plans, and the last one (in infinite loop)",
wpa_s->max_sched_scan_plans - 1);
os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1],
&scan_plans[num - 1], sizeof(*scan_plans));
num = wpa_s->max_sched_scan_plans;
}
os_free(wpa_s->sched_scan_plans);
wpa_s->sched_scan_plans = scan_plans;
wpa_s->sched_scan_plans_num = num;
return 0;
fail:
os_free(scan_plans);
wpa_printf(MSG_ERROR, "invalid scan plans list");
return -1;
}
/**
* wpas_scan_reset_sched_scan - Reset sched_scan state
* @wpa_s: Pointer to wpa_supplicant data
*
* This function is used to cancel a running scheduled scan and to reset an
* internal scan state to continue with a regular scan on the following
* wpa_supplicant_req_scan() calls.
*/
void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s)
{
wpa_s->normal_scans = 0;
if (wpa_s->sched_scanning) {
wpa_s->sched_scan_timed_out = 0;
wpa_s->prev_sched_ssid = NULL;
wpa_supplicant_cancel_sched_scan(wpa_s);
}
}
void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s)
{
/* simulate timeout to restart the sched scan */
wpa_s->sched_scan_timed_out = 1;
wpa_s->prev_sched_ssid = NULL;
wpa_supplicant_cancel_sched_scan(wpa_s);
}
diff --git a/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in
index 75a37a8cdbd3..58a622887cd9 100644
--- a/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in
+++ b/contrib/wpa/wpa_supplicant/systemd/wpa_supplicant.service.in
@@ -1,13 +1,14 @@
[Unit]
Description=WPA supplicant
Before=network.target
+After=dbus.service
Wants=network.target
[Service]
Type=dbus
BusName=fi.w1.wpa_supplicant1
ExecStart=@BINDIR@/wpa_supplicant -u
[Install]
WantedBy=multi-user.target
Alias=dbus-fi.w1.wpa_supplicant1.service
diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c
index 215ac4975cb5..075d587be2f1 100644
--- a/contrib/wpa/wpa_supplicant/wpa_cli.c
+++ b/contrib/wpa/wpa_supplicant/wpa_cli.c
@@ -1,5037 +1,5084 @@
/*
* WPA Supplicant - command line interface for wpa_supplicant daemon
* Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#ifdef CONFIG_CTRL_IFACE
#ifdef CONFIG_CTRL_IFACE_UNIX
#include <dirent.h>
#endif /* CONFIG_CTRL_IFACE_UNIX */
#include "common/cli.h"
#include "common/wpa_ctrl.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/edit.h"
#include "utils/list.h"
#include "common/version.h"
#include "common/ieee802_11_defs.h"
#ifdef ANDROID
#include <cutils/properties.h>
#endif /* ANDROID */
static const char *const wpa_cli_version =
"wpa_cli v" VERSION_STR "\n"
"Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi> and contributors";
#define VENDOR_ELEM_FRAME_ID \
" 0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \
"3: Beacon (GO), 4: PD Req, 5: PD Resp, 6: GO Neg Req, " \
"7: GO Neg Resp, 8: GO Neg Conf, 9: Inv Req, 10: Inv Resp, " \
"11: Assoc Req (P2P), 12: Assoc Resp (P2P)"
static struct wpa_ctrl *ctrl_conn;
static struct wpa_ctrl *mon_conn;
static int wpa_cli_quit = 0;
static int wpa_cli_attached = 0;
static int wpa_cli_connected = -1;
static int wpa_cli_last_id = 0;
#ifndef CONFIG_CTRL_IFACE_DIR
#define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant"
#endif /* CONFIG_CTRL_IFACE_DIR */
static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
static const char *client_socket_dir = NULL;
static char *ctrl_ifname = NULL;
static const char *global = NULL;
static const char *pid_file = NULL;
static const char *action_file = NULL;
static int reconnect = 0;
static int ping_interval = 5;
static int interactive = 0;
static char *ifname_prefix = NULL;
static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */
static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */
static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */
static DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */
static DEFINE_DL_LIST(networks); /* struct cli_txt_entry */
static DEFINE_DL_LIST(creds); /* struct cli_txt_entry */
#ifdef CONFIG_AP
static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */
#endif /* CONFIG_AP */
static void print_help(const char *cmd);
static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx);
static void wpa_cli_close_connection(void);
static char * wpa_cli_get_default_ifname(void);
static char ** wpa_list_cmd_list(void);
static void update_creds(struct wpa_ctrl *ctrl);
static void update_networks(struct wpa_ctrl *ctrl);
static void update_stations(struct wpa_ctrl *ctrl);
static void update_ifnames(struct wpa_ctrl *ctrl);
static void usage(void)
{
printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvBr] "
"[-a<action file>] \\\n"
" [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] "
"\\\n"
" [-s<wpa_client_socket_file_path>] "
"[command..]\n"
" -h = help (show this usage text)\n"
" -v = shown version information\n"
" -a = run in daemon mode executing the action file based on "
"events from\n"
" wpa_supplicant\n"
" -r = try to reconnect when client socket is disconnected.\n"
" This is useful only when used with -a.\n"
" -B = run a daemon in the background\n"
" default path: " CONFIG_CTRL_IFACE_DIR "\n"
" default interface: first interface found in socket path\n");
print_help(NULL);
}
static int wpa_cli_show_event(const char *event)
{
const char *start;
start = os_strchr(event, '>');
if (start == NULL)
return 1;
start++;
/*
* Skip BSS added/removed events since they can be relatively frequent
* and are likely of not much use for an interactive user.
*/
if (str_starts(start, WPA_EVENT_BSS_ADDED) ||
str_starts(start, WPA_EVENT_BSS_REMOVED))
return 0;
return 1;
}
static int wpa_cli_open_connection(const char *ifname, int attach)
{
#if defined(CONFIG_CTRL_IFACE_UDP) || defined(CONFIG_CTRL_IFACE_NAMED_PIPE)
ctrl_conn = wpa_ctrl_open(ifname);
if (ctrl_conn == NULL)
return -1;
if (attach && interactive)
mon_conn = wpa_ctrl_open(ifname);
else
mon_conn = NULL;
#else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
char *cfile = NULL;
int flen, res;
if (ifname == NULL)
return -1;
#ifdef ANDROID
if (access(ctrl_iface_dir, F_OK) < 0) {
cfile = os_strdup(ifname);
if (cfile == NULL)
return -1;
}
#endif /* ANDROID */
if (client_socket_dir && client_socket_dir[0] &&
access(client_socket_dir, F_OK) < 0) {
perror(client_socket_dir);
os_free(cfile);
return -1;
}
if (cfile == NULL) {
flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
cfile = os_malloc(flen);
if (cfile == NULL)
return -1;
res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir,
ifname);
if (os_snprintf_error(flen, res)) {
os_free(cfile);
return -1;
}
}
ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
if (ctrl_conn == NULL) {
os_free(cfile);
return -1;
}
if (attach && interactive)
mon_conn = wpa_ctrl_open2(cfile, client_socket_dir);
else
mon_conn = NULL;
os_free(cfile);
#endif /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
if (mon_conn) {
if (wpa_ctrl_attach(mon_conn) == 0) {
wpa_cli_attached = 1;
if (interactive)
eloop_register_read_sock(
wpa_ctrl_get_fd(mon_conn),
wpa_cli_mon_receive, NULL, NULL);
} else {
printf("Warning: Failed to attach to "
"wpa_supplicant.\n");
wpa_cli_close_connection();
return -1;
}
}
return 0;
}
static void wpa_cli_close_connection(void)
{
if (ctrl_conn == NULL)
return;
if (wpa_cli_attached) {
wpa_ctrl_detach(interactive ? mon_conn : ctrl_conn);
wpa_cli_attached = 0;
}
wpa_ctrl_close(ctrl_conn);
ctrl_conn = NULL;
if (mon_conn) {
eloop_unregister_read_sock(wpa_ctrl_get_fd(mon_conn));
wpa_ctrl_close(mon_conn);
mon_conn = NULL;
}
}
static void wpa_cli_msg_cb(char *msg, size_t len)
{
printf("%s\n", msg);
}
static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
{
char buf[4096];
size_t len;
int ret;
if (ctrl_conn == NULL) {
printf("Not connected to wpa_supplicant - command dropped.\n");
return -1;
}
if (ifname_prefix) {
os_snprintf(buf, sizeof(buf), "IFNAME=%s %s",
ifname_prefix, cmd);
buf[sizeof(buf) - 1] = '\0';
cmd = buf;
}
len = sizeof(buf) - 1;
ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
wpa_cli_msg_cb);
if (ret == -2) {
printf("'%s' command timed out.\n", cmd);
return -2;
} else if (ret < 0) {
printf("'%s' command failed.\n", cmd);
return -1;
}
if (print) {
buf[len] = '\0';
printf("%s", buf);
if (interactive && len > 0 && buf[len - 1] != '\n')
printf("\n");
}
return 0;
}
static int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
{
return _wpa_ctrl_command(ctrl, cmd, 1);
}
static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args,
int argc, char *argv[])
{
char buf[4096];
if (argc < min_args) {
printf("Invalid %s command - at least %d argument%s "
"required.\n", cmd, min_args,
min_args > 1 ? "s are" : " is");
return -1;
}
if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0)
return -1;
return wpa_ctrl_command(ctrl, buf);
}
static int wpa_cli_cmd_ifname(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "IFNAME");
}
static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc > 0 && os_strcmp(argv[0], "verbose") == 0)
return wpa_ctrl_command(ctrl, "STATUS-VERBOSE");
if (argc > 0 && os_strcmp(argv[0], "wps") == 0)
return wpa_ctrl_command(ctrl, "STATUS-WPS");
if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
#ifdef ANDROID
if (argc > 0 && os_strcmp(argv[0], "no_events") == 0)
return wpa_ctrl_command(ctrl, "STATUS-NO_EVENTS");
#endif /* ANDROID */
return wpa_ctrl_command(ctrl, "STATUS");
}
static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "PING");
}
static int wpa_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "RELOG");
}
static int wpa_cli_cmd_note(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "NOTE", 1, argc, argv);
}
static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "MIB");
}
static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "PMKSA");
}
static int wpa_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "PMKSA_FLUSH");
}
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
static int wpa_cli_cmd_pmksa_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "PMKSA_GET", 1, argc, argv);
}
static int wpa_cli_cmd_pmksa_add(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "PMKSA_ADD", 8, argc, argv);
}
#ifdef CONFIG_MESH
static int wpa_cli_mesh_cmd_pmksa_get(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "MESH_PMKSA_GET", 1, argc, argv);
}
static int wpa_cli_mesh_cmd_pmksa_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "MESH_PMKSA_ADD", 4, argc, argv);
}
#endif /* CONFIG_MESH */
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
print_help(argc > 0 ? argv[0] : NULL);
return 0;
}
static char ** wpa_cli_complete_help(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
switch (arg) {
case 1:
res = wpa_list_cmd_list();
break;
}
return res;
}
static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
printf("%s\n\n%s\n", wpa_cli_version, cli_full_license);
return 0;
}
static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
wpa_cli_quit = 1;
if (interactive)
eloop_terminate();
return 0;
}
static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
int res;
if (argc == 1) {
res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]);
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long SET command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
return wpa_cli_cmd(ctrl, "SET", 2, argc, argv);
}
static char ** wpa_cli_complete_set(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
const char *fields[] = {
/* runtime values */
"EAPOL::heldPeriod", "EAPOL::authPeriod", "EAPOL::startPeriod",
"EAPOL::maxStart", "dot11RSNAConfigPMKLifetime",
"dot11RSNAConfigPMKReauthThreshold", "dot11RSNAConfigSATimeout",
"wps_fragment_size", "wps_version_number", "ampdu",
"tdls_testing", "tdls_disabled", "pno", "radio_disabled",
"uapsd", "ps", "wifi_display", "bssid_filter", "disallow_aps",
"no_keep_alive",
/* global configuration parameters */
#ifdef CONFIG_CTRL_IFACE
"ctrl_interface", "no_ctrl_interface", "ctrl_interface_group",
#endif /* CONFIG_CTRL_IFACE */
"eapol_version", "ap_scan", "bgscan",
#ifdef CONFIG_MESH
"user_mpm", "max_peer_links", "mesh_max_inactivity",
"dot11RSNASAERetransPeriod",
#endif /* CONFIG_MESH */
"disable_scan_offload", "fast_reauth", "opensc_engine_path",
"pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
"pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
"dot11RSNAConfigPMKLifetime",
"dot11RSNAConfigPMKReauthThreshold",
"dot11RSNAConfigSATimeout",
#ifndef CONFIG_NO_CONFIG_WRITE
"update_config",
#endif /* CONFIG_NO_CONFIG_WRITE */
"load_dynamic_eap",
#ifdef CONFIG_WPS
"uuid", "device_name", "manufacturer", "model_name",
"model_number", "serial_number", "device_type", "os_version",
"config_methods", "wps_cred_processing", "wps_vendor_ext_m1",
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
"sec_device_type",
"p2p_listen_reg_class", "p2p_listen_channel",
"p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
"p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
"p2p_group_idle", "p2p_passphrase_len", "p2p_pref_chan",
"p2p_no_go_freq", "p2p_add_cli_chan",
"p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
"p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
"p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
"ip_addr_start", "ip_addr_end", "p2p_go_edmg",
#endif /* CONFIG_P2P */
"country", "bss_max_count", "bss_expiration_age",
"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
"max_num_sta", "disassoc_low_ack", "ap_isolate",
#ifdef CONFIG_HS20
"hs20",
#endif /* CONFIG_HS20 */
"interworking", "hessid", "access_network_type", "pbc_in_m1",
"go_interworking", "go_access_network_type", "go_internet",
"go_venue_group", "go_venue_type",
"autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
"wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
"sae_groups", "dtim_period", "beacon_int",
"ap_vendor_elements", "ignore_old_scan_res", "freq_list",
"scan_cur_freq", "scan_res_valid_for_connect",
"sched_scan_interval",
"tdls_external_control", "osu_dir", "wowlan_triggers",
"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
"reassoc_same_bss_optim", "wps_priority",
+ "ap_assocresp_elements",
#ifdef CONFIG_TESTING_OPTIONS
"ignore_auth_resp",
#endif /* CONFIG_TESTING_OPTIONS */
"relative_rssi", "relative_band_adjust",
"extended_key_id",
};
int i, num_fields = ARRAY_SIZE(fields);
if (arg == 1) {
char **res = os_calloc(num_fields + 1, sizeof(char *));
if (res == NULL)
return NULL;
for (i = 0; i < num_fields; i++) {
res[i] = os_strdup(fields[i]);
if (res[i] == NULL)
return res;
}
return res;
}
if (arg > 1 && os_strncasecmp(str, "set bssid_filter ", 17) == 0)
return cli_txt_list_array(&bsses);
return NULL;
}
static int wpa_cli_cmd_dump(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "DUMP");
}
static int wpa_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DRIVER_FLAGS");
}
static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "GET", 1, argc, argv);
}
static char ** wpa_cli_complete_get(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
const char *fields[] = {
#ifdef CONFIG_CTRL_IFACE
"ctrl_interface", "ctrl_interface_group",
#endif /* CONFIG_CTRL_IFACE */
"eapol_version", "ap_scan",
#ifdef CONFIG_MESH
"user_mpm", "max_peer_links", "mesh_max_inactivity",
#endif /* CONFIG_MESH */
"disable_scan_offload", "fast_reauth", "opensc_engine_path",
"pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
"pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
"dot11RSNAConfigPMKLifetime",
"dot11RSNAConfigPMKReauthThreshold",
"dot11RSNAConfigSATimeout",
#ifndef CONFIG_NO_CONFIG_WRITE
"update_config",
#endif /* CONFIG_NO_CONFIG_WRITE */
#ifdef CONFIG_WPS
"device_name", "manufacturer", "model_name", "model_number",
"serial_number", "config_methods", "wps_cred_processing",
#endif /* CONFIG_WPS */
#ifdef CONFIG_P2P
"p2p_listen_reg_class", "p2p_listen_channel",
"p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
"p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
"p2p_group_idle", "p2p_passphrase_len", "p2p_add_cli_chan",
"p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
"p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
"p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
"ip_addr_start", "ip_addr_end",
#endif /* CONFIG_P2P */
"bss_max_count", "bss_expiration_age",
"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
"max_num_sta", "disassoc_low_ack", "ap_isolate",
#ifdef CONFIG_HS20
"hs20",
#endif /* CONFIG_HS20 */
"interworking", "access_network_type", "pbc_in_m1", "autoscan",
"go_interworking", "go_access_network_type", "go_internet",
"go_venue_group", "go_venue_type",
"wps_nfc_dev_pw_id", "ext_password_backend",
"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
"dtim_period", "beacon_int", "ignore_old_scan_res",
"scan_cur_freq", "scan_res_valid_for_connect",
"sched_scan_interval",
"sched_scan_start_delay",
"tdls_external_control", "osu_dir", "wowlan_triggers",
"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
"reassoc_same_bss_optim", "extended_key_id"
};
int i, num_fields = ARRAY_SIZE(fields);
if (arg == 1) {
char **res = os_calloc(num_fields + 1, sizeof(char *));
if (res == NULL)
return NULL;
for (i = 0; i < num_fields; i++) {
res[i] = os_strdup(fields[i]);
if (res[i] == NULL)
return res;
}
return res;
}
return NULL;
}
static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "LOGOFF");
}
static int wpa_cli_cmd_logon(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "LOGON");
}
static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "REASSOCIATE");
}
static int wpa_cli_cmd_reattach(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "REATTACH");
}
static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "PREAUTH", 1, argc, argv);
}
static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "AP_SCAN", 1, argc, argv);
}
static int wpa_cli_cmd_scan_interval(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "SCAN_INTERVAL", 1, argc, argv);
}
static int wpa_cli_cmd_bss_expire_age(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "BSS_EXPIRE_AGE", 1, argc, argv);
}
static int wpa_cli_cmd_bss_expire_count(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "BSS_EXPIRE_COUNT", 1, argc, argv);
}
static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
int res;
if (argc < 1)
res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0");
else
res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]);
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long BSS_FLUSH command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv);
}
static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "WPS_PBC", 0, argc, argv);
}
static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc == 0) {
printf("Invalid WPS_PIN command: need one or two arguments:\n"
"- BSSID: use 'any' to select any\n"
"- PIN: optional, used only with devices that have no "
"display\n");
return -1;
}
return wpa_cli_cmd(ctrl, "WPS_PIN", 1, argc, argv);
}
static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "WPS_CHECK_PIN", 1, argc, argv);
}
static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "WPS_CANCEL");
}
#ifdef CONFIG_WPS_NFC
static int wpa_cli_cmd_wps_nfc(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "WPS_NFC", 0, argc, argv);
}
static int wpa_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "WPS_NFC_CONFIG_TOKEN", 1, argc, argv);
}
static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "WPS_NFC_TOKEN", 1, argc, argv);
}
static int wpa_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
int ret;
char *buf;
size_t buflen;
if (argc != 1) {
printf("Invalid 'wps_nfc_tag_read' command - one argument "
"is required.\n");
return -1;
}
buflen = 18 + os_strlen(argv[0]);
buf = os_malloc(buflen);
if (buf == NULL)
return -1;
os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]);
ret = wpa_ctrl_command(ctrl, buf);
os_free(buf);
return ret;
}
static int wpa_cli_cmd_nfc_get_handover_req(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_REQ", 2, argc, argv);
}
static int wpa_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_SEL", 2, argc, argv);
}
static int wpa_cli_cmd_nfc_report_handover(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "NFC_REPORT_HANDOVER", 4, argc, argv);
}
#endif /* CONFIG_WPS_NFC */
static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256];
int res;
if (argc == 2)
res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s",
argv[0], argv[1]);
else if (argc == 5 || argc == 6) {
char ssid_hex[2 * SSID_MAX_LEN + 1];
char key_hex[2 * 64 + 1];
int i;
ssid_hex[0] = '\0';
for (i = 0; i < SSID_MAX_LEN; i++) {
if (argv[2][i] == '\0')
break;
os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
}
key_hex[0] = '\0';
if (argc == 6) {
for (i = 0; i < 64; i++) {
if (argv[5][i] == '\0')
break;
os_snprintf(&key_hex[i * 2], 3, "%02x",
argv[5][i]);
}
}
res = os_snprintf(cmd, sizeof(cmd),
"WPS_REG %s %s %s %s %s %s",
argv[0], argv[1], ssid_hex, argv[3], argv[4],
key_hex);
} else {
printf("Invalid WPS_REG command: need two arguments:\n"
"- BSSID of the target AP\n"
"- AP PIN\n");
printf("Alternatively, six arguments can be used to "
"reconfigure the AP:\n"
"- BSSID of the target AP\n"
"- AP PIN\n"
"- new SSID\n"
"- new auth (OPEN, WPAPSK, WPA2PSK)\n"
"- new encr (NONE, WEP, TKIP, CCMP)\n"
"- new key\n");
return -1;
}
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long WPS_REG command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "WPS_AP_PIN", 1, argc, argv);
}
static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "WPS_ER_START", 0, argc, argv);
}
static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "WPS_ER_STOP");
}
static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc < 2) {
printf("Invalid WPS_ER_PIN command: need at least two "
"arguments:\n"
"- UUID: use 'any' to select any\n"
"- PIN: Enrollee PIN\n"
"optional: - Enrollee MAC address\n");
return -1;
}
return wpa_cli_cmd(ctrl, "WPS_ER_PIN", 2, argc, argv);
}
static int wpa_cli_cmd_wps_er_pbc(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "WPS_ER_PBC", 1, argc, argv);
}
static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc != 2) {
printf("Invalid WPS_ER_LEARN command: need two arguments:\n"
"- UUID: specify which AP to use\n"
"- PIN: AP PIN\n");
return -1;
}
return wpa_cli_cmd(ctrl, "WPS_ER_LEARN", 2, argc, argv);
}
static int wpa_cli_cmd_wps_er_set_config(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc != 2) {
printf("Invalid WPS_ER_SET_CONFIG command: need two "
"arguments:\n"
"- UUID: specify which AP to use\n"
"- Network configuration id\n");
return -1;
}
return wpa_cli_cmd(ctrl, "WPS_ER_SET_CONFIG", 2, argc, argv);
}
static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256];
int res;
if (argc == 5 || argc == 6) {
char ssid_hex[2 * SSID_MAX_LEN + 1];
char key_hex[2 * 64 + 1];
int i;
ssid_hex[0] = '\0';
for (i = 0; i < SSID_MAX_LEN; i++) {
if (argv[2][i] == '\0')
break;
os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
}
key_hex[0] = '\0';
if (argc == 6) {
for (i = 0; i < 64; i++) {
if (argv[5][i] == '\0')
break;
os_snprintf(&key_hex[i * 2], 3, "%02x",
argv[5][i]);
}
}
res = os_snprintf(cmd, sizeof(cmd),
"WPS_ER_CONFIG %s %s %s %s %s %s",
argv[0], argv[1], ssid_hex, argv[3], argv[4],
key_hex);
} else {
printf("Invalid WPS_ER_CONFIG command: need six arguments:\n"
"- AP UUID\n"
"- AP PIN\n"
"- new SSID\n"
"- new auth (OPEN, WPAPSK, WPA2PSK)\n"
"- new encr (NONE, WEP, TKIP, CCMP)\n"
"- new key\n");
return -1;
}
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long WPS_ER_CONFIG command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
#ifdef CONFIG_WPS_NFC
static int wpa_cli_cmd_wps_er_nfc_config_token(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc != 2) {
printf("Invalid WPS_ER_NFC_CONFIG_TOKEN command: need two "
"arguments:\n"
"- WPS/NDEF: token format\n"
"- UUID: specify which AP to use\n");
return -1;
}
return wpa_cli_cmd(ctrl, "WPS_ER_NFC_CONFIG_TOKEN", 2, argc, argv);
}
#endif /* CONFIG_WPS_NFC */
static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "IBSS_RSN", 1, argc, argv);
}
static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "LEVEL", 1, argc, argv);
}
static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256], *pos, *end;
int i, ret;
if (argc < 2) {
printf("Invalid IDENTITY command: needs two arguments "
"(network id and identity)\n");
return -1;
}
end = cmd + sizeof(cmd);
pos = cmd;
ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s",
argv[0], argv[1]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long IDENTITY command.\n");
return -1;
}
pos += ret;
for (i = 2; i < argc; i++) {
ret = os_snprintf(pos, end - pos, " %s", argv[i]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long IDENTITY command.\n");
return -1;
}
pos += ret;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256], *pos, *end;
int i, ret;
if (argc < 2) {
printf("Invalid PASSWORD command: needs two arguments "
"(network id and password)\n");
return -1;
}
end = cmd + sizeof(cmd);
pos = cmd;
ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s",
argv[0], argv[1]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long PASSWORD command.\n");
return -1;
}
pos += ret;
for (i = 2; i < argc; i++) {
ret = os_snprintf(pos, end - pos, " %s", argv[i]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long PASSWORD command.\n");
return -1;
}
pos += ret;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256], *pos, *end;
int i, ret;
if (argc < 2) {
printf("Invalid NEW_PASSWORD command: needs two arguments "
"(network id and password)\n");
return -1;
}
end = cmd + sizeof(cmd);
pos = cmd;
ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s",
argv[0], argv[1]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long NEW_PASSWORD command.\n");
return -1;
}
pos += ret;
for (i = 2; i < argc; i++) {
ret = os_snprintf(pos, end - pos, " %s", argv[i]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long NEW_PASSWORD command.\n");
return -1;
}
pos += ret;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256], *pos, *end;
int i, ret;
if (argc < 2) {
printf("Invalid PIN command: needs two arguments "
"(network id and pin)\n");
return -1;
}
end = cmd + sizeof(cmd);
pos = cmd;
ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s",
argv[0], argv[1]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long PIN command.\n");
return -1;
}
pos += ret;
for (i = 2; i < argc; i++) {
ret = os_snprintf(pos, end - pos, " %s", argv[i]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long PIN command.\n");
return -1;
}
pos += ret;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256], *pos, *end;
int i, ret;
if (argc < 2) {
printf("Invalid OTP command: needs two arguments (network "
"id and password)\n");
return -1;
}
end = cmd + sizeof(cmd);
pos = cmd;
ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s",
argv[0], argv[1]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long OTP command.\n");
return -1;
}
pos += ret;
for (i = 2; i < argc; i++) {
ret = os_snprintf(pos, end - pos, " %s", argv[i]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long OTP command.\n");
return -1;
}
pos += ret;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_sim(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[256], *pos, *end;
int i, ret;
if (argc < 2) {
printf("Invalid SIM command: needs two arguments "
"(network id and SIM operation response)\n");
return -1;
}
end = cmd + sizeof(cmd);
pos = cmd;
ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "SIM-%s:%s",
argv[0], argv[1]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long SIM command.\n");
return -1;
}
pos += ret;
for (i = 2; i < argc; i++) {
ret = os_snprintf(pos, end - pos, " %s", argv[i]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long SIM command.\n");
return -1;
}
pos += ret;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_psk_passphrase(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256], *pos, *end;
int i, ret;
if (argc < 2) {
printf("Invalid PSK_PASSPHRASE command: needs two arguments (network id and PSK/passphrase)\n");
return -1;
}
end = cmd + sizeof(cmd);
pos = cmd;
ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PSK_PASSPHRASE-%s:%s",
argv[0], argv[1]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long PSK_PASSPHRASE command.\n");
return -1;
}
pos += ret;
for (i = 2; i < argc; i++) {
ret = os_snprintf(pos, end - pos, " %s", argv[i]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long PSK_PASSPHRASE command.\n");
return -1;
}
pos += ret;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256], *pos, *end;
int i, ret;
if (argc < 2) {
printf("Invalid PASSPHRASE command: needs two arguments "
"(network id and passphrase)\n");
return -1;
}
end = cmd + sizeof(cmd);
pos = cmd;
ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s",
argv[0], argv[1]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long PASSPHRASE command.\n");
return -1;
}
pos += ret;
for (i = 2; i < argc; i++) {
ret = os_snprintf(pos, end - pos, " %s", argv[i]);
if (os_snprintf_error(end - pos, ret)) {
printf("Too long PASSPHRASE command.\n");
return -1;
}
pos += ret;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc < 2) {
printf("Invalid BSSID command: needs two arguments (network "
"id and BSSID)\n");
return -1;
}
return wpa_cli_cmd(ctrl, "BSSID", 2, argc, argv);
}
static int wpa_cli_cmd_bssid_ignore(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "BSSID_IGNORE", 0, argc, argv);
}
static int wpa_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "LOG_LEVEL", 0, argc, argv);
}
static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "LIST_NETWORKS", 0, argc, argv);
}
static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "SELECT_NETWORK", 1, argc, argv);
}
static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "ENABLE_NETWORK", 1, argc, argv);
}
static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DISABLE_NETWORK", 1, argc, argv);
}
static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
int res = wpa_ctrl_command(ctrl, "ADD_NETWORK");
if (interactive)
update_networks(ctrl);
return res;
}
static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
int res = wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv);
if (interactive)
update_networks(ctrl);
return res;
}
static void wpa_cli_show_network_variables(void)
{
printf("set_network variables:\n"
" ssid (network name, SSID)\n"
" psk (WPA passphrase or pre-shared key)\n"
" key_mgmt (key management protocol)\n"
" identity (EAP identity)\n"
" password (EAP password)\n"
" ...\n"
"\n"
"Note: Values are entered in the same format as the "
"configuration file is using,\n"
"i.e., strings values need to be inside double quotation "
"marks.\n"
"For example: set_network 1 ssid \"network name\"\n"
"\n"
"Please see wpa_supplicant.conf documentation for full list "
"of\navailable variables.\n");
}
static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc == 0) {
wpa_cli_show_network_variables();
return 0;
}
if (argc < 3) {
printf("Invalid SET_NETWORK command: needs three arguments\n"
"(network id, variable name, and value)\n");
return -1;
}
return wpa_cli_cmd(ctrl, "SET_NETWORK", 3, argc, argv);
}
static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc == 0) {
wpa_cli_show_network_variables();
return 0;
}
if (argc != 2) {
printf("Invalid GET_NETWORK command: needs two arguments\n"
"(network id and variable name)\n");
return -1;
}
return wpa_cli_cmd(ctrl, "GET_NETWORK", 2, argc, argv);
}
static const char *network_fields[] = {
"ssid", "scan_ssid", "bssid", "bssid_ignore",
"bssid_accept", "psk", "proto", "key_mgmt",
"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
"freq_list", "max_oper_chwidth", "ht40", "vht", "vht_center_freq1",
"vht_center_freq2", "ht", "edmg",
#ifdef IEEE8021X_EAPOL
"eap", "identity", "anonymous_identity", "password", "ca_cert",
"ca_path", "client_cert", "private_key", "private_key_passwd",
"dh_file", "subject_match", "altsubject_match",
"check_cert_subject",
"domain_suffix_match", "domain_match", "ca_cert2", "ca_path2",
"client_cert2", "private_key2", "private_key2_passwd",
"dh_file2", "subject_match2", "altsubject_match2",
"check_cert_subject2",
"domain_suffix_match2", "domain_match2", "phase1", "phase2",
"pcsc", "pin", "engine_id", "key_id", "cert_id", "ca_cert_id",
"pin2", "engine2_id", "key2_id", "cert2_id", "ca_cert2_id",
"engine", "engine2", "eapol_flags", "sim_num",
"openssl_ciphers", "erp",
#endif /* IEEE8021X_EAPOL */
"wep_key0", "wep_key1", "wep_key2", "wep_key3",
"wep_tx_keyidx", "priority",
#ifdef IEEE8021X_EAPOL
"eap_workaround", "pac_file", "fragment_size", "ocsp",
#endif /* IEEE8021X_EAPOL */
"mode",
"proactive_key_caching", "disabled", "id_str",
"ieee80211w",
"mixed_cell", "frequency", "fixed_freq",
#ifdef CONFIG_MESH
"no_auto_peer", "mesh_rssi_threshold",
"mesh_basic_rates", "dot11MeshMaxRetries",
"dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
"dot11MeshHoldingTimeout",
#endif /* CONFIG_MESH */
"wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid",
"wpa_deny_ptk0_rekey",
"enable_edmg", "edmg_channel",
#ifdef CONFIG_P2P
"go_p2p_dev_addr", "p2p_client_list", "psk_list",
#endif /* CONFIG_P2P */
#ifdef CONFIG_HT_OVERRIDES
"disable_ht", "disable_ht40", "disable_sgi", "disable_ldpc",
"ht40_intolerant", "disable_max_amsdu", "ampdu_factor",
"ampdu_density", "ht_mcs", "rx_stbc", "tx_stbc",
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
"disable_vht", "vht_capa", "vht_capa_mask", "vht_rx_mcs_nss_1",
"vht_rx_mcs_nss_2", "vht_rx_mcs_nss_3", "vht_rx_mcs_nss_4",
"vht_rx_mcs_nss_5", "vht_rx_mcs_nss_6", "vht_rx_mcs_nss_7",
"vht_rx_mcs_nss_8", "vht_tx_mcs_nss_1", "vht_tx_mcs_nss_2",
"vht_tx_mcs_nss_3", "vht_tx_mcs_nss_4", "vht_tx_mcs_nss_5",
"vht_tx_mcs_nss_6", "vht_tx_mcs_nss_7", "vht_tx_mcs_nss_8",
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_HE_OVERRIDES
"disable_he",
#endif /* CONFIG_HE_OVERRIDES */
"ap_max_inactivity", "dtim_period", "beacon_int",
#ifdef CONFIG_MACSEC
"macsec_policy",
"macsec_integ_only",
"macsec_replay_protect",
"macsec_replay_window",
"macsec_port",
"mka_priority",
#endif /* CONFIG_MACSEC */
#ifdef CONFIG_HS20
"update_identifier",
#endif /* CONFIG_HS20 */
"mac_addr", "pbss", "wps_disabled"
};
static char ** wpa_cli_complete_network(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
int i, num_fields = ARRAY_SIZE(network_fields);
char **res = NULL;
switch (arg) {
case 1:
res = cli_txt_list_array(&networks);
break;
case 2:
res = os_calloc(num_fields + 1, sizeof(char *));
if (res == NULL)
return NULL;
for (i = 0; i < num_fields; i++) {
res[i] = os_strdup(network_fields[i]);
if (res[i] == NULL)
break;
}
}
return res;
}
static char ** wpa_cli_complete_network_id(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
if (arg == 1)
return cli_txt_list_array(&networks);
return NULL;
}
static int wpa_cli_cmd_dup_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc == 0) {
wpa_cli_show_network_variables();
return 0;
}
if (argc < 3) {
printf("Invalid DUP_NETWORK command: needs three arguments\n"
"(src netid, dest netid, and variable name)\n");
return -1;
}
return wpa_cli_cmd(ctrl, "DUP_NETWORK", 3, argc, argv);
}
static char ** wpa_cli_complete_dup_network(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
int i, num_fields = ARRAY_SIZE(network_fields);
char **res = NULL;
switch (arg) {
case 1:
case 2:
res = cli_txt_list_array(&networks);
break;
case 3:
res = os_calloc(num_fields + 1, sizeof(char *));
if (res == NULL)
return NULL;
for (i = 0; i < num_fields; i++) {
res[i] = os_strdup(network_fields[i]);
if (res[i] == NULL)
break;
}
}
return res;
}
static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "LIST_CREDS");
}
static int wpa_cli_cmd_add_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
int res = wpa_ctrl_command(ctrl, "ADD_CRED");
if (interactive)
update_creds(ctrl);
return res;
}
static int wpa_cli_cmd_remove_cred(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
int res = wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv);
if (interactive)
update_creds(ctrl);
return res;
}
static const char * const cred_fields[] = {
"temporary", "priority", "sp_priority", "pcsc", "eap",
"update_identifier", "min_dl_bandwidth_home", "min_ul_bandwidth_home",
"min_dl_bandwidth_roaming", "min_ul_bandwidth_roaming", "max_bss_load",
"req_conn_capab", "ocsp", "sim_num", "realm", "username", "password",
"ca_cert", "client_cert", "private_key", "private_key_passwd", "imsi",
"milenage", "domain_suffix_match", "domain", "phase1", "phase2",
"roaming_consortium", "required_roaming_consortium", "excluded_ssid",
"roaming_partner", "provisioning_sp"
};
static char ** wpa_cli_complete_cred(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
int i, num_fields = ARRAY_SIZE(cred_fields);
char **res = NULL;
switch (arg) {
case 1:
res = cli_txt_list_array(&creds);
break;
case 2:
res = os_calloc(num_fields + 1, sizeof(char *));
if (res == NULL)
return NULL;
for (i = 0; i < num_fields; i++) {
res[i] = os_strdup(cred_fields[i]);
if (res[i] == NULL)
break;
}
}
return res;
}
static int wpa_cli_cmd_set_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc != 3) {
printf("Invalid SET_CRED command: needs three arguments\n"
"(cred id, variable name, and value)\n");
return -1;
}
return wpa_cli_cmd(ctrl, "SET_CRED", 3, argc, argv);
}
static int wpa_cli_cmd_get_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc != 2) {
printf("Invalid GET_CRED command: needs two arguments\n"
"(cred id, variable name)\n");
return -1;
}
return wpa_cli_cmd(ctrl, "GET_CRED", 2, argc, argv);
}
static int wpa_cli_cmd_disconnect(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DISCONNECT");
}
static int wpa_cli_cmd_reconnect(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "RECONNECT");
}
static int wpa_cli_cmd_save_config(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "SAVE_CONFIG");
}
static int wpa_cli_cmd_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "SCAN", 0, argc, argv);
}
static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "SCAN_RESULTS");
}
static int wpa_cli_cmd_abort_scan(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "ABORT_SCAN");
}
static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv);
}
static char ** wpa_cli_complete_bss(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
switch (arg) {
case 1:
res = cli_txt_list_array(&bsses);
break;
}
return res;
}
static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc < 1 || argc > 3) {
printf("Invalid GET_CAPABILITY command: need at least one argument and max three arguments\n");
return -1;
}
if (argc > 1 && os_strcmp(argv[0], "key_mgmt") != 0 &&
os_strncmp(argv[1], "iftype=", 7) == 0) {
printf("Invalid GET_CAPABILITY command: 'iftype=' param is allowed only for 'key_mgmt'\n");
return -1;
}
if (argc == 2 && os_strcmp(argv[1], "strict") != 0 &&
os_strncmp(argv[1], "iftype=", 7) != 0) {
printf("Invalid GET_CAPABILITY command: the second argument, if any, must be 'strict' OR 'iftype=<iftype_name>'\n");
return -1;
}
if (argc == 3 && os_strcmp(argv[2], "strict") != 0) {
printf("Invalid GET_CAPABILITY command: the third argument, if any, must be 'strict'\n");
return -1;
}
return wpa_cli_cmd(ctrl, "GET_CAPABILITY", 1, argc, argv);
}
static char ** wpa_cli_complete_get_capability(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
const char *fields[] = {
"eap", "pairwise", "group", "group_mgmt", "key_mgmt",
"proto", "auth_alg", "modes", "channels", "freq",
#ifdef CONFIG_TDLS
"tdls",
#endif /* CONFIG_TDLS */
#ifdef CONFIG_ERP
"erp",
#endif /* CONFIG_ERP */
#ifdef CONFIG_FIPS
"fips",
#endif /* CONFIG_FIPS */
#ifdef CONFIG_ACS
"acs",
#endif /* CONFIG_ACS */
};
const char *iftypes[] = {
"iftype=STATION", "iftype=AP", "iftype=P2P_CLIENT",
"iftype=P2P_GO", "iftype=AP_VLAN", "iftype=IBSS", "iftype=NAN",
"iftype=P2P_DEVICE", "iftype=MESH",
};
int i, num_fields = ARRAY_SIZE(fields);
int num_iftypes = ARRAY_SIZE(iftypes);
char **res = NULL;
if (arg == 1) {
res = os_calloc(num_fields + 1, sizeof(char *));
if (res == NULL)
return NULL;
for (i = 0; i < num_fields; i++) {
res[i] = os_strdup(fields[i]);
if (res[i] == NULL)
return res;
}
}
if (arg == 2) {
/* the second argument can be "iftype=<iftype_name>" OR
* "strict" */
res = os_calloc(num_iftypes + 2, sizeof(char *));
if (!res)
return NULL;
res[0] = os_strdup("strict");
if (!res[0])
return res;
for (i = 0; i < num_iftypes; i++) {
res[i + 1] = os_strdup(iftypes[i]);
if (!res[i + 1])
return res;
}
}
if (arg == 3) {
res = os_calloc(1 + 1, sizeof(char *));
if (res == NULL)
return NULL;
res[0] = os_strdup("strict");
}
return res;
}
static int wpa_cli_list_interfaces(struct wpa_ctrl *ctrl)
{
printf("Available interfaces:\n");
return wpa_ctrl_command(ctrl, "INTERFACES");
}
static int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc < 1) {
wpa_cli_list_interfaces(ctrl);
return 0;
}
wpa_cli_close_connection();
os_free(ctrl_ifname);
ctrl_ifname = os_strdup(argv[0]);
if (!ctrl_ifname) {
printf("Failed to allocate memory\n");
return 0;
}
if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
printf("Connected to interface '%s'.\n", ctrl_ifname);
} else {
printf("Could not connect to interface '%s' - re-trying\n",
ctrl_ifname);
}
return 0;
}
static int wpa_cli_cmd_reconfigure(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "RECONFIGURE");
}
static int wpa_cli_cmd_terminate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "TERMINATE");
}
static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256];
int res;
if (argc < 1) {
printf("Invalid INTERFACE_ADD command: needs at least one "
"argument (interface name)\n"
"All arguments: ifname confname driver ctrl_interface "
"driver_param bridge_name [create]\n");
return -1;
}
/*
* INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
* <driver_param>TAB<bridge_name>[TAB<create>[TAB<type>]]
*/
res = os_snprintf(cmd, sizeof(cmd),
"INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s\t%s",
argv[0],
argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "",
argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "",
argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "",
argc > 7 ? argv[7] : "");
if (os_snprintf_error(sizeof(cmd), res))
return -1;
cmd[sizeof(cmd) - 1] = '\0';
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "INTERFACE_REMOVE", 1, argc, argv);
}
static int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "INTERFACE_LIST");
}
#ifdef CONFIG_AP
static int wpa_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "STA", 1, argc, argv);
}
static char ** wpa_cli_complete_sta(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
switch (arg) {
case 1:
res = cli_txt_list_array(&stations);
break;
}
return res;
}
static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
char *addr, size_t addr_len, int print)
{
char buf[4096], *pos;
size_t len;
int ret;
if (ctrl_conn == NULL) {
printf("Not connected to hostapd - command dropped.\n");
return -1;
}
if (ifname_prefix) {
os_snprintf(buf, sizeof(buf), "IFNAME=%s %s",
ifname_prefix, cmd);
buf[sizeof(buf) - 1] = '\0';
cmd = buf;
}
len = sizeof(buf) - 1;
ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
wpa_cli_msg_cb);
if (ret == -2) {
printf("'%s' command timed out.\n", cmd);
return -2;
} else if (ret < 0) {
printf("'%s' command failed.\n", cmd);
return -1;
}
buf[len] = '\0';
if (os_memcmp(buf, "FAIL", 4) == 0 ||
os_memcmp(buf, "UNKNOWN COMMAND", 15) == 0)
return -1;
if (print)
printf("%s", buf);
pos = buf;
while (*pos != '\0' && *pos != '\n')
pos++;
*pos = '\0';
os_strlcpy(addr, buf, addr_len);
return 0;
}
static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char addr[32], cmd[64];
if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1))
return 0;
do {
os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0);
return -1;
}
static int wpa_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char addr[32], cmd[64];
if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
return 0;
do {
if (os_strcmp(addr, "") != 0)
printf("%s\n", addr);
os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
return 0;
}
static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DEAUTHENTICATE", 1, argc, argv);
}
static char ** wpa_cli_complete_deauthenticate(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
switch (arg) {
case 1:
res = cli_txt_list_array(&stations);
break;
}
return res;
}
static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv);
}
static char ** wpa_cli_complete_disassociate(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
switch (arg) {
case 1:
res = cli_txt_list_array(&stations);
break;
}
return res;
}
static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "CHAN_SWITCH", 2, argc, argv);
}
+
+static int wpa_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "UPDATE_BEACON");
+}
+
#endif /* CONFIG_AP */
static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "SUSPEND");
}
static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "RESUME");
}
#ifdef CONFIG_TESTING_OPTIONS
static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "DROP_SA");
}
#endif /* CONFIG_TESTING_OPTIONS */
static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "ROAM", 1, argc, argv);
}
#ifdef CONFIG_MESH
static int wpa_cli_cmd_mesh_interface_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "MESH_INTERFACE_ADD", 0, argc, argv);
}
static int wpa_cli_cmd_mesh_group_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "MESH_GROUP_ADD", 1, argc, argv);
}
static int wpa_cli_cmd_mesh_group_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "MESH_GROUP_REMOVE", 1, argc, argv);
}
static int wpa_cli_cmd_mesh_peer_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "MESH_PEER_REMOVE", 1, argc, argv);
}
static int wpa_cli_cmd_mesh_peer_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "MESH_PEER_ADD", 1, argc, argv);
}
static int wpa_cli_cmd_mesh_link_probe(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "MESH_LINK_PROBE", 1, argc, argv);
}
#endif /* CONFIG_MESH */
#ifdef CONFIG_P2P
static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_FIND", 0, argc, argv);
}
static char ** wpa_cli_complete_p2p_find(const char *str, int pos)
{
char **res = NULL;
int arg = get_cmd_arg_num(str, pos);
res = os_calloc(6, sizeof(char *));
if (res == NULL)
return NULL;
res[0] = os_strdup("type=social");
if (res[0] == NULL) {
os_free(res);
return NULL;
}
res[1] = os_strdup("type=progressive");
if (res[1] == NULL)
return res;
res[2] = os_strdup("delay=");
if (res[2] == NULL)
return res;
res[3] = os_strdup("dev_id=");
if (res[3] == NULL)
return res;
if (arg == 1)
res[4] = os_strdup("[timeout]");
return res;
}
static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "P2P_STOP_FIND");
}
static int wpa_cli_cmd_p2p_asp_provision(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION", 3, argc, argv);
}
static int wpa_cli_cmd_p2p_asp_provision_resp(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION_RESP", 2, argc, argv);
}
static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_CONNECT", 2, argc, argv);
}
static char ** wpa_cli_complete_p2p_connect(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
switch (arg) {
case 1:
res = cli_txt_list_array(&p2p_peers);
break;
}
return res;
}
static int wpa_cli_cmd_p2p_listen(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_LISTEN", 0, argc, argv);
}
static int wpa_cli_cmd_p2p_group_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_GROUP_REMOVE", 1, argc, argv);
}
static char ** wpa_cli_complete_p2p_group_remove(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
switch (arg) {
case 1:
res = cli_txt_list_array(&p2p_groups);
break;
}
return res;
}
static int wpa_cli_cmd_p2p_group_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_GROUP_ADD", 0, argc, argv);
}
static int wpa_cli_cmd_p2p_group_member(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_GROUP_MEMBER", 1, argc, argv);
}
static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc != 2 && argc != 3) {
printf("Invalid P2P_PROV_DISC command: needs at least "
"two arguments, address and config method\n"
"(display, keypad, or pbc) and an optional join\n");
return -1;
}
return wpa_cli_cmd(ctrl, "P2P_PROV_DISC", 2, argc, argv);
}
static int wpa_cli_cmd_p2p_get_passphrase(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "P2P_GET_PASSPHRASE");
}
static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[4096];
if (argc < 2) {
printf("Invalid P2P_SERV_DISC_REQ command: needs two "
"or more arguments (address and TLVs)\n");
return -1;
}
if (write_cmd(cmd, sizeof(cmd), "P2P_SERV_DISC_REQ", argc, argv) < 0)
return -1;
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_p2p_serv_disc_cancel_req(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_CANCEL_REQ", 1, argc, argv);
}
static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[4096];
int res;
if (argc != 4) {
printf("Invalid P2P_SERV_DISC_RESP command: needs four "
"arguments (freq, address, dialog token, and TLVs)\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s",
argv[0], argv[1], argv[2], argv[3]);
if (os_snprintf_error(sizeof(cmd), res))
return -1;
cmd[sizeof(cmd) - 1] = '\0';
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_p2p_service_update(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "P2P_SERVICE_UPDATE");
}
static int wpa_cli_cmd_p2p_serv_disc_external(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_SERV_DISC_EXTERNAL", 1, argc, argv);
}
static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "P2P_SERVICE_FLUSH");
}
static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc < 3) {
printf("Invalid P2P_SERVICE_ADD command: needs 3-6 arguments\n");
return -1;
}
return wpa_cli_cmd(ctrl, "P2P_SERVICE_ADD", 3, argc, argv);
}
static int wpa_cli_cmd_p2p_service_rep(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc < 5 || argc > 6) {
printf("Invalid P2P_SERVICE_REP command: needs 5-6 "
"arguments\n");
return -1;
}
return wpa_cli_cmd(ctrl, "P2P_SERVICE_REP", 5, argc, argv);
}
static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[4096];
int res;
if (argc != 2 && argc != 3) {
printf("Invalid P2P_SERVICE_DEL command: needs two or three "
"arguments\n");
return -1;
}
if (argc == 3)
res = os_snprintf(cmd, sizeof(cmd),
"P2P_SERVICE_DEL %s %s %s",
argv[0], argv[1], argv[2]);
else
res = os_snprintf(cmd, sizeof(cmd),
"P2P_SERVICE_DEL %s %s",
argv[0], argv[1]);
if (os_snprintf_error(sizeof(cmd), res))
return -1;
cmd[sizeof(cmd) - 1] = '\0';
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_p2p_reject(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_REJECT", 1, argc, argv);
}
static int wpa_cli_cmd_p2p_invite(struct wpa_ctrl *ctrl,
int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_INVITE", 1, argc, argv);
}
static int wpa_cli_cmd_p2p_peer(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_PEER", 1, argc, argv);
}
static char ** wpa_cli_complete_p2p_peer(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
char **res = NULL;
switch (arg) {
case 1:
res = cli_txt_list_array(&p2p_peers);
break;
}
return res;
}
static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, const char *cmd,
char *addr, size_t addr_len,
int discovered)
{
char buf[4096], *pos;
size_t len;
int ret;
if (ctrl_conn == NULL)
return -1;
len = sizeof(buf) - 1;
ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
wpa_cli_msg_cb);
if (ret == -2) {
printf("'%s' command timed out.\n", cmd);
return -2;
} else if (ret < 0) {
printf("'%s' command failed.\n", cmd);
return -1;
}
buf[len] = '\0';
if (os_memcmp(buf, "FAIL", 4) == 0)
return -1;
pos = buf;
while (*pos != '\0' && *pos != '\n')
pos++;
*pos++ = '\0';
os_strlcpy(addr, buf, addr_len);
if (!discovered || os_strstr(pos, "[PROBE_REQ_ONLY]") == NULL)
printf("%s\n", addr);
return 0;
}
static int wpa_cli_cmd_p2p_peers(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char addr[32], cmd[64];
int discovered;
discovered = argc > 0 && os_strcmp(argv[0], "discovered") == 0;
if (wpa_ctrl_command_p2p_peer(ctrl, "P2P_PEER FIRST",
addr, sizeof(addr), discovered))
return -1;
do {
os_snprintf(cmd, sizeof(cmd), "P2P_PEER NEXT-%s", addr);
} while (wpa_ctrl_command_p2p_peer(ctrl, cmd, addr, sizeof(addr),
discovered) == 0);
return 0;
}
static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_SET", 2, argc, argv);
}
static char ** wpa_cli_complete_p2p_set(const char *str, int pos)
{
int arg = get_cmd_arg_num(str, pos);
const char *fields[] = {
"discoverability",
"managed",
"listen_channel",
"ssid_postfix",
"noa",
"ps",
"oppps",
"ctwindow",
"disabled",
"conc_pref",
"force_long_sd",
"peer_filter",
"cross_connect",
"go_apsd",
"client_apsd",
"disallow_freq",
"disc_int",
"per_sta_psk",
};
int i, num_fields = ARRAY_SIZE(fields);
if (arg == 1) {
char **res = os_calloc(num_fields + 1, sizeof(char *));
if (res == NULL)
return NULL;
for (i = 0; i < num_fields; i++) {
res[i] = os_strdup(fields[i]);
if (res[i] == NULL)
return res;
}
return res;
}
if (arg == 2 && os_strncasecmp(str, "p2p_set peer_filter ", 20) == 0)
return cli_txt_list_array(&p2p_peers);
return NULL;
}
static int wpa_cli_cmd_p2p_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "P2P_FLUSH");
}
static int wpa_cli_cmd_p2p_cancel(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "P2P_CANCEL");
}
static int wpa_cli_cmd_p2p_unauthorize(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_UNAUTHORIZE", 1, argc, argv);
}
static int wpa_cli_cmd_p2p_presence_req(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc != 0 && argc != 2 && argc != 4) {
printf("Invalid P2P_PRESENCE_REQ command: needs two arguments "
"(preferred duration, interval; in microsecods).\n"
"Optional second pair can be used to provide "
"acceptable values.\n");
return -1;
}
return wpa_cli_cmd(ctrl, "P2P_PRESENCE_REQ", 0, argc, argv);
}
static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
if (argc != 0 && argc != 2) {
printf("Invalid P2P_EXT_LISTEN command: needs two arguments "
"(availability period, availability interval; in "
"millisecods).\n"
"Extended Listen Timing can be cancelled with this "
"command when used without parameters.\n");
return -1;
}
return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv);
}
static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv);
}
#endif /* CONFIG_P2P */
static int wpa_cli_cmd_vendor_elem_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "VENDOR_ELEM_ADD", 2, argc, argv);
}
static int wpa_cli_cmd_vendor_elem_get(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "VENDOR_ELEM_GET", 1, argc, argv);
}
static int wpa_cli_cmd_vendor_elem_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "VENDOR_ELEM_REMOVE", 2, argc, argv);
}
#ifdef CONFIG_WIFI_DISPLAY
static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[100];
int res;
if (argc != 1 && argc != 2) {
printf("Invalid WFD_SUBELEM_SET command: needs one or two "
"arguments (subelem, hexdump)\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s",
argv[0], argc > 1 ? argv[1] : "");
if (os_snprintf_error(sizeof(cmd), res))
return -1;
cmd[sizeof(cmd) - 1] = '\0';
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[100];
int res;
if (argc != 1) {
printf("Invalid WFD_SUBELEM_GET command: needs one "
"argument (subelem)\n");
return -1;
}
res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s",
argv[0]);
if (os_snprintf_error(sizeof(cmd), res))
return -1;
cmd[sizeof(cmd) - 1] = '\0';
return wpa_ctrl_command(ctrl, cmd);
}
#endif /* CONFIG_WIFI_DISPLAY */
#ifdef CONFIG_INTERWORKING
static int wpa_cli_cmd_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "FETCH_ANQP");
}
static int wpa_cli_cmd_stop_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "STOP_FETCH_ANQP");
}
static int wpa_cli_cmd_interworking_select(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "INTERWORKING_SELECT", 0, argc, argv);
}
static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "INTERWORKING_CONNECT", 1, argc, argv);
}
static int wpa_cli_cmd_interworking_add_network(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "INTERWORKING_ADD_NETWORK", 1, argc, argv);
}
static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv);
}
static int wpa_cli_cmd_gas_request(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "GAS_REQUEST", 2, argc, argv);
}
static int wpa_cli_cmd_gas_response_get(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "GAS_RESPONSE_GET", 2, argc, argv);
}
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
static int wpa_cli_cmd_hs20_anqp_get(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "HS20_ANQP_GET", 2, argc, argv);
}
static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[512];
if (argc == 0) {
printf("Command needs one or two arguments (dst mac addr and "
"optional home realm)\n");
return -1;
}
if (write_cmd(cmd, sizeof(cmd), "HS20_GET_NAI_HOME_REALM_LIST",
argc, argv) < 0)
return -1;
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_hs20_icon_request(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[512];
if (argc < 2) {
printf("Command needs two arguments (dst mac addr and "
"icon name)\n");
return -1;
}
if (write_cmd(cmd, sizeof(cmd), "HS20_ICON_REQUEST", argc, argv) < 0)
return -1;
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_fetch_osu(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "FETCH_OSU");
}
static int wpa_cli_cmd_cancel_fetch_osu(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "CANCEL_FETCH_OSU");
}
#endif /* CONFIG_HS20 */
static int wpa_cli_cmd_sta_autoconnect(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "STA_AUTOCONNECT", 1, argc, argv);
}
static int wpa_cli_cmd_tdls_discover(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "TDLS_DISCOVER", 1, argc, argv);
}
static int wpa_cli_cmd_tdls_setup(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "TDLS_SETUP", 1, argc, argv);
}
static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "TDLS_TEARDOWN", 1, argc, argv);
}
static int wpa_cli_cmd_tdls_link_status(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "TDLS_LINK_STATUS", 1, argc, argv);
}
static int wpa_cli_cmd_wmm_ac_addts(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "WMM_AC_ADDTS", 3, argc, argv);
}
static int wpa_cli_cmd_wmm_ac_delts(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "WMM_AC_DELTS", 1, argc, argv);
}
static int wpa_cli_cmd_wmm_ac_status(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "WMM_AC_STATUS");
}
static int wpa_cli_cmd_tdls_chan_switch(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "TDLS_CHAN_SWITCH", 2, argc, argv);
}
static int wpa_cli_cmd_tdls_cancel_chan_switch(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "TDLS_CANCEL_CHAN_SWITCH", 1, argc, argv);
}
static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "SIGNAL_POLL");
}
static int wpa_cli_cmd_signal_monitor(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "SIGNAL_MONITOR", 0, argc, argv);
}
static int wpa_cli_cmd_pktcnt_poll(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "PKTCNT_POLL");
}
static int wpa_cli_cmd_reauthenticate(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "REAUTHENTICATE");
}
#ifdef CONFIG_AUTOSCAN
static int wpa_cli_cmd_autoscan(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc == 0)
return wpa_ctrl_command(ctrl, "AUTOSCAN ");
return wpa_cli_cmd(ctrl, "AUTOSCAN", 0, argc, argv);
}
#endif /* CONFIG_AUTOSCAN */
#ifdef CONFIG_WNM
static int wpa_cli_cmd_wnm_sleep(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv);
}
static int wpa_cli_cmd_wnm_bss_query(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "WNM_BSS_QUERY", 1, argc, argv);
}
#endif /* CONFIG_WNM */
static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
if (argc == 0)
return -1;
return wpa_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]);
}
#ifdef ANDROID
static int wpa_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "DRIVER", 1, argc, argv);
}
#endif /* ANDROID */
static int wpa_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "VENDOR", 1, argc, argv);
}
static int wpa_cli_cmd_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "FLUSH");
}
static int wpa_cli_cmd_radio_work(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_cli_cmd(ctrl, "RADIO_WORK", 1, argc, argv);
}
static int wpa_cli_cmd_neighbor_rep_request(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "NEIGHBOR_REP_REQUEST", 0, argc, argv);
}
static int wpa_cli_cmd_twt_setup(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "TWT_SETUP", 0, argc, argv);
}
static int wpa_cli_cmd_twt_teardown(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "TWT_TEARDOWN", 0, argc, argv);
}
static int wpa_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
return wpa_ctrl_command(ctrl, "ERP_FLUSH");
}
static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "MAC_RAND_SCAN", 1, argc, argv);
}
static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv);
}
static int wpa_cli_cmd_p2p_lo_start(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_LO_START", 4, argc, argv);
}
static int wpa_cli_cmd_p2p_lo_stop(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "P2P_LO_STOP", 0, argc, argv);
}
#ifdef CONFIG_DPP
static int wpa_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_bootstrap_set(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_SET", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN");
}
static int wpa_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv);
}
static int wpa_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_configurator_sign(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_SIGN", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
}
#ifdef CONFIG_DPP2
static int wpa_cli_cmd_dpp_controller_start(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_CONTROLLER_START", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_controller_stop(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DPP_CONTROLLER_STOP");
}
static int wpa_cli_cmd_dpp_chirp(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "DPP_CHIRP", 1, argc, argv);
}
static int wpa_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_ctrl_command(ctrl, "DPP_STOP_CHIRP");
}
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
static int wpa_ctrl_command_bss(struct wpa_ctrl *ctrl, const char *cmd)
{
char buf[512], *pos, *bssid = NULL, *freq = NULL, *level = NULL,
*flags = NULL, *ssid = NULL;
size_t len;
int ret, id = -1;
if (!ctrl_conn)
return -1;
len = sizeof(buf) - 1;
ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
wpa_cli_msg_cb);
if (ret == -2) {
printf("'%s' command timed out.\n", cmd);
return -2;
} else if (ret < 0) {
printf("'%s' command failed.\n", cmd);
return -1;
}
buf[len] = '\0';
if (os_memcmp(buf, "FAIL", 4) == 0)
return -1;
pos = buf;
while (*pos != '\0') {
if (str_starts(pos, "id="))
id = atoi(pos + 3);
if (str_starts(pos, "bssid="))
bssid = pos + 6;
if (str_starts(pos, "freq="))
freq = pos + 5;
if (str_starts(pos, "level="))
level = pos + 6;
if (str_starts(pos, "flags="))
flags = pos + 6;
if (str_starts(pos, "ssid="))
ssid = pos + 5;
while (*pos != '\0' && *pos != '\n')
pos++;
*pos++ = '\0';
}
if (id != -1)
printf("%s\t%s\t%s\t%s\t%s\n", bssid ? bssid : "N/A",
freq ? freq : "N/A", level ? level : "N/A",
flags ? flags : "N/A", ssid ? ssid : "N/A");
return id;
}
static int wpa_cli_cmd_all_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
char cmd[64];
int id = -1;
unsigned int mask;
printf("bssid / frequency / signal level / flags / ssid\n");
mask = WPA_BSS_MASK_ID | WPA_BSS_MASK_BSSID | WPA_BSS_MASK_FREQ |
WPA_BSS_MASK_LEVEL | WPA_BSS_MASK_FLAGS | WPA_BSS_MASK_SSID;
do {
if (id < 0)
os_snprintf(cmd, sizeof(cmd), "BSS FIRST MASK=0x%x",
mask);
else
os_snprintf(cmd, sizeof(cmd), "BSS NEXT-%d MASK=0x%x",
id, mask);
id = wpa_ctrl_command_bss(ctrl, cmd);
} while (id >= 0);
return 0;
}
#ifdef CONFIG_PASN
static int wpa_cli_cmd_pasn_auth_start(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "PASN_AUTH_START", 4, argc, argv);
}
static int wpa_cli_cmd_pasn_auth_stop(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "PASN_AUTH_STOP", 0, argc, argv);
}
static int wpa_cli_cmd_ptksa_cache_list(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "PTKSA_CACHE_LIST", 0, argc, argv);
}
static int wpa_cli_cmd_pasn_deauth(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "PASN_DEAUTH", 1, argc, argv);
}
#endif /* CONFIG_PASN */
+static int wpa_cli_cmd_mscs(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "MSCS", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_scs(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "SCS", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dscp_resp(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DSCP_RESP", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_dscp_query(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "DSCP_QUERY", 1, argc, argv);
+}
+
+
enum wpa_cli_cmd_flags {
cli_cmd_flag_none = 0x00,
cli_cmd_flag_sensitive = 0x01
};
struct wpa_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
char ** (*completion)(const char *str, int pos);
enum wpa_cli_cmd_flags flags;
const char *usage;
};
static const struct wpa_cli_cmd wpa_cli_commands[] = {
{ "status", wpa_cli_cmd_status, NULL,
cli_cmd_flag_none,
"[verbose] = get current WPA/EAPOL/EAP status" },
{ "ifname", wpa_cli_cmd_ifname, NULL,
cli_cmd_flag_none,
"= get current interface name" },
{ "ping", wpa_cli_cmd_ping, NULL,
cli_cmd_flag_none,
"= pings wpa_supplicant" },
{ "relog", wpa_cli_cmd_relog, NULL,
cli_cmd_flag_none,
"= re-open log-file (allow rolling logs)" },
{ "note", wpa_cli_cmd_note, NULL,
cli_cmd_flag_none,
"<text> = add a note to wpa_supplicant debug log" },
{ "mib", wpa_cli_cmd_mib, NULL,
cli_cmd_flag_none,
"= get MIB variables (dot1x, dot11)" },
{ "help", wpa_cli_cmd_help, wpa_cli_complete_help,
cli_cmd_flag_none,
"[command] = show usage help" },
{ "interface", wpa_cli_cmd_interface, NULL,
cli_cmd_flag_none,
"[ifname] = show interfaces/select interface" },
{ "level", wpa_cli_cmd_level, NULL,
cli_cmd_flag_none,
"<debug level> = change debug level" },
{ "license", wpa_cli_cmd_license, NULL,
cli_cmd_flag_none,
"= show full wpa_cli license" },
{ "quit", wpa_cli_cmd_quit, NULL,
cli_cmd_flag_none,
"= exit wpa_cli" },
{ "set", wpa_cli_cmd_set, wpa_cli_complete_set,
cli_cmd_flag_none,
"= set variables (shows list of variables when run without "
"arguments)" },
{ "dump", wpa_cli_cmd_dump, NULL,
cli_cmd_flag_none,
"= dump config variables" },
{ "get", wpa_cli_cmd_get, wpa_cli_complete_get,
cli_cmd_flag_none,
"<name> = get information" },
{ "driver_flags", wpa_cli_cmd_driver_flags, NULL,
cli_cmd_flag_none,
"= list driver flags" },
{ "logon", wpa_cli_cmd_logon, NULL,
cli_cmd_flag_none,
"= IEEE 802.1X EAPOL state machine logon" },
{ "logoff", wpa_cli_cmd_logoff, NULL,
cli_cmd_flag_none,
"= IEEE 802.1X EAPOL state machine logoff" },
{ "pmksa", wpa_cli_cmd_pmksa, NULL,
cli_cmd_flag_none,
"= show PMKSA cache" },
{ "pmksa_flush", wpa_cli_cmd_pmksa_flush, NULL,
cli_cmd_flag_none,
"= flush PMKSA cache entries" },
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
{ "pmksa_get", wpa_cli_cmd_pmksa_get, NULL,
cli_cmd_flag_none,
"<network_id> = fetch all stored PMKSA cache entries" },
{ "pmksa_add", wpa_cli_cmd_pmksa_add, NULL,
cli_cmd_flag_sensitive,
"<network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> = store PMKSA cache entry from external storage" },
#ifdef CONFIG_MESH
{ "mesh_pmksa_get", wpa_cli_mesh_cmd_pmksa_get, NULL,
cli_cmd_flag_none,
"<peer MAC address | any> = fetch all stored mesh PMKSA cache entries" },
{ "mesh_pmksa_add", wpa_cli_mesh_cmd_pmksa_add, NULL,
cli_cmd_flag_sensitive,
"<BSSID> <PMKID> <PMK> <expiration in seconds> = store mesh PMKSA cache entry from external storage" },
#endif /* CONFIG_MESH */
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
{ "reassociate", wpa_cli_cmd_reassociate, NULL,
cli_cmd_flag_none,
"= force reassociation" },
{ "reattach", wpa_cli_cmd_reattach, NULL,
cli_cmd_flag_none,
"= force reassociation back to the same BSS" },
{ "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<BSSID> = force preauthentication" },
{ "identity", wpa_cli_cmd_identity, wpa_cli_complete_network_id,
cli_cmd_flag_none,
"<network id> <identity> = configure identity for an SSID" },
{ "password", wpa_cli_cmd_password, wpa_cli_complete_network_id,
cli_cmd_flag_sensitive,
"<network id> <password> = configure password for an SSID" },
{ "new_password", wpa_cli_cmd_new_password,
wpa_cli_complete_network_id, cli_cmd_flag_sensitive,
"<network id> <password> = change password for an SSID" },
{ "pin", wpa_cli_cmd_pin, wpa_cli_complete_network_id,
cli_cmd_flag_sensitive,
"<network id> <pin> = configure pin for an SSID" },
{ "otp", wpa_cli_cmd_otp, wpa_cli_complete_network_id,
cli_cmd_flag_sensitive,
"<network id> <password> = configure one-time-password for an SSID"
},
{ "psk_passphrase", wpa_cli_cmd_psk_passphrase,
wpa_cli_complete_network_id, cli_cmd_flag_sensitive,
"<network id> <PSK/passphrase> = configure PSK/passphrase for an SSID" },
{ "passphrase", wpa_cli_cmd_passphrase, wpa_cli_complete_network_id,
cli_cmd_flag_sensitive,
"<network id> <passphrase> = configure private key passphrase\n"
" for an SSID" },
{ "sim", wpa_cli_cmd_sim, wpa_cli_complete_network_id,
cli_cmd_flag_sensitive,
"<network id> <pin> = report SIM operation result" },
{ "bssid", wpa_cli_cmd_bssid, wpa_cli_complete_network_id,
cli_cmd_flag_none,
"<network id> <BSSID> = set preferred BSSID for an SSID" },
{ "bssid_ignore", wpa_cli_cmd_bssid_ignore, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<BSSID> = add a BSSID to the list of temporarily ignored BSSs\n"
"bssid_ignore clear = clear the list of temporarily ignored BSSIDs\n"
"bssid_ignore = display the list of temporarily ignored BSSIDs" },
{ "blacklist", /* deprecated alias for bssid_ignore */
wpa_cli_cmd_bssid_ignore, wpa_cli_complete_bss,
cli_cmd_flag_none,
"= deprecated alias for bssid_ignore" },
{ "log_level", wpa_cli_cmd_log_level, NULL,
cli_cmd_flag_none,
"<level> [<timestamp>] = update the log level/timestamp\n"
"log_level = display the current log level and log options" },
{ "list_networks", wpa_cli_cmd_list_networks, NULL,
cli_cmd_flag_none,
"= list configured networks" },
{ "select_network", wpa_cli_cmd_select_network,
wpa_cli_complete_network_id,
cli_cmd_flag_none,
"<network id> = select a network (disable others)" },
{ "enable_network", wpa_cli_cmd_enable_network,
wpa_cli_complete_network_id,
cli_cmd_flag_none,
"<network id> = enable a network" },
{ "disable_network", wpa_cli_cmd_disable_network,
wpa_cli_complete_network_id,
cli_cmd_flag_none,
"<network id> = disable a network" },
{ "add_network", wpa_cli_cmd_add_network, NULL,
cli_cmd_flag_none,
"= add a network" },
{ "remove_network", wpa_cli_cmd_remove_network,
wpa_cli_complete_network_id,
cli_cmd_flag_none,
"<network id> = remove a network" },
{ "set_network", wpa_cli_cmd_set_network, wpa_cli_complete_network,
cli_cmd_flag_sensitive,
"<network id> <variable> <value> = set network variables (shows\n"
" list of variables when run without arguments)" },
{ "get_network", wpa_cli_cmd_get_network, wpa_cli_complete_network,
cli_cmd_flag_none,
"<network id> <variable> = get network variables" },
{ "dup_network", wpa_cli_cmd_dup_network, wpa_cli_complete_dup_network,
cli_cmd_flag_none,
"<src network id> <dst network id> <variable> = duplicate network variables"
},
{ "list_creds", wpa_cli_cmd_list_creds, NULL,
cli_cmd_flag_none,
"= list configured credentials" },
{ "add_cred", wpa_cli_cmd_add_cred, NULL,
cli_cmd_flag_none,
"= add a credential" },
{ "remove_cred", wpa_cli_cmd_remove_cred, NULL,
cli_cmd_flag_none,
"<cred id> = remove a credential" },
{ "set_cred", wpa_cli_cmd_set_cred, wpa_cli_complete_cred,
cli_cmd_flag_sensitive,
"<cred id> <variable> <value> = set credential variables" },
{ "get_cred", wpa_cli_cmd_get_cred, wpa_cli_complete_cred,
cli_cmd_flag_none,
"<cred id> <variable> = get credential variables" },
{ "save_config", wpa_cli_cmd_save_config, NULL,
cli_cmd_flag_none,
"= save the current configuration" },
{ "disconnect", wpa_cli_cmd_disconnect, NULL,
cli_cmd_flag_none,
"= disconnect and wait for reassociate/reconnect command before\n"
" connecting" },
{ "reconnect", wpa_cli_cmd_reconnect, NULL,
cli_cmd_flag_none,
"= like reassociate, but only takes effect if already disconnected"
},
{ "scan", wpa_cli_cmd_scan, NULL,
cli_cmd_flag_none,
"= request new BSS scan" },
{ "scan_results", wpa_cli_cmd_scan_results, NULL,
cli_cmd_flag_none,
"= get latest scan results" },
{ "abort_scan", wpa_cli_cmd_abort_scan, NULL,
cli_cmd_flag_none,
"= request ongoing scan to be aborted" },
{ "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<<idx> | <bssid>> = get detailed scan result info" },
{ "get_capability", wpa_cli_cmd_get_capability,
wpa_cli_complete_get_capability, cli_cmd_flag_none,
"<eap/pairwise/group/key_mgmt/proto/auth_alg/channels/freq/modes> "
"= get capabilities" },
{ "reconfigure", wpa_cli_cmd_reconfigure, NULL,
cli_cmd_flag_none,
"= force wpa_supplicant to re-read its configuration file" },
{ "terminate", wpa_cli_cmd_terminate, NULL,
cli_cmd_flag_none,
"= terminate wpa_supplicant" },
{ "interface_add", wpa_cli_cmd_interface_add, NULL,
cli_cmd_flag_none,
"<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n"
" <bridge_name> <create> <type> = adds new interface, all "
"parameters but\n"
" <ifname> are optional. Supported types are station ('sta') and "
"AP ('ap')" },
{ "interface_remove", wpa_cli_cmd_interface_remove, NULL,
cli_cmd_flag_none,
"<ifname> = removes the interface" },
{ "interface_list", wpa_cli_cmd_interface_list, NULL,
cli_cmd_flag_none,
"= list available interfaces" },
{ "ap_scan", wpa_cli_cmd_ap_scan, NULL,
cli_cmd_flag_none,
"<value> = set ap_scan parameter" },
{ "scan_interval", wpa_cli_cmd_scan_interval, NULL,
cli_cmd_flag_none,
"<value> = set scan_interval parameter (in seconds)" },
{ "bss_expire_age", wpa_cli_cmd_bss_expire_age, NULL,
cli_cmd_flag_none,
"<value> = set BSS expiration age parameter" },
{ "bss_expire_count", wpa_cli_cmd_bss_expire_count, NULL,
cli_cmd_flag_none,
"<value> = set BSS expiration scan count parameter" },
{ "bss_flush", wpa_cli_cmd_bss_flush, NULL,
cli_cmd_flag_none,
"<value> = set BSS flush age (0 by default)" },
{ "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<addr> = request over-the-DS FT with <addr>" },
{ "wps_pbc", wpa_cli_cmd_wps_pbc, wpa_cli_complete_bss,
cli_cmd_flag_none,
"[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" },
{ "wps_pin", wpa_cli_cmd_wps_pin, wpa_cli_complete_bss,
cli_cmd_flag_sensitive,
"<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
"hardcoded)" },
{ "wps_check_pin", wpa_cli_cmd_wps_check_pin, NULL,
cli_cmd_flag_sensitive,
"<PIN> = verify PIN checksum" },
{ "wps_cancel", wpa_cli_cmd_wps_cancel, NULL, cli_cmd_flag_none,
"Cancels the pending WPS operation" },
#ifdef CONFIG_WPS_NFC
{ "wps_nfc", wpa_cli_cmd_wps_nfc, wpa_cli_complete_bss,
cli_cmd_flag_none,
"[BSSID] = start Wi-Fi Protected Setup: NFC" },
{ "wps_nfc_config_token", wpa_cli_cmd_wps_nfc_config_token, NULL,
cli_cmd_flag_none,
"<WPS|NDEF> = build configuration token" },
{ "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, NULL,
cli_cmd_flag_none,
"<WPS|NDEF> = create password token" },
{ "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL,
cli_cmd_flag_sensitive,
"<hexdump of payload> = report read NFC tag with WPS data" },
{ "nfc_get_handover_req", wpa_cli_cmd_nfc_get_handover_req, NULL,
cli_cmd_flag_none,
"<NDEF> <WPS> = create NFC handover request" },
{ "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL,
cli_cmd_flag_none,
"<NDEF> <WPS> = create NFC handover select" },
{ "nfc_report_handover", wpa_cli_cmd_nfc_report_handover, NULL,
cli_cmd_flag_none,
"<role> <type> <hexdump of req> <hexdump of sel> = report completed "
"NFC handover" },
#endif /* CONFIG_WPS_NFC */
{ "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss,
cli_cmd_flag_sensitive,
"<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
{ "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, NULL,
cli_cmd_flag_sensitive,
"[params..] = enable/disable AP PIN" },
{ "wps_er_start", wpa_cli_cmd_wps_er_start, NULL,
cli_cmd_flag_none,
"[IP address] = start Wi-Fi Protected Setup External Registrar" },
{ "wps_er_stop", wpa_cli_cmd_wps_er_stop, NULL,
cli_cmd_flag_none,
"= stop Wi-Fi Protected Setup External Registrar" },
{ "wps_er_pin", wpa_cli_cmd_wps_er_pin, NULL,
cli_cmd_flag_sensitive,
"<UUID> <PIN> = add an Enrollee PIN to External Registrar" },
{ "wps_er_pbc", wpa_cli_cmd_wps_er_pbc, NULL,
cli_cmd_flag_none,
"<UUID> = accept an Enrollee PBC using External Registrar" },
{ "wps_er_learn", wpa_cli_cmd_wps_er_learn, NULL,
cli_cmd_flag_sensitive,
"<UUID> <PIN> = learn AP configuration" },
{ "wps_er_set_config", wpa_cli_cmd_wps_er_set_config, NULL,
cli_cmd_flag_none,
"<UUID> <network id> = set AP configuration for enrolling" },
{ "wps_er_config", wpa_cli_cmd_wps_er_config, NULL,
cli_cmd_flag_sensitive,
"<UUID> <PIN> <SSID> <auth> <encr> <key> = configure AP" },
#ifdef CONFIG_WPS_NFC
{ "wps_er_nfc_config_token", wpa_cli_cmd_wps_er_nfc_config_token, NULL,
cli_cmd_flag_none,
"<WPS/NDEF> <UUID> = build NFC configuration token" },
#endif /* CONFIG_WPS_NFC */
{ "ibss_rsn", wpa_cli_cmd_ibss_rsn, NULL,
cli_cmd_flag_none,
"<addr> = request RSN authentication with <addr> in IBSS" },
#ifdef CONFIG_AP
{ "sta", wpa_cli_cmd_sta, wpa_cli_complete_sta,
cli_cmd_flag_none,
"<addr> = get information about an associated station (AP)" },
{ "all_sta", wpa_cli_cmd_all_sta, NULL,
cli_cmd_flag_none,
"= get information about all associated stations (AP)" },
{ "list_sta", wpa_cli_cmd_list_sta, NULL,
cli_cmd_flag_none,
"= list all stations (AP)" },
{ "deauthenticate", wpa_cli_cmd_deauthenticate,
wpa_cli_complete_deauthenticate, cli_cmd_flag_none,
"<addr> = deauthenticate a station" },
{ "disassociate", wpa_cli_cmd_disassociate,
wpa_cli_complete_disassociate, cli_cmd_flag_none,
"<addr> = disassociate a station" },
{ "chan_switch", wpa_cli_cmd_chanswitch, NULL,
cli_cmd_flag_none,
"<cs_count> <freq> [sec_channel_offset=] [center_freq1=]"
" [center_freq2=] [bandwidth=] [blocktx] [ht|vht]"
" = CSA parameters" },
+ { "update_beacon", wpa_cli_cmd_update_beacon, NULL,
+ cli_cmd_flag_none,
+ "= update Beacon frame contents"},
#endif /* CONFIG_AP */
{ "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none,
"= notification of suspend/hibernate" },
{ "resume", wpa_cli_cmd_resume, NULL, cli_cmd_flag_none,
"= notification of resume/thaw" },
#ifdef CONFIG_TESTING_OPTIONS
{ "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none,
"= drop SA without deauth/disassoc (test command)" },
#endif /* CONFIG_TESTING_OPTIONS */
{ "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<addr> = roam to the specified BSS" },
#ifdef CONFIG_MESH
{ "mesh_interface_add", wpa_cli_cmd_mesh_interface_add, NULL,
cli_cmd_flag_none,
"[ifname] = Create a new mesh interface" },
{ "mesh_group_add", wpa_cli_cmd_mesh_group_add, NULL,
cli_cmd_flag_none,
"<network id> = join a mesh network (disable others)" },
{ "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL,
cli_cmd_flag_none,
"<ifname> = Remove mesh group interface" },
{ "mesh_peer_remove", wpa_cli_cmd_mesh_peer_remove, NULL,
cli_cmd_flag_none,
"<addr> = Remove a mesh peer" },
{ "mesh_peer_add", wpa_cli_cmd_mesh_peer_add, NULL,
cli_cmd_flag_none,
"<addr> [duration=<seconds>] = Add a mesh peer" },
{ "mesh_link_probe", wpa_cli_cmd_mesh_link_probe, NULL,
cli_cmd_flag_none,
"<addr> [payload=<hex dump of payload>] = Probe a mesh link for a given peer by injecting a frame." },
#endif /* CONFIG_MESH */
#ifdef CONFIG_P2P
{ "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,
cli_cmd_flag_none,
"[timeout] [type=*] = find P2P Devices for up-to timeout seconds" },
{ "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none,
"= stop P2P Devices search" },
{ "p2p_asp_provision", wpa_cli_cmd_p2p_asp_provision, NULL,
cli_cmd_flag_none,
"<addr> adv_id=<adv_id> conncap=<conncap> [info=<infodata>] = provision with a P2P ASP Device" },
{ "p2p_asp_provision_resp", wpa_cli_cmd_p2p_asp_provision_resp, NULL,
cli_cmd_flag_none,
"<addr> adv_id=<adv_id> [role<conncap>] [info=<infodata>] = provision with a P2P ASP Device" },
{ "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect,
cli_cmd_flag_none,
"<addr> <\"pbc\"|PIN> [ht40] = connect to a P2P Device" },
{ "p2p_listen", wpa_cli_cmd_p2p_listen, NULL, cli_cmd_flag_none,
"[timeout] = listen for P2P Devices for up-to timeout seconds" },
{ "p2p_group_remove", wpa_cli_cmd_p2p_group_remove,
wpa_cli_complete_p2p_group_remove, cli_cmd_flag_none,
"<ifname> = remove P2P group interface (terminate group if GO)" },
{ "p2p_group_add", wpa_cli_cmd_p2p_group_add, NULL, cli_cmd_flag_none,
"[ht40] = add a new P2P group (local end as GO)" },
{ "p2p_group_member", wpa_cli_cmd_p2p_group_member, NULL,
cli_cmd_flag_none,
"<dev_addr> = Get peer interface address on local GO using peer Device Address" },
{ "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc,
wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
"<addr> <method> = request provisioning discovery" },
{ "p2p_get_passphrase", wpa_cli_cmd_p2p_get_passphrase, NULL,
cli_cmd_flag_none,
"= get the passphrase for a group (GO only)" },
{ "p2p_serv_disc_req", wpa_cli_cmd_p2p_serv_disc_req,
wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
"<addr> <TLVs> = schedule service discovery request" },
{ "p2p_serv_disc_cancel_req", wpa_cli_cmd_p2p_serv_disc_cancel_req,
NULL, cli_cmd_flag_none,
"<id> = cancel pending service discovery request" },
{ "p2p_serv_disc_resp", wpa_cli_cmd_p2p_serv_disc_resp, NULL,
cli_cmd_flag_none,
"<freq> <addr> <dialog token> <TLVs> = service discovery response" },
{ "p2p_service_update", wpa_cli_cmd_p2p_service_update, NULL,
cli_cmd_flag_none,
"= indicate change in local services" },
{ "p2p_serv_disc_external", wpa_cli_cmd_p2p_serv_disc_external, NULL,
cli_cmd_flag_none,
"<external> = set external processing of service discovery" },
{ "p2p_service_flush", wpa_cli_cmd_p2p_service_flush, NULL,
cli_cmd_flag_none,
"= remove all stored service entries" },
{ "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL,
cli_cmd_flag_none,
"<bonjour|upnp|asp> <query|version> <response|service> = add a local "
"service" },
{ "p2p_service_rep", wpa_cli_cmd_p2p_service_rep, NULL,
cli_cmd_flag_none,
"asp <auto> <adv_id> <svc_state> <svc_string> [<svc_info>] = replace "
"local ASP service" },
{ "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL,
cli_cmd_flag_none,
"<bonjour|upnp> <query|version> [|service] = remove a local "
"service" },
{ "p2p_reject", wpa_cli_cmd_p2p_reject, wpa_cli_complete_p2p_peer,
cli_cmd_flag_none,
"<addr> = reject connection attempts from a specific peer" },
{ "p2p_invite", wpa_cli_cmd_p2p_invite, NULL,
cli_cmd_flag_none,
"<cmd> [peer=addr] = invite peer" },
{ "p2p_peers", wpa_cli_cmd_p2p_peers, NULL, cli_cmd_flag_none,
"[discovered] = list known (optionally, only fully discovered) P2P "
"peers" },
{ "p2p_peer", wpa_cli_cmd_p2p_peer, wpa_cli_complete_p2p_peer,
cli_cmd_flag_none,
"<address> = show information about known P2P peer" },
{ "p2p_set", wpa_cli_cmd_p2p_set, wpa_cli_complete_p2p_set,
cli_cmd_flag_none,
"<field> <value> = set a P2P parameter" },
{ "p2p_flush", wpa_cli_cmd_p2p_flush, NULL, cli_cmd_flag_none,
"= flush P2P state" },
{ "p2p_cancel", wpa_cli_cmd_p2p_cancel, NULL, cli_cmd_flag_none,
"= cancel P2P group formation" },
{ "p2p_unauthorize", wpa_cli_cmd_p2p_unauthorize,
wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
"<address> = unauthorize a peer" },
{ "p2p_presence_req", wpa_cli_cmd_p2p_presence_req, NULL,
cli_cmd_flag_none,
"[<duration> <interval>] [<duration> <interval>] = request GO "
"presence" },
{ "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL,
cli_cmd_flag_none,
"[<period> <interval>] = set extended listen timing" },
{ "p2p_remove_client", wpa_cli_cmd_p2p_remove_client,
wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
"<address|iface=address> = remove a peer from all groups" },
#endif /* CONFIG_P2P */
{ "vendor_elem_add", wpa_cli_cmd_vendor_elem_add, NULL,
cli_cmd_flag_none,
"<frame id> <hexdump of elem(s)> = add vendor specific IEs to frame(s)\n"
VENDOR_ELEM_FRAME_ID },
{ "vendor_elem_get", wpa_cli_cmd_vendor_elem_get, NULL,
cli_cmd_flag_none,
"<frame id> = get vendor specific IE(s) to frame(s)\n"
VENDOR_ELEM_FRAME_ID },
{ "vendor_elem_remove", wpa_cli_cmd_vendor_elem_remove, NULL,
cli_cmd_flag_none,
"<frame id> <hexdump of elem(s)> = remove vendor specific IE(s) in frame(s)\n"
VENDOR_ELEM_FRAME_ID },
#ifdef CONFIG_WIFI_DISPLAY
{ "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
cli_cmd_flag_none,
"<subelem> [contents] = set Wi-Fi Display subelement" },
{ "wfd_subelem_get", wpa_cli_cmd_wfd_subelem_get, NULL,
cli_cmd_flag_none,
"<subelem> = get Wi-Fi Display subelement" },
#endif /* CONFIG_WIFI_DISPLAY */
#ifdef CONFIG_INTERWORKING
{ "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none,
"= fetch ANQP information for all APs" },
{ "stop_fetch_anqp", wpa_cli_cmd_stop_fetch_anqp, NULL,
cli_cmd_flag_none,
"= stop fetch_anqp operation" },
{ "interworking_select", wpa_cli_cmd_interworking_select, NULL,
cli_cmd_flag_none,
"[auto] = perform Interworking network selection" },
{ "interworking_connect", wpa_cli_cmd_interworking_connect,
wpa_cli_complete_bss, cli_cmd_flag_none,
"<BSSID> = connect using Interworking credentials" },
{ "interworking_add_network", wpa_cli_cmd_interworking_add_network,
wpa_cli_complete_bss, cli_cmd_flag_none,
"<BSSID> = connect using Interworking credentials" },
{ "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<addr> <info id>[,<info id>]... = request ANQP information" },
{ "gas_request", wpa_cli_cmd_gas_request, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<addr> <AdvProtoID> [QueryReq] = GAS request" },
{ "gas_response_get", wpa_cli_cmd_gas_response_get,
wpa_cli_complete_bss, cli_cmd_flag_none,
"<addr> <dialog token> [start,len] = Fetch last GAS response" },
#endif /* CONFIG_INTERWORKING */
#ifdef CONFIG_HS20
{ "hs20_anqp_get", wpa_cli_cmd_hs20_anqp_get, wpa_cli_complete_bss,
cli_cmd_flag_none,
"<addr> <subtype>[,<subtype>]... = request HS 2.0 ANQP information"
},
{ "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list,
wpa_cli_complete_bss, cli_cmd_flag_none,
"<addr> <home realm> = get HS20 nai home realm list" },
{ "hs20_icon_request", wpa_cli_cmd_hs20_icon_request,
wpa_cli_complete_bss, cli_cmd_flag_none,
"<addr> <icon name> = get Hotspot 2.0 OSU icon" },
{ "fetch_osu", wpa_cli_cmd_fetch_osu, NULL, cli_cmd_flag_none,
"= fetch OSU provider information from all APs" },
{ "cancel_fetch_osu", wpa_cli_cmd_cancel_fetch_osu, NULL,
cli_cmd_flag_none,
"= cancel fetch_osu command" },
#endif /* CONFIG_HS20 */
{ "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL,
cli_cmd_flag_none,
"<0/1> = disable/enable automatic reconnection" },
{ "tdls_discover", wpa_cli_cmd_tdls_discover, NULL,
cli_cmd_flag_none,
"<addr> = request TDLS discovery with <addr>" },
{ "tdls_setup", wpa_cli_cmd_tdls_setup, NULL,
cli_cmd_flag_none,
"<addr> = request TDLS setup with <addr>" },
{ "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL,
cli_cmd_flag_none,
"<addr> = tear down TDLS with <addr>" },
{ "tdls_link_status", wpa_cli_cmd_tdls_link_status, NULL,
cli_cmd_flag_none,
"<addr> = TDLS link status with <addr>" },
{ "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL,
cli_cmd_flag_none,
"<uplink/downlink/bidi> <tsid=0..7> <up=0..7> [nominal_msdu_size=#] "
"[mean_data_rate=#] [min_phy_rate=#] [sba=#] [fixed_nominal_msdu] "
"= add WMM-AC traffic stream" },
{ "wmm_ac_delts", wpa_cli_cmd_wmm_ac_delts, NULL,
cli_cmd_flag_none,
"<tsid> = delete WMM-AC traffic stream" },
{ "wmm_ac_status", wpa_cli_cmd_wmm_ac_status, NULL,
cli_cmd_flag_none,
"= show status for Wireless Multi-Media Admission-Control" },
{ "tdls_chan_switch", wpa_cli_cmd_tdls_chan_switch, NULL,
cli_cmd_flag_none,
"<addr> <oper class> <freq> [sec_channel_offset=] [center_freq1=] "
"[center_freq2=] [bandwidth=] [ht|vht] = enable channel switching "
"with TDLS peer" },
{ "tdls_cancel_chan_switch", wpa_cli_cmd_tdls_cancel_chan_switch, NULL,
cli_cmd_flag_none,
"<addr> = disable channel switching with TDLS peer <addr>" },
{ "signal_poll", wpa_cli_cmd_signal_poll, NULL,
cli_cmd_flag_none,
"= get signal parameters" },
{ "signal_monitor", wpa_cli_cmd_signal_monitor, NULL,
cli_cmd_flag_none,
"= set signal monitor parameters" },
{ "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL,
cli_cmd_flag_none,
"= get TX/RX packet counters" },
{ "reauthenticate", wpa_cli_cmd_reauthenticate, NULL,
cli_cmd_flag_none,
"= trigger IEEE 802.1X/EAPOL reauthentication" },
#ifdef CONFIG_AUTOSCAN
{ "autoscan", wpa_cli_cmd_autoscan, NULL, cli_cmd_flag_none,
"[params] = Set or unset (if none) autoscan parameters" },
#endif /* CONFIG_AUTOSCAN */
#ifdef CONFIG_WNM
{ "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none,
"<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" },
{ "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none,
"<query reason> [list]"
" [neighbor=<BSSID>,<BSSID information>,<operating class>,<channel number>,<PHY type>[,<hexdump of optional subelements>]"
" = Send BSS Transition Management Query" },
#endif /* CONFIG_WNM */
{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
"<params..> = Sent unprocessed command" },
{ "flush", wpa_cli_cmd_flush, NULL, cli_cmd_flag_none,
"= flush wpa_supplicant state" },
#ifdef ANDROID
{ "driver", wpa_cli_cmd_driver, NULL, cli_cmd_flag_none,
"<command> = driver private commands" },
#endif /* ANDROID */
{ "radio_work", wpa_cli_cmd_radio_work, NULL, cli_cmd_flag_none,
"= radio_work <show/add/done>" },
{ "vendor", wpa_cli_cmd_vendor, NULL, cli_cmd_flag_none,
"<vendor id> <command id> [<hex formatted command argument>] = Send vendor command"
},
{ "neighbor_rep_request",
wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none,
"[ssid=<SSID>] [lci] [civic] = Trigger request to AP for neighboring AP report (with optional given SSID in hex or enclosed in double quotes, default: current SSID; with optional LCI and location civic request)"
},
{ "twt_setup",
wpa_cli_cmd_twt_setup, NULL, cli_cmd_flag_none,
"[dialog=<token>] [exponent=<exponent>] [mantissa=<mantissa>] [min_twt=<Min TWT>] [setup_cmd=<setup-cmd>] [twt=<u64>] [requestor=0|1] [trigger=0|1] [implicit=0|1] [flow_type=0|1] [flow_id=<3-bit-id>] [protection=0|1] [twt_channel=<twt chanel id>] [control=<control-u8>] = Send TWT Setup frame"
},
{ "twt_teardown",
wpa_cli_cmd_twt_teardown, NULL, cli_cmd_flag_none,
"[flags=<value>] = Send TWT Teardown frame"
},
{ "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none,
"= flush ERP keys" },
{ "mac_rand_scan",
wpa_cli_cmd_mac_rand_scan, NULL, cli_cmd_flag_none,
"<scan|sched|pno|all> enable=<0/1> [addr=mac-address "
"mask=mac-address-mask] = scan MAC randomization"
},
{ "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
cli_cmd_flag_none,
"<interface type> = retrieve preferred freq list for the specified interface type" },
{ "p2p_lo_start", wpa_cli_cmd_p2p_lo_start, NULL,
cli_cmd_flag_none,
"<freq> <period> <interval> <count> = start P2P listen offload" },
{ "p2p_lo_stop", wpa_cli_cmd_p2p_lo_stop, NULL,
cli_cmd_flag_none,
"= stop P2P listen offload" },
#ifdef CONFIG_DPP
{ "dpp_qr_code", wpa_cli_cmd_dpp_qr_code, NULL, cli_cmd_flag_none,
"report a scanned DPP URI from a QR Code" },
{ "dpp_bootstrap_gen", wpa_cli_cmd_dpp_bootstrap_gen, NULL,
cli_cmd_flag_sensitive,
"type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" },
{ "dpp_bootstrap_remove", wpa_cli_cmd_dpp_bootstrap_remove, NULL,
cli_cmd_flag_none,
"*|<id> = remove DPP bootstrap information" },
{ "dpp_bootstrap_get_uri", wpa_cli_cmd_dpp_bootstrap_get_uri, NULL,
cli_cmd_flag_none,
"<id> = get DPP bootstrap URI" },
{ "dpp_bootstrap_info", wpa_cli_cmd_dpp_bootstrap_info, NULL,
cli_cmd_flag_none,
"<id> = show DPP bootstrap information" },
{ "dpp_bootstrap_set", wpa_cli_cmd_dpp_bootstrap_set, NULL,
cli_cmd_flag_none,
"<id> [conf=..] [ssid=<SSID>] [ssid_charset=#] [psk=<PSK>] [pass=<passphrase>] [configurator=<id>] [conn_status=#] [akm_use_selector=<0|1>] [group_id=..] [expiry=#] [csrattrs=..] = set DPP configurator parameters" },
{ "dpp_auth_init", wpa_cli_cmd_dpp_auth_init, NULL, cli_cmd_flag_none,
"peer=<id> [own=<id>] = initiate DPP bootstrapping" },
{ "dpp_listen", wpa_cli_cmd_dpp_listen, NULL, cli_cmd_flag_none,
"<freq in MHz> = start DPP listen" },
{ "dpp_stop_listen", wpa_cli_cmd_dpp_stop_listen, NULL,
cli_cmd_flag_none,
"= stop DPP listen" },
{ "dpp_configurator_add", wpa_cli_cmd_dpp_configurator_add, NULL,
cli_cmd_flag_sensitive,
"[curve=..] [key=..] = add DPP configurator" },
{ "dpp_configurator_remove", wpa_cli_cmd_dpp_configurator_remove, NULL,
cli_cmd_flag_none,
"*|<id> = remove DPP configurator" },
{ "dpp_configurator_get_key", wpa_cli_cmd_dpp_configurator_get_key,
NULL, cli_cmd_flag_none,
"<id> = Get DPP configurator's private key" },
{ "dpp_configurator_sign", wpa_cli_cmd_dpp_configurator_sign, NULL,
cli_cmd_flag_none,
"conf=<role> configurator=<id> = generate self DPP configuration" },
{ "dpp_pkex_add", wpa_cli_cmd_dpp_pkex_add, NULL,
cli_cmd_flag_sensitive,
"add PKEX code" },
{ "dpp_pkex_remove", wpa_cli_cmd_dpp_pkex_remove, NULL,
cli_cmd_flag_none,
"*|<id> = remove DPP pkex information" },
#ifdef CONFIG_DPP2
{ "dpp_controller_start", wpa_cli_cmd_dpp_controller_start, NULL,
cli_cmd_flag_none,
"[tcp_port=<port>] [role=..] = start DPP controller" },
{ "dpp_controller_stop", wpa_cli_cmd_dpp_controller_stop, NULL,
cli_cmd_flag_none,
"= stop DPP controller" },
{ "dpp_chirp", wpa_cli_cmd_dpp_chirp, NULL,
cli_cmd_flag_none,
"own=<BI ID> iter=<count> = start DPP chirp" },
{ "dpp_stop_chirp", wpa_cli_cmd_dpp_stop_chirp, NULL,
cli_cmd_flag_none,
"= stop DPP chirp" },
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
{ "all_bss", wpa_cli_cmd_all_bss, NULL, cli_cmd_flag_none,
"= list all BSS entries (scan results)" },
#ifdef CONFIG_PASN
{ "pasn_auth_start", wpa_cli_cmd_pasn_auth_start, NULL,
cli_cmd_flag_none,
"bssid=<BSSID> akmp=<WPA key mgmt> cipher=<WPA cipher> group=<group> nid=<network id> = Start PASN authentication" },
{ "pasn_auth_stop", wpa_cli_cmd_pasn_auth_stop, NULL,
cli_cmd_flag_none,
"= Stop PASN authentication" },
{ "ptksa_cache_list", wpa_cli_cmd_ptksa_cache_list, NULL,
cli_cmd_flag_none,
"= Get the PTKSA Cache" },
{ "pasn_deauth", wpa_cli_cmd_pasn_deauth, NULL,
cli_cmd_flag_none,
"bssid=<BSSID> = Remove PASN PTKSA state" },
#endif /* CONFIG_PASN */
+ { "mscs", wpa_cli_cmd_mscs, NULL,
+ cli_cmd_flag_none,
+ "<add|remove|change> [up_bitmap=<hex byte>] [up_limit=<integer>] [stream_timeout=<in TUs>] [frame_classifier=<hex bytes>] = Configure MSCS request" },
+ { "scs", wpa_cli_cmd_scs, NULL,
+ cli_cmd_flag_none,
+ "[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>] [scs_id=<decimal number>] ... = Send SCS request" },
+ { "dscp_resp", wpa_cli_cmd_dscp_resp, NULL,
+ cli_cmd_flag_none,
+ "<[reset]>/<[solicited] [policy_id=1 status=0...]> [more] = Send DSCP response" },
+ { "dscp_query", wpa_cli_cmd_dscp_query, NULL,
+ cli_cmd_flag_none,
+ "wildcard/domain_name=<string> = Send DSCP Query" },
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
};
/*
* Prints command usage, lines are padded with the specified string.
*/
static void print_cmd_help(const struct wpa_cli_cmd *cmd, const char *pad)
{
char c;
size_t n;
printf("%s%s ", pad, cmd->cmd);
for (n = 0; (c = cmd->usage[n]); n++) {
printf("%c", c);
if (c == '\n')
printf("%s", pad);
}
printf("\n");
}
static void print_help(const char *cmd)
{
int n;
printf("commands:\n");
for (n = 0; wpa_cli_commands[n].cmd; n++) {
if (cmd == NULL || str_starts(wpa_cli_commands[n].cmd, cmd))
print_cmd_help(&wpa_cli_commands[n], " ");
}
}
static int wpa_cli_edit_filter_history_cb(void *ctx, const char *cmd)
{
const char *c, *delim;
int n;
size_t len;
delim = os_strchr(cmd, ' ');
if (delim)
len = delim - cmd;
else
len = os_strlen(cmd);
for (n = 0; (c = wpa_cli_commands[n].cmd); n++) {
if (os_strncasecmp(cmd, c, len) == 0 && len == os_strlen(c))
return (wpa_cli_commands[n].flags &
cli_cmd_flag_sensitive);
}
return 0;
}
static char ** wpa_list_cmd_list(void)
{
char **res;
int i, count;
struct cli_txt_entry *e;
count = ARRAY_SIZE(wpa_cli_commands);
count += dl_list_len(&p2p_groups);
count += dl_list_len(&ifnames);
res = os_calloc(count + 1, sizeof(char *));
if (res == NULL)
return NULL;
for (i = 0; wpa_cli_commands[i].cmd; i++) {
res[i] = os_strdup(wpa_cli_commands[i].cmd);
if (res[i] == NULL)
break;
}
dl_list_for_each(e, &p2p_groups, struct cli_txt_entry, list) {
size_t len = 8 + os_strlen(e->txt);
res[i] = os_malloc(len);
if (res[i] == NULL)
break;
os_snprintf(res[i], len, "ifname=%s", e->txt);
i++;
}
dl_list_for_each(e, &ifnames, struct cli_txt_entry, list) {
res[i] = os_strdup(e->txt);
if (res[i] == NULL)
break;
i++;
}
return res;
}
static char ** wpa_cli_cmd_completion(const char *cmd, const char *str,
int pos)
{
int i;
for (i = 0; wpa_cli_commands[i].cmd; i++) {
if (os_strcasecmp(wpa_cli_commands[i].cmd, cmd) == 0) {
if (wpa_cli_commands[i].completion)
return wpa_cli_commands[i].completion(str,
pos);
edit_clear_line();
printf("\r%s\n", wpa_cli_commands[i].usage);
edit_redraw();
break;
}
}
return NULL;
}
static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos)
{
char **res;
const char *end;
char *cmd;
if (pos > 7 && os_strncasecmp(str, "IFNAME=", 7) == 0) {
end = os_strchr(str, ' ');
if (end && pos > end - str) {
pos -= end - str + 1;
str = end + 1;
}
}
end = os_strchr(str, ' ');
if (end == NULL || str + pos < end)
return wpa_list_cmd_list();
cmd = os_malloc(pos + 1);
if (cmd == NULL)
return NULL;
os_memcpy(cmd, str, pos);
cmd[end - str] = '\0';
res = wpa_cli_cmd_completion(cmd, str, pos);
os_free(cmd);
return res;
}
static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
{
const struct wpa_cli_cmd *cmd, *match = NULL;
int count;
int ret = 0;
if (argc > 1 && os_strncasecmp(argv[0], "IFNAME=", 7) == 0) {
ifname_prefix = argv[0] + 7;
argv = &argv[1];
argc--;
} else
ifname_prefix = NULL;
if (argc == 0)
return -1;
count = 0;
cmd = wpa_cli_commands;
while (cmd->cmd) {
if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
{
match = cmd;
if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
/* we have an exact match */
count = 1;
break;
}
count++;
}
cmd++;
}
if (count > 1) {
printf("Ambiguous command '%s'; possible commands:", argv[0]);
cmd = wpa_cli_commands;
while (cmd->cmd) {
if (os_strncasecmp(cmd->cmd, argv[0],
os_strlen(argv[0])) == 0) {
printf(" %s", cmd->cmd);
}
cmd++;
}
printf("\n");
ret = 1;
} else if (count == 0) {
printf("Unknown command '%s'\n", argv[0]);
ret = 1;
} else {
ret = match->handler(ctrl, argc - 1, &argv[1]);
}
return ret;
}
static int wpa_cli_exec(const char *program, const char *arg1,
const char *arg2)
{
char *arg;
size_t len;
int res;
/* If no interface is specified, set the global */
if (!arg1)
arg1 = "global";
len = os_strlen(arg1) + os_strlen(arg2) + 2;
arg = os_malloc(len);
if (arg == NULL)
return -1;
os_snprintf(arg, len, "%s %s", arg1, arg2);
res = os_exec(program, arg, 1);
os_free(arg);
return res;
}
static void wpa_cli_action_process(const char *msg)
{
const char *pos;
char *copy = NULL, *id, *pos2;
const char *ifname = ctrl_ifname;
char ifname_buf[100];
if (eloop_terminated())
return;
pos = msg;
if (os_strncmp(pos, "IFNAME=", 7) == 0) {
const char *end;
end = os_strchr(pos + 7, ' ');
if (end && (unsigned int) (end - pos) < sizeof(ifname_buf)) {
pos += 7;
os_memcpy(ifname_buf, pos, end - pos);
ifname_buf[end - pos] = '\0';
ifname = ifname_buf;
pos = end + 1;
}
}
if (*pos == '<') {
const char *prev = pos;
/* skip priority */
pos = os_strchr(pos, '>');
if (pos)
pos++;
else
pos = prev;
}
if (str_starts(pos, WPA_EVENT_CONNECTED)) {
int new_id = -1;
os_unsetenv("WPA_ID");
os_unsetenv("WPA_ID_STR");
os_unsetenv("WPA_CTRL_DIR");
pos = os_strstr(pos, "[id=");
if (pos)
copy = os_strdup(pos + 4);
if (copy) {
pos2 = id = copy;
while (*pos2 && *pos2 != ' ')
pos2++;
*pos2++ = '\0';
new_id = atoi(id);
os_setenv("WPA_ID", id, 1);
while (*pos2 && *pos2 != '=')
pos2++;
if (*pos2 == '=')
pos2++;
id = pos2;
while (*pos2 && *pos2 != ']')
pos2++;
*pos2 = '\0';
os_setenv("WPA_ID_STR", id, 1);
os_free(copy);
}
os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1);
if (wpa_cli_connected <= 0 || new_id != wpa_cli_last_id) {
wpa_cli_connected = 1;
wpa_cli_last_id = new_id;
wpa_cli_exec(action_file, ifname, "CONNECTED");
}
} else if (str_starts(pos, WPA_EVENT_DISCONNECTED)) {
if (wpa_cli_connected) {
wpa_cli_connected = 0;
wpa_cli_exec(action_file, ifname, "DISCONNECTED");
}
} else if (str_starts(pos, WPA_EVENT_TEMP_DISABLED)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPA_EVENT_CHANNEL_SWITCH_STARTED)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_starts(pos, AP_EVENT_ENABLED)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_starts(pos, AP_EVENT_DISABLED)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_starts(pos, MESH_GROUP_STARTED)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_starts(pos, MESH_GROUP_REMOVED)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_starts(pos, MESH_PEER_CONNECTED)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_starts(pos, MESH_PEER_DISCONNECTED)) {
wpa_cli_exec(action_file, ctrl_ifname, pos);
} else if (str_starts(pos, P2P_EVENT_GROUP_STARTED)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, P2P_EVENT_GROUP_REMOVED)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, P2P_EVENT_GO_NEG_FAILURE)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPS_EVENT_SUCCESS)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPS_EVENT_ACTIVE)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPS_EVENT_OVERLAP)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPS_EVENT_PIN_ACTIVE)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPS_EVENT_CANCEL)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPS_EVENT_TIMEOUT)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPS_EVENT_FAIL)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, AP_STA_CONNECTED)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, AP_STA_DISCONNECTED)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, ESS_DISASSOC_IMMINENT)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, HS20_SUBSCRIPTION_REMEDIATION)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, HS20_DEAUTH_IMMINENT_NOTICE)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, HS20_T_C_ACCEPTANCE)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, DPP_EVENT_CONF_RECEIVED)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, DPP_EVENT_CONFOBJ_AKM)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, DPP_EVENT_CONFOBJ_SSID)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, DPP_EVENT_CONNECTOR)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, DPP_EVENT_CONFOBJ_PASS)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, DPP_EVENT_CONFOBJ_PSK)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, DPP_EVENT_C_SIGN_KEY)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, DPP_EVENT_NET_ACCESS_KEY)) {
wpa_cli_exec(action_file, ifname, pos);
} else if (str_starts(pos, WPA_EVENT_TERMINATING)) {
printf("wpa_supplicant is terminating - stop monitoring\n");
if (!reconnect)
wpa_cli_quit = 1;
}
}
#ifndef CONFIG_ANSI_C_EXTRA
static void wpa_cli_action_cb(char *msg, size_t len)
{
wpa_cli_action_process(msg);
}
#endif /* CONFIG_ANSI_C_EXTRA */
static int wpa_cli_open_global_ctrl(void)
{
#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
ctrl_conn = wpa_ctrl_open(NULL);
#else /* CONFIG_CTRL_IFACE_NAMED_PIPE */
ctrl_conn = wpa_ctrl_open(global);
#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
if (!ctrl_conn) {
fprintf(stderr,
"Failed to connect to wpa_supplicant global interface: %s error: %s\n",
global, strerror(errno));
return -1;
}
if (interactive) {
update_ifnames(ctrl_conn);
mon_conn = wpa_ctrl_open(global);
if (mon_conn) {
if (wpa_ctrl_attach(mon_conn) == 0) {
wpa_cli_attached = 1;
eloop_register_read_sock(
wpa_ctrl_get_fd(mon_conn),
wpa_cli_mon_receive,
NULL, NULL);
} else {
printf("Failed to open monitor connection through global control interface\n");
}
}
update_stations(ctrl_conn);
}
return 0;
}
static void wpa_cli_reconnect(void)
{
wpa_cli_close_connection();
if ((global && wpa_cli_open_global_ctrl() < 0) ||
(!global && wpa_cli_open_connection(ctrl_ifname, 1) < 0))
return;
if (interactive) {
edit_clear_line();
printf("\rConnection to wpa_supplicant re-established\n");
edit_redraw();
update_stations(ctrl_conn);
}
}
static void cli_event(const char *str)
{
const char *start, *s;
start = os_strchr(str, '>');
if (start == NULL)
return;
start++;
if (str_starts(start, WPA_EVENT_BSS_ADDED)) {
s = os_strchr(start, ' ');
if (s == NULL)
return;
s = os_strchr(s + 1, ' ');
if (s == NULL)
return;
cli_txt_list_add(&bsses, s + 1);
return;
}
if (str_starts(start, WPA_EVENT_BSS_REMOVED)) {
s = os_strchr(start, ' ');
if (s == NULL)
return;
s = os_strchr(s + 1, ' ');
if (s == NULL)
return;
cli_txt_list_del_addr(&bsses, s + 1);
return;
}
#ifdef CONFIG_P2P
if (str_starts(start, P2P_EVENT_DEVICE_FOUND)) {
s = os_strstr(start, " p2p_dev_addr=");
if (s == NULL)
return;
cli_txt_list_add_addr(&p2p_peers, s + 14);
return;
}
if (str_starts(start, P2P_EVENT_DEVICE_LOST)) {
s = os_strstr(start, " p2p_dev_addr=");
if (s == NULL)
return;
cli_txt_list_del_addr(&p2p_peers, s + 14);
return;
}
if (str_starts(start, P2P_EVENT_GROUP_STARTED)) {
s = os_strchr(start, ' ');
if (s == NULL)
return;
cli_txt_list_add_word(&p2p_groups, s + 1, ' ');
return;
}
if (str_starts(start, P2P_EVENT_GROUP_REMOVED)) {
s = os_strchr(start, ' ');
if (s == NULL)
return;
cli_txt_list_del_word(&p2p_groups, s + 1, ' ');
return;
}
#endif /* CONFIG_P2P */
}
static int check_terminating(const char *msg)
{
const char *pos = msg;
if (*pos == '<') {
/* skip priority */
pos = os_strchr(pos, '>');
if (pos)
pos++;
else
pos = msg;
}
if (str_starts(pos, WPA_EVENT_TERMINATING) && ctrl_conn) {
edit_clear_line();
printf("\rConnection to wpa_supplicant lost - trying to "
"reconnect\n");
edit_redraw();
wpa_cli_attached = 0;
wpa_cli_close_connection();
return 1;
}
return 0;
}
static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
{
if (ctrl_conn == NULL) {
wpa_cli_reconnect();
return;
}
while (wpa_ctrl_pending(ctrl) > 0) {
char buf[4096];
size_t len = sizeof(buf) - 1;
if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
buf[len] = '\0';
if (action_monitor)
wpa_cli_action_process(buf);
else {
cli_event(buf);
if (wpa_cli_show_event(buf)) {
edit_clear_line();
printf("\r%s\n", buf);
edit_redraw();
}
if (interactive && check_terminating(buf) > 0)
return;
}
} else {
printf("Could not read pending message.\n");
break;
}
}
if (wpa_ctrl_pending(ctrl) < 0) {
printf("Connection to wpa_supplicant lost - trying to "
"reconnect\n");
if (reconnect) {
eloop_terminate();
return;
}
wpa_cli_reconnect();
}
}
static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx)
{
if (ctrl_conn) {
int res;
char *prefix = ifname_prefix;
ifname_prefix = NULL;
res = _wpa_ctrl_command(ctrl_conn, "PING", 0);
ifname_prefix = prefix;
if (res) {
printf("Connection to wpa_supplicant lost - trying to "
"reconnect\n");
wpa_cli_close_connection();
}
}
if (!ctrl_conn)
wpa_cli_reconnect();
eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
}
static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
wpa_cli_recv_pending(mon_conn, 0);
}
static void wpa_cli_edit_cmd_cb(void *ctx, char *cmd)
{
char *argv[max_args];
int argc;
argc = tokenize_cmd(cmd, argv);
if (argc)
wpa_request(ctrl_conn, argc, argv);
}
static void wpa_cli_edit_eof_cb(void *ctx)
{
eloop_terminate();
}
static int warning_displayed = 0;
static char *hfile = NULL;
static int edit_started = 0;
static void start_edit(void)
{
char *home;
char *ps = NULL;
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
ps = wpa_ctrl_get_remote_ifname(ctrl_conn);
#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
#ifdef CONFIG_WPA_CLI_HISTORY_DIR
home = CONFIG_WPA_CLI_HISTORY_DIR;
#else /* CONFIG_WPA_CLI_HISTORY_DIR */
home = getenv("HOME");
#endif /* CONFIG_WPA_CLI_HISTORY_DIR */
if (home) {
const char *fname = ".wpa_cli_history";
int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
hfile = os_malloc(hfile_len);
if (hfile)
os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
}
if (edit_init(wpa_cli_edit_cmd_cb, wpa_cli_edit_eof_cb,
wpa_cli_edit_completion_cb, NULL, hfile, ps) < 0) {
eloop_terminate();
return;
}
edit_started = 1;
eloop_register_timeout(ping_interval, 0, wpa_cli_ping, NULL, NULL);
}
static void update_bssid_list(struct wpa_ctrl *ctrl)
{
char buf[4096];
size_t len = sizeof(buf);
int ret;
const char *cmd = "BSS RANGE=ALL MASK=0x2";
char *pos, *end;
if (ctrl == NULL)
return;
ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
if (ret < 0)
return;
buf[len] = '\0';
pos = buf;
while (pos) {
pos = os_strstr(pos, "bssid=");
if (pos == NULL)
break;
pos += 6;
end = os_strchr(pos, '\n');
if (end == NULL)
break;
*end = '\0';
cli_txt_list_add(&bsses, pos);
pos = end + 1;
}
}
static void update_ifnames(struct wpa_ctrl *ctrl)
{
char buf[4096];
size_t len = sizeof(buf);
int ret;
const char *cmd = "INTERFACES";
char *pos, *end;
char txt[200];
cli_txt_list_flush(&ifnames);
if (ctrl == NULL)
return;
ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
if (ret < 0)
return;
buf[len] = '\0';
pos = buf;
while (pos) {
end = os_strchr(pos, '\n');
if (end == NULL)
break;
*end = '\0';
ret = os_snprintf(txt, sizeof(txt), "ifname=%s", pos);
if (!os_snprintf_error(sizeof(txt), ret))
cli_txt_list_add(&ifnames, txt);
pos = end + 1;
}
}
static void update_creds(struct wpa_ctrl *ctrl)
{
char buf[4096];
size_t len = sizeof(buf);
int ret;
const char *cmd = "LIST_CREDS";
char *pos, *end;
int header = 1;
cli_txt_list_flush(&creds);
if (ctrl == NULL)
return;
ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
if (ret < 0)
return;
buf[len] = '\0';
pos = buf;
while (pos) {
end = os_strchr(pos, '\n');
if (end == NULL)
break;
*end = '\0';
if (!header)
cli_txt_list_add_word(&creds, pos, '\t');
header = 0;
pos = end + 1;
}
}
static void update_networks(struct wpa_ctrl *ctrl)
{
char buf[4096];
size_t len = sizeof(buf);
int ret;
const char *cmd = "LIST_NETWORKS";
char *pos, *end;
int header = 1;
cli_txt_list_flush(&networks);
if (ctrl == NULL)
return;
ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
if (ret < 0)
return;
buf[len] = '\0';
pos = buf;
while (pos) {
end = os_strchr(pos, '\n');
if (end == NULL)
break;
*end = '\0';
if (!header)
cli_txt_list_add_word(&networks, pos, '\t');
header = 0;
pos = end + 1;
}
}
static void update_stations(struct wpa_ctrl *ctrl)
{
#ifdef CONFIG_AP
char addr[32], cmd[64];
if (!ctrl || !interactive)
return;
cli_txt_list_flush(&stations);
if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
return;
do {
if (os_strcmp(addr, "") != 0)
cli_txt_list_add(&stations, addr);
os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
#endif /* CONFIG_AP */
}
static void try_connection(void *eloop_ctx, void *timeout_ctx)
{
if (ctrl_conn)
goto done;
if (ctrl_ifname == NULL)
ctrl_ifname = wpa_cli_get_default_ifname();
if (wpa_cli_open_connection(ctrl_ifname, 1)) {
if (!warning_displayed) {
printf("Could not connect to wpa_supplicant: "
"%s - re-trying\n",
ctrl_ifname ? ctrl_ifname : "(nil)");
warning_displayed = 1;
}
eloop_register_timeout(1, 0, try_connection, NULL, NULL);
return;
}
update_bssid_list(ctrl_conn);
update_creds(ctrl_conn);
update_networks(ctrl_conn);
update_stations(ctrl_conn);
if (warning_displayed)
printf("Connection established.\n");
done:
start_edit();
}
static void wpa_cli_interactive(void)
{
printf("\nInteractive mode\n\n");
eloop_register_timeout(0, 0, try_connection, NULL, NULL);
eloop_run();
eloop_cancel_timeout(try_connection, NULL, NULL);
cli_txt_list_flush(&p2p_peers);
cli_txt_list_flush(&p2p_groups);
cli_txt_list_flush(&bsses);
cli_txt_list_flush(&ifnames);
cli_txt_list_flush(&creds);
cli_txt_list_flush(&networks);
if (edit_started)
edit_deinit(hfile, wpa_cli_edit_filter_history_cb);
os_free(hfile);
eloop_cancel_timeout(wpa_cli_ping, NULL, NULL);
wpa_cli_close_connection();
}
static void wpa_cli_action_ping(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_ctrl *ctrl = eloop_ctx;
char buf[256];
size_t len;
/* verify that connection is still working */
len = sizeof(buf) - 1;
if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
wpa_cli_action_cb) < 0 ||
len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
printf("wpa_supplicant did not reply to PING command - exiting\n");
eloop_terminate();
return;
}
eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping,
ctrl, NULL);
}
static void wpa_cli_action_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
struct wpa_ctrl *ctrl = eloop_ctx;
wpa_cli_recv_pending(ctrl, 1);
}
static void wpa_cli_action(struct wpa_ctrl *ctrl)
{
#ifdef CONFIG_ANSI_C_EXTRA
/* TODO: ANSI C version(?) */
printf("Action processing not supported in ANSI C build.\n");
#else /* CONFIG_ANSI_C_EXTRA */
int fd;
fd = wpa_ctrl_get_fd(ctrl);
eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping,
ctrl, NULL);
eloop_register_read_sock(fd, wpa_cli_action_receive, ctrl, NULL);
eloop_run();
eloop_cancel_timeout(wpa_cli_action_ping, ctrl, NULL);
eloop_unregister_read_sock(fd);
#endif /* CONFIG_ANSI_C_EXTRA */
}
static void wpa_cli_cleanup(void)
{
wpa_cli_close_connection();
if (pid_file)
os_daemonize_terminate(pid_file);
os_program_deinit();
}
static void wpa_cli_terminate(int sig, void *ctx)
{
eloop_terminate();
if (reconnect)
wpa_cli_quit = 1;
}
static char * wpa_cli_get_default_ifname(void)
{
char *ifname = NULL;
#ifdef ANDROID
char ifprop[PROPERTY_VALUE_MAX];
if (property_get("wifi.interface", ifprop, NULL) != 0) {
ifname = os_strdup(ifprop);
printf("Using interface '%s'\n", ifname ? ifname : "N/A");
}
#else /* ANDROID */
#ifdef CONFIG_CTRL_IFACE_UNIX
struct dirent *dent;
DIR *dir = opendir(ctrl_iface_dir);
if (!dir) {
return NULL;
}
while ((dent = readdir(dir))) {
#ifdef _DIRENT_HAVE_D_TYPE
/*
* Skip the file if it is not a socket. Also accept
* DT_UNKNOWN (0) in case the C library or underlying
* file system does not support d_type.
*/
if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
continue;
#endif /* _DIRENT_HAVE_D_TYPE */
/* Skip current/previous directory and special P2P Device
* interfaces. */
if (os_strcmp(dent->d_name, ".") == 0 ||
os_strcmp(dent->d_name, "..") == 0 ||
os_strncmp(dent->d_name, "p2p-dev-", 8) == 0)
continue;
printf("Selected interface '%s'\n", dent->d_name);
ifname = os_strdup(dent->d_name);
break;
}
closedir(dir);
#endif /* CONFIG_CTRL_IFACE_UNIX */
#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
char buf[4096], *pos;
size_t len;
struct wpa_ctrl *ctrl;
int ret;
ctrl = wpa_ctrl_open(NULL);
if (ctrl == NULL)
return NULL;
len = sizeof(buf) - 1;
ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL);
if (ret >= 0) {
buf[len] = '\0';
pos = os_strchr(buf, '\n');
if (pos)
*pos = '\0';
ifname = os_strdup(buf);
}
wpa_ctrl_close(ctrl);
#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
#endif /* ANDROID */
return ifname;
}
int main(int argc, char *argv[])
{
int c;
int daemonize = 0;
int ret = 0;
if (os_program_init())
return -1;
for (;;) {
c = getopt(argc, argv, "a:Bg:G:hi:p:P:rs:v");
if (c < 0)
break;
switch (c) {
case 'a':
action_file = optarg;
break;
case 'B':
daemonize = 1;
break;
case 'g':
global = optarg;
break;
case 'G':
ping_interval = atoi(optarg);
break;
case 'h':
usage();
return 0;
case 'v':
printf("%s\n", wpa_cli_version);
return 0;
case 'i':
os_free(ctrl_ifname);
ctrl_ifname = os_strdup(optarg);
break;
case 'p':
ctrl_iface_dir = optarg;
break;
case 'P':
pid_file = optarg;
break;
case 'r':
reconnect = 1;
break;
case 's':
client_socket_dir = optarg;
break;
default:
usage();
return -1;
}
}
interactive = (argc == optind) && (action_file == NULL);
if (interactive)
printf("%s\n\n%s\n\n", wpa_cli_version, cli_license);
if (eloop_init())
return -1;
if (global && wpa_cli_open_global_ctrl() < 0)
return -1;
eloop_register_signal_terminate(wpa_cli_terminate, NULL);
if (ctrl_ifname == NULL)
ctrl_ifname = wpa_cli_get_default_ifname();
if (reconnect && action_file && ctrl_ifname) {
while (!wpa_cli_quit) {
if (ctrl_conn)
wpa_cli_action(ctrl_conn);
else
os_sleep(1, 0);
wpa_cli_close_connection();
wpa_cli_open_connection(ctrl_ifname, 0);
if (ctrl_conn) {
if (wpa_ctrl_attach(ctrl_conn) != 0)
wpa_cli_close_connection();
else
wpa_cli_attached = 1;
}
}
} else if (interactive) {
wpa_cli_interactive();
} else {
if (!global &&
wpa_cli_open_connection(ctrl_ifname, 0) < 0) {
fprintf(stderr, "Failed to connect to non-global "
"ctrl_ifname: %s error: %s\n",
ctrl_ifname ? ctrl_ifname : "(nil)",
strerror(errno));
return -1;
}
if (action_file) {
if (wpa_ctrl_attach(ctrl_conn) == 0) {
wpa_cli_attached = 1;
} else {
printf("Warning: Failed to attach to "
"wpa_supplicant.\n");
return -1;
}
}
if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
return -1;
if (action_file)
wpa_cli_action(ctrl_conn);
else
ret = wpa_request(ctrl_conn, argc - optind,
&argv[optind]);
}
os_free(ctrl_ifname);
eloop_destroy();
wpa_cli_cleanup();
return ret;
}
#else /* CONFIG_CTRL_IFACE */
int main(int argc, char *argv[])
{
printf("CONFIG_CTRL_IFACE not defined - wpa_cli disabled\n");
return -1;
}
#endif /* CONFIG_CTRL_IFACE */
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c
index 47b542e91dd7..71bf95f951a1 100644
--- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c
@@ -1,8425 +1,8575 @@
/*
* WPA Supplicant
* Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*
* This file implements functions for registering and unregistering
* %wpa_supplicant interfaces. In addition, this file contains number of
* functions for managing network connections.
*/
#include "includes.h"
#ifdef CONFIG_MATCH_IFACE
#include <net/if.h>
#include <fnmatch.h>
#endif /* CONFIG_MATCH_IFACE */
#include "common.h"
#include "crypto/random.h"
#include "crypto/sha1.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eap_peer/eap.h"
#include "eap_peer/eap_proxy.h"
#include "eap_server/eap_methods.h"
#include "rsn_supp/wpa.h"
#include "eloop.h"
#include "config.h"
#include "utils/ext_password.h"
#include "l2_packet/l2_packet.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "ctrl_iface.h"
#include "pcsc_funcs.h"
#include "common/version.h"
#include "rsn_supp/preauth.h"
#include "rsn_supp/pmksa_cache.h"
#include "common/wpa_ctrl.h"
#include "common/ieee802_11_common.h"
#include "common/ieee802_11_defs.h"
#include "common/hw_features_common.h"
#include "common/gas_server.h"
#include "common/dpp.h"
#include "common/ptksa_cache.h"
#include "p2p/p2p.h"
#include "fst/fst.h"
#include "bssid_ignore.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
#include "ibss_rsn.h"
#include "sme.h"
#include "gas_query.h"
#include "ap.h"
#include "p2p_supplicant.h"
#include "wifi_display.h"
#include "notify.h"
#include "bgscan.h"
#include "autoscan.h"
#include "bss.h"
#include "scan.h"
#include "offchannel.h"
#include "hs20_supplicant.h"
#include "wnm_sta.h"
#include "wpas_kay.h"
#include "mesh.h"
#include "dpp_supplicant.h"
#ifdef CONFIG_MESH
#include "ap/ap_config.h"
#include "ap/hostapd.h"
#endif /* CONFIG_MESH */
const char *const wpa_supplicant_version =
"wpa_supplicant v" VERSION_STR "\n"
"Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi> and contributors";
const char *const wpa_supplicant_license =
"This software may be distributed under the terms of the BSD license.\n"
"See README for more details.\n"
#ifdef EAP_TLS_OPENSSL
"\nThis product includes software developed by the OpenSSL Project\n"
"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n"
#endif /* EAP_TLS_OPENSSL */
;
#ifndef CONFIG_NO_STDOUT_DEBUG
/* Long text divided into parts in order to fit in C89 strings size limits. */
const char *const wpa_supplicant_full_license1 =
"";
const char *const wpa_supplicant_full_license2 =
"This software may be distributed under the terms of the BSD license.\n"
"\n"
"Redistribution and use in source and binary forms, with or without\n"
"modification, are permitted provided that the following conditions are\n"
"met:\n"
"\n";
const char *const wpa_supplicant_full_license3 =
"1. Redistributions of source code must retain the above copyright\n"
" notice, this list of conditions and the following disclaimer.\n"
"\n"
"2. Redistributions in binary form must reproduce the above copyright\n"
" notice, this list of conditions and the following disclaimer in the\n"
" documentation and/or other materials provided with the distribution.\n"
"\n";
const char *const wpa_supplicant_full_license4 =
"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
" names of its contributors may be used to endorse or promote products\n"
" derived from this software without specific prior written permission.\n"
"\n"
"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n";
const char *const wpa_supplicant_full_license5 =
"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
"\n";
#endif /* CONFIG_NO_STDOUT_DEBUG */
static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx);
#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s);
#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
#ifdef CONFIG_OWE
static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s);
#endif /* CONFIG_OWE */
#ifdef CONFIG_WEP
/* Configure default/group WEP keys for static WEP */
int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
int i, set = 0;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i] == 0)
continue;
set = 1;
wpa_drv_set_key(wpa_s, WPA_ALG_WEP, NULL,
i, i == ssid->wep_tx_keyidx, NULL, 0,
ssid->wep_key[i], ssid->wep_key_len[i],
i == ssid->wep_tx_keyidx ?
KEY_FLAG_GROUP_RX_TX_DEFAULT :
KEY_FLAG_GROUP_RX_TX);
}
return set;
}
#endif /* CONFIG_WEP */
int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
u8 key[32];
size_t keylen;
enum wpa_alg alg;
u8 seq[6] = { 0 };
int ret;
/* IBSS/WPA-None uses only one key (Group) for both receiving and
* sending unicast and multicast packets. */
if (ssid->mode != WPAS_MODE_IBSS) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid mode %d (not "
"IBSS/ad-hoc) for WPA-None", ssid->mode);
return -1;
}
if (!ssid->psk_set) {
wpa_msg(wpa_s, MSG_INFO, "WPA: No PSK configured for "
"WPA-None");
return -1;
}
switch (wpa_s->group_cipher) {
case WPA_CIPHER_CCMP:
os_memcpy(key, ssid->psk, 16);
keylen = 16;
alg = WPA_ALG_CCMP;
break;
case WPA_CIPHER_GCMP:
os_memcpy(key, ssid->psk, 16);
keylen = 16;
alg = WPA_ALG_GCMP;
break;
case WPA_CIPHER_TKIP:
/* WPA-None uses the same Michael MIC key for both TX and RX */
os_memcpy(key, ssid->psk, 16 + 8);
os_memcpy(key + 16 + 8, ssid->psk + 16, 8);
keylen = 32;
alg = WPA_ALG_TKIP;
break;
default:
wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid group cipher %d for "
"WPA-None", wpa_s->group_cipher);
return -1;
}
/* TODO: should actually remember the previously used seq#, both for TX
* and RX from each STA.. */
ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen,
KEY_FLAG_GROUP_RX_TX_DEFAULT);
os_memset(key, 0, sizeof(key));
return ret;
}
static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
const u8 *bssid = wpa_s->bssid;
if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
(wpa_s->wpa_state == WPA_AUTHENTICATING ||
wpa_s->wpa_state == WPA_ASSOCIATING))
bssid = wpa_s->pending_bssid;
wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
MAC2STR(bssid));
wpa_bssid_ignore_add(wpa_s, bssid);
wpa_sm_notify_disassoc(wpa_s->wpa);
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
wpa_s->reassociate = 1;
/*
* If we timed out, the AP or the local radio may be busy.
* So, wait a second until scanning again.
*/
wpa_supplicant_req_scan(wpa_s, 1, 0);
}
/**
* wpa_supplicant_req_auth_timeout - Schedule a timeout for authentication
* @wpa_s: Pointer to wpa_supplicant data
* @sec: Number of seconds after which to time out authentication
* @usec: Number of microseconds after which to time out authentication
*
* This function is used to schedule a timeout for the current authentication
* attempt.
*/
void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
int sec, int usec)
{
if (wpa_s->conf->ap_scan == 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
return;
wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
"%d usec", sec, usec);
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
wpa_s->last_auth_timeout_sec = sec;
eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL);
}
/*
* wpas_auth_timeout_restart - Restart and change timeout for authentication
* @wpa_s: Pointer to wpa_supplicant data
* @sec_diff: difference in seconds applied to original timeout value
*/
void wpas_auth_timeout_restart(struct wpa_supplicant *wpa_s, int sec_diff)
{
int new_sec = wpa_s->last_auth_timeout_sec + sec_diff;
if (eloop_is_timeout_registered(wpa_supplicant_timeout, wpa_s, NULL)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Authentication timeout restart: %d sec", new_sec);
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
eloop_register_timeout(new_sec, 0, wpa_supplicant_timeout,
wpa_s, NULL);
}
}
/**
* wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout
* @wpa_s: Pointer to wpa_supplicant data
*
* This function is used to cancel authentication timeout scheduled with
* wpa_supplicant_req_auth_timeout() and it is called when authentication has
* been completed.
*/
void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
{
wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
wpa_bssid_ignore_del(wpa_s, wpa_s->bssid);
os_free(wpa_s->last_con_fail_realm);
wpa_s->last_con_fail_realm = NULL;
wpa_s->last_con_fail_realm_len = 0;
}
/**
* wpa_supplicant_initiate_eapol - Configure EAPOL state machine
* @wpa_s: Pointer to wpa_supplicant data
*
* This function is used to configure EAPOL state machine based on the selected
* authentication mode.
*/
void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
{
#ifdef IEEE8021X_EAPOL
struct eapol_config eapol_conf;
struct wpa_ssid *ssid = wpa_s->current_ssid;
#ifdef CONFIG_IBSS_RSN
if (ssid->mode == WPAS_MODE_IBSS &&
wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
/*
* RSN IBSS authentication is per-STA and we can disable the
* per-BSSID EAPOL authentication.
*/
eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
eapol_sm_notify_eap_success(wpa_s->eapol, true);
eapol_sm_notify_eap_fail(wpa_s->eapol, false);
return;
}
#endif /* CONFIG_IBSS_RSN */
eapol_sm_notify_eap_success(wpa_s->eapol, false);
eapol_sm_notify_eap_fail(wpa_s->eapol, false);
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
else
eapol_sm_notify_portControl(wpa_s->eapol, Auto);
os_memset(&eapol_conf, 0, sizeof(eapol_conf));
if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
eapol_conf.accept_802_1x_keys = 1;
eapol_conf.required_keys = 0;
if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) {
eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST;
}
if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) {
eapol_conf.required_keys |=
EAPOL_REQUIRE_KEY_BROADCAST;
}
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)
eapol_conf.required_keys = 0;
}
eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
eapol_conf.workaround = ssid->eap_workaround;
eapol_conf.eap_disabled =
!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
wpa_s->key_mgmt != WPA_KEY_MGMT_WPS;
eapol_conf.external_sim = wpa_s->conf->external_sim;
#ifdef CONFIG_WPS
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
eapol_conf.wps |= EAPOL_LOCAL_WPS_IN_USE;
if (wpa_s->current_bss) {
struct wpabuf *ie;
ie = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
WPS_IE_VENDOR_TYPE);
if (ie) {
if (wps_is_20(ie))
eapol_conf.wps |=
EAPOL_PEER_IS_WPS20_AP;
wpabuf_free(ie);
}
}
}
#endif /* CONFIG_WPS */
eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
#ifdef CONFIG_MACSEC
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE && ssid->mka_psk_set)
ieee802_1x_create_preshared_mka(wpa_s, ssid);
else
ieee802_1x_alloc_kay_sm(wpa_s, ssid);
#endif /* CONFIG_MACSEC */
#endif /* IEEE8021X_EAPOL */
}
/**
* wpa_supplicant_set_non_wpa_policy - Set WPA parameters to non-WPA mode
* @wpa_s: Pointer to wpa_supplicant data
* @ssid: Configuration data for the network
*
* This function is used to configure WPA state machine and related parameters
* to a mode where WPA is not enabled. This is called as part of the
* authentication configuration when the selected network does not use WPA.
*/
void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
#ifdef CONFIG_WEP
int i;
#endif /* CONFIG_WEP */
if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
wpa_s->key_mgmt = WPA_KEY_MGMT_WPS;
else if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
else
wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
wpa_sm_set_ap_rsnxe(wpa_s->wpa, NULL, 0);
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
wpa_s->rsnxe_len = 0;
wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
wpa_s->group_cipher = WPA_CIPHER_NONE;
wpa_s->mgmt_group_cipher = 0;
#ifdef CONFIG_WEP
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i] > 5) {
wpa_s->pairwise_cipher = WPA_CIPHER_WEP104;
wpa_s->group_cipher = WPA_CIPHER_WEP104;
break;
} else if (ssid->wep_key_len[i] > 0) {
wpa_s->pairwise_cipher = WPA_CIPHER_WEP40;
wpa_s->group_cipher = WPA_CIPHER_WEP40;
break;
}
}
#endif /* CONFIG_WEP */
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, 0);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
wpa_s->pairwise_cipher);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
wpa_s->mgmt_group_cipher);
pmksa_cache_clear_current(wpa_s->wpa);
}
void free_hw_features(struct wpa_supplicant *wpa_s)
{
int i;
if (wpa_s->hw.modes == NULL)
return;
for (i = 0; i < wpa_s->hw.num_modes; i++) {
os_free(wpa_s->hw.modes[i].channels);
os_free(wpa_s->hw.modes[i].rates);
}
os_free(wpa_s->hw.modes);
wpa_s->hw.modes = NULL;
}
static void remove_bss_tmp_disallowed_entry(struct wpa_supplicant *wpa_s,
struct wpa_bss_tmp_disallowed *bss)
{
eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss);
dl_list_del(&bss->list);
os_free(bss);
}
void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s)
{
struct wpa_bss_tmp_disallowed *bss, *prev;
dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list)
remove_bss_tmp_disallowed_entry(wpa_s, bss);
}
void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s)
{
struct fils_hlp_req *req;
while ((req = dl_list_first(&wpa_s->fils_hlp_req, struct fils_hlp_req,
list)) != NULL) {
dl_list_del(&req->list);
wpabuf_free(req->pkt);
os_free(req);
}
}
void wpas_clear_disabled_interface(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
return;
wpa_dbg(wpa_s, MSG_DEBUG, "Clear cached state on disabled interface");
wpa_bss_flush(wpa_s);
}
#ifdef CONFIG_TESTING_OPTIONS
void wpas_clear_driver_signal_override(struct wpa_supplicant *wpa_s)
{
struct driver_signal_override *dso;
while ((dso = dl_list_first(&wpa_s->drv_signal_override,
struct driver_signal_override, list))) {
dl_list_del(&dso->list);
os_free(dso);
}
}
#endif /* CONFIG_TESTING_OPTIONS */
static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
{
int i;
bgscan_deinit(wpa_s);
autoscan_deinit(wpa_s);
scard_deinit(wpa_s->scard);
wpa_s->scard = NULL;
wpa_sm_set_scard_ctx(wpa_s->wpa, NULL);
eapol_sm_register_scard_ctx(wpa_s->eapol, NULL);
l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = NULL;
if (wpa_s->l2_br) {
l2_packet_deinit(wpa_s->l2_br);
wpa_s->l2_br = NULL;
}
#ifdef CONFIG_TESTING_OPTIONS
l2_packet_deinit(wpa_s->l2_test);
wpa_s->l2_test = NULL;
os_free(wpa_s->get_pref_freq_list_override);
wpa_s->get_pref_freq_list_override = NULL;
wpabuf_free(wpa_s->last_assoc_req_wpa_ie);
wpa_s->last_assoc_req_wpa_ie = 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);
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->conf != NULL) {
struct wpa_ssid *ssid;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
wpas_notify_network_removed(wpa_s, ssid);
}
os_free(wpa_s->confname);
wpa_s->confname = NULL;
os_free(wpa_s->confanother);
wpa_s->confanother = NULL;
os_free(wpa_s->last_con_fail_realm);
wpa_s->last_con_fail_realm = NULL;
wpa_s->last_con_fail_realm_len = 0;
wpa_sm_set_eapol(wpa_s->wpa, NULL);
eapol_sm_deinit(wpa_s->eapol);
wpa_s->eapol = NULL;
rsn_preauth_deinit(wpa_s->wpa);
#ifdef CONFIG_TDLS
wpa_tdls_deinit(wpa_s->wpa);
#endif /* CONFIG_TDLS */
wmm_ac_clear_saved_tspecs(wpa_s);
pmksa_candidate_free(wpa_s->wpa);
ptksa_cache_deinit(wpa_s->ptksa);
wpa_s->ptksa = NULL;
wpa_sm_deinit(wpa_s->wpa);
wpa_s->wpa = NULL;
wpa_bssid_ignore_clear(wpa_s);
#ifdef CONFIG_PASN
wpas_pasn_auth_stop(wpa_s);
#endif /* CONFIG_PASN */
wpa_bss_deinit(wpa_s);
wpa_supplicant_cancel_delayed_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
wpa_supplicant_cancel_auth_timeout(wpa_s);
eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
eloop_cancel_timeout(wpa_supplicant_delayed_mic_error_report,
wpa_s, NULL);
#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
eloop_cancel_timeout(wpas_clear_disabled_interface, wpa_s, NULL);
wpas_wps_deinit(wpa_s);
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = NULL;
#ifdef CONFIG_IBSS_RSN
ibss_rsn_deinit(wpa_s->ibss_rsn);
wpa_s->ibss_rsn = NULL;
#endif /* CONFIG_IBSS_RSN */
sme_deinit(wpa_s);
#ifdef CONFIG_AP
wpa_supplicant_ap_deinit(wpa_s);
#endif /* CONFIG_AP */
wpas_p2p_deinit(wpa_s);
#ifdef CONFIG_OFFCHANNEL
offchannel_deinit(wpa_s);
#endif /* CONFIG_OFFCHANNEL */
wpa_supplicant_cancel_sched_scan(wpa_s);
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL;
os_free(wpa_s->manual_scan_freqs);
wpa_s->manual_scan_freqs = NULL;
os_free(wpa_s->select_network_scan_freqs);
wpa_s->select_network_scan_freqs = NULL;
os_free(wpa_s->manual_sched_scan_freqs);
wpa_s->manual_sched_scan_freqs = NULL;
wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL);
/*
* Need to remove any pending gas-query radio work before the
* gas_query_deinit() call because gas_query::work has not yet been set
* for works that have not been started. gas_query_free() will be unable
* to cancel such pending radio works and once the pending gas-query
* radio work eventually gets removed, the deinit notification call to
* gas_query_start_cb() would result in dereferencing freed memory.
*/
if (wpa_s->radio)
radio_remove_works(wpa_s, "gas-query", 0);
gas_query_deinit(wpa_s->gas);
wpa_s->gas = NULL;
gas_server_deinit(wpa_s->gas_server);
wpa_s->gas_server = NULL;
free_hw_features(wpa_s);
ieee802_1x_dealloc_kay_sm(wpa_s);
os_free(wpa_s->bssid_filter);
wpa_s->bssid_filter = NULL;
os_free(wpa_s->disallow_aps_bssid);
wpa_s->disallow_aps_bssid = NULL;
os_free(wpa_s->disallow_aps_ssid);
wpa_s->disallow_aps_ssid = NULL;
wnm_bss_keep_alive_deinit(wpa_s);
#ifdef CONFIG_WNM
wnm_deallocate_memory(wpa_s);
#endif /* CONFIG_WNM */
ext_password_deinit(wpa_s->ext_pw);
wpa_s->ext_pw = NULL;
wpabuf_free(wpa_s->last_gas_resp);
wpa_s->last_gas_resp = NULL;
wpabuf_free(wpa_s->prev_gas_resp);
wpa_s->prev_gas_resp = NULL;
os_free(wpa_s->last_scan_res);
wpa_s->last_scan_res = NULL;
#ifdef CONFIG_HS20
if (wpa_s->drv_priv)
wpa_drv_configure_frame_filters(wpa_s, 0);
hs20_deinit(wpa_s);
#endif /* CONFIG_HS20 */
for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
wpabuf_free(wpa_s->vendor_elem[i]);
wpa_s->vendor_elem[i] = NULL;
}
wmm_ac_notify_disassoc(wpa_s);
wpa_s->sched_scan_plans_num = 0;
os_free(wpa_s->sched_scan_plans);
wpa_s->sched_scan_plans = NULL;
#ifdef CONFIG_MBO
wpa_s->non_pref_chan_num = 0;
os_free(wpa_s->non_pref_chan);
wpa_s->non_pref_chan = NULL;
#endif /* CONFIG_MBO */
free_bss_tmp_disallowed(wpa_s);
wpabuf_free(wpa_s->lci);
wpa_s->lci = NULL;
wpas_clear_beacon_rep_data(wpa_s);
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
#ifdef CONFIG_MESH
{
struct external_pmksa_cache *entry;
while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache,
struct external_pmksa_cache,
list)) != NULL) {
dl_list_del(&entry->list);
os_free(entry->pmksa_cache);
os_free(entry);
}
}
#endif /* CONFIG_MESH */
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
wpas_flush_fils_hlp_req(wpa_s);
wpabuf_free(wpa_s->ric_ies);
wpa_s->ric_ies = NULL;
#ifdef CONFIG_DPP
wpas_dpp_deinit(wpa_s);
dpp_global_deinit(wpa_s->dpp);
wpa_s->dpp = NULL;
#endif /* CONFIG_DPP */
#ifdef CONFIG_PASN
wpas_pasn_auth_stop(wpa_s);
#endif /* CONFIG_PASN */
+ wpas_scs_deinit(wpa_s);
+ wpas_dscp_deinit(wpa_s);
}
/**
* wpa_clear_keys - Clear keys configured for the driver
* @wpa_s: Pointer to wpa_supplicant data
* @addr: Previously used BSSID or %NULL if not available
*
* This function clears the encryption keys that has been previously configured
* for the driver.
*/
void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
{
int i, max = 6;
/* MLME-DELETEKEYS.request */
for (i = 0; i < max; i++) {
if (wpa_s->keys_cleared & BIT(i))
continue;
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
NULL, 0, KEY_FLAG_GROUP);
}
/* Pairwise Key ID 1 for Extended Key ID is tracked in bit 15 */
if (~wpa_s->keys_cleared & (BIT(0) | BIT(15)) && addr &&
!is_zero_ether_addr(addr)) {
if (!(wpa_s->keys_cleared & BIT(0)))
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL,
0, NULL, 0, KEY_FLAG_PAIRWISE);
if (!(wpa_s->keys_cleared & BIT(15)))
wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 1, 0, NULL,
0, NULL, 0, KEY_FLAG_PAIRWISE);
/* MLME-SETPROTECTION.request(None) */
wpa_drv_mlme_setprotection(
wpa_s, addr,
MLME_SETPROTECTION_PROTECT_TYPE_NONE,
MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
}
wpa_s->keys_cleared = (u32) -1;
}
/**
* wpa_supplicant_state_txt - Get the connection state name as a text string
* @state: State (wpa_state; WPA_*)
* Returns: The state name as a printable text string
*/
const char * wpa_supplicant_state_txt(enum wpa_states state)
{
switch (state) {
case WPA_DISCONNECTED:
return "DISCONNECTED";
case WPA_INACTIVE:
return "INACTIVE";
case WPA_INTERFACE_DISABLED:
return "INTERFACE_DISABLED";
case WPA_SCANNING:
return "SCANNING";
case WPA_AUTHENTICATING:
return "AUTHENTICATING";
case WPA_ASSOCIATING:
return "ASSOCIATING";
case WPA_ASSOCIATED:
return "ASSOCIATED";
case WPA_4WAY_HANDSHAKE:
return "4WAY_HANDSHAKE";
case WPA_GROUP_HANDSHAKE:
return "GROUP_HANDSHAKE";
case WPA_COMPLETED:
return "COMPLETED";
default:
return "UNKNOWN";
}
}
#ifdef CONFIG_BGSCAN
static void wpa_supplicant_stop_bgscan(struct wpa_supplicant *wpa_s)
{
if (wpa_s->bgscan_ssid) {
bgscan_deinit(wpa_s);
wpa_s->bgscan_ssid = NULL;
}
}
/**
* wpa_supplicant_reset_bgscan - Reset the bgscan for the current SSID.
* @wpa_s: Pointer to the wpa_supplicant data
*
* Stop, start, or reconfigure the scan parameters depending on the method.
*/
void wpa_supplicant_reset_bgscan(struct wpa_supplicant *wpa_s)
{
const char *name;
if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan)
name = wpa_s->current_ssid->bgscan;
else
name = wpa_s->conf->bgscan;
if (!name || name[0] == '\0') {
wpa_supplicant_stop_bgscan(wpa_s);
return;
}
if (wpas_driver_bss_selection(wpa_s))
return;
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
return;
#endif /* CONFIG_P2P */
bgscan_deinit(wpa_s);
if (wpa_s->current_ssid) {
if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
"bgscan");
/*
* Live without bgscan; it is only used as a roaming
* optimization, so the initial connection is not
* affected.
*/
} else {
struct wpa_scan_results *scan_res;
wpa_s->bgscan_ssid = wpa_s->current_ssid;
scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL,
0);
if (scan_res) {
bgscan_notify_scan(wpa_s, scan_res);
wpa_scan_results_free(scan_res);
}
}
} else
wpa_s->bgscan_ssid = NULL;
}
#endif /* CONFIG_BGSCAN */
static void wpa_supplicant_start_autoscan(struct wpa_supplicant *wpa_s)
{
if (autoscan_init(wpa_s, 0))
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize autoscan");
}
static void wpa_supplicant_stop_autoscan(struct wpa_supplicant *wpa_s)
{
autoscan_deinit(wpa_s);
}
void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s)
{
if (wpa_s->wpa_state == WPA_DISCONNECTED ||
wpa_s->wpa_state == WPA_SCANNING) {
autoscan_deinit(wpa_s);
wpa_supplicant_start_autoscan(wpa_s);
}
}
/**
* wpa_supplicant_set_state - Set current connection state
* @wpa_s: Pointer to wpa_supplicant data
* @state: The new connection state
*
* This function is called whenever the connection state changes, e.g.,
* association is completed for WPA/WPA2 4-Way Handshake is started.
*/
void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
enum wpa_states state)
{
enum wpa_states old_state = wpa_s->wpa_state;
#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
bool update_fils_connect_params = false;
#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
wpa_dbg(wpa_s, MSG_DEBUG, "State: %s -> %s",
wpa_supplicant_state_txt(wpa_s->wpa_state),
wpa_supplicant_state_txt(state));
if (state == WPA_COMPLETED &&
os_reltime_initialized(&wpa_s->roam_start)) {
os_reltime_age(&wpa_s->roam_start, &wpa_s->roam_time);
wpa_s->roam_start.sec = 0;
wpa_s->roam_start.usec = 0;
wpas_notify_auth_changed(wpa_s);
wpas_notify_roam_time(wpa_s);
wpas_notify_roam_complete(wpa_s);
} else if (state == WPA_DISCONNECTED &&
os_reltime_initialized(&wpa_s->roam_start)) {
wpa_s->roam_start.sec = 0;
wpa_s->roam_start.usec = 0;
wpa_s->roam_time.sec = 0;
wpa_s->roam_time.usec = 0;
wpas_notify_roam_complete(wpa_s);
}
if (state == WPA_INTERFACE_DISABLED) {
/* Assure normal scan when interface is restored */
wpa_s->normal_scans = 0;
}
if (state == WPA_COMPLETED) {
wpas_connect_work_done(wpa_s);
/* Reinitialize normal_scan counter */
wpa_s->normal_scans = 0;
}
#ifdef CONFIG_P2P
/*
* P2PS client has to reply to Probe Request frames received on the
* group operating channel. Enable Probe Request frame reporting for
* P2P connected client in case p2p_cli_probe configuration property is
* set to 1.
*/
if (wpa_s->conf->p2p_cli_probe && wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
wpa_s->current_ssid->p2p_group) {
if (state == WPA_COMPLETED && !wpa_s->p2p_cli_probe) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Enable CLI Probe Request RX reporting");
wpa_s->p2p_cli_probe =
wpa_drv_probe_req_report(wpa_s, 1) >= 0;
} else if (state != WPA_COMPLETED && wpa_s->p2p_cli_probe) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P: Disable CLI Probe Request RX reporting");
wpa_s->p2p_cli_probe = 0;
wpa_drv_probe_req_report(wpa_s, 0);
}
}
#endif /* CONFIG_P2P */
if (state != WPA_SCANNING)
wpa_supplicant_notify_scanning(wpa_s, 0);
if (state == WPA_COMPLETED && wpa_s->new_connection) {
struct wpa_ssid *ssid = wpa_s->current_ssid;
int fils_hlp_sent = 0;
#ifdef CONFIG_SME
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
wpa_auth_alg_fils(wpa_s->sme.auth_alg))
fils_hlp_sent = 1;
#endif /* CONFIG_SME */
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
wpa_auth_alg_fils(wpa_s->auth_alg))
fils_hlp_sent = 1;
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
MACSTR " completed [id=%d id_str=%s%s]",
MAC2STR(wpa_s->bssid),
ssid ? ssid->id : -1,
ssid && ssid->id_str ? ssid->id_str : "",
fils_hlp_sent ? " FILS_HLP_SENT" : "");
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
wpas_clear_temp_disabled(wpa_s, ssid, 1);
wpa_s->consecutive_conn_failures = 0;
wpa_s->new_connection = 0;
wpa_drv_set_operstate(wpa_s, 1);
#ifndef IEEE8021X_EAPOL
wpa_drv_set_supp_port(wpa_s, 1);
#endif /* IEEE8021X_EAPOL */
wpa_s->after_wps = 0;
wpa_s->known_wps_freq = 0;
wpas_p2p_completed(wpa_s);
sme_sched_obss_scan(wpa_s, 1);
#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
if (!fils_hlp_sent && ssid && ssid->eap.erp)
update_fils_connect_params = true;
#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
#ifdef CONFIG_OWE
if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_OWE))
wpas_update_owe_connect_params(wpa_s);
#endif /* CONFIG_OWE */
} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
state == WPA_ASSOCIATED) {
wpa_s->new_connection = 1;
wpa_drv_set_operstate(wpa_s, 0);
#ifndef IEEE8021X_EAPOL
wpa_drv_set_supp_port(wpa_s, 0);
#endif /* IEEE8021X_EAPOL */
sme_sched_obss_scan(wpa_s, 0);
}
wpa_s->wpa_state = state;
#ifdef CONFIG_BGSCAN
if (state == WPA_COMPLETED && wpa_s->current_ssid != wpa_s->bgscan_ssid)
wpa_supplicant_reset_bgscan(wpa_s);
else if (state < WPA_ASSOCIATED)
wpa_supplicant_stop_bgscan(wpa_s);
#endif /* CONFIG_BGSCAN */
if (state > WPA_SCANNING)
wpa_supplicant_stop_autoscan(wpa_s);
if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
wpa_supplicant_start_autoscan(wpa_s);
if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED)
wmm_ac_notify_disassoc(wpa_s);
if (wpa_s->wpa_state != old_state) {
wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
/*
* Notify the P2P Device interface about a state change in one
* of the interfaces.
*/
wpas_p2p_indicate_state_change(wpa_s);
if (wpa_s->wpa_state == WPA_COMPLETED ||
old_state == WPA_COMPLETED)
wpas_notify_auth_changed(wpa_s);
#ifdef CONFIG_DPP2
if (wpa_s->wpa_state == WPA_COMPLETED)
wpas_dpp_connected(wpa_s);
#endif /* CONFIG_DPP2 */
}
#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
if (update_fils_connect_params)
wpas_update_fils_connect_params(wpa_s);
#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
}
void wpa_supplicant_terminate_proc(struct wpa_global *global)
{
int pending = 0;
#ifdef CONFIG_WPS
struct wpa_supplicant *wpa_s = global->ifaces;
while (wpa_s) {
struct wpa_supplicant *next = wpa_s->next;
if (wpas_wps_terminate_pending(wpa_s) == 1)
pending = 1;
#ifdef CONFIG_P2P
if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE ||
(wpa_s->current_ssid && wpa_s->current_ssid->p2p_group))
wpas_p2p_disconnect(wpa_s);
#endif /* CONFIG_P2P */
wpa_s = next;
}
#endif /* CONFIG_WPS */
if (pending)
return;
eloop_terminate();
}
static void wpa_supplicant_terminate(int sig, void *signal_ctx)
{
struct wpa_global *global = signal_ctx;
wpa_supplicant_terminate_proc(global);
}
void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
{
enum wpa_states old_state = wpa_s->wpa_state;
enum wpa_states new_state;
if (old_state == WPA_SCANNING)
new_state = WPA_SCANNING;
else
new_state = WPA_DISCONNECTED;
wpa_s->pairwise_cipher = 0;
wpa_s->group_cipher = 0;
wpa_s->mgmt_group_cipher = 0;
wpa_s->key_mgmt = 0;
if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
wpa_supplicant_set_state(wpa_s, new_state);
if (wpa_s->wpa_state != old_state)
wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
}
/**
* wpa_supplicant_reload_configuration - Reload configuration data
* @wpa_s: Pointer to wpa_supplicant data
* Returns: 0 on success or -1 if configuration parsing failed
*
* This function can be used to request that the configuration data is reloaded
* (e.g., after configuration file change). This function is reloading
* configuration only for one interface, so this may need to be called multiple
* times if %wpa_supplicant is controlling multiple interfaces and all
* interfaces need reconfiguration.
*/
int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
{
struct wpa_config *conf;
int reconf_ctrl;
int old_ap_scan;
if (wpa_s->confname == NULL)
return -1;
conf = wpa_config_read(wpa_s->confname, NULL);
if (conf == NULL) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
"file '%s' - exiting", wpa_s->confname);
return -1;
}
if (wpa_s->confanother &&
!wpa_config_read(wpa_s->confanother, conf)) {
wpa_msg(wpa_s, MSG_ERROR,
"Failed to parse the configuration file '%s' - exiting",
wpa_s->confanother);
return -1;
}
conf->changed_parameters = (unsigned int) -1;
reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
|| (conf->ctrl_interface && wpa_s->conf->ctrl_interface &&
os_strcmp(conf->ctrl_interface,
wpa_s->conf->ctrl_interface) != 0);
if (reconf_ctrl) {
wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
wpa_s->ctrl_iface = NULL;
}
eapol_sm_invalidate_cached_session(wpa_s->eapol);
if (wpa_s->current_ssid) {
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
}
/*
* TODO: should notify EAPOL SM about changes in opensc_engine_path,
* pkcs11_engine_path, pkcs11_module_path, openssl_ciphers.
*/
if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) {
/*
* Clear forced success to clear EAP state for next
* authentication.
*/
eapol_sm_notify_eap_success(wpa_s->eapol, false);
}
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_sm_set_config(wpa_s->wpa, NULL);
wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
rsn_preauth_deinit(wpa_s->wpa);
old_ap_scan = wpa_s->conf->ap_scan;
wpa_config_free(wpa_s->conf);
wpa_s->conf = conf;
if (old_ap_scan != wpa_s->conf->ap_scan)
wpas_notify_ap_scan_changed(wpa_s);
if (reconf_ctrl)
wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
wpa_supplicant_update_config(wpa_s);
wpa_supplicant_clear_status(wpa_s);
if (wpa_supplicant_enabled_networks(wpa_s)) {
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
wpa_bssid_ignore_clear(wpa_s);
wpa_dbg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
return 0;
}
static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
{
struct wpa_global *global = signal_ctx;
struct wpa_supplicant *wpa_s;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
wpa_dbg(wpa_s, MSG_DEBUG, "Signal %d received - reconfiguring",
sig);
if (wpa_supplicant_reload_configuration(wpa_s) < 0) {
wpa_supplicant_terminate_proc(global);
}
}
if (wpa_debug_reopen_file() < 0) {
/* Ignore errors since we cannot really do much to fix this */
wpa_printf(MSG_DEBUG, "Could not reopen debug log file");
}
}
static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct wpa_ie_data *ie)
{
int ret = wpa_sm_parse_own_wpa_ie(wpa_s->wpa, ie);
if (ret) {
if (ret == -2) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE "
"from association info");
}
return -1;
}
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set "
"cipher suites");
if (!(ie->group_cipher & ssid->group_cipher)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group "
"cipher 0x%x (mask 0x%x) - reject",
ie->group_cipher, ssid->group_cipher);
return -1;
}
if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise "
"cipher 0x%x (mask 0x%x) - reject",
ie->pairwise_cipher, ssid->pairwise_cipher);
return -1;
}
if (!(ie->key_mgmt & ssid->key_mgmt)) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key "
"management 0x%x (mask 0x%x) - reject",
ie->key_mgmt, ssid->key_mgmt);
return -1;
}
if (!(ie->capabilities & WPA_CAPABILITY_MFPC) &&
wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP "
"that does not support management frame protection - "
"reject");
return -1;
}
return 0;
}
static int matching_ciphers(struct wpa_ssid *ssid, struct wpa_ie_data *ie,
int freq)
{
if (!ie->has_group)
ie->group_cipher = wpa_default_rsn_cipher(freq);
if (!ie->has_pairwise)
ie->pairwise_cipher = wpa_default_rsn_cipher(freq);
return (ie->group_cipher & ssid->group_cipher) &&
(ie->pairwise_cipher & ssid->pairwise_cipher);
}
+void wpas_set_mgmt_group_cipher(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, struct wpa_ie_data *ie)
+{
+ int sel;
+
+ sel = ie->mgmt_group_cipher;
+ if (ssid->group_mgmt_cipher)
+ sel &= ssid->group_mgmt_cipher;
+ if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION ||
+ !(ie->capabilities & WPA_CAPABILITY_MFPC))
+ sel = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: AP mgmt_group_cipher 0x%x network profile mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x",
+ ie->mgmt_group_cipher, ssid->group_mgmt_cipher, sel);
+ if (sel & WPA_CIPHER_AES_128_CMAC) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: using MGMT group cipher AES-128-CMAC");
+ } else if (sel & WPA_CIPHER_BIP_GMAC_128) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: using MGMT group cipher BIP-GMAC-128");
+ } else if (sel & WPA_CIPHER_BIP_GMAC_256) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: using MGMT group cipher BIP-GMAC-256");
+ } else if (sel & WPA_CIPHER_BIP_CMAC_256) {
+ wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256;
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "WPA: using MGMT group cipher BIP-CMAC-256");
+ } else {
+ wpa_s->mgmt_group_cipher = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
+ }
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
+ wpa_s->mgmt_group_cipher);
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
+ wpas_get_ssid_pmf(wpa_s, ssid));
+}
+
+
/**
* wpa_supplicant_set_suites - Set authentication and encryption parameters
* @wpa_s: Pointer to wpa_supplicant data
* @bss: Scan results for the selected BSS, or %NULL if not available
* @ssid: Configuration data for the selected network
* @wpa_ie: Buffer for the WPA/RSN IE
* @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the
* used buffer length in case the functions returns success.
* Returns: 0 on success or -1 on failure
*
* This function is used to configure authentication and encryption parameters
* based on the network configuration and scan result for the selected BSS (if
* available).
*/
int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
u8 *wpa_ie, size_t *wpa_ie_len)
{
struct wpa_ie_data ie;
int sel, proto, sae_pwe;
const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen;
if (bss) {
bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
bss_rsnx = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
} else {
bss_wpa = bss_rsn = bss_rsnx = bss_osen = NULL;
}
if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
matching_ciphers(ssid, &ie, bss->freq) &&
(ie.key_mgmt & ssid->key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
proto = WPA_PROTO_RSN;
} else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie) == 0 &&
(ie.group_cipher & ssid->group_cipher) &&
(ie.pairwise_cipher & ssid->pairwise_cipher) &&
(ie.key_mgmt & ssid->key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
proto = WPA_PROTO_WPA;
#ifdef CONFIG_HS20
} else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN) &&
wpa_parse_wpa_ie(bss_osen, 2 + bss_osen[1], &ie) == 0 &&
(ie.group_cipher & ssid->group_cipher) &&
(ie.pairwise_cipher & ssid->pairwise_cipher) &&
(ie.key_mgmt & ssid->key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN");
proto = WPA_PROTO_OSEN;
} else if (bss_rsn && (ssid->proto & WPA_PROTO_OSEN) &&
wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
(ie.group_cipher & ssid->group_cipher) &&
(ie.pairwise_cipher & ssid->pairwise_cipher) &&
(ie.key_mgmt & ssid->key_mgmt)) {
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using OSEN (within RSN)");
proto = WPA_PROTO_RSN;
#endif /* CONFIG_HS20 */
} else if (bss) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
ssid->proto, ssid->pairwise_cipher, ssid->group_cipher,
ssid->key_mgmt);
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s",
MAC2STR(bss->bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len),
bss_wpa ? " WPA" : "",
bss_rsn ? " RSN" : "",
bss_osen ? " OSEN" : "");
if (bss_rsn) {
wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]);
if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Could not parse RSN element");
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"RSN: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
ie.pairwise_cipher, ie.group_cipher,
ie.key_mgmt);
}
}
if (bss_wpa) {
wpa_hexdump(MSG_DEBUG, "WPA", bss_wpa, 2 + bss_wpa[1]);
if (wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Could not parse WPA element");
} else {
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
ie.pairwise_cipher, ie.group_cipher,
ie.key_mgmt);
}
}
return -1;
} else {
if (ssid->proto & WPA_PROTO_OSEN)
proto = WPA_PROTO_OSEN;
else if (ssid->proto & WPA_PROTO_RSN)
proto = WPA_PROTO_RSN;
else
proto = WPA_PROTO_WPA;
if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) {
os_memset(&ie, 0, sizeof(ie));
ie.group_cipher = ssid->group_cipher;
ie.pairwise_cipher = ssid->pairwise_cipher;
ie.key_mgmt = ssid->key_mgmt;
ie.mgmt_group_cipher = 0;
if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
if (ssid->group_mgmt_cipher &
WPA_CIPHER_BIP_GMAC_256)
ie.mgmt_group_cipher =
WPA_CIPHER_BIP_GMAC_256;
else if (ssid->group_mgmt_cipher &
WPA_CIPHER_BIP_CMAC_256)
ie.mgmt_group_cipher =
WPA_CIPHER_BIP_CMAC_256;
else if (ssid->group_mgmt_cipher &
WPA_CIPHER_BIP_GMAC_128)
ie.mgmt_group_cipher =
WPA_CIPHER_BIP_GMAC_128;
else
ie.mgmt_group_cipher =
WPA_CIPHER_AES_128_CMAC;
}
#ifdef CONFIG_OWE
if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
!ssid->owe_only &&
!bss_wpa && !bss_rsn && !bss_osen) {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
wpa_s->wpa_proto = 0;
*wpa_ie_len = 0;
return 0;
}
#endif /* CONFIG_OWE */
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites "
"based on configuration");
} else
proto = ie.proto;
}
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected cipher suites: group %d "
"pairwise %d key_mgmt %d proto %d",
ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
if (ssid->ieee80211w) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
ie.mgmt_group_cipher);
}
wpa_s->wpa_proto = proto;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
!!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)));
if (bss || !wpa_s->ap_ies_from_associnfo) {
if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
bss_wpa ? 2 + bss_wpa[1] : 0) ||
wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
bss_rsn ? 2 + bss_rsn[1] : 0) ||
wpa_sm_set_ap_rsnxe(wpa_s->wpa, bss_rsnx,
bss_rsnx ? 2 + bss_rsnx[1] : 0))
return -1;
}
#ifdef CONFIG_NO_WPA
wpa_s->group_cipher = WPA_CIPHER_NONE;
wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
#else /* CONFIG_NO_WPA */
sel = ie.group_cipher & ssid->group_cipher;
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: AP group 0x%x network profile group 0x%x; available group 0x%x",
ie.group_cipher, ssid->group_cipher, sel);
wpa_s->group_cipher = wpa_pick_group_cipher(sel);
if (wpa_s->group_cipher < 0) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group "
"cipher");
return -1;
}
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK %s",
wpa_cipher_txt(wpa_s->group_cipher));
sel = ie.pairwise_cipher & ssid->pairwise_cipher;
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: AP pairwise 0x%x network profile pairwise 0x%x; available pairwise 0x%x",
ie.pairwise_cipher, ssid->pairwise_cipher, sel);
wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1);
if (wpa_s->pairwise_cipher < 0) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise "
"cipher");
return -1;
}
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
wpa_cipher_txt(wpa_s->pairwise_cipher));
#endif /* CONFIG_NO_WPA */
sel = ie.key_mgmt & ssid->key_mgmt;
#ifdef CONFIG_SAE
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE))
sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE);
#endif /* CONFIG_SAE */
#ifdef CONFIG_IEEE80211R
if (!(wpa_s->drv_flags & (WPA_DRIVER_FLAGS_SME |
WPA_DRIVER_FLAGS_UPDATE_FT_IES)))
sel &= ~WPA_KEY_MGMT_FT;
#endif /* CONFIG_IEEE80211R */
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: AP key_mgmt 0x%x network profile key_mgmt 0x%x; available key_mgmt 0x%x",
ie.key_mgmt, ssid->key_mgmt, sel);
if (0) {
#ifdef CONFIG_IEEE80211R
#ifdef CONFIG_SHA384
} else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) &&
os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT FT/802.1X-SHA384");
if (!ssid->ft_eap_pmksa_caching &&
pmksa_cache_get_current(wpa_s->wpa)) {
/* PMKSA caching with FT may have interoperability
* issues, so disable that case by default for now. */
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: Disable PMKSA caching for FT/802.1X connection");
pmksa_cache_clear_current(wpa_s->wpa);
}
#endif /* CONFIG_SHA384 */
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_SUITEB192
} else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT 802.1X with Suite B (192-bit)");
#endif /* CONFIG_SUITEB192 */
#ifdef CONFIG_SUITEB
} else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT 802.1X with Suite B");
#endif /* CONFIG_SUITEB */
#ifdef CONFIG_FILS
#ifdef CONFIG_IEEE80211R
} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA384");
} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256");
#endif /* CONFIG_IEEE80211R */
} else if (sel & WPA_KEY_MGMT_FILS_SHA384) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA384");
} else if (sel & WPA_KEY_MGMT_FILS_SHA256) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256");
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
} else if ((sel & WPA_KEY_MGMT_FT_IEEE8021X) &&
os_strcmp(wpa_supplicant_get_eap_mode(wpa_s), "LEAP") != 0) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
if (!ssid->ft_eap_pmksa_caching &&
pmksa_cache_get_current(wpa_s->wpa)) {
/* PMKSA caching with FT may have interoperability
* issues, so disable that case by default for now. */
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: Disable PMKSA caching for FT/802.1X connection");
pmksa_cache_clear_current(wpa_s->wpa);
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_DPP
} else if (sel & WPA_KEY_MGMT_DPP) {
wpa_s->key_mgmt = WPA_KEY_MGMT_DPP;
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP");
#endif /* CONFIG_DPP */
#ifdef CONFIG_SAE
} else if (sel & WPA_KEY_MGMT_FT_SAE) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_SAE;
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT FT/SAE");
} else if (sel & WPA_KEY_MGMT_SAE) {
wpa_s->key_mgmt = WPA_KEY_MGMT_SAE;
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT SAE");
#endif /* CONFIG_SAE */
#ifdef CONFIG_IEEE80211R
} else if (sel & WPA_KEY_MGMT_FT_PSK) {
wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
#endif /* CONFIG_IEEE80211R */
} else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) {
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT 802.1X with SHA256");
} else if (sel & WPA_KEY_MGMT_PSK_SHA256) {
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
wpa_dbg(wpa_s, MSG_DEBUG,
"WPA: using KEY_MGMT PSK with SHA256");
} else if (sel & WPA_KEY_MGMT_IEEE8021X) {
wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
} else if (sel & WPA_KEY_MGMT_PSK) {
wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
} else if (sel & WPA_KEY_MGMT_WPA_NONE) {
wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
#ifdef CONFIG_HS20
} else if (sel & WPA_KEY_MGMT_OSEN) {
wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN;
wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN");
#endif /* CONFIG_HS20 */
#ifdef CONFIG_OWE
} else if (sel & WPA_KEY_MGMT_OWE) {
wpa_s->key_mgmt = WPA_KEY_MGMT_OWE;
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT OWE");
#endif /* CONFIG_OWE */
} else {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
"authenticated key management type");
return -1;
}
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
wpa_s->pairwise_cipher);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
wpa_msg(wpa_s, MSG_INFO,
"RSN: Management frame protection required but the selected AP does not enable it");
return -1;
}
- sel = ie.mgmt_group_cipher;
- if (ssid->group_mgmt_cipher)
- sel &= ssid->group_mgmt_cipher;
- if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION ||
- !(ie.capabilities & WPA_CAPABILITY_MFPC))
- sel = 0;
- wpa_dbg(wpa_s, MSG_DEBUG,
- "WPA: AP mgmt_group_cipher 0x%x network profile mgmt_group_cipher 0x%x; available mgmt_group_cipher 0x%x",
- ie.mgmt_group_cipher, ssid->group_mgmt_cipher, sel);
- if (sel & WPA_CIPHER_AES_128_CMAC) {
- wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
- wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
- "AES-128-CMAC");
- } else if (sel & WPA_CIPHER_BIP_GMAC_128) {
- wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128;
- wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
- "BIP-GMAC-128");
- } else if (sel & WPA_CIPHER_BIP_GMAC_256) {
- wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256;
- wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
- "BIP-GMAC-256");
- } else if (sel & WPA_CIPHER_BIP_CMAC_256) {
- wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256;
- wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
- "BIP-CMAC-256");
- } else {
- wpa_s->mgmt_group_cipher = 0;
- wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
- }
- wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
- wpa_s->mgmt_group_cipher);
- wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
- wpas_get_ssid_pmf(wpa_s, ssid));
+ wpas_set_mgmt_group_cipher(wpa_s, ssid, &ie);
#ifdef CONFIG_OCV
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) ||
(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_OCV))
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCV, ssid->ocv);
#endif /* CONFIG_OCV */
sae_pwe = wpa_s->conf->sae_pwe;
if (ssid->sae_password_id && sae_pwe != 3)
sae_pwe = 1;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PWE, sae_pwe);
#ifdef CONFIG_SAE_PK
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PK,
wpa_key_mgmt_sae(ssid->key_mgmt) &&
ssid->sae_pk != SAE_PK_MODE_DISABLED &&
((ssid->sae_password &&
sae_pk_valid_password(ssid->sae_password)) ||
(!ssid->sae_password && ssid->passphrase &&
sae_pk_valid_password(ssid->passphrase))));
#endif /* CONFIG_SAE_PK */
#ifdef CONFIG_TESTING_OPTIONS
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_FT_RSNXE_USED,
wpa_s->ft_rsnxe_used);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL,
wpa_s->oci_freq_override_eapol);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_EAPOL_G2,
wpa_s->oci_freq_override_eapol_g2);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FT_ASSOC,
wpa_s->oci_freq_override_ft_assoc);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_OCI_FREQ_FILS_ASSOC,
wpa_s->oci_freq_override_fils_assoc);
#endif /* CONFIG_TESTING_OPTIONS */
/* Extended Key ID is only supported in infrastructure BSS so far */
if (ssid->mode == WPAS_MODE_INFRA && wpa_s->conf->extended_key_id &&
(ssid->proto & WPA_PROTO_RSN) &&
ssid->pairwise_cipher & (WPA_CIPHER_CCMP | WPA_CIPHER_CCMP_256 |
WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID)) {
int use_ext_key_id = 0;
wpa_msg(wpa_s, MSG_DEBUG,
"WPA: Enable Extended Key ID support");
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXT_KEY_ID,
wpa_s->conf->extended_key_id);
if (bss_rsn &&
wpa_s->conf->extended_key_id &&
wpa_s->pairwise_cipher != WPA_CIPHER_TKIP &&
(ie.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST))
use_ext_key_id = 1;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_USE_EXT_KEY_ID,
use_ext_key_id);
} else {
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_EXT_KEY_ID, 0);
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_USE_EXT_KEY_ID, 0);
}
if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to generate WPA IE");
return -1;
}
wpa_s->rsnxe_len = sizeof(wpa_s->rsnxe);
if (wpa_sm_set_assoc_rsnxe_default(wpa_s->wpa, wpa_s->rsnxe,
&wpa_s->rsnxe_len)) {
wpa_msg(wpa_s, MSG_WARNING, "RSN: Failed to generate RSNXE");
return -1;
}
if (0) {
#ifdef CONFIG_DPP
} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) {
/* Use PMK from DPP network introduction (PMKSA entry) */
wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
#ifdef CONFIG_DPP2
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DPP_PFS, ssid->dpp_pfs);
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
} else if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
int psk_set = 0;
int sae_only;
sae_only = (ssid->key_mgmt & (WPA_KEY_MGMT_PSK |
WPA_KEY_MGMT_FT_PSK |
WPA_KEY_MGMT_PSK_SHA256)) == 0;
if (ssid->psk_set && !sae_only) {
wpa_hexdump_key(MSG_MSGDUMP, "PSK (set in config)",
ssid->psk, PMK_LEN);
wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL,
NULL);
psk_set = 1;
}
if (wpa_key_mgmt_sae(ssid->key_mgmt) &&
(ssid->sae_password || ssid->passphrase))
psk_set = 1;
#ifndef CONFIG_NO_PBKDF2
if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
ssid->passphrase && !sae_only) {
u8 psk[PMK_LEN];
pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len,
4096, psk, PMK_LEN);
wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
psk, PMK_LEN);
wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL);
psk_set = 1;
os_memset(psk, 0, sizeof(psk));
}
#endif /* CONFIG_NO_PBKDF2 */
#ifdef CONFIG_EXT_PASSWORD
if (ssid->ext_psk && !sae_only) {
struct wpabuf *pw = ext_password_get(wpa_s->ext_pw,
ssid->ext_psk);
char pw_str[64 + 1];
u8 psk[PMK_LEN];
if (pw == NULL) {
wpa_msg(wpa_s, MSG_INFO, "EXT PW: No PSK "
"found from external storage");
return -1;
}
if (wpabuf_len(pw) < 8 || wpabuf_len(pw) > 64) {
wpa_msg(wpa_s, MSG_INFO, "EXT PW: Unexpected "
"PSK length %d in external storage",
(int) wpabuf_len(pw));
ext_password_free(pw);
return -1;
}
os_memcpy(pw_str, wpabuf_head(pw), wpabuf_len(pw));
pw_str[wpabuf_len(pw)] = '\0';
#ifndef CONFIG_NO_PBKDF2
if (wpabuf_len(pw) >= 8 && wpabuf_len(pw) < 64 && bss)
{
pbkdf2_sha1(pw_str, bss->ssid, bss->ssid_len,
4096, psk, PMK_LEN);
os_memset(pw_str, 0, sizeof(pw_str));
wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
"external passphrase)",
psk, PMK_LEN);
wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
NULL);
psk_set = 1;
os_memset(psk, 0, sizeof(psk));
} else
#endif /* CONFIG_NO_PBKDF2 */
if (wpabuf_len(pw) == 2 * PMK_LEN) {
if (hexstr2bin(pw_str, psk, PMK_LEN) < 0) {
wpa_msg(wpa_s, MSG_INFO, "EXT PW: "
"Invalid PSK hex string");
os_memset(pw_str, 0, sizeof(pw_str));
ext_password_free(pw);
return -1;
}
wpa_hexdump_key(MSG_MSGDUMP,
"PSK (from external PSK)",
psk, PMK_LEN);
wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
NULL);
psk_set = 1;
os_memset(psk, 0, sizeof(psk));
} else {
wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
"PSK available");
os_memset(pw_str, 0, sizeof(pw_str));
ext_password_free(pw);
return -1;
}
os_memset(pw_str, 0, sizeof(pw_str));
ext_password_free(pw);
}
#endif /* CONFIG_EXT_PASSWORD */
if (!psk_set) {
wpa_msg(wpa_s, MSG_INFO,
"No PSK available for association");
wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE");
return -1;
}
#ifdef CONFIG_OWE
} else if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) {
/* OWE Diffie-Hellman exchange in (Re)Association
* Request/Response frames set the PMK, so do not override it
* here. */
#endif /* CONFIG_OWE */
} else
wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
if (ssid->mode != WPAS_MODE_IBSS &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED) &&
(ssid->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER ||
(ssid->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) {
wpa_msg(wpa_s, MSG_INFO,
"Disable PTK0 rekey support - replaced with reconnect");
wpa_s->deny_ptk0_rekey = 1;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DENY_PTK0_REKEY, 1);
} else {
wpa_s->deny_ptk0_rekey = 0;
wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DENY_PTK0_REKEY, 0);
}
return 0;
}
static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
{
+ bool scs = true, mscs = true;
+
*pos = 0x00;
switch (idx) {
case 0: /* Bits 0-7 */
break;
case 1: /* Bits 8-15 */
if (wpa_s->conf->coloc_intf_reporting) {
/* Bit 13 - Collocated Interference Reporting */
*pos |= 0x20;
}
break;
case 2: /* Bits 16-23 */
#ifdef CONFIG_WNM
*pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */
if (!wpa_s->disable_mbo_oce && !wpa_s->conf->disable_btm)
*pos |= 0x08; /* Bit 19 - BSS Transition */
#endif /* CONFIG_WNM */
break;
case 3: /* Bits 24-31 */
#ifdef CONFIG_WNM
*pos |= 0x02; /* Bit 25 - SSID List */
#endif /* CONFIG_WNM */
#ifdef CONFIG_INTERWORKING
if (wpa_s->conf->interworking)
*pos |= 0x80; /* Bit 31 - Interworking */
#endif /* CONFIG_INTERWORKING */
break;
case 4: /* Bits 32-39 */
#ifdef CONFIG_INTERWORKING
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING)
*pos |= 0x01; /* Bit 32 - QoS Map */
#endif /* CONFIG_INTERWORKING */
break;
case 5: /* Bits 40-47 */
#ifdef CONFIG_HS20
if (wpa_s->conf->hs20)
*pos |= 0x40; /* Bit 46 - WNM-Notification */
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
*pos |= 0x40; /* Bit 46 - WNM-Notification */
#endif /* CONFIG_MBO */
break;
case 6: /* Bits 48-55 */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->disable_scs_support)
+ scs = false;
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (scs)
+ *pos |= 0x40; /* Bit 54 - SCS */
break;
case 7: /* Bits 56-63 */
break;
case 8: /* Bits 64-71 */
if (wpa_s->conf->ftm_responder)
*pos |= 0x40; /* Bit 70 - FTM responder */
if (wpa_s->conf->ftm_initiator)
*pos |= 0x80; /* Bit 71 - FTM initiator */
break;
case 9: /* Bits 72-79 */
#ifdef CONFIG_FILS
if (!wpa_s->disable_fils)
*pos |= 0x01;
#endif /* CONFIG_FILS */
break;
case 10: /* Bits 80-87 */
- *pos |= 0x20; /* Bit 85 - Mirrored SCS */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->disable_mscs_support)
+ mscs = false;
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (mscs)
+ *pos |= 0x20; /* Bit 85 - Mirrored SCS */
break;
}
}
int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
{
u8 *pos = buf;
u8 len = 11, i;
if (len < wpa_s->extended_capa_len)
len = wpa_s->extended_capa_len;
if (buflen < (size_t) len + 2) {
wpa_printf(MSG_INFO,
"Not enough room for building extended capabilities element");
return -1;
}
*pos++ = WLAN_EID_EXT_CAPAB;
*pos++ = len;
for (i = 0; i < len; i++, pos++) {
wpas_ext_capab_byte(wpa_s, pos, i);
if (i < wpa_s->extended_capa_len) {
*pos &= ~wpa_s->extended_capa_mask[i];
*pos |= wpa_s->extended_capa[i];
}
}
while (len > 0 && buf[1 + len] == 0) {
len--;
buf[1] = len;
}
if (len == 0)
return 0;
return 2 + len;
}
static int wpas_valid_bss(struct wpa_supplicant *wpa_s,
struct wpa_bss *test_bss)
{
struct wpa_bss *bss;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (bss == test_bss)
return 1;
}
return 0;
}
static int wpas_valid_ssid(struct wpa_supplicant *wpa_s,
struct wpa_ssid *test_ssid)
{
struct wpa_ssid *ssid;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (ssid == test_ssid)
return 1;
}
return 0;
}
int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
struct wpa_ssid *test_ssid)
{
if (test_bss && !wpas_valid_bss(wpa_s, test_bss))
return 0;
return test_ssid == NULL || wpas_valid_ssid(wpa_s, test_ssid);
}
void wpas_connect_work_free(struct wpa_connect_work *cwork)
{
if (cwork == NULL)
return;
os_free(cwork);
}
void wpas_connect_work_done(struct wpa_supplicant *wpa_s)
{
struct wpa_connect_work *cwork;
struct wpa_radio_work *work = wpa_s->connect_work;
if (!work)
return;
wpa_s->connect_work = NULL;
cwork = work->ctx;
work->ctx = NULL;
wpas_connect_work_free(cwork);
radio_work_done(work);
}
int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
{
struct os_reltime now;
u8 addr[ETH_ALEN];
os_get_reltime(&now);
if (wpa_s->last_mac_addr_style == style &&
wpa_s->last_mac_addr_change.sec != 0 &&
!os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
wpa_s->conf->rand_addr_lifetime)) {
wpa_msg(wpa_s, MSG_DEBUG,
"Previously selected random MAC address has not yet expired");
return 0;
}
switch (style) {
case 1:
if (random_mac_addr(addr) < 0)
return -1;
break;
case 2:
os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN);
if (random_mac_addr_keep_oui(addr) < 0)
return -1;
break;
default:
return -1;
}
if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Failed to set random MAC address");
return -1;
}
os_get_reltime(&wpa_s->last_mac_addr_change);
wpa_s->mac_addr_changed = 1;
wpa_s->last_mac_addr_style = style;
if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Could not update MAC address information");
return -1;
}
wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
MAC2STR(addr));
return 0;
}
int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s)
{
if (wpa_s->wpa_state >= WPA_AUTHENTICATING ||
!wpa_s->conf->preassoc_mac_addr)
return 0;
return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr);
}
static void wpa_s_setup_sae_pt(struct wpa_config *conf, struct wpa_ssid *ssid)
{
#ifdef CONFIG_SAE
int *groups = conf->sae_groups;
int default_groups[] = { 19, 20, 21, 0 };
const char *password;
if (!groups || groups[0] <= 0)
groups = default_groups;
password = ssid->sae_password;
if (!password)
password = ssid->passphrase;
if (!password ||
(conf->sae_pwe == 0 && !ssid->sae_password_id &&
!sae_pk_valid_password(password)) ||
conf->sae_pwe == 3) {
/* PT derivation not needed */
sae_deinit_pt(ssid->pt);
ssid->pt = NULL;
return;
}
if (ssid->pt)
return; /* PT already derived */
ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
(const u8 *) password, os_strlen(password),
ssid->sae_password_id);
#endif /* CONFIG_SAE */
}
static void wpa_s_clear_sae_rejected(struct wpa_supplicant *wpa_s)
{
#if defined(CONFIG_SAE) && defined(CONFIG_SME)
os_free(wpa_s->sme.sae_rejected_groups);
wpa_s->sme.sae_rejected_groups = NULL;
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->extra_sae_rejected_groups) {
int i, *groups = wpa_s->extra_sae_rejected_groups;
for (i = 0; groups[i]; i++) {
wpa_printf(MSG_DEBUG,
"TESTING: Indicate rejection of an extra SAE group %d",
groups[i]);
int_array_add_unique(&wpa_s->sme.sae_rejected_groups,
groups[i]);
}
}
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_SAE && CONFIG_SME */
}
int wpas_restore_permanent_mac_addr(struct wpa_supplicant *wpa_s)
{
if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Could not restore permanent MAC address");
return -1;
}
wpa_s->mac_addr_changed = 0;
if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
wpa_msg(wpa_s, MSG_INFO,
"Could not update MAC address information");
return -1;
}
wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address");
return 0;
}
static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit);
/**
* wpa_supplicant_associate - Request association
* @wpa_s: Pointer to wpa_supplicant data
* @bss: Scan results for the selected BSS, or %NULL if not available
* @ssid: Configuration data for the selected network
*
* This function is used to request %wpa_supplicant to associate with a BSS.
*/
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid)
{
struct wpa_connect_work *cwork;
int rand_style;
wpa_s->own_disconnect_req = 0;
wpa_s->own_reconnect_req = 0;
/*
* If we are starting a new connection, any previously pending EAPOL
* RX cannot be valid anymore.
*/
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = NULL;
if (ssid->mac_addr == -1)
rand_style = wpa_s->conf->mac_addr;
else
rand_style = ssid->mac_addr;
wpa_s->multi_ap_ie = 0;
wmm_ac_clear_saved_tspecs(wpa_s);
wpa_s->reassoc_same_bss = 0;
wpa_s->reassoc_same_ess = 0;
#ifdef CONFIG_TESTING_OPTIONS
wpa_s->testing_resend_assoc = 0;
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->last_ssid == ssid) {
wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
wpa_s->reassoc_same_ess = 1;
if (wpa_s->current_bss && wpa_s->current_bss == bss) {
wmm_ac_save_tspecs(wpa_s);
wpa_s->reassoc_same_bss = 1;
} else if (wpa_s->current_bss && wpa_s->current_bss != bss) {
os_get_reltime(&wpa_s->roam_start);
}
} else {
#ifdef CONFIG_SAE
wpa_s_clear_sae_rejected(wpa_s);
- wpa_s_setup_sae_pt(wpa_s->conf, ssid);
#endif /* CONFIG_SAE */
}
+#ifdef CONFIG_SAE
+ wpa_s_setup_sae_pt(wpa_s->conf, ssid);
+#endif /* CONFIG_SAE */
if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
if (wpas_update_random_addr(wpa_s, rand_style) < 0)
return;
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
} else if (rand_style == 0 && wpa_s->mac_addr_changed) {
if (wpas_restore_permanent_mac_addr(wpa_s) < 0)
return;
}
wpa_s->last_ssid = ssid;
#ifdef CONFIG_IBSS_RSN
ibss_rsn_deinit(wpa_s->ibss_rsn);
wpa_s->ibss_rsn = NULL;
#else /* CONFIG_IBSS_RSN */
if (ssid->mode == WPAS_MODE_IBSS &&
!(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {
wpa_msg(wpa_s, MSG_INFO,
"IBSS RSN not supported in the build");
return;
}
#endif /* CONFIG_IBSS_RSN */
if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
#ifdef CONFIG_AP
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) {
wpa_msg(wpa_s, MSG_INFO, "Driver does not support AP "
"mode");
return;
}
if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
wpas_p2p_ap_setup_failed(wpa_s);
return;
}
wpa_s->current_bss = bss;
#else /* CONFIG_AP */
wpa_msg(wpa_s, MSG_ERROR, "AP mode support not included in "
"the build");
#endif /* CONFIG_AP */
return;
}
if (ssid->mode == WPAS_MODE_MESH) {
#ifdef CONFIG_MESH
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) {
wpa_msg(wpa_s, MSG_INFO,
"Driver does not support mesh mode");
return;
}
if (bss)
ssid->frequency = bss->freq;
if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) {
wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh");
return;
}
wpa_s->current_bss = bss;
#else /* CONFIG_MESH */
wpa_msg(wpa_s, MSG_ERROR,
"mesh mode support not included in the build");
#endif /* CONFIG_MESH */
return;
}
/*
* Set WPA state machine configuration to match the selected network now
* so that the information is available before wpas_start_assoc_cb()
* gets called. This is needed at least for RSN pre-authentication where
* candidate APs are added to a list based on scan result processing
* before completion of the first association.
*/
wpa_supplicant_rsn_supp_set_config(wpa_s, ssid);
#ifdef CONFIG_DPP
if (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0)
return;
#endif /* CONFIG_DPP */
#ifdef CONFIG_TDLS
if (bss)
wpa_tdls_ap_ies(wpa_s->wpa, wpa_bss_ie_ptr(bss), bss->ie_len);
#endif /* CONFIG_TDLS */
#ifdef CONFIG_MBO
wpas_mbo_check_pmf(wpa_s, bss, ssid);
#endif /* CONFIG_MBO */
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
ssid->mode == WPAS_MODE_INFRA) {
sme_authenticate(wpa_s, bss, ssid);
return;
}
if (wpa_s->connect_work) {
wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist");
return;
}
if (radio_work_pending(wpa_s, "connect")) {
wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist");
return;
}
#ifdef CONFIG_SME
if (ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) {
/* Clear possibly set auth_alg, if any, from last attempt. */
wpa_s->sme.auth_alg = WPA_AUTH_ALG_OPEN;
}
#endif /* CONFIG_SME */
wpas_abort_ongoing_scan(wpa_s);
cwork = os_zalloc(sizeof(*cwork));
if (cwork == NULL)
return;
cwork->bss = bss;
cwork->ssid = ssid;
if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1,
wpas_start_assoc_cb, cwork) < 0) {
os_free(cwork);
}
}
static int bss_is_ibss(struct wpa_bss *bss)
{
return (bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
IEEE80211_CAP_IBSS;
}
static int drv_supports_vht(struct wpa_supplicant *wpa_s,
const struct wpa_ssid *ssid)
{
enum hostapd_hw_mode hw_mode;
struct hostapd_hw_modes *mode = NULL;
u8 channel;
int i;
hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
if (hw_mode == NUM_HOSTAPD_MODES)
return 0;
for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
if (wpa_s->hw.modes[i].mode == hw_mode) {
mode = &wpa_s->hw.modes[i];
break;
}
}
if (!mode)
return 0;
return mode->vht_capab != 0;
}
+static bool ibss_mesh_is_80mhz_avail(int channel, struct hostapd_hw_modes *mode)
+{
+ int i;
+
+ for (i = channel; i < channel + 16; i += 4) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_chan(mode, i, NULL);
+ if (!chan ||
+ chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+ return false;
+ }
+
+ return true;
+}
+
+
void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
const struct wpa_ssid *ssid,
struct hostapd_freq_params *freq)
{
int ieee80211_mode = wpas_mode_to_ieee80211_mode(ssid->mode);
enum hostapd_hw_mode hw_mode;
struct hostapd_hw_modes *mode = NULL;
int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
184, 192 };
- int vht80[] = { 36, 52, 100, 116, 132, 149 };
+ int bw80[] = { 5180, 5260, 5500, 5580, 5660, 5745, 5955,
+ 6035, 6115, 6195, 6275, 6355, 6435, 6515,
+ 6595, 6675, 6755, 6835, 6915, 6995 };
+ int bw160[] = { 5955, 6115, 6275, 6435, 6595, 6755, 6915 };
struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
u8 channel;
int i, chan_idx, ht40 = -1, res, obss_scan = 1;
unsigned int j, k;
struct hostapd_freq_params vht_freq;
int chwidth, seg0, seg1;
u32 vht_caps = 0;
- int is_24ghz;
+ bool is_24ghz, is_6ghz;
freq->freq = ssid->frequency;
for (j = 0; j < wpa_s->last_scan_res_used; j++) {
struct wpa_bss *bss = wpa_s->last_scan_res[j];
if (ssid->mode != WPAS_MODE_IBSS)
break;
/* Don't adjust control freq in case of fixed_freq */
if (ssid->fixed_freq)
break;
if (!bss_is_ibss(bss))
continue;
if (ssid->ssid_len == bss->ssid_len &&
os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0) {
wpa_printf(MSG_DEBUG,
"IBSS already found in scan results, adjust control freq: %d",
bss->freq);
freq->freq = bss->freq;
obss_scan = 0;
break;
}
}
/* For IBSS check HT_IBSS flag */
if (ssid->mode == WPAS_MODE_IBSS &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_HT_IBSS))
return;
if (wpa_s->group_cipher == WPA_CIPHER_WEP40 ||
wpa_s->group_cipher == WPA_CIPHER_WEP104 ||
wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
wpa_printf(MSG_DEBUG,
"IBSS: WEP/TKIP detected, do not try to enable HT");
return;
}
hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
if (wpa_s->hw.modes[i].mode == hw_mode) {
mode = &wpa_s->hw.modes[i];
break;
}
}
if (!mode)
return;
freq->channel = channel;
is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
hw_mode == HOSTAPD_MODE_IEEE80211B;
+ /* HT/VHT and corresponding overrides are not applicable to 6 GHz.
+ * However, HE is mandatory for 6 GHz.
+ */
+ is_6ghz = is_6ghz_freq(freq->freq);
+ if (is_6ghz)
+ goto skip_to_6ghz;
+
#ifdef CONFIG_HT_OVERRIDES
if (ssid->disable_ht) {
freq->ht_enabled = 0;
return;
}
#endif /* CONFIG_HT_OVERRIDES */
freq->ht_enabled = ht_supported(mode);
if (!freq->ht_enabled)
return;
/* Allow HE on 2.4 GHz without VHT: see nl80211_put_freq_params() */
if (is_24ghz)
freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported;
#ifdef CONFIG_HE_OVERRIDES
if (is_24ghz && ssid->disable_he)
freq->he_enabled = 0;
#endif /* CONFIG_HE_OVERRIDES */
/* Setup higher BW only for 5 GHz */
if (mode->mode != HOSTAPD_MODE_IEEE80211A)
return;
for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) {
pri_chan = &mode->channels[chan_idx];
if (pri_chan->chan == channel)
break;
pri_chan = NULL;
}
if (!pri_chan)
return;
/* Check primary channel flags */
if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
return;
freq->channel = pri_chan->chan;
#ifdef CONFIG_HT_OVERRIDES
if (ssid->disable_ht40) {
#ifdef CONFIG_VHT_OVERRIDES
if (ssid->disable_vht)
return;
#endif /* CONFIG_VHT_OVERRIDES */
goto skip_ht40;
}
#endif /* CONFIG_HT_OVERRIDES */
/* Check/setup HT40+/HT40- */
for (j = 0; j < ARRAY_SIZE(ht40plus); j++) {
if (ht40plus[j] == channel) {
ht40 = 1;
break;
}
}
/* Find secondary channel */
for (i = 0; i < mode->num_channels; i++) {
sec_chan = &mode->channels[i];
if (sec_chan->chan == channel + ht40 * 4)
break;
sec_chan = NULL;
}
if (!sec_chan)
return;
/* Check secondary channel flags */
if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
return;
if (ht40 == -1) {
if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
return;
} else {
if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS))
return;
}
freq->sec_channel_offset = ht40;
if (obss_scan) {
struct wpa_scan_results *scan_res;
scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
if (scan_res == NULL) {
/* Back to HT20 */
freq->sec_channel_offset = 0;
return;
}
res = check_40mhz_5g(scan_res, pri_chan, sec_chan);
switch (res) {
case 0:
/* Back to HT20 */
freq->sec_channel_offset = 0;
break;
case 1:
/* Configuration allowed */
break;
case 2:
/* Switch pri/sec channels */
freq->freq = hw_get_freq(mode, sec_chan->chan);
freq->sec_channel_offset = -freq->sec_channel_offset;
freq->channel = sec_chan->chan;
break;
default:
freq->sec_channel_offset = 0;
break;
}
wpa_scan_results_free(scan_res);
}
#ifdef CONFIG_HT_OVERRIDES
skip_ht40:
#endif /* CONFIG_HT_OVERRIDES */
wpa_printf(MSG_DEBUG,
"IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
freq->channel, freq->sec_channel_offset);
if (!drv_supports_vht(wpa_s, ssid))
return;
/* For IBSS check VHT_IBSS flag */
if (ssid->mode == WPAS_MODE_IBSS &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
return;
- vht_freq = *freq;
-
#ifdef CONFIG_VHT_OVERRIDES
if (ssid->disable_vht) {
freq->vht_enabled = 0;
return;
}
#endif /* CONFIG_VHT_OVERRIDES */
+skip_to_6ghz:
+ vht_freq = *freq;
+
+ /* 6 GHz does not have VHT enabled, so allow that exception here. */
vht_freq.vht_enabled = vht_supported(mode);
- if (!vht_freq.vht_enabled)
+ if (!vht_freq.vht_enabled && !is_6ghz)
return;
/* Enable HE with VHT for 5 GHz */
freq->he_enabled = mode->he_capab[ieee80211_mode].he_supported;
/* setup center_freq1, bandwidth */
- for (j = 0; j < ARRAY_SIZE(vht80); j++) {
- if (freq->channel >= vht80[j] &&
- freq->channel < vht80[j] + 16)
+ for (j = 0; j < ARRAY_SIZE(bw80); j++) {
+ if (freq->freq >= bw80[j] &&
+ freq->freq < bw80[j] + 80)
break;
}
- if (j == ARRAY_SIZE(vht80))
+ if (j == ARRAY_SIZE(bw80) ||
+ ieee80211_freq_to_chan(bw80[j], &channel) == NUM_HOSTAPD_MODES)
return;
- for (i = vht80[j]; i < vht80[j] + 16; i += 4) {
- struct hostapd_channel_data *chan;
+ /* Back to HT configuration if channel not usable */
+ if (!ibss_mesh_is_80mhz_avail(channel, mode))
+ return;
- chan = hw_get_channel_chan(mode, i, NULL);
- if (!chan)
- return;
+ chwidth = CHANWIDTH_80MHZ;
+ seg0 = channel + 6;
+ seg1 = 0;
- /* Back to HT configuration if channel not usable */
- if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+ if ((mode->he_capab[ieee80211_mode].phy_cap[
+ HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
+ HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G) && is_6ghz) {
+ /* In 160 MHz, the initial four 20 MHz channels were validated
+ * above; check the remaining four 20 MHz channels for the total
+ * of 160 MHz bandwidth.
+ */
+ if (!ibss_mesh_is_80mhz_avail(channel + 16, mode))
return;
- }
- chwidth = CHANWIDTH_80MHZ;
- seg0 = vht80[j] + 6;
- seg1 = 0;
+ for (j = 0; j < ARRAY_SIZE(bw160); j++) {
+ if (freq->freq == bw160[j]) {
+ chwidth = CHANWIDTH_160MHZ;
+ seg0 = channel + 14;
+ break;
+ }
+ }
+ }
if (ssid->max_oper_chwidth == CHANWIDTH_80P80MHZ) {
/* setup center_freq2, bandwidth */
- for (k = 0; k < ARRAY_SIZE(vht80); k++) {
+ for (k = 0; k < ARRAY_SIZE(bw80); k++) {
/* Only accept 80 MHz segments separated by a gap */
- if (j == k || abs(vht80[j] - vht80[k]) == 16)
+ if (j == k || abs(bw80[j] - bw80[k]) == 80)
continue;
- for (i = vht80[k]; i < vht80[k] + 16; i += 4) {
+
+ if (ieee80211_freq_to_chan(bw80[k], &channel) ==
+ NUM_HOSTAPD_MODES)
+ return;
+
+ for (i = channel; i < channel + 16; i += 4) {
struct hostapd_channel_data *chan;
chan = hw_get_channel_chan(mode, i, NULL);
if (!chan)
continue;
if (chan->flag & (HOSTAPD_CHAN_DISABLED |
HOSTAPD_CHAN_NO_IR |
HOSTAPD_CHAN_RADAR))
continue;
/* Found a suitable second segment for 80+80 */
chwidth = CHANWIDTH_80P80MHZ;
- vht_caps |=
- VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
- seg1 = vht80[k] + 6;
+ if (!is_6ghz)
+ vht_caps |=
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ seg1 = channel + 6;
}
if (chwidth == CHANWIDTH_80P80MHZ)
break;
}
} else if (ssid->max_oper_chwidth == CHANWIDTH_160MHZ) {
if (freq->freq == 5180) {
chwidth = CHANWIDTH_160MHZ;
vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
seg0 = 50;
} else if (freq->freq == 5520) {
chwidth = CHANWIDTH_160MHZ;
vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
seg0 = 114;
}
} else if (ssid->max_oper_chwidth == CHANWIDTH_USE_HT) {
chwidth = CHANWIDTH_USE_HT;
- seg0 = vht80[j] + 2;
+ seg0 = channel + 2;
#ifdef CONFIG_HT_OVERRIDES
if (ssid->disable_ht40)
seg0 = 0;
#endif /* CONFIG_HT_OVERRIDES */
}
#ifdef CONFIG_HE_OVERRIDES
if (ssid->disable_he) {
vht_freq.he_enabled = 0;
freq->he_enabled = 0;
}
#endif /* CONFIG_HE_OVERRIDES */
if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
freq->channel, ssid->enable_edmg,
ssid->edmg_channel, freq->ht_enabled,
vht_freq.vht_enabled, freq->he_enabled,
freq->sec_channel_offset,
chwidth, seg0, seg1, vht_caps,
&mode->he_capab[ieee80211_mode]) != 0)
return;
*freq = vht_freq;
wpa_printf(MSG_DEBUG, "IBSS: VHT setup freq cf1 %d, cf2 %d, bw %d",
freq->center_freq1, freq->center_freq2, freq->bandwidth);
}
#ifdef CONFIG_FILS
static size_t wpas_add_fils_hlp_req(struct wpa_supplicant *wpa_s, u8 *ie_buf,
size_t ie_buf_len)
{
struct fils_hlp_req *req;
size_t rem_len, hdr_len, hlp_len, len, ie_len = 0;
const u8 *pos;
u8 *buf = ie_buf;
dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
list) {
rem_len = ie_buf_len - ie_len;
pos = wpabuf_head(req->pkt);
hdr_len = 1 + 2 * ETH_ALEN + 6;
hlp_len = wpabuf_len(req->pkt);
if (rem_len < 2 + hdr_len + hlp_len) {
wpa_printf(MSG_ERROR,
"FILS: Cannot fit HLP - rem_len=%lu to_fill=%lu",
(unsigned long) rem_len,
(unsigned long) (2 + hdr_len + hlp_len));
break;
}
len = (hdr_len + hlp_len) > 255 ? 255 : hdr_len + hlp_len;
/* Element ID */
*buf++ = WLAN_EID_EXTENSION;
/* Length */
*buf++ = len;
/* Element ID Extension */
*buf++ = WLAN_EID_EXT_FILS_HLP_CONTAINER;
/* Destination MAC address */
os_memcpy(buf, req->dst, ETH_ALEN);
buf += ETH_ALEN;
/* Source MAC address */
os_memcpy(buf, wpa_s->own_addr, ETH_ALEN);
buf += ETH_ALEN;
/* LLC/SNAP Header */
os_memcpy(buf, "\xaa\xaa\x03\x00\x00\x00", 6);
buf += 6;
/* HLP Packet */
os_memcpy(buf, pos, len - hdr_len);
buf += len - hdr_len;
pos += len - hdr_len;
hlp_len -= len - hdr_len;
ie_len += 2 + len;
rem_len -= 2 + len;
while (hlp_len) {
len = (hlp_len > 255) ? 255 : hlp_len;
if (rem_len < 2 + len)
break;
*buf++ = WLAN_EID_FRAGMENT;
*buf++ = len;
os_memcpy(buf, pos, len);
buf += len;
pos += len;
hlp_len -= len;
ie_len += 2 + len;
rem_len -= 2 + len;
}
}
return ie_len;
}
int wpa_is_fils_supported(struct wpa_supplicant *wpa_s)
{
return (((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) ||
(!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)));
}
int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_FILS_SK_PFS
return (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS);
#else /* CONFIG_FILS_SK_PFS */
return 0;
#endif /* CONFIG_FILS_SK_PFS */
}
#endif /* CONFIG_FILS */
+static int wpas_populate_wfa_capa(struct wpa_supplicant *wpa_s,
+ struct wpa_bss *bss,
+ u8 *wpa_ie, size_t wpa_ie_len,
+ size_t max_wpa_ie_len)
+{
+ struct wpabuf *wfa_ie = NULL;
+ u8 wfa_capa[1];
+ size_t wfa_ie_len, buf_len;
+
+ os_memset(wfa_capa, 0, sizeof(wfa_capa));
+ if (wpa_s->enable_dscp_policy_capa)
+ wfa_capa[0] |= WFA_CAPA_QM_DSCP_POLICY;
+
+ if (!wfa_capa[0])
+ return wpa_ie_len;
+
+ /* Wi-Fi Alliance element */
+ buf_len = 1 + /* Element ID */
+ 1 + /* Length */
+ 3 + /* OUI */
+ 1 + /* OUI Type */
+ 1 + /* Capabilities Length */
+ sizeof(wfa_capa); /* Capabilities */
+ wfa_ie = wpabuf_alloc(buf_len);
+ if (!wfa_ie)
+ return wpa_ie_len;
+
+ wpabuf_put_u8(wfa_ie, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(wfa_ie, buf_len - 2);
+ wpabuf_put_be24(wfa_ie, OUI_WFA);
+ wpabuf_put_u8(wfa_ie, WFA_CAPA_OUI_TYPE);
+ wpabuf_put_u8(wfa_ie, sizeof(wfa_capa));
+ wpabuf_put_data(wfa_ie, wfa_capa, sizeof(wfa_capa));
+
+ wfa_ie_len = wpabuf_len(wfa_ie);
+ if (wpa_ie_len + wfa_ie_len <= max_wpa_ie_len) {
+ wpa_hexdump_buf(MSG_MSGDUMP, "WFA Capabilities element",
+ wfa_ie);
+ os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(wfa_ie),
+ wfa_ie_len);
+ wpa_ie_len += wfa_ie_len;
+ }
+
+ wpabuf_free(wfa_ie);
+ return wpa_ie_len;
+}
+
+
static u8 * wpas_populate_assoc_ies(
struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params,
enum wpa_drv_update_connect_params_mask *mask)
{
u8 *wpa_ie;
size_t max_wpa_ie_len = 500;
size_t wpa_ie_len;
int algs = WPA_AUTH_ALG_OPEN;
#ifdef CONFIG_MBO
const u8 *mbo_ie;
#endif
#if defined(CONFIG_SAE) || defined(CONFIG_FILS)
int pmksa_cached = 0;
#endif /* CONFIG_SAE || CONFIG_FILS */
#ifdef CONFIG_FILS
const u8 *realm, *username, *rrk;
size_t realm_len, username_len, rrk_len;
u16 next_seq_num;
struct fils_hlp_req *req;
dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
list) {
max_wpa_ie_len += 3 + 2 * ETH_ALEN + 6 + wpabuf_len(req->pkt) +
2 + 2 * wpabuf_len(req->pkt) / 255;
}
#endif /* CONFIG_FILS */
wpa_ie = os_malloc(max_wpa_ie_len);
if (!wpa_ie) {
wpa_printf(MSG_ERROR,
"Failed to allocate connect IE buffer for %lu bytes",
(unsigned long) max_wpa_ie_len);
return NULL;
}
if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
wpa_key_mgmt_wpa(ssid->key_mgmt)) {
int try_opportunistic;
const u8 *cache_id = NULL;
try_opportunistic = (ssid->proactive_key_caching < 0 ?
wpa_s->conf->okc :
ssid->proactive_key_caching) &&
(ssid->proto & WPA_PROTO_RSN);
#ifdef CONFIG_FILS
if (wpa_key_mgmt_fils(ssid->key_mgmt))
cache_id = wpa_bss_get_fils_cache_id(bss);
#endif /* CONFIG_FILS */
if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
ssid, try_opportunistic,
cache_id, 0) == 0) {
eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
#if defined(CONFIG_SAE) || defined(CONFIG_FILS)
pmksa_cached = 1;
#endif /* CONFIG_SAE || CONFIG_FILS */
}
wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_ie, &wpa_ie_len)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites");
os_free(wpa_ie);
return NULL;
}
#ifdef CONFIG_HS20
} else if (bss && wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) &&
(ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) {
/* No PMKSA caching, but otherwise similar to RSN/WPA */
wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
wpa_ie, &wpa_ie_len)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites");
os_free(wpa_ie);
return NULL;
}
#endif /* CONFIG_HS20 */
} else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) {
/*
* Both WPA and non-WPA IEEE 802.1X enabled in configuration -
* use non-WPA since the scan results did not indicate that the
* AP is using WPA or WPA2.
*/
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
wpa_ie_len = 0;
wpa_s->wpa_proto = 0;
} else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) {
wpa_ie_len = max_wpa_ie_len;
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
wpa_ie, &wpa_ie_len)) {
wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
"key management and encryption suites (no "
"scan results)");
os_free(wpa_ie);
return NULL;
}
#ifdef CONFIG_WPS
} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
struct wpabuf *wps_ie;
wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
if (wps_ie && wpabuf_len(wps_ie) <= max_wpa_ie_len) {
wpa_ie_len = wpabuf_len(wps_ie);
os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
} else
wpa_ie_len = 0;
wpabuf_free(wps_ie);
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY))
params->wps = WPS_MODE_PRIVACY;
else
params->wps = WPS_MODE_OPEN;
wpa_s->wpa_proto = 0;
#endif /* CONFIG_WPS */
} else {
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
wpa_ie_len = 0;
wpa_s->wpa_proto = 0;
}
#ifdef IEEE8021X_EAPOL
if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if (ssid->leap) {
if (ssid->non_leap == 0)
algs = WPA_AUTH_ALG_LEAP;
else
algs |= WPA_AUTH_ALG_LEAP;
}
}
#ifdef CONFIG_FILS
/* Clear FILS association */
wpa_sm_set_reset_fils_completed(wpa_s->wpa, 0);
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) &&
ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) &&
eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, &username,
&username_len, &realm, &realm_len,
&next_seq_num, &rrk, &rrk_len) == 0 &&
(!wpa_s->last_con_fail_realm ||
wpa_s->last_con_fail_realm_len != realm_len ||
os_memcmp(wpa_s->last_con_fail_realm, realm, realm_len) != 0)) {
algs = WPA_AUTH_ALG_FILS;
params->fils_erp_username = username;
params->fils_erp_username_len = username_len;
params->fils_erp_realm = realm;
params->fils_erp_realm_len = realm_len;
params->fils_erp_next_seq_num = next_seq_num;
params->fils_erp_rrk = rrk;
params->fils_erp_rrk_len = rrk_len;
if (mask)
*mask |= WPA_DRV_UPDATE_FILS_ERP_INFO;
} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) &&
ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) &&
pmksa_cached) {
algs = WPA_AUTH_ALG_FILS;
}
#endif /* CONFIG_FILS */
#endif /* IEEE8021X_EAPOL */
#ifdef CONFIG_SAE
if (wpa_s->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE))
algs = WPA_AUTH_ALG_SAE;
#endif /* CONFIG_SAE */
wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
if (ssid->auth_alg) {
algs = ssid->auth_alg;
wpa_dbg(wpa_s, MSG_DEBUG,
"Overriding auth_alg selection: 0x%x", algs);
}
#ifdef CONFIG_SAE
if (pmksa_cached && algs == WPA_AUTH_ALG_SAE) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SAE: Use WPA_AUTH_ALG_OPEN for PMKSA caching attempt");
algs = WPA_AUTH_ALG_OPEN;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_P2P
if (wpa_s->global->p2p) {
u8 *pos;
size_t len;
int res;
pos = wpa_ie + wpa_ie_len;
len = max_wpa_ie_len - wpa_ie_len;
res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len,
ssid->p2p_group);
if (res >= 0)
wpa_ie_len += res;
}
wpa_s->cross_connect_disallowed = 0;
if (bss) {
struct wpabuf *p2p;
p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
if (p2p) {
wpa_s->cross_connect_disallowed =
p2p_get_cross_connect_disallowed(p2p);
wpabuf_free(p2p);
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: WLAN AP %s cross "
"connection",
wpa_s->cross_connect_disallowed ?
"disallows" : "allows");
}
}
os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
#endif /* CONFIG_P2P */
if (bss) {
wpa_ie_len += wpas_supp_op_class_ie(wpa_s, ssid, bss,
wpa_ie + wpa_ie_len,
max_wpa_ie_len -
wpa_ie_len);
}
/*
* Workaround: Add Extended Capabilities element only if the AP
* included this element in Beacon/Probe Response frames. Some older
* APs seem to have interoperability issues if this element is
* included, so while the standard may require us to include the
* element in all cases, it is justifiable to skip it to avoid
* interoperability issues.
*/
if (ssid->p2p_group)
wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
else
wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
u8 ext_capab[18];
int ext_capab_len;
ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
sizeof(ext_capab));
if (ext_capab_len > 0 &&
wpa_ie_len + ext_capab_len <= max_wpa_ie_len) {
u8 *pos = wpa_ie;
if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
pos += 2 + pos[1];
os_memmove(pos + ext_capab_len, pos,
wpa_ie_len - (pos - wpa_ie));
wpa_ie_len += ext_capab_len;
os_memcpy(pos, ext_capab, ext_capab_len);
}
}
#ifdef CONFIG_HS20
if (is_hs20_network(wpa_s, ssid, bss)) {
struct wpabuf *hs20;
hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN);
if (hs20) {
int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
size_t len;
wpas_hs20_add_indication(hs20, pps_mo_id,
get_hs20_version(bss));
wpas_hs20_add_roam_cons_sel(hs20, ssid);
len = max_wpa_ie_len - wpa_ie_len;
if (wpabuf_len(hs20) <= len) {
os_memcpy(wpa_ie + wpa_ie_len,
wpabuf_head(hs20), wpabuf_len(hs20));
wpa_ie_len += wpabuf_len(hs20);
}
wpabuf_free(hs20);
hs20_configure_frame_filters(wpa_s);
}
}
#endif /* CONFIG_HS20 */
if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
size_t len;
len = max_wpa_ie_len - wpa_ie_len;
if (wpabuf_len(buf) <= len) {
os_memcpy(wpa_ie + wpa_ie_len,
wpabuf_head(buf), wpabuf_len(buf));
wpa_ie_len += wpabuf_len(buf);
}
}
#ifdef CONFIG_FST
if (wpa_s->fst_ies) {
int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
if (wpa_ie_len + fst_ies_len <= max_wpa_ie_len) {
os_memcpy(wpa_ie + wpa_ie_len,
wpabuf_head(wpa_s->fst_ies), fst_ies_len);
wpa_ie_len += fst_ies_len;
}
}
#endif /* CONFIG_FST */
#ifdef CONFIG_MBO
mbo_ie = bss ? wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE) : NULL;
if (!wpa_s->disable_mbo_oce && mbo_ie) {
int len;
len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
max_wpa_ie_len - wpa_ie_len,
!!mbo_attr_from_mbo_ie(mbo_ie,
OCE_ATTR_ID_CAPA_IND));
if (len >= 0)
wpa_ie_len += len;
}
#endif /* CONFIG_MBO */
#ifdef CONFIG_FILS
if (algs == WPA_AUTH_ALG_FILS) {
size_t len;
len = wpas_add_fils_hlp_req(wpa_s, wpa_ie + wpa_ie_len,
max_wpa_ie_len - wpa_ie_len);
wpa_ie_len += len;
}
#endif /* CONFIG_FILS */
#ifdef CONFIG_OWE
#ifdef CONFIG_TESTING_OPTIONS
if (get_ie_ext(wpa_ie, wpa_ie_len, WLAN_EID_EXT_OWE_DH_PARAM)) {
wpa_printf(MSG_INFO, "TESTING: Override OWE DH element");
} else
#endif /* CONFIG_TESTING_OPTIONS */
if (algs == WPA_AUTH_ALG_OPEN &&
ssid->key_mgmt == WPA_KEY_MGMT_OWE) {
struct wpabuf *owe_ie;
u16 group;
if (ssid->owe_group) {
group = ssid->owe_group;
} else if (wpa_s->assoc_status_code ==
WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) {
if (wpa_s->last_owe_group == 19)
group = 20;
else if (wpa_s->last_owe_group == 20)
group = 21;
else
group = OWE_DH_GROUP;
} else {
group = OWE_DH_GROUP;
}
wpa_s->last_owe_group = group;
wpa_printf(MSG_DEBUG, "OWE: Try to use group %u", group);
owe_ie = owe_build_assoc_req(wpa_s->wpa, group);
if (owe_ie &&
wpabuf_len(owe_ie) <= max_wpa_ie_len - wpa_ie_len) {
os_memcpy(wpa_ie + wpa_ie_len,
wpabuf_head(owe_ie), wpabuf_len(owe_ie));
wpa_ie_len += wpabuf_len(owe_ie);
}
wpabuf_free(owe_ie);
}
#endif /* CONFIG_OWE */
#ifdef CONFIG_DPP2
if (DPP_VERSION > 1 &&
wpa_sm_get_key_mgmt(wpa_s->wpa) == WPA_KEY_MGMT_DPP &&
ssid->dpp_netaccesskey &&
ssid->dpp_pfs != 2 && !ssid->dpp_pfs_fallback) {
struct rsn_pmksa_cache_entry *pmksa;
pmksa = pmksa_cache_get_current(wpa_s->wpa);
if (!pmksa || !pmksa->dpp_pfs)
goto pfs_fail;
dpp_pfs_free(wpa_s->dpp_pfs);
wpa_s->dpp_pfs = dpp_pfs_init(ssid->dpp_netaccesskey,
ssid->dpp_netaccesskey_len);
if (!wpa_s->dpp_pfs) {
wpa_printf(MSG_DEBUG, "DPP: Could not initialize PFS");
/* Try to continue without PFS */
goto pfs_fail;
}
if (wpabuf_len(wpa_s->dpp_pfs->ie) <=
max_wpa_ie_len - wpa_ie_len) {
os_memcpy(wpa_ie + wpa_ie_len,
wpabuf_head(wpa_s->dpp_pfs->ie),
wpabuf_len(wpa_s->dpp_pfs->ie));
wpa_ie_len += wpabuf_len(wpa_s->dpp_pfs->ie);
}
}
pfs_fail:
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_IEEE80211R
/*
* Add MDIE under these conditions: the network profile allows FT,
* the AP supports FT, and the mobility domain ID matches.
*/
if (bss && wpa_key_mgmt_ft(wpa_sm_get_key_mgmt(wpa_s->wpa))) {
const u8 *mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
if (mdie && mdie[1] >= MOBILITY_DOMAIN_ID_LEN) {
size_t len = 0;
const u8 *md = mdie + 2;
const u8 *wpa_md = wpa_sm_get_ft_md(wpa_s->wpa);
if (os_memcmp(md, wpa_md,
MOBILITY_DOMAIN_ID_LEN) == 0) {
/* Add mobility domain IE */
len = wpa_ft_add_mdie(
wpa_s->wpa, wpa_ie + wpa_ie_len,
max_wpa_ie_len - wpa_ie_len, mdie);
wpa_ie_len += len;
}
#ifdef CONFIG_SME
if (len > 0 && wpa_s->sme.ft_used &&
wpa_sm_has_ptk(wpa_s->wpa)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"SME: Trying to use FT over-the-air");
algs |= WPA_AUTH_ALG_FT;
}
#endif /* CONFIG_SME */
}
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->rsnxe_override_assoc &&
wpabuf_len(wpa_s->rsnxe_override_assoc) <=
max_wpa_ie_len - wpa_ie_len) {
wpa_printf(MSG_DEBUG, "TESTING: RSNXE AssocReq override");
os_memcpy(wpa_ie + wpa_ie_len,
wpabuf_head(wpa_s->rsnxe_override_assoc),
wpabuf_len(wpa_s->rsnxe_override_assoc));
wpa_ie_len += wpabuf_len(wpa_s->rsnxe_override_assoc);
} else
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->rsnxe_len > 0 &&
wpa_s->rsnxe_len <= max_wpa_ie_len - wpa_ie_len) {
os_memcpy(wpa_ie + wpa_ie_len, wpa_s->rsnxe, wpa_s->rsnxe_len);
wpa_ie_len += wpa_s->rsnxe_len;
}
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->disable_mscs_support)
+ goto mscs_end;
+#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_bss_ext_capab(bss, WLAN_EXT_CAPAB_MSCS) &&
wpa_s->robust_av.valid_config) {
struct wpabuf *mscs_ie;
size_t mscs_ie_len, buf_len;
buf_len = 3 + /* MSCS descriptor IE header */
1 + /* Request type */
2 + /* User priority control */
4 + /* Stream timeout */
3 + /* TCLAS Mask IE header */
wpa_s->robust_av.frame_classifier_len;
mscs_ie = wpabuf_alloc(buf_len);
if (!mscs_ie) {
wpa_printf(MSG_INFO,
"MSCS: Failed to allocate MSCS IE");
- goto mscs_fail;
+ goto mscs_end;
}
wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, mscs_ie);
if ((wpa_ie_len + wpabuf_len(mscs_ie)) <= max_wpa_ie_len) {
wpa_hexdump_buf(MSG_MSGDUMP, "MSCS IE", mscs_ie);
mscs_ie_len = wpabuf_len(mscs_ie);
os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(mscs_ie),
mscs_ie_len);
wpa_ie_len += mscs_ie_len;
}
wpabuf_free(mscs_ie);
}
-mscs_fail:
+mscs_end:
+
+ wpa_ie_len = wpas_populate_wfa_capa(wpa_s, bss, wpa_ie, wpa_ie_len,
+ max_wpa_ie_len);
if (ssid->multi_ap_backhaul_sta) {
size_t multi_ap_ie_len;
multi_ap_ie_len = add_multi_ap_ie(wpa_ie + wpa_ie_len,
max_wpa_ie_len - wpa_ie_len,
MULTI_AP_BACKHAUL_STA);
if (multi_ap_ie_len == 0) {
wpa_printf(MSG_ERROR,
"Multi-AP: Failed to build Multi-AP IE");
os_free(wpa_ie);
return NULL;
}
wpa_ie_len += multi_ap_ie_len;
}
params->wpa_ie = wpa_ie;
params->wpa_ie_len = wpa_ie_len;
params->auth_alg = algs;
if (mask)
*mask |= WPA_DRV_UPDATE_ASSOC_IES | WPA_DRV_UPDATE_AUTH_TYPE;
return wpa_ie;
}
#ifdef CONFIG_OWE
static void wpas_update_owe_connect_params(struct wpa_supplicant *wpa_s)
{
struct wpa_driver_associate_params params;
u8 *wpa_ie;
os_memset(&params, 0, sizeof(params));
wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid, &params, NULL);
if (!wpa_ie)
return;
wpa_drv_update_connect_params(wpa_s, &params, WPA_DRV_UPDATE_ASSOC_IES);
os_free(wpa_ie);
}
#endif /* CONFIG_OWE */
#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL)
static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s)
{
struct wpa_driver_associate_params params;
enum wpa_drv_update_connect_params_mask mask = 0;
u8 *wpa_ie;
if (wpa_s->auth_alg != WPA_AUTH_ALG_OPEN)
return; /* nothing to do */
os_memset(&params, 0, sizeof(params));
wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid, &params, &mask);
if (!wpa_ie)
return;
if (params.auth_alg == WPA_AUTH_ALG_FILS) {
wpa_s->auth_alg = params.auth_alg;
wpa_drv_update_connect_params(wpa_s, &params, mask);
}
os_free(wpa_ie);
}
#endif /* CONFIG_FILS && IEEE8021X_EAPOL */
static u8 wpa_ie_get_edmg_oper_chans(const u8 *edmg_ie)
{
if (!edmg_ie || edmg_ie[1] < 6)
return 0;
return edmg_ie[EDMG_BSS_OPERATING_CHANNELS_OFFSET];
}
static u8 wpa_ie_get_edmg_oper_chan_width(const u8 *edmg_ie)
{
if (!edmg_ie || edmg_ie[1] < 6)
return 0;
return edmg_ie[EDMG_OPERATING_CHANNEL_WIDTH_OFFSET];
}
/* Returns the intersection of two EDMG configurations.
* Note: The current implementation is limited to CB2 only (CB1 included),
* i.e., the implementation supports up to 2 contiguous channels.
* For supporting non-contiguous (aggregated) channels and for supporting
* CB3 and above, this function will need to be extended.
*/
static struct ieee80211_edmg_config
get_edmg_intersection(struct ieee80211_edmg_config a,
struct ieee80211_edmg_config b,
u8 primary_channel)
{
struct ieee80211_edmg_config result;
int i, contiguous = 0;
int max_contiguous = 0;
result.channels = b.channels & a.channels;
if (!result.channels) {
wpa_printf(MSG_DEBUG,
"EDMG not possible: cannot intersect channels 0x%x and 0x%x",
a.channels, b.channels);
goto fail;
}
if (!(result.channels & BIT(primary_channel - 1))) {
wpa_printf(MSG_DEBUG,
"EDMG not possible: the primary channel %d is not one of the intersected channels 0x%x",
primary_channel, result.channels);
goto fail;
}
/* Find max contiguous channels */
for (i = 0; i < 6; i++) {
if (result.channels & BIT(i))
contiguous++;
else
contiguous = 0;
if (contiguous > max_contiguous)
max_contiguous = contiguous;
}
/* Assuming AP and STA supports ONLY contiguous channels,
* bw configuration can have value between 4-7.
*/
if ((b.bw_config < a.bw_config))
result.bw_config = b.bw_config;
else
result.bw_config = a.bw_config;
if ((max_contiguous >= 2 && result.bw_config < EDMG_BW_CONFIG_5) ||
(max_contiguous >= 1 && result.bw_config < EDMG_BW_CONFIG_4)) {
wpa_printf(MSG_DEBUG,
"EDMG not possible: not enough contiguous channels %d for supporting CB1 or CB2",
max_contiguous);
goto fail;
}
return result;
fail:
result.channels = 0;
result.bw_config = 0;
return result;
}
static struct ieee80211_edmg_config
get_supported_edmg(struct wpa_supplicant *wpa_s,
struct hostapd_freq_params *freq,
struct ieee80211_edmg_config request_edmg)
{
enum hostapd_hw_mode hw_mode;
struct hostapd_hw_modes *mode = NULL;
u8 primary_channel;
if (!wpa_s->hw.modes)
goto fail;
hw_mode = ieee80211_freq_to_chan(freq->freq, &primary_channel);
if (hw_mode == NUM_HOSTAPD_MODES)
goto fail;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, hw_mode, false);
if (!mode)
goto fail;
return get_edmg_intersection(mode->edmg, request_edmg, primary_channel);
fail:
request_edmg.channels = 0;
request_edmg.bw_config = 0;
return request_edmg;
}
#ifdef CONFIG_MBO
void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s)
{
struct wpa_driver_associate_params params;
u8 *wpa_ie;
/*
* Update MBO connect params only in case of change of MBO attributes
* when connected, if the AP support MBO.
*/
if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid ||
!wpa_s->current_bss ||
!wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE))
return;
os_memset(&params, 0, sizeof(params));
wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss,
wpa_s->current_ssid, &params, NULL);
if (!wpa_ie)
return;
wpa_drv_update_connect_params(wpa_s, &params, WPA_DRV_UPDATE_ASSOC_IES);
os_free(wpa_ie);
}
#endif /* CONFIG_MBO */
static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
{
struct wpa_connect_work *cwork = work->ctx;
struct wpa_bss *bss = cwork->bss;
struct wpa_ssid *ssid = cwork->ssid;
struct wpa_supplicant *wpa_s = work->wpa_s;
u8 *wpa_ie;
const u8 *edmg_ie_oper;
int use_crypt, ret, bssid_changed;
unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt;
struct wpa_driver_associate_params params;
#if defined(CONFIG_WEP) || defined(IEEE8021X_EAPOL)
int wep_keys_set = 0;
#endif /* CONFIG_WEP || IEEE8021X_EAPOL */
int assoc_failed = 0;
struct wpa_ssid *old_ssid;
u8 prev_bssid[ETH_ALEN];
#ifdef CONFIG_HT_OVERRIDES
struct ieee80211_ht_capabilities htcaps;
struct ieee80211_ht_capabilities htcaps_mask;
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
struct ieee80211_vht_capabilities vhtcaps;
struct ieee80211_vht_capabilities vhtcaps_mask;
#endif /* CONFIG_VHT_OVERRIDES */
if (deinit) {
if (work->started) {
wpa_s->connect_work = NULL;
/* cancel possible auth. timeout */
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
NULL);
}
wpas_connect_work_free(cwork);
return;
}
wpa_s->connect_work = work;
if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
wpas_network_disabled(wpa_s, ssid)) {
wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
wpas_connect_work_done(wpa_s);
return;
}
os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN);
os_memset(&params, 0, sizeof(params));
wpa_s->reassociate = 0;
wpa_s->eap_expected_failure = 0;
/* Starting new association, so clear the possibly used WPA IE from the
* previous association. */
wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
wpa_sm_set_assoc_rsnxe(wpa_s->wpa, NULL, 0);
wpa_s->rsnxe_len = 0;
wpa_s->mscs_setup_done = false;
wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, &params, NULL);
if (!wpa_ie) {
wpas_connect_work_done(wpa_s);
return;
}
if (bss &&
(!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {
#ifdef CONFIG_IEEE80211R
const u8 *ie, *md = NULL;
#endif /* CONFIG_IEEE80211R */
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
" (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
os_memset(wpa_s->bssid, 0, ETH_ALEN);
os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
if (bssid_changed)
wpas_notify_bssid_changed(wpa_s);
#ifdef CONFIG_IEEE80211R
ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
md = ie + 2;
wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
if (md) {
/* Prepare for the next transition */
wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_WPS
} else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
wpa_s->conf->ap_scan == 2 &&
(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
/* Use ap_scan==1 style network selection to find the network
*/
wpas_connect_work_done(wpa_s);
wpa_s->scan_req = MANUAL_SCAN_REQ;
wpa_s->reassociate = 1;
wpa_supplicant_req_scan(wpa_s, 0, 0);
os_free(wpa_ie);
return;
#endif /* CONFIG_WPS */
} else {
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
if (bss)
os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
else
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
}
if (!wpa_s->pno)
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
use_crypt = 1;
cipher_pairwise = wpa_s->pairwise_cipher;
cipher_group = wpa_s->group_cipher;
cipher_group_mgmt = wpa_s->mgmt_group_cipher;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
use_crypt = 0;
#ifdef CONFIG_WEP
if (wpa_set_wep_keys(wpa_s, ssid)) {
use_crypt = 1;
wep_keys_set = 1;
}
#endif /* CONFIG_WEP */
}
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS)
use_crypt = 0;
#ifdef IEEE8021X_EAPOL
if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
if ((ssid->eapol_flags &
(EAPOL_FLAG_REQUIRE_KEY_UNICAST |
EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 &&
!wep_keys_set) {
use_crypt = 0;
} else {
/* Assume that dynamic WEP-104 keys will be used and
* set cipher suites in order for drivers to expect
* encryption. */
cipher_pairwise = cipher_group = WPA_CIPHER_WEP104;
}
}
#endif /* IEEE8021X_EAPOL */
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/* Set the key before (and later after) association */
wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
}
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
if (bss) {
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
MACSTR " freq=%u MHz based on scan results "
"(bssid_set=%d wps=%d)",
MAC2STR(bss->bssid), bss->freq,
ssid->bssid_set,
wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
params.bssid = bss->bssid;
params.freq.freq = bss->freq;
}
params.bssid_hint = bss->bssid;
params.freq_hint = bss->freq;
params.pbss = bss_is_pbss(bss);
} else {
if (ssid->bssid_hint_set)
params.bssid_hint = ssid->bssid_hint;
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0;
}
if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
wpa_s->conf->ap_scan == 2) {
params.bssid = ssid->bssid;
params.fixed_bssid = 1;
}
/* Initial frequency for IBSS/mesh */
if ((ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) &&
ssid->frequency > 0 && params.freq.freq == 0)
ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
if (ssid->mode == WPAS_MODE_IBSS) {
params.fixed_freq = ssid->fixed_freq;
if (ssid->beacon_int)
params.beacon_int = ssid->beacon_int;
else
params.beacon_int = wpa_s->conf->beacon_int;
}
if (bss && ssid->enable_edmg)
edmg_ie_oper = wpa_bss_get_ie_ext(bss,
WLAN_EID_EXT_EDMG_OPERATION);
else
edmg_ie_oper = NULL;
if (edmg_ie_oper) {
params.freq.edmg.channels =
wpa_ie_get_edmg_oper_chans(edmg_ie_oper);
params.freq.edmg.bw_config =
wpa_ie_get_edmg_oper_chan_width(edmg_ie_oper);
wpa_printf(MSG_DEBUG,
"AP supports EDMG channels 0x%x, bw_config %d",
params.freq.edmg.channels,
params.freq.edmg.bw_config);
/* User may ask for specific EDMG channel for EDMG connection
* (must be supported by AP)
*/
if (ssid->edmg_channel) {
struct ieee80211_edmg_config configured_edmg;
enum hostapd_hw_mode hw_mode;
u8 primary_channel;
hw_mode = ieee80211_freq_to_chan(bss->freq,
&primary_channel);
if (hw_mode == NUM_HOSTAPD_MODES)
goto edmg_fail;
hostapd_encode_edmg_chan(ssid->enable_edmg,
ssid->edmg_channel,
primary_channel,
&configured_edmg);
if (ieee802_edmg_is_allowed(params.freq.edmg,
configured_edmg)) {
params.freq.edmg = configured_edmg;
wpa_printf(MSG_DEBUG,
"Use EDMG channel %d for connection",
ssid->edmg_channel);
} else {
edmg_fail:
params.freq.edmg.channels = 0;
params.freq.edmg.bw_config = 0;
wpa_printf(MSG_WARNING,
"EDMG channel %d not supported by AP, fallback to DMG",
ssid->edmg_channel);
}
}
if (params.freq.edmg.channels) {
wpa_printf(MSG_DEBUG,
"EDMG before: channels 0x%x, bw_config %d",
params.freq.edmg.channels,
params.freq.edmg.bw_config);
params.freq.edmg = get_supported_edmg(wpa_s,
&params.freq,
params.freq.edmg);
wpa_printf(MSG_DEBUG,
"EDMG after: channels 0x%x, bw_config %d",
params.freq.edmg.channels,
params.freq.edmg.bw_config);
}
}
params.pairwise_suite = cipher_pairwise;
params.group_suite = cipher_group;
params.mgmt_group_suite = cipher_group_mgmt;
params.key_mgmt_suite = wpa_s->key_mgmt;
params.wpa_proto = wpa_s->wpa_proto;
wpa_s->auth_alg = params.auth_alg;
params.mode = ssid->mode;
params.bg_scan_period = ssid->bg_scan_period;
#ifdef CONFIG_WEP
{
int i;
for (i = 0; i < NUM_WEP_KEYS; i++) {
if (ssid->wep_key_len[i])
params.wep_key[i] = ssid->wep_key[i];
params.wep_key_len[i] = ssid->wep_key_len[i];
}
params.wep_tx_keyidx = ssid->wep_tx_keyidx;
}
#endif /* CONFIG_WEP */
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) &&
(params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
params.passphrase = ssid->passphrase;
if (ssid->psk_set)
params.psk = ssid->psk;
}
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X) &&
(params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192))
params.req_handshake_offload = 1;
if (wpa_s->conf->key_mgmt_offload) {
if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
params.req_key_mgmt_offload =
ssid->proactive_key_caching < 0 ?
wpa_s->conf->okc : ssid->proactive_key_caching;
else
params.req_key_mgmt_offload = 1;
if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
ssid->psk_set)
params.psk = ssid->psk;
}
params.drop_unencrypted = use_crypt;
params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid);
if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) {
const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
struct wpa_ie_data ie;
if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
ie.capabilities &
(WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Selected AP supports "
"MFP: require MFP");
params.mgmt_frame_protection =
MGMT_FRAME_PROTECTION_REQUIRED;
#ifdef CONFIG_OWE
} else if (!rsn && (ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
!ssid->owe_only) {
params.mgmt_frame_protection = NO_MGMT_FRAME_PROTECTION;
#endif /* CONFIG_OWE */
}
}
params.p2p = ssid->p2p_group;
if (wpa_s->p2pdev->set_sta_uapsd)
params.uapsd = wpa_s->p2pdev->sta_uapsd;
else
params.uapsd = -1;
#ifdef CONFIG_HT_OVERRIDES
os_memset(&htcaps, 0, sizeof(htcaps));
os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
params.htcaps = (u8 *) &htcaps;
params.htcaps_mask = (u8 *) &htcaps_mask;
wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
os_memset(&vhtcaps, 0, sizeof(vhtcaps));
os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
params.vhtcaps = &vhtcaps;
params.vhtcaps_mask = &vhtcaps_mask;
wpa_supplicant_apply_vht_overrides(wpa_s, ssid, &params);
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_HE_OVERRIDES
wpa_supplicant_apply_he_overrides(wpa_s, ssid, &params);
#endif /* CONFIG_HE_OVERRIDES */
#ifdef CONFIG_P2P
/*
* If multi-channel concurrency is not supported, check for any
* frequency conflict. In case of any frequency conflict, remove the
* least prioritized connection.
*/
if (wpa_s->num_multichan_concurrent < 2) {
int freq, num;
num = get_shared_radio_freqs(wpa_s, &freq, 1);
if (num > 0 && freq > 0 && freq != params.freq.freq) {
wpa_printf(MSG_DEBUG,
"Assoc conflicting freq found (%d != %d)",
freq, params.freq.freq);
if (wpas_p2p_handle_frequency_conflicts(
wpa_s, params.freq.freq, ssid) < 0) {
wpas_connect_work_done(wpa_s);
os_free(wpa_ie);
return;
}
}
}
#endif /* CONFIG_P2P */
if (wpa_s->reassoc_same_ess && !is_zero_ether_addr(prev_bssid) &&
wpa_s->current_ssid)
params.prev_bssid = prev_bssid;
#ifdef CONFIG_SAE
params.sae_pwe = wpa_s->conf->sae_pwe;
#endif /* CONFIG_SAE */
ret = wpa_drv_associate(wpa_s, &params);
os_free(wpa_ie);
if (ret < 0) {
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
"failed");
- if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SANE_ERROR_CODES) {
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_VALID_ERROR_CODES) {
/*
* The driver is known to mean what is saying, so we
* can stop right here; the association will not
* succeed.
*/
wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
return;
}
/* try to continue anyway; new association will be tried again
* after timeout */
assoc_failed = 1;
}
if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
/* Set the key after the association just in case association
* cleared the previously configured key. */
wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
/* No need to timeout authentication since there is no key
* management. */
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
#ifdef CONFIG_IBSS_RSN
} else if (ssid->mode == WPAS_MODE_IBSS &&
wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
/*
* RSN IBSS authentication is per-STA and we can disable the
* per-BSSID authentication.
*/
wpa_supplicant_cancel_auth_timeout(wpa_s);
#endif /* CONFIG_IBSS_RSN */
} else {
/* Timeout for IEEE 802.11 authentication and association */
int timeout = 60;
if (assoc_failed) {
/* give IBSS a bit more time */
timeout = ssid->mode == WPAS_MODE_IBSS ? 10 : 5;
} else if (wpa_s->conf->ap_scan == 1) {
/* give IBSS a bit more time */
timeout = ssid->mode == WPAS_MODE_IBSS ? 20 : 10;
}
wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
}
#ifdef CONFIG_WEP
if (wep_keys_set &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC)) {
/* Set static WEP keys again */
wpa_set_wep_keys(wpa_s, ssid);
}
#endif /* CONFIG_WEP */
if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) {
/*
* Do not allow EAP session resumption between different
* network configurations.
*/
eapol_sm_invalidate_cached_session(wpa_s->eapol);
}
old_ssid = wpa_s->current_ssid;
wpa_s->current_ssid = ssid;
if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
wpa_s->current_bss = bss;
#ifdef CONFIG_HS20
hs20_configure_frame_filters(wpa_s);
#endif /* CONFIG_HS20 */
}
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
wpa_supplicant_initiate_eapol(wpa_s);
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
}
static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
const u8 *addr)
{
struct wpa_ssid *old_ssid;
wpas_connect_work_done(wpa_s);
wpa_clear_keys(wpa_s, addr);
old_ssid = wpa_s->current_ssid;
wpa_supplicant_mark_disassoc(wpa_s);
wpa_sm_set_config(wpa_s->wpa, NULL);
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
if (old_ssid != wpa_s->current_ssid)
wpas_notify_network_changed(wpa_s);
+
+ wpas_scs_deinit(wpa_s);
+ wpas_dscp_deinit(wpa_s);
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
}
/**
* wpa_supplicant_deauthenticate - Deauthenticate the current connection
* @wpa_s: Pointer to wpa_supplicant data
* @reason_code: IEEE 802.11 reason code for the deauthenticate frame
*
* This function is used to request %wpa_supplicant to deauthenticate from the
* current AP.
*/
void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
u16 reason_code)
{
u8 *addr = NULL;
union wpa_event_data event;
int zero_addr = 0;
wpa_dbg(wpa_s, MSG_DEBUG, "Request to deauthenticate - bssid=" MACSTR
" pending_bssid=" MACSTR " reason=%d (%s) state=%s",
MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
reason_code, reason2str(reason_code),
wpa_supplicant_state_txt(wpa_s->wpa_state));
if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
(wpa_s->wpa_state == WPA_AUTHENTICATING ||
wpa_s->wpa_state == WPA_ASSOCIATING))
addr = wpa_s->pending_bssid;
else if (!is_zero_ether_addr(wpa_s->bssid))
addr = wpa_s->bssid;
else if (wpa_s->wpa_state == WPA_ASSOCIATING) {
/*
* When using driver-based BSS selection, we may not know the
* BSSID with which we are currently trying to associate. We
* need to notify the driver of this disconnection even in such
* a case, so use the all zeros address here.
*/
addr = wpa_s->bssid;
zero_addr = 1;
}
if (wpa_s->enabled_4addr_mode && wpa_drv_set_4addr_mode(wpa_s, 0) == 0)
wpa_s->enabled_4addr_mode = 0;
#ifdef CONFIG_TDLS
wpa_tdls_teardown_peers(wpa_s->wpa);
#endif /* CONFIG_TDLS */
#ifdef CONFIG_MESH
if (wpa_s->ifmsh) {
struct mesh_conf *mconf;
mconf = wpa_s->ifmsh->mconf;
wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
wpa_s->ifname);
wpas_notify_mesh_group_removed(wpa_s, mconf->meshid,
mconf->meshid_len, reason_code);
wpa_supplicant_leave_mesh(wpa_s, true);
}
#endif /* CONFIG_MESH */
if (addr) {
wpa_drv_deauthenticate(wpa_s, addr, reason_code);
os_memset(&event, 0, sizeof(event));
event.deauth_info.reason_code = reason_code;
event.deauth_info.locally_generated = 1;
wpa_supplicant_event(wpa_s, EVENT_DEAUTH, &event);
if (zero_addr)
addr = NULL;
}
wpa_supplicant_clear_connection(wpa_s, addr);
}
void wpa_supplicant_reconnect(struct wpa_supplicant *wpa_s)
{
wpa_s->own_reconnect_req = 1;
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
}
static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (!ssid || !ssid->disabled || ssid->disabled == 2)
return;
ssid->disabled = 0;
ssid->owe_transition_bss_select_count = 0;
wpas_clear_temp_disabled(wpa_s, ssid, 1);
wpas_notify_network_enabled_changed(wpa_s, ssid);
/*
* Try to reassociate since there is no current configuration and a new
* network was made available.
*/
if (!wpa_s->current_ssid && !wpa_s->disconnected)
wpa_s->reassociate = 1;
}
/**
* wpa_supplicant_add_network - Add a new network
* @wpa_s: wpa_supplicant structure for a network interface
* Returns: The new network configuration or %NULL if operation failed
*
* This function performs the following operations:
* 1. Adds a new network.
* 2. Send network addition notification.
* 3. Marks the network disabled.
* 4. Set network default parameters.
*/
struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid;
ssid = wpa_config_add_network(wpa_s->conf);
if (!ssid)
return NULL;
wpas_notify_network_added(wpa_s, ssid);
ssid->disabled = 1;
wpa_config_set_network_defaults(ssid);
return ssid;
}
/**
* wpa_supplicant_remove_network - Remove a configured network based on id
* @wpa_s: wpa_supplicant structure for a network interface
* @id: Unique network id to search for
* Returns: 0 on success, or -1 if the network was not found, -2 if the network
* could not be removed
*
* This function performs the following operations:
* 1. Removes the network.
* 2. Send network removal notification.
* 3. Update internal state machines.
* 4. Stop any running sched scans.
*/
int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id)
{
struct wpa_ssid *ssid;
int was_disabled;
ssid = wpa_config_get_network(wpa_s->conf, id);
if (!ssid)
return -1;
wpas_notify_network_removed(wpa_s, ssid);
if (wpa_s->last_ssid == ssid)
wpa_s->last_ssid = NULL;
if (ssid == wpa_s->current_ssid || !wpa_s->current_ssid) {
#ifdef CONFIG_SME
wpa_s->sme.prev_bssid_set = 0;
#endif /* CONFIG_SME */
/*
* Invalidate the EAP session cache if the current or
* previously used network is removed.
*/
eapol_sm_invalidate_cached_session(wpa_s->eapol);
}
if (ssid == wpa_s->current_ssid) {
wpa_sm_set_config(wpa_s->wpa, NULL);
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(wpa_s,
WLAN_REASON_DEAUTH_LEAVING);
}
was_disabled = ssid->disabled;
if (wpa_config_remove_network(wpa_s->conf, id) < 0)
return -2;
if (!was_disabled && wpa_s->sched_scanning) {
wpa_printf(MSG_DEBUG,
"Stop ongoing sched_scan to remove network from filters");
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
return 0;
}
/**
* wpa_supplicant_remove_all_networks - Remove all configured networks
* @wpa_s: wpa_supplicant structure for a network interface
* Returns: 0 on success (errors are currently ignored)
*
* This function performs the following operations:
* 1. Remove all networks.
* 2. Send network removal notifications.
* 3. Update internal state machines.
* 4. Stop any running sched scans.
*/
int wpa_supplicant_remove_all_networks(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid;
if (wpa_s->sched_scanning)
wpa_supplicant_cancel_sched_scan(wpa_s);
eapol_sm_invalidate_cached_session(wpa_s->eapol);
if (wpa_s->current_ssid) {
#ifdef CONFIG_SME
wpa_s->sme.prev_bssid_set = 0;
#endif /* CONFIG_SME */
wpa_sm_set_config(wpa_s->wpa, NULL);
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
ssid = wpa_s->conf->ssid;
while (ssid) {
struct wpa_ssid *remove_ssid = ssid;
int id;
id = ssid->id;
ssid = ssid->next;
if (wpa_s->last_ssid == remove_ssid)
wpa_s->last_ssid = NULL;
wpas_notify_network_removed(wpa_s, remove_ssid);
wpa_config_remove_network(wpa_s->conf, id);
}
return 0;
}
/**
* wpa_supplicant_enable_network - Mark a configured network as enabled
* @wpa_s: wpa_supplicant structure for a network interface
* @ssid: wpa_ssid structure for a configured network or %NULL
*
* Enables the specified network or all networks if no network specified.
*/
void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
if (ssid == NULL) {
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
wpa_supplicant_enable_one_network(wpa_s, ssid);
} else
wpa_supplicant_enable_one_network(wpa_s, ssid);
if (wpa_s->reassociate && !wpa_s->disconnected &&
(!wpa_s->current_ssid ||
wpa_s->wpa_state == WPA_DISCONNECTED ||
wpa_s->wpa_state == WPA_SCANNING)) {
if (wpa_s->sched_scanning) {
wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add "
"new network to scan filters");
wpa_supplicant_cancel_sched_scan(wpa_s);
}
if (wpa_supplicant_fast_associate(wpa_s) != 1) {
wpa_s->scan_req = NORMAL_SCAN_REQ;
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
}
}
/**
* wpa_supplicant_disable_network - Mark a configured network as disabled
* @wpa_s: wpa_supplicant structure for a network interface
* @ssid: wpa_ssid structure for a configured network or %NULL
*
* Disables the specified network or all networks if no network specified.
*/
void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct wpa_ssid *other_ssid;
int was_disabled;
if (ssid == NULL) {
if (wpa_s->sched_scanning)
wpa_supplicant_cancel_sched_scan(wpa_s);
for (other_ssid = wpa_s->conf->ssid; other_ssid;
other_ssid = other_ssid->next) {
was_disabled = other_ssid->disabled;
if (was_disabled == 2)
continue; /* do not change persistent P2P group
* data */
other_ssid->disabled = 1;
if (was_disabled != other_ssid->disabled)
wpas_notify_network_enabled_changed(
wpa_s, other_ssid);
}
if (wpa_s->current_ssid) {
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
} else if (ssid->disabled != 2) {
if (ssid == wpa_s->current_ssid) {
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
}
was_disabled = ssid->disabled;
ssid->disabled = 1;
if (was_disabled != ssid->disabled) {
wpas_notify_network_enabled_changed(wpa_s, ssid);
if (wpa_s->sched_scanning) {
wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan "
"to remove network from filters");
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
}
}
}
/**
* wpa_supplicant_select_network - Attempt association with a network
* @wpa_s: wpa_supplicant structure for a network interface
* @ssid: wpa_ssid structure for a configured network or %NULL for any network
*/
void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct wpa_ssid *other_ssid;
int disconnected = 0;
if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
wpa_s->own_disconnect_req = 1;
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
disconnected = 1;
}
if (ssid)
wpas_clear_temp_disabled(wpa_s, ssid, 1);
/*
* Mark all other networks disabled or mark all networks enabled if no
* network specified.
*/
for (other_ssid = wpa_s->conf->ssid; other_ssid;
other_ssid = other_ssid->next) {
int was_disabled = other_ssid->disabled;
if (was_disabled == 2)
continue; /* do not change persistent P2P group data */
other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
if (was_disabled && !other_ssid->disabled)
wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
if (was_disabled != other_ssid->disabled)
wpas_notify_network_enabled_changed(wpa_s, other_ssid);
}
if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid &&
wpa_s->wpa_state >= WPA_AUTHENTICATING) {
/* We are already associated with the selected network */
wpa_printf(MSG_DEBUG, "Already associated with the "
"selected network - do nothing");
return;
}
if (ssid) {
wpa_s->current_ssid = ssid;
eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
wpa_s->connect_without_scan =
(ssid->mode == WPAS_MODE_MESH) ? ssid : NULL;
/*
* Don't optimize next scan freqs since a new ESS has been
* selected.
*/
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = NULL;
} else {
wpa_s->connect_without_scan = NULL;
}
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
wpa_s_clear_sae_rejected(wpa_s);
wpa_s->last_owe_group = 0;
if (ssid) {
ssid->owe_transition_bss_select_count = 0;
wpa_s_setup_sae_pt(wpa_s->conf, ssid);
}
if (wpa_s->connect_without_scan ||
wpa_supplicant_fast_associate(wpa_s) != 1) {
wpa_s->scan_req = NORMAL_SCAN_REQ;
wpas_scan_reset_sched_scan(wpa_s);
wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
}
if (ssid)
wpas_notify_network_selected(wpa_s, ssid);
}
/**
* wpas_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
* @wpa_s: wpa_supplicant structure for a network interface
* @pkcs11_engine_path: PKCS #11 engine path or NULL
* @pkcs11_module_path: PKCS #11 module path or NULL
* Returns: 0 on success; -1 on failure
*
* Sets the PKCS #11 engine and module path. Both have to be NULL or a valid
* path. If resetting the EAPOL state machine with the new PKCS #11 engine and
* module path fails the paths will be reset to the default value (NULL).
*/
int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
const char *pkcs11_engine_path,
const char *pkcs11_module_path)
{
char *pkcs11_engine_path_copy = NULL;
char *pkcs11_module_path_copy = NULL;
if (pkcs11_engine_path != NULL) {
pkcs11_engine_path_copy = os_strdup(pkcs11_engine_path);
if (pkcs11_engine_path_copy == NULL)
return -1;
}
if (pkcs11_module_path != NULL) {
pkcs11_module_path_copy = os_strdup(pkcs11_module_path);
if (pkcs11_module_path_copy == NULL) {
os_free(pkcs11_engine_path_copy);
return -1;
}
}
os_free(wpa_s->conf->pkcs11_engine_path);
os_free(wpa_s->conf->pkcs11_module_path);
wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path_copy;
wpa_s->conf->pkcs11_module_path = pkcs11_module_path_copy;
wpa_sm_set_eapol(wpa_s->wpa, NULL);
eapol_sm_deinit(wpa_s->eapol);
wpa_s->eapol = NULL;
if (wpa_supplicant_init_eapol(wpa_s)) {
/* Error -> Reset paths to the default value (NULL) once. */
if (pkcs11_engine_path != NULL && pkcs11_module_path != NULL)
wpas_set_pkcs11_engine_and_module_path(wpa_s, NULL,
NULL);
return -1;
}
wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
return 0;
}
/**
* wpa_supplicant_set_ap_scan - Set AP scan mode for interface
* @wpa_s: wpa_supplicant structure for a network interface
* @ap_scan: AP scan mode
* Returns: 0 if succeed or -1 if ap_scan has an invalid value
*
*/
int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan)
{
int old_ap_scan;
if (ap_scan < 0 || ap_scan > 2)
return -1;
if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
wpa_printf(MSG_INFO,
"Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
}
#ifdef ANDROID
if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan &&
wpa_s->wpa_state >= WPA_ASSOCIATING &&
wpa_s->wpa_state < WPA_COMPLETED) {
wpa_printf(MSG_ERROR, "ap_scan = %d (%d) rejected while "
"associating", wpa_s->conf->ap_scan, ap_scan);
return 0;
}
#endif /* ANDROID */
old_ap_scan = wpa_s->conf->ap_scan;
wpa_s->conf->ap_scan = ap_scan;
if (old_ap_scan != wpa_s->conf->ap_scan)
wpas_notify_ap_scan_changed(wpa_s);
return 0;
}
/**
* wpa_supplicant_set_bss_expiration_age - Set BSS entry expiration age
* @wpa_s: wpa_supplicant structure for a network interface
* @expire_age: Expiration age in seconds
* Returns: 0 if succeed or -1 if expire_age has an invalid value
*
*/
int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
unsigned int bss_expire_age)
{
if (bss_expire_age < 10) {
wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration age %u",
bss_expire_age);
return -1;
}
wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration age: %d sec",
bss_expire_age);
wpa_s->conf->bss_expiration_age = bss_expire_age;
return 0;
}
/**
* wpa_supplicant_set_bss_expiration_count - Set BSS entry expiration scan count
* @wpa_s: wpa_supplicant structure for a network interface
* @expire_count: number of scans after which an unseen BSS is reclaimed
* Returns: 0 if succeed or -1 if expire_count has an invalid value
*
*/
int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
unsigned int bss_expire_count)
{
if (bss_expire_count < 1) {
wpa_msg(wpa_s, MSG_ERROR, "Invalid bss expiration count %u",
bss_expire_count);
return -1;
}
wpa_msg(wpa_s, MSG_DEBUG, "Setting bss expiration scan count: %u",
bss_expire_count);
wpa_s->conf->bss_expiration_scan_count = bss_expire_count;
return 0;
}
/**
* wpa_supplicant_set_scan_interval - Set scan interval
* @wpa_s: wpa_supplicant structure for a network interface
* @scan_interval: scan interval in seconds
* Returns: 0 if succeed or -1 if scan_interval has an invalid value
*
*/
int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
int scan_interval)
{
if (scan_interval < 0) {
wpa_msg(wpa_s, MSG_ERROR, "Invalid scan interval %d",
scan_interval);
return -1;
}
wpa_msg(wpa_s, MSG_DEBUG, "Setting scan interval: %d sec",
scan_interval);
wpa_supplicant_update_scan_int(wpa_s, scan_interval);
return 0;
}
/**
* wpa_supplicant_set_debug_params - Set global debug params
* @global: wpa_global structure
* @debug_level: debug level
* @debug_timestamp: determines if show timestamp in debug data
* @debug_show_keys: determines if show keys in debug data
* Returns: 0 if succeed or -1 if debug_level has wrong value
*/
int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level,
int debug_timestamp, int debug_show_keys)
{
int old_level, old_timestamp, old_show_keys;
/* check for allowed debuglevels */
if (debug_level != MSG_EXCESSIVE &&
debug_level != MSG_MSGDUMP &&
debug_level != MSG_DEBUG &&
debug_level != MSG_INFO &&
debug_level != MSG_WARNING &&
debug_level != MSG_ERROR)
return -1;
old_level = wpa_debug_level;
old_timestamp = wpa_debug_timestamp;
old_show_keys = wpa_debug_show_keys;
wpa_debug_level = debug_level;
wpa_debug_timestamp = debug_timestamp ? 1 : 0;
wpa_debug_show_keys = debug_show_keys ? 1 : 0;
if (wpa_debug_level != old_level)
wpas_notify_debug_level_changed(global);
if (wpa_debug_timestamp != old_timestamp)
wpas_notify_debug_timestamp_changed(global);
if (wpa_debug_show_keys != old_show_keys)
wpas_notify_debug_show_keys_changed(global);
return 0;
}
#ifdef CONFIG_OWE
static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid,
const u8 *entry_ssid, size_t entry_ssid_len)
{
const u8 *owe, *pos, *end;
u8 ssid_len;
struct wpa_bss *bss;
/* Check network profile SSID aganst the SSID in the
* OWE Transition Mode element. */
bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
if (!bss)
return 0;
owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
if (!owe)
return 0;
pos = owe + 6;
end = owe + 2 + owe[1];
if (end - pos < ETH_ALEN + 1)
return 0;
pos += ETH_ALEN;
ssid_len = *pos++;
if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN)
return 0;
return entry_ssid_len == ssid_len &&
os_memcmp(pos, entry_ssid, ssid_len) == 0;
}
#endif /* CONFIG_OWE */
/**
* wpa_supplicant_get_ssid - Get a pointer to the current network structure
* @wpa_s: Pointer to wpa_supplicant data
* Returns: A pointer to the current network structure or %NULL on failure
*/
struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *entry;
u8 ssid[SSID_MAX_LEN];
int res;
size_t ssid_len;
u8 bssid[ETH_ALEN];
int wired;
res = wpa_drv_get_ssid(wpa_s, ssid);
if (res < 0) {
wpa_msg(wpa_s, MSG_WARNING, "Could not read SSID from "
"driver");
return NULL;
}
ssid_len = res;
if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
wpa_msg(wpa_s, MSG_WARNING, "Could not read BSSID from "
"driver");
return NULL;
}
wired = wpa_s->conf->ap_scan == 0 &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED);
entry = wpa_s->conf->ssid;
while (entry) {
if (!wpas_network_disabled(wpa_s, entry) &&
((ssid_len == entry->ssid_len &&
(!entry->ssid ||
os_memcmp(ssid, entry->ssid, ssid_len) == 0)) ||
wired) &&
(!entry->bssid_set ||
os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
return entry;
#ifdef CONFIG_WPS
if (!wpas_network_disabled(wpa_s, entry) &&
(entry->key_mgmt & WPA_KEY_MGMT_WPS) &&
(entry->ssid == NULL || entry->ssid_len == 0) &&
(!entry->bssid_set ||
os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
return entry;
#endif /* CONFIG_WPS */
#ifdef CONFIG_OWE
if (!wpas_network_disabled(wpa_s, entry) &&
owe_trans_ssid_match(wpa_s, bssid, entry->ssid,
entry->ssid_len) &&
(!entry->bssid_set ||
os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
return entry;
#endif /* CONFIG_OWE */
if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set &&
entry->ssid_len == 0 &&
os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)
return entry;
entry = entry->next;
}
return NULL;
}
static int select_driver(struct wpa_supplicant *wpa_s, int i)
{
struct wpa_global *global = wpa_s->global;
if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {
global->drv_priv[i] = wpa_drivers[i]->global_init(global);
if (global->drv_priv[i] == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize driver "
"'%s'", wpa_drivers[i]->name);
return -1;
}
}
wpa_s->driver = wpa_drivers[i];
wpa_s->global_drv_priv = global->drv_priv[i];
return 0;
}
static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
const char *name)
{
int i;
size_t len;
const char *pos, *driver = name;
if (wpa_s == NULL)
return -1;
if (wpa_drivers[0] == NULL) {
wpa_msg(wpa_s, MSG_ERROR, "No driver interfaces build into "
"wpa_supplicant");
return -1;
}
if (name == NULL) {
- /* default to first driver in the list */
- return select_driver(wpa_s, 0);
+ /* Default to first successful driver in the list */
+ for (i = 0; wpa_drivers[i]; i++) {
+ if (select_driver(wpa_s, i) == 0)
+ return 0;
+ }
+ /* Drivers have each reported failure, so no wpa_msg() here. */
+ return -1;
}
do {
pos = os_strchr(driver, ',');
if (pos)
len = pos - driver;
else
len = os_strlen(driver);
for (i = 0; wpa_drivers[i]; i++) {
if (os_strlen(wpa_drivers[i]->name) == len &&
os_strncmp(driver, wpa_drivers[i]->name, len) ==
0) {
/* First driver that succeeds wins */
if (select_driver(wpa_s, i) == 0)
return 0;
}
}
driver = pos + 1;
} while (pos);
wpa_msg(wpa_s, MSG_ERROR, "Unsupported driver '%s'", name);
return -1;
}
/**
* wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant
* @ctx: Context pointer (wpa_s); this is the ctx variable registered
* with struct wpa_driver_ops::init()
* @src_addr: Source address of the EAPOL frame
* @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header)
* @len: Length of the EAPOL data
*
* This function is called for each received EAPOL frame. Most driver
* interfaces rely on more generic OS mechanism for receiving frames through
* l2_packet, but if such a mechanism is not available, the driver wrapper may
* take care of received EAPOL frames and deliver them to the core supplicant
* code by calling this function.
*/
void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
const u8 *buf, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
if (wpa_s->own_disconnect_req) {
wpa_printf(MSG_DEBUG,
"Drop received EAPOL frame as we are disconnecting");
return;
}
#ifdef CONFIG_TESTING_OPTIONS
wpa_msg_ctrl(wpa_s, MSG_INFO, "EAPOL-RX " MACSTR " %zu",
MAC2STR(src_addr), len);
if (wpa_s->ignore_auth_resp) {
wpa_printf(MSG_INFO, "RX EAPOL - ignore_auth_resp active!");
return;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->wpa_state < WPA_ASSOCIATED ||
(wpa_s->last_eapol_matches_bssid &&
#ifdef CONFIG_AP
!wpa_s->ap_iface &&
#endif /* CONFIG_AP */
os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) != 0)) {
/*
* There is possible race condition between receiving the
* association event and the EAPOL frame since they are coming
* through different paths from the driver. In order to avoid
* issues in trying to process the EAPOL frame before receiving
* association information, lets queue it for processing until
* the association event is received. This may also be needed in
* driver-based roaming case, so also use src_addr != BSSID as a
* trigger if we have previously confirmed that the
* Authenticator uses BSSID as the src_addr (which is not the
* case with wired IEEE 802.1X).
*/
wpa_dbg(wpa_s, MSG_DEBUG, "Not associated - Delay processing "
"of received EAPOL frame (state=%s bssid=" MACSTR ")",
wpa_supplicant_state_txt(wpa_s->wpa_state),
MAC2STR(wpa_s->bssid));
wpabuf_free(wpa_s->pending_eapol_rx);
wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
if (wpa_s->pending_eapol_rx) {
os_get_reltime(&wpa_s->pending_eapol_rx_time);
os_memcpy(wpa_s->pending_eapol_rx_src, src_addr,
ETH_ALEN);
}
return;
}
wpa_s->last_eapol_matches_bssid =
os_memcmp(src_addr, wpa_s->bssid, ETH_ALEN) == 0;
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len);
return;
}
#endif /* CONFIG_AP */
if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
wpa_dbg(wpa_s, MSG_DEBUG, "Ignored received EAPOL frame since "
"no key management is configured");
return;
}
if (wpa_s->eapol_received == 0 &&
(!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK) ||
!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
wpa_s->wpa_state != WPA_COMPLETED) &&
(wpa_s->current_ssid == NULL ||
wpa_s->current_ssid->mode != WPAS_MODE_IBSS)) {
/* Timeout for completing IEEE 802.1X and WPA authentication */
int timeout = 10;
if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA ||
wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
/* Use longer timeout for IEEE 802.1X/EAP */
timeout = 70;
}
#ifdef CONFIG_WPS
if (wpa_s->current_ssid && wpa_s->current_bss &&
(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
/*
* Use shorter timeout if going through WPS AP iteration
* for PIN config method with an AP that does not
* advertise Selected Registrar.
*/
struct wpabuf *wps_ie;
wps_ie = wpa_bss_get_vendor_ie_multi(
wpa_s->current_bss, WPS_IE_VENDOR_TYPE);
if (wps_ie &&
!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1))
timeout = 10;
wpabuf_free(wps_ie);
}
#endif /* CONFIG_WPS */
wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
}
wpa_s->eapol_received++;
if (wpa_s->countermeasures) {
wpa_msg(wpa_s, MSG_INFO, "WPA: Countermeasures - dropped "
"EAPOL packet");
return;
}
#ifdef CONFIG_IBSS_RSN
if (wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_IBSS) {
ibss_rsn_rx_eapol(wpa_s->ibss_rsn, src_addr, buf, len);
return;
}
#endif /* CONFIG_IBSS_RSN */
/* Source address of the incoming EAPOL frame could be compared to the
* current BSSID. However, it is possible that a centralized
* Authenticator could be using another MAC address than the BSSID of
* an AP, so just allow any address to be used for now. The replies are
* still sent to the current BSSID (if available), though. */
os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN);
if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) &&
wpa_s->key_mgmt != WPA_KEY_MGMT_OWE &&
wpa_s->key_mgmt != WPA_KEY_MGMT_DPP &&
eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0)
return;
wpa_drv_poll(wpa_s);
if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK))
wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len);
else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
/*
* Set portValid = true here since we are going to skip 4-way
* handshake processing which would normally set portValid. We
* need this to allow the EAPOL state machines to be completed
* without going through EAPOL-Key handshake.
*/
eapol_sm_notify_portValid(wpa_s->eapol, true);
}
}
static int wpas_eapol_needs_l2_packet(struct wpa_supplicant *wpa_s)
{
return !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_CONTROL_PORT) ||
!(wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX);
}
int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
{
if ((!wpa_s->p2p_mgmt ||
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
l2_packet_deinit(wpa_s->l2);
wpa_s->l2 = l2_packet_init(wpa_s->ifname,
wpa_drv_get_mac_addr(wpa_s),
ETH_P_EAPOL,
wpas_eapol_needs_l2_packet(wpa_s) ?
wpa_supplicant_rx_eapol : NULL,
wpa_s, 0);
if (wpa_s->l2 == NULL)
return -1;
if (l2_packet_set_packet_filter(wpa_s->l2,
L2_PACKET_FILTER_PKTTYPE))
wpa_dbg(wpa_s, MSG_DEBUG,
"Failed to attach pkt_type filter");
if (l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
wpa_msg(wpa_s, MSG_ERROR,
"Failed to get own L2 address");
return -1;
}
} else {
const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
if (addr)
os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
}
wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
wpas_wps_update_mac_addr(wpa_s);
#ifdef CONFIG_FST
if (wpa_s->fst)
fst_update_mac_addr(wpa_s->fst, wpa_s->own_addr);
#endif /* CONFIG_FST */
return 0;
}
static void wpa_supplicant_rx_eapol_bridge(void *ctx, const u8 *src_addr,
const u8 *buf, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
const struct l2_ethhdr *eth;
if (len < sizeof(*eth))
return;
eth = (const struct l2_ethhdr *) buf;
if (os_memcmp(eth->h_dest, wpa_s->own_addr, ETH_ALEN) != 0 &&
!(eth->h_dest[0] & 0x01)) {
wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
" (bridge - not for this interface - ignore)",
MAC2STR(src_addr), MAC2STR(eth->h_dest));
return;
}
wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR " to " MACSTR
" (bridge)", MAC2STR(src_addr), MAC2STR(eth->h_dest));
wpa_supplicant_rx_eapol(wpa_s, src_addr, buf + sizeof(*eth),
len - sizeof(*eth));
}
int wpa_supplicant_update_bridge_ifname(struct wpa_supplicant *wpa_s,
const char *bridge_ifname)
{
if (wpa_s->wpa_state > WPA_SCANNING)
return -EBUSY;
if (bridge_ifname &&
os_strlen(bridge_ifname) >= sizeof(wpa_s->bridge_ifname))
return -EINVAL;
if (!bridge_ifname)
bridge_ifname = "";
if (os_strcmp(wpa_s->bridge_ifname, bridge_ifname) == 0)
return 0;
if (wpa_s->l2_br) {
l2_packet_deinit(wpa_s->l2_br);
wpa_s->l2_br = NULL;
}
os_strlcpy(wpa_s->bridge_ifname, bridge_ifname,
sizeof(wpa_s->bridge_ifname));
if (wpa_s->bridge_ifname[0]) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Receiving packets from bridge interface '%s'",
wpa_s->bridge_ifname);
wpa_s->l2_br = l2_packet_init_bridge(
wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr,
ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1);
if (!wpa_s->l2_br) {
wpa_msg(wpa_s, MSG_ERROR,
"Failed to open l2_packet connection for the bridge interface '%s'",
wpa_s->bridge_ifname);
goto fail;
}
}
#ifdef CONFIG_TDLS
if (!wpa_s->p2p_mgmt && wpa_tdls_init(wpa_s->wpa))
goto fail;
#endif /* CONFIG_TDLS */
return 0;
fail:
wpa_s->bridge_ifname[0] = 0;
if (wpa_s->l2_br) {
l2_packet_deinit(wpa_s->l2_br);
wpa_s->l2_br = NULL;
}
#ifdef CONFIG_TDLS
if (!wpa_s->p2p_mgmt)
wpa_tdls_init(wpa_s->wpa);
#endif /* CONFIG_TDLS */
return -EIO;
}
/**
* wpa_supplicant_driver_init - Initialize driver interface parameters
* @wpa_s: Pointer to wpa_supplicant data
* Returns: 0 on success, -1 on failure
*
* This function is called to initialize driver interface parameters.
* wpa_drv_init() must have been called before this function to initialize the
* driver interface.
*/
int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
{
static int interface_count = 0;
if (wpa_supplicant_update_mac_addr(wpa_s) < 0)
return -1;
wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR,
MAC2STR(wpa_s->own_addr));
os_memcpy(wpa_s->perm_addr, wpa_s->own_addr, ETH_ALEN);
wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
if (wpa_s->bridge_ifname[0] && wpas_eapol_needs_l2_packet(wpa_s)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
"interface '%s'", wpa_s->bridge_ifname);
wpa_s->l2_br = l2_packet_init_bridge(
wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr,
ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1);
if (wpa_s->l2_br == NULL) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
"connection for the bridge interface '%s'",
wpa_s->bridge_ifname);
return -1;
}
}
if (wpa_s->conf->ap_scan == 2 &&
os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
wpa_printf(MSG_INFO,
"Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
}
wpa_clear_keys(wpa_s, NULL);
/* Make sure that TKIP countermeasures are not left enabled (could
* happen if wpa_supplicant is killed during countermeasures. */
wpa_drv_set_countermeasures(wpa_s, 0);
wpa_dbg(wpa_s, MSG_DEBUG, "RSN: flushing PMKID list in the driver");
wpa_drv_flush_pmkid(wpa_s);
wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
wpa_s->prev_scan_wildcard = 0;
if (wpa_supplicant_enabled_networks(wpa_s)) {
if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
interface_count = 0;
}
#ifndef ANDROID
if (!wpa_s->p2p_mgmt &&
wpa_supplicant_delayed_sched_scan(wpa_s,
interface_count % 3,
100000))
wpa_supplicant_req_scan(wpa_s, interface_count % 3,
100000);
#endif /* ANDROID */
interface_count++;
} else
wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
return 0;
}
static int wpa_supplicant_daemon(const char *pid_file)
{
wpa_printf(MSG_DEBUG, "Daemonize..");
return os_daemonize(pid_file);
}
static struct wpa_supplicant *
wpa_supplicant_alloc(struct wpa_supplicant *parent)
{
struct wpa_supplicant *wpa_s;
wpa_s = os_zalloc(sizeof(*wpa_s));
if (wpa_s == NULL)
return NULL;
wpa_s->scan_req = INITIAL_SCAN_REQ;
wpa_s->scan_interval = 5;
wpa_s->new_connection = 1;
wpa_s->parent = parent ? parent : wpa_s;
wpa_s->p2pdev = wpa_s->parent;
wpa_s->sched_scanning = 0;
wpa_s->setband_mask = WPA_SETBAND_AUTO;
dl_list_init(&wpa_s->bss_tmp_disallowed);
dl_list_init(&wpa_s->fils_hlp_req);
#ifdef CONFIG_TESTING_OPTIONS
dl_list_init(&wpa_s->drv_signal_override);
#endif /* CONFIG_TESTING_OPTIONS */
+ dl_list_init(&wpa_s->active_scs_ids);
return wpa_s;
}
#ifdef CONFIG_HT_OVERRIDES
static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
const char *ht_mcs)
{
/* parse ht_mcs into hex array */
int i;
const char *tmp = ht_mcs;
char *end = NULL;
/* If ht_mcs is null, do not set anything */
if (!ht_mcs)
return 0;
/* This is what we are setting in the kernel */
os_memset(&htcaps->supported_mcs_set, 0, IEEE80211_HT_MCS_MASK_LEN);
wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs);
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
long v;
errno = 0;
v = strtol(tmp, &end, 16);
if (errno == 0) {
wpa_msg(wpa_s, MSG_DEBUG,
"htcap value[%i]: %ld end: %p tmp: %p",
i, v, end, tmp);
if (end == tmp)
break;
htcaps->supported_mcs_set[i] = v;
tmp = end;
} else {
wpa_msg(wpa_s, MSG_ERROR,
"Failed to parse ht-mcs: %s, error: %s\n",
ht_mcs, strerror(errno));
return -1;
}
}
/*
* If we were able to parse any values, then set mask for the MCS set.
*/
if (i) {
os_memset(&htcaps_mask->supported_mcs_set, 0xff,
IEEE80211_HT_MCS_MASK_LEN - 1);
/* skip the 3 reserved bits */
htcaps_mask->supported_mcs_set[IEEE80211_HT_MCS_MASK_LEN - 1] =
0x1f;
}
return 0;
}
static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int disabled)
{
le16 msk;
if (disabled == -1)
return 0;
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
msk = host_to_le16(HT_CAP_INFO_MAX_AMSDU_SIZE);
htcaps_mask->ht_capabilities_info |= msk;
if (disabled)
htcaps->ht_capabilities_info &= msk;
else
htcaps->ht_capabilities_info |= msk;
return 0;
}
static int wpa_set_ampdu_factor(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int factor)
{
if (factor == -1)
return 0;
wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_factor: %d", factor);
if (factor < 0 || factor > 3) {
wpa_msg(wpa_s, MSG_ERROR, "ampdu_factor: %d out of range. "
"Must be 0-3 or -1", factor);
return -EINVAL;
}
htcaps_mask->a_mpdu_params |= 0x3; /* 2 bits for factor */
htcaps->a_mpdu_params &= ~0x3;
htcaps->a_mpdu_params |= factor & 0x3;
return 0;
}
static int wpa_set_ampdu_density(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int density)
{
if (density == -1)
return 0;
wpa_msg(wpa_s, MSG_DEBUG, "set_ampdu_density: %d", density);
if (density < 0 || density > 7) {
wpa_msg(wpa_s, MSG_ERROR,
"ampdu_density: %d out of range. Must be 0-7 or -1.",
density);
return -EINVAL;
}
htcaps_mask->a_mpdu_params |= 0x1C;
htcaps->a_mpdu_params &= ~(0x1C);
htcaps->a_mpdu_params |= (density << 2) & 0x1C;
return 0;
}
static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int disabled)
{
if (disabled)
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
set_disable_ht40(htcaps, disabled);
set_disable_ht40(htcaps_mask, 0);
return 0;
}
static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int disabled)
{
/* Masking these out disables SGI */
le16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
HT_CAP_INFO_SHORT_GI40MHZ);
if (disabled)
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled);
if (disabled)
htcaps->ht_capabilities_info &= ~msk;
else
htcaps->ht_capabilities_info |= msk;
htcaps_mask->ht_capabilities_info |= msk;
return 0;
}
static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int disabled)
{
/* Masking these out disables LDPC */
le16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP);
if (disabled)
wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled);
if (disabled)
htcaps->ht_capabilities_info &= ~msk;
else
htcaps->ht_capabilities_info |= msk;
htcaps_mask->ht_capabilities_info |= msk;
return 0;
}
static int wpa_set_tx_stbc(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int tx_stbc)
{
le16 msk = host_to_le16(HT_CAP_INFO_TX_STBC);
if (tx_stbc == -1)
return 0;
wpa_msg(wpa_s, MSG_DEBUG, "set_tx_stbc: %d", tx_stbc);
if (tx_stbc < 0 || tx_stbc > 1) {
wpa_msg(wpa_s, MSG_ERROR,
"tx_stbc: %d out of range. Must be 0-1 or -1", tx_stbc);
return -EINVAL;
}
htcaps_mask->ht_capabilities_info |= msk;
htcaps->ht_capabilities_info &= ~msk;
htcaps->ht_capabilities_info |= (tx_stbc << 7) & msk;
return 0;
}
static int wpa_set_rx_stbc(struct wpa_supplicant *wpa_s,
struct ieee80211_ht_capabilities *htcaps,
struct ieee80211_ht_capabilities *htcaps_mask,
int rx_stbc)
{
le16 msk = host_to_le16(HT_CAP_INFO_RX_STBC_MASK);
if (rx_stbc == -1)
return 0;
wpa_msg(wpa_s, MSG_DEBUG, "set_rx_stbc: %d", rx_stbc);
if (rx_stbc < 0 || rx_stbc > 3) {
wpa_msg(wpa_s, MSG_ERROR,
"rx_stbc: %d out of range. Must be 0-3 or -1", rx_stbc);
return -EINVAL;
}
htcaps_mask->ht_capabilities_info |= msk;
htcaps->ht_capabilities_info &= ~msk;
htcaps->ht_capabilities_info |= (rx_stbc << 8) & msk;
return 0;
}
void wpa_supplicant_apply_ht_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params)
{
struct ieee80211_ht_capabilities *htcaps;
struct ieee80211_ht_capabilities *htcaps_mask;
if (!ssid)
return;
params->disable_ht = ssid->disable_ht;
if (!params->htcaps || !params->htcaps_mask)
return;
htcaps = (struct ieee80211_ht_capabilities *) params->htcaps;
htcaps_mask = (struct ieee80211_ht_capabilities *) params->htcaps_mask;
wpa_set_htcap_mcs(wpa_s, htcaps, htcaps_mask, ssid->ht_mcs);
wpa_disable_max_amsdu(wpa_s, htcaps, htcaps_mask,
ssid->disable_max_amsdu);
wpa_set_ampdu_factor(wpa_s, htcaps, htcaps_mask, ssid->ampdu_factor);
wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi);
wpa_set_disable_ldpc(wpa_s, htcaps, htcaps_mask, ssid->disable_ldpc);
wpa_set_rx_stbc(wpa_s, htcaps, htcaps_mask, ssid->rx_stbc);
wpa_set_tx_stbc(wpa_s, htcaps, htcaps_mask, ssid->tx_stbc);
if (ssid->ht40_intolerant) {
le16 bit = host_to_le16(HT_CAP_INFO_40MHZ_INTOLERANT);
htcaps->ht_capabilities_info |= bit;
htcaps_mask->ht_capabilities_info |= bit;
}
}
#endif /* CONFIG_HT_OVERRIDES */
#ifdef CONFIG_VHT_OVERRIDES
void wpa_supplicant_apply_vht_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params)
{
struct ieee80211_vht_capabilities *vhtcaps;
struct ieee80211_vht_capabilities *vhtcaps_mask;
if (!ssid)
return;
params->disable_vht = ssid->disable_vht;
vhtcaps = (void *) params->vhtcaps;
vhtcaps_mask = (void *) params->vhtcaps_mask;
if (!vhtcaps || !vhtcaps_mask)
return;
vhtcaps->vht_capabilities_info = host_to_le32(ssid->vht_capa);
vhtcaps_mask->vht_capabilities_info = host_to_le32(ssid->vht_capa_mask);
#ifdef CONFIG_HT_OVERRIDES
if (ssid->disable_sgi) {
vhtcaps_mask->vht_capabilities_info |= (VHT_CAP_SHORT_GI_80 |
VHT_CAP_SHORT_GI_160);
vhtcaps->vht_capabilities_info &= ~(VHT_CAP_SHORT_GI_80 |
VHT_CAP_SHORT_GI_160);
wpa_msg(wpa_s, MSG_DEBUG,
"disable-sgi override specified, vht-caps: 0x%x",
vhtcaps->vht_capabilities_info);
}
/* if max ampdu is <= 3, we have to make the HT cap the same */
if (ssid->vht_capa_mask & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) {
int max_ampdu;
max_ampdu = (ssid->vht_capa &
VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >>
VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT;
max_ampdu = max_ampdu < 3 ? max_ampdu : 3;
wpa_set_ampdu_factor(wpa_s,
(void *) params->htcaps,
(void *) params->htcaps_mask,
max_ampdu);
}
#endif /* CONFIG_HT_OVERRIDES */
#define OVERRIDE_MCS(i) \
if (ssid->vht_tx_mcs_nss_ ##i >= 0) { \
vhtcaps_mask->vht_supported_mcs_set.tx_map |= \
host_to_le16(3 << 2 * (i - 1)); \
vhtcaps->vht_supported_mcs_set.tx_map |= \
host_to_le16(ssid->vht_tx_mcs_nss_ ##i << \
2 * (i - 1)); \
} \
if (ssid->vht_rx_mcs_nss_ ##i >= 0) { \
vhtcaps_mask->vht_supported_mcs_set.rx_map |= \
host_to_le16(3 << 2 * (i - 1)); \
vhtcaps->vht_supported_mcs_set.rx_map |= \
host_to_le16(ssid->vht_rx_mcs_nss_ ##i << \
2 * (i - 1)); \
}
OVERRIDE_MCS(1);
OVERRIDE_MCS(2);
OVERRIDE_MCS(3);
OVERRIDE_MCS(4);
OVERRIDE_MCS(5);
OVERRIDE_MCS(6);
OVERRIDE_MCS(7);
OVERRIDE_MCS(8);
}
#endif /* CONFIG_VHT_OVERRIDES */
#ifdef CONFIG_HE_OVERRIDES
void wpa_supplicant_apply_he_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params)
{
if (!ssid)
return;
params->disable_he = ssid->disable_he;
}
#endif /* CONFIG_HE_OVERRIDES */
static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
{
#ifdef PCSC_FUNCS
size_t len;
if (!wpa_s->conf->pcsc_reader)
return 0;
wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
if (!wpa_s->scard)
return 1;
if (wpa_s->conf->pcsc_pin &&
scard_set_pin(wpa_s->scard, wpa_s->conf->pcsc_pin) < 0) {
scard_deinit(wpa_s->scard);
wpa_s->scard = NULL;
wpa_msg(wpa_s, MSG_ERROR, "PC/SC PIN validation failed");
return -1;
}
len = sizeof(wpa_s->imsi) - 1;
if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) {
scard_deinit(wpa_s->scard);
wpa_s->scard = NULL;
wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI");
return -1;
}
wpa_s->imsi[len] = '\0';
wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard);
wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)",
wpa_s->imsi, wpa_s->mnc_len);
wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
#endif /* PCSC_FUNCS */
return 0;
}
int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
{
char *val, *pos;
ext_password_deinit(wpa_s->ext_pw);
wpa_s->ext_pw = NULL;
eapol_sm_set_ext_pw_ctx(wpa_s->eapol, NULL);
if (!wpa_s->conf->ext_password_backend)
return 0;
val = os_strdup(wpa_s->conf->ext_password_backend);
if (val == NULL)
return -1;
pos = os_strchr(val, ':');
if (pos)
*pos++ = '\0';
wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val);
wpa_s->ext_pw = ext_password_init(val, pos);
os_free(val);
if (wpa_s->ext_pw == NULL) {
wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend");
return -1;
}
eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw);
return 0;
}
#ifdef CONFIG_FST
static const u8 * wpas_fst_get_bssid_cb(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
return (is_zero_ether_addr(wpa_s->bssid) ||
wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid;
}
static void wpas_fst_get_channel_info_cb(void *ctx,
enum hostapd_hw_mode *hw_mode,
u8 *channel)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->current_bss) {
*hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq,
channel);
} else if (wpa_s->hw.num_modes) {
*hw_mode = wpa_s->hw.modes[0].mode;
} else {
WPA_ASSERT(0);
*hw_mode = 0;
}
}
static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes)
{
struct wpa_supplicant *wpa_s = ctx;
*modes = wpa_s->hw.modes;
return wpa_s->hw.num_modes;
}
static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies);
wpa_s->fst_ies = fst_ies;
}
static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data)
{
struct wpa_supplicant *wpa_s = ctx;
if (os_memcmp(wpa_s->bssid, da, ETH_ALEN) != 0) {
wpa_printf(MSG_INFO, "FST:%s:bssid=" MACSTR " != da=" MACSTR,
__func__, MAC2STR(wpa_s->bssid), MAC2STR(da));
return -1;
}
return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
wpa_s->own_addr, wpa_s->bssid,
wpabuf_head(data), wpabuf_len(data),
0);
}
static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr)
{
struct wpa_supplicant *wpa_s = ctx;
WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
return wpa_s->received_mb_ies;
}
static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr,
const u8 *buf, size_t size)
{
struct wpa_supplicant *wpa_s = ctx;
struct mb_ies_info info;
WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
if (!mb_ies_info_by_ies(&info, buf, size)) {
wpabuf_free(wpa_s->received_mb_ies);
wpa_s->received_mb_ies = mb_ies_by_info(&info);
}
}
static const u8 * wpas_fst_get_peer_first(void *ctx,
struct fst_get_peer_ctx **get_ctx,
bool mb_only)
{
struct wpa_supplicant *wpa_s = ctx;
*get_ctx = NULL;
if (!is_zero_ether_addr(wpa_s->bssid))
return (wpa_s->received_mb_ies || !mb_only) ?
wpa_s->bssid : NULL;
return NULL;
}
static const u8 * wpas_fst_get_peer_next(void *ctx,
struct fst_get_peer_ctx **get_ctx,
bool mb_only)
{
return NULL;
}
void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
struct fst_wpa_obj *iface_obj)
{
iface_obj->ctx = wpa_s;
iface_obj->get_bssid = wpas_fst_get_bssid_cb;
iface_obj->get_channel_info = wpas_fst_get_channel_info_cb;
iface_obj->get_hw_modes = wpas_fst_get_hw_modes;
iface_obj->set_ies = wpas_fst_set_ies_cb;
iface_obj->send_action = wpas_fst_send_action_cb;
iface_obj->get_mb_ie = wpas_fst_get_mb_ie_cb;
iface_obj->update_mb_ie = wpas_fst_update_mb_ie_cb;
iface_obj->get_peer_first = wpas_fst_get_peer_first;
iface_obj->get_peer_next = wpas_fst_get_peer_next;
}
#endif /* CONFIG_FST */
static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
const struct wpa_driver_capa *capa)
{
struct wowlan_triggers *triggers;
int ret = 0;
if (!wpa_s->conf->wowlan_triggers)
return 0;
triggers = wpa_get_wowlan_triggers(wpa_s->conf->wowlan_triggers, capa);
if (triggers) {
ret = wpa_drv_wowlan(wpa_s, triggers);
os_free(triggers);
}
return ret;
}
enum wpa_radio_work_band wpas_freq_to_band(int freq)
{
if (freq < 3000)
return BAND_2_4_GHZ;
if (freq > 50000)
return BAND_60_GHZ;
return BAND_5_GHZ;
}
unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs)
{
int i;
unsigned int band = 0;
if (freqs) {
/* freqs are specified for the radio work */
for (i = 0; freqs[i]; i++)
band |= wpas_freq_to_band(freqs[i]);
} else {
/*
* freqs are not specified, implies all
* the supported freqs by HW
*/
for (i = 0; i < wpa_s->hw.num_modes; i++) {
if (wpa_s->hw.modes[i].num_channels != 0) {
if (wpa_s->hw.modes[i].mode ==
HOSTAPD_MODE_IEEE80211B ||
wpa_s->hw.modes[i].mode ==
HOSTAPD_MODE_IEEE80211G)
band |= BAND_2_4_GHZ;
else if (wpa_s->hw.modes[i].mode ==
HOSTAPD_MODE_IEEE80211A)
band |= BAND_5_GHZ;
else if (wpa_s->hw.modes[i].mode ==
HOSTAPD_MODE_IEEE80211AD)
band |= BAND_60_GHZ;
else if (wpa_s->hw.modes[i].mode ==
HOSTAPD_MODE_IEEE80211ANY)
band = BAND_2_4_GHZ | BAND_5_GHZ |
BAND_60_GHZ;
}
}
}
return band;
}
static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
const char *rn)
{
struct wpa_supplicant *iface = wpa_s->global->ifaces;
struct wpa_radio *radio;
while (rn && iface) {
radio = iface->radio;
if (radio && os_strcmp(rn, radio->name) == 0) {
wpa_printf(MSG_DEBUG, "Add interface %s to existing radio %s",
wpa_s->ifname, rn);
dl_list_add(&radio->ifaces, &wpa_s->radio_list);
return radio;
}
iface = iface->next;
}
wpa_printf(MSG_DEBUG, "Add interface %s to a new radio %s",
wpa_s->ifname, rn ? rn : "N/A");
radio = os_zalloc(sizeof(*radio));
if (radio == NULL)
return NULL;
if (rn)
os_strlcpy(radio->name, rn, sizeof(radio->name));
dl_list_init(&radio->ifaces);
dl_list_init(&radio->work);
dl_list_add(&radio->ifaces, &wpa_s->radio_list);
return radio;
}
static void radio_work_free(struct wpa_radio_work *work)
{
if (work->wpa_s->scan_work == work) {
/* This should not really happen. */
wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as scan_work",
work->type, work, work->started);
work->wpa_s->scan_work = NULL;
}
#ifdef CONFIG_P2P
if (work->wpa_s->p2p_scan_work == work) {
/* This should not really happen. */
wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as p2p_scan_work",
work->type, work, work->started);
work->wpa_s->p2p_scan_work = NULL;
}
#endif /* CONFIG_P2P */
if (work->started) {
work->wpa_s->radio->num_active_works--;
wpa_dbg(work->wpa_s, MSG_DEBUG,
"radio_work_free('%s'@%p): num_active_works --> %u",
work->type, work,
work->wpa_s->radio->num_active_works);
}
dl_list_del(&work->list);
os_free(work);
}
static int radio_work_is_connect(struct wpa_radio_work *work)
{
return os_strcmp(work->type, "sme-connect") == 0 ||
os_strcmp(work->type, "connect") == 0;
}
static int radio_work_is_scan(struct wpa_radio_work *work)
{
return os_strcmp(work->type, "scan") == 0 ||
os_strcmp(work->type, "p2p-scan") == 0;
}
static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
{
struct wpa_radio_work *active_work = NULL;
struct wpa_radio_work *tmp;
/* Get the active work to know the type and band. */
dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
if (tmp->started) {
active_work = tmp;
break;
}
}
if (!active_work) {
/* No active work, start one */
radio->num_active_works = 0;
dl_list_for_each(tmp, &radio->work, struct wpa_radio_work,
list) {
if (os_strcmp(tmp->type, "scan") == 0 &&
external_scan_running(radio) &&
(((struct wpa_driver_scan_params *)
tmp->ctx)->only_new_results ||
tmp->wpa_s->clear_driver_scan_cache))
continue;
return tmp;
}
return NULL;
}
if (radio_work_is_connect(active_work)) {
/*
* If the active work is either connect or sme-connect,
* do not parallelize them with other radio works.
*/
wpa_dbg(active_work->wpa_s, MSG_DEBUG,
"Do not parallelize radio work with %s",
active_work->type);
return NULL;
}
dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
if (tmp->started)
continue;
/*
* If connect or sme-connect are enqueued, parallelize only
* those operations ahead of them in the queue.
*/
if (radio_work_is_connect(tmp))
break;
/* Serialize parallel scan and p2p_scan operations on the same
* interface since the driver_nl80211 mechanism for tracking
* scan cookies does not yet have support for this. */
if (active_work->wpa_s == tmp->wpa_s &&
radio_work_is_scan(active_work) &&
radio_work_is_scan(tmp)) {
wpa_dbg(active_work->wpa_s, MSG_DEBUG,
"Do not start work '%s' when another work '%s' is already scheduled",
tmp->type, active_work->type);
continue;
}
/*
* Check that the radio works are distinct and
* on different bands.
*/
if (os_strcmp(active_work->type, tmp->type) != 0 &&
(active_work->bands != tmp->bands)) {
/*
* If a scan has to be scheduled through nl80211 scan
* interface and if an external scan is already running,
* do not schedule the scan since it is likely to get
* rejected by kernel.
*/
if (os_strcmp(tmp->type, "scan") == 0 &&
external_scan_running(radio) &&
(((struct wpa_driver_scan_params *)
tmp->ctx)->only_new_results ||
tmp->wpa_s->clear_driver_scan_cache))
continue;
wpa_dbg(active_work->wpa_s, MSG_DEBUG,
"active_work:%s new_work:%s",
active_work->type, tmp->type);
return tmp;
}
}
/* Did not find a radio work to schedule in parallel. */
return NULL;
}
static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_radio *radio = eloop_ctx;
struct wpa_radio_work *work;
struct os_reltime now, diff;
struct wpa_supplicant *wpa_s;
work = dl_list_first(&radio->work, struct wpa_radio_work, list);
if (work == NULL) {
radio->num_active_works = 0;
return;
}
wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
radio_list);
if (!(wpa_s &&
wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)) {
if (work->started)
return; /* already started and still in progress */
if (wpa_s && external_scan_running(wpa_s->radio)) {
wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
return;
}
} else {
work = NULL;
if (radio->num_active_works < MAX_ACTIVE_WORKS) {
/* get the work to schedule next */
work = radio_work_get_next_work(radio);
}
if (!work)
return;
}
wpa_s = work->wpa_s;
os_get_reltime(&now);
os_reltime_sub(&now, &work->time, &diff);
wpa_dbg(wpa_s, MSG_DEBUG,
"Starting radio work '%s'@%p after %ld.%06ld second wait",
work->type, work, diff.sec, diff.usec);
work->started = 1;
work->time = now;
radio->num_active_works++;
work->cb(work, 0);
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) &&
radio->num_active_works < MAX_ACTIVE_WORKS)
radio_work_check_next(wpa_s);
}
/*
* This function removes both started and pending radio works running on
* the provided interface's radio.
* Prior to the removal of the radio work, its callback (cb) is called with
* deinit set to be 1. Each work's callback is responsible for clearing its
* internal data and restoring to a correct state.
* @wpa_s: wpa_supplicant data
* @type: type of works to be removed
* @remove_all: 1 to remove all the works on this radio, 0 to remove only
* this interface's works.
*/
void radio_remove_works(struct wpa_supplicant *wpa_s,
const char *type, int remove_all)
{
struct wpa_radio_work *work, *tmp;
struct wpa_radio *radio = wpa_s->radio;
dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work,
list) {
if (type && os_strcmp(type, work->type) != 0)
continue;
/* skip other ifaces' works */
if (!remove_all && work->wpa_s != wpa_s)
continue;
wpa_dbg(wpa_s, MSG_DEBUG, "Remove radio work '%s'@%p%s",
work->type, work, work->started ? " (started)" : "");
work->cb(work, 1);
radio_work_free(work);
}
/* in case we removed the started work */
radio_work_check_next(wpa_s);
}
void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx)
{
struct wpa_radio_work *work;
struct wpa_radio *radio = wpa_s->radio;
dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
if (work->ctx != ctx)
continue;
wpa_dbg(wpa_s, MSG_DEBUG, "Free pending radio work '%s'@%p%s",
work->type, work, work->started ? " (started)" : "");
radio_work_free(work);
break;
}
}
static void radio_remove_interface(struct wpa_supplicant *wpa_s)
{
struct wpa_radio *radio = wpa_s->radio;
if (!radio)
return;
wpa_printf(MSG_DEBUG, "Remove interface %s from radio %s",
wpa_s->ifname, radio->name);
dl_list_del(&wpa_s->radio_list);
radio_remove_works(wpa_s, NULL, 0);
/* If the interface that triggered the external scan was removed, the
* external scan is no longer running. */
if (wpa_s == radio->external_scan_req_interface)
radio->external_scan_req_interface = NULL;
wpa_s->radio = NULL;
if (!dl_list_empty(&radio->ifaces))
return; /* Interfaces remain for this radio */
wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name);
eloop_cancel_timeout(radio_start_next_work, radio, NULL);
os_free(radio);
}
void radio_work_check_next(struct wpa_supplicant *wpa_s)
{
struct wpa_radio *radio = wpa_s->radio;
if (dl_list_empty(&radio->work))
return;
if (wpa_s->ext_work_in_progress) {
wpa_printf(MSG_DEBUG,
"External radio work in progress - delay start of pending item");
return;
}
eloop_cancel_timeout(radio_start_next_work, radio, NULL);
eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL);
}
/**
* radio_add_work - Add a radio work item
* @wpa_s: Pointer to wpa_supplicant data
* @freq: Frequency of the offchannel operation in MHz or 0
* @type: Unique identifier for each type of work
* @next: Force as the next work to be executed
* @cb: Callback function for indicating when radio is available
* @ctx: Context pointer for the work (work->ctx in cb())
* Returns: 0 on success, -1 on failure
*
* This function is used to request time for an operation that requires
* exclusive radio control. Once the radio is available, the registered callback
* function will be called. radio_work_done() must be called once the exclusive
* radio operation has been completed, so that the radio is freed for other
* operations. The special case of deinit=1 is used to free the context data
* during interface removal. That does not allow the callback function to start
* the radio operation, i.e., it must free any resources allocated for the radio
* work and return.
*
* The @freq parameter can be used to indicate a single channel on which the
* offchannel operation will occur. This may allow multiple radio work
* operations to be performed in parallel if they apply for the same channel.
* Setting this to 0 indicates that the work item may use multiple channels or
* requires exclusive control of the radio.
*/
int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
const char *type, int next,
void (*cb)(struct wpa_radio_work *work, int deinit),
void *ctx)
{
struct wpa_radio *radio = wpa_s->radio;
struct wpa_radio_work *work;
int was_empty;
work = os_zalloc(sizeof(*work));
if (work == NULL)
return -1;
wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work);
os_get_reltime(&work->time);
work->freq = freq;
work->type = type;
work->wpa_s = wpa_s;
work->cb = cb;
work->ctx = ctx;
if (freq)
work->bands = wpas_freq_to_band(freq);
else if (os_strcmp(type, "scan") == 0 ||
os_strcmp(type, "p2p-scan") == 0)
work->bands = wpas_get_bands(wpa_s,
((struct wpa_driver_scan_params *)
ctx)->freqs);
else
work->bands = wpas_get_bands(wpa_s, NULL);
was_empty = dl_list_empty(&wpa_s->radio->work);
if (next)
dl_list_add(&wpa_s->radio->work, &work->list);
else
dl_list_add_tail(&wpa_s->radio->work, &work->list);
if (was_empty) {
wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
radio_work_check_next(wpa_s);
} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)
&& radio->num_active_works < MAX_ACTIVE_WORKS) {
wpa_dbg(wpa_s, MSG_DEBUG,
"Try to schedule a radio work (num_active_works=%u)",
radio->num_active_works);
radio_work_check_next(wpa_s);
}
return 0;
}
/**
* radio_work_done - Indicate that a radio work item has been completed
* @work: Completed work
*
* This function is called once the callback function registered with
* radio_add_work() has completed its work.
*/
void radio_work_done(struct wpa_radio_work *work)
{
struct wpa_supplicant *wpa_s = work->wpa_s;
struct os_reltime now, diff;
unsigned int started = work->started;
os_get_reltime(&now);
os_reltime_sub(&now, &work->time, &diff);
wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p %s in %ld.%06ld seconds",
work->type, work, started ? "done" : "canceled",
diff.sec, diff.usec);
radio_work_free(work);
if (started)
radio_work_check_next(wpa_s);
}
struct wpa_radio_work *
radio_work_pending(struct wpa_supplicant *wpa_s, const char *type)
{
struct wpa_radio_work *work;
struct wpa_radio *radio = wpa_s->radio;
dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
if (work->wpa_s == wpa_s && os_strcmp(work->type, type) == 0)
return work;
}
return NULL;
}
static int wpas_init_driver(struct wpa_supplicant *wpa_s,
const struct wpa_interface *iface)
{
const char *ifname, *driver, *rn;
driver = iface->driver;
next_driver:
if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
return -1;
wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
if (wpa_s->drv_priv == NULL) {
const char *pos;
int level = MSG_ERROR;
pos = driver ? os_strchr(driver, ',') : NULL;
if (pos) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
"driver interface - try next driver wrapper");
driver = pos + 1;
goto next_driver;
}
#ifdef CONFIG_MATCH_IFACE
if (wpa_s->matched == WPA_IFACE_MATCHED_NULL)
level = MSG_DEBUG;
#endif /* CONFIG_MATCH_IFACE */
wpa_msg(wpa_s, level, "Failed to initialize driver interface");
return -1;
}
if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
"driver_param '%s'", wpa_s->conf->driver_param);
return -1;
}
ifname = wpa_drv_get_ifname(wpa_s);
if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
"interface name with '%s'", ifname);
os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
}
rn = wpa_driver_get_radio_name(wpa_s);
if (rn && rn[0] == '\0')
rn = NULL;
wpa_s->radio = radio_add_interface(wpa_s, rn);
if (wpa_s->radio == NULL)
return -1;
return 0;
}
#ifdef CONFIG_GAS_SERVER
static void wpas_gas_server_tx_status(struct wpa_supplicant *wpa_s,
unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid,
const u8 *data, size_t data_len,
enum offchannel_send_action_result result)
{
wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
" result=%s",
freq, MAC2STR(dst),
result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
(result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
"FAILED"));
gas_server_tx_status(wpa_s->gas_server, dst, data, data_len,
result == OFFCHANNEL_SEND_ACTION_SUCCESS);
}
static void wpas_gas_server_tx(void *ctx, int freq, const u8 *da,
struct wpabuf *buf, unsigned int wait_time)
{
struct wpa_supplicant *wpa_s = ctx;
const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
if (wait_time > wpa_s->max_remain_on_chan)
wait_time = wpa_s->max_remain_on_chan;
offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, broadcast,
wpabuf_head(buf), wpabuf_len(buf),
wait_time, wpas_gas_server_tx_status, 0);
}
#endif /* CONFIG_GAS_SERVER */
static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
const struct wpa_interface *iface)
{
struct wpa_driver_capa capa;
int capa_res;
u8 dfs_domain;
wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
"'%s' ctrl_interface '%s' bridge '%s'", iface->ifname,
iface->confname ? iface->confname : "N/A",
iface->driver ? iface->driver : "default",
iface->ctrl_interface ? iface->ctrl_interface : "N/A",
iface->bridge_ifname ? iface->bridge_ifname : "N/A");
if (iface->confname) {
#ifdef CONFIG_BACKEND_FILE
wpa_s->confname = os_rel2abs_path(iface->confname);
if (wpa_s->confname == NULL) {
wpa_printf(MSG_ERROR, "Failed to get absolute path "
"for configuration file '%s'.",
iface->confname);
return -1;
}
wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'",
iface->confname, wpa_s->confname);
#else /* CONFIG_BACKEND_FILE */
wpa_s->confname = os_strdup(iface->confname);
#endif /* CONFIG_BACKEND_FILE */
wpa_s->conf = wpa_config_read(wpa_s->confname, NULL);
if (wpa_s->conf == NULL) {
wpa_printf(MSG_ERROR, "Failed to read or parse "
"configuration '%s'.", wpa_s->confname);
return -1;
}
wpa_s->confanother = os_rel2abs_path(iface->confanother);
if (wpa_s->confanother &&
!wpa_config_read(wpa_s->confanother, wpa_s->conf)) {
wpa_printf(MSG_ERROR,
"Failed to read or parse configuration '%s'.",
wpa_s->confanother);
return -1;
}
/*
* Override ctrl_interface and driver_param if set on command
* line.
*/
if (iface->ctrl_interface) {
os_free(wpa_s->conf->ctrl_interface);
wpa_s->conf->ctrl_interface =
os_strdup(iface->ctrl_interface);
}
if (iface->driver_param) {
os_free(wpa_s->conf->driver_param);
wpa_s->conf->driver_param =
os_strdup(iface->driver_param);
}
if (iface->p2p_mgmt && !iface->ctrl_interface) {
os_free(wpa_s->conf->ctrl_interface);
wpa_s->conf->ctrl_interface = NULL;
}
} else
wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface,
iface->driver_param);
if (wpa_s->conf == NULL) {
wpa_printf(MSG_ERROR, "\nNo configuration found.");
return -1;
}
if (iface->ifname == NULL) {
wpa_printf(MSG_ERROR, "\nInterface name is required.");
return -1;
}
if (os_strlen(iface->ifname) >= sizeof(wpa_s->ifname)) {
wpa_printf(MSG_ERROR, "\nToo long interface name '%s'.",
iface->ifname);
return -1;
}
os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname));
#ifdef CONFIG_MATCH_IFACE
wpa_s->matched = iface->matched;
#endif /* CONFIG_MATCH_IFACE */
if (iface->bridge_ifname) {
if (os_strlen(iface->bridge_ifname) >=
sizeof(wpa_s->bridge_ifname)) {
wpa_printf(MSG_ERROR, "\nToo long bridge interface "
"name '%s'.", iface->bridge_ifname);
return -1;
}
os_strlcpy(wpa_s->bridge_ifname, iface->bridge_ifname,
sizeof(wpa_s->bridge_ifname));
}
/* RSNA Supplicant Key Management - INITIALIZE */
eapol_sm_notify_portEnabled(wpa_s->eapol, false);
eapol_sm_notify_portValid(wpa_s->eapol, false);
/* Initialize driver interface and register driver event handler before
* L2 receive handler so that association events are processed before
* EAPOL-Key packets if both become available for the same select()
* call. */
if (wpas_init_driver(wpa_s, iface) < 0)
return -1;
if (wpa_supplicant_init_wpa(wpa_s) < 0)
return -1;
wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname,
wpa_s->bridge_ifname[0] ? wpa_s->bridge_ifname :
NULL);
wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
if (wpa_s->conf->dot11RSNAConfigPMKLifetime &&
wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
wpa_s->conf->dot11RSNAConfigPMKLifetime)) {
wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
"dot11RSNAConfigPMKLifetime");
return -1;
}
if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold &&
wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) {
wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
"dot11RSNAConfigPMKReauthThreshold");
return -1;
}
if (wpa_s->conf->dot11RSNAConfigSATimeout &&
wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
wpa_s->conf->dot11RSNAConfigSATimeout)) {
wpa_msg(wpa_s, MSG_ERROR, "Invalid WPA parameter value for "
"dot11RSNAConfigSATimeout");
return -1;
}
wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
&wpa_s->hw.num_modes,
&wpa_s->hw.flags,
&dfs_domain);
if (wpa_s->hw.modes) {
u16 i;
for (i = 0; i < wpa_s->hw.num_modes; i++) {
if (wpa_s->hw.modes[i].vht_capab) {
wpa_s->hw_capab = CAPAB_VHT;
break;
}
if (wpa_s->hw.modes[i].ht_capab &
HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
wpa_s->hw_capab = CAPAB_HT40;
else if (wpa_s->hw.modes[i].ht_capab &&
wpa_s->hw_capab == CAPAB_NO_HT_VHT)
wpa_s->hw_capab = CAPAB_HT;
}
}
capa_res = wpa_drv_get_capa(wpa_s, &capa);
if (capa_res == 0) {
wpa_s->drv_capa_known = 1;
wpa_s->drv_flags = capa.flags;
wpa_s->drv_flags2 = capa.flags2;
wpa_s->drv_enc = capa.enc;
wpa_s->drv_rrm_flags = capa.rrm_flags;
wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
wpa_s->max_scan_ssids = capa.max_scan_ssids;
wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
wpa_s->max_sched_scan_plans = capa.max_sched_scan_plans;
wpa_s->max_sched_scan_plan_interval =
capa.max_sched_scan_plan_interval;
wpa_s->max_sched_scan_plan_iterations =
capa.max_sched_scan_plan_iterations;
wpa_s->sched_scan_supported = capa.sched_scan_supported;
wpa_s->max_match_sets = capa.max_match_sets;
wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
wpa_s->max_stations = capa.max_stations;
wpa_s->extended_capa = capa.extended_capa;
wpa_s->extended_capa_mask = capa.extended_capa_mask;
wpa_s->extended_capa_len = capa.extended_capa_len;
wpa_s->num_multichan_concurrent =
capa.num_multichan_concurrent;
wpa_s->wmm_ac_supported = capa.wmm_ac_supported;
if (capa.mac_addr_rand_scan_supported)
wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN;
if (wpa_s->sched_scan_supported &&
capa.mac_addr_rand_sched_scan_supported)
wpa_s->mac_addr_rand_supported |=
(MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO);
wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
if (wpa_s->extended_capa &&
wpa_s->extended_capa_len >= 3 &&
wpa_s->extended_capa[2] & 0x40)
wpa_s->multi_bss_support = 1;
}
if (wpa_s->max_remain_on_chan == 0)
wpa_s->max_remain_on_chan = 1000;
/*
* Only take p2p_mgmt parameters when P2P Device is supported.
* Doing it here as it determines whether l2_packet_init() will be done
* during wpa_supplicant_driver_init().
*/
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
wpa_s->p2p_mgmt = iface->p2p_mgmt;
if (wpa_s->num_multichan_concurrent == 0)
wpa_s->num_multichan_concurrent = 1;
if (wpa_supplicant_driver_init(wpa_s) < 0)
return -1;
#ifdef CONFIG_TDLS
if (!iface->p2p_mgmt && wpa_tdls_init(wpa_s->wpa))
return -1;
#endif /* CONFIG_TDLS */
if (wpa_s->conf->country[0] && wpa_s->conf->country[1] &&
wpa_drv_set_country(wpa_s, wpa_s->conf->country)) {
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to set country");
return -1;
}
#ifdef CONFIG_FST
if (wpa_s->conf->fst_group_id) {
struct fst_iface_cfg cfg;
struct fst_wpa_obj iface_obj;
fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id,
sizeof(cfg.group_id));
cfg.priority = wpa_s->conf->fst_priority;
cfg.llt = wpa_s->conf->fst_llt;
wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr,
&iface_obj, &cfg);
if (!wpa_s->fst) {
wpa_msg(wpa_s, MSG_ERROR,
"FST: Cannot attach iface %s to group %s",
wpa_s->ifname, cfg.group_id);
return -1;
}
}
#endif /* CONFIG_FST */
if (wpas_wps_init(wpa_s))
return -1;
#ifdef CONFIG_GAS_SERVER
wpa_s->gas_server = gas_server_init(wpa_s, wpas_gas_server_tx);
if (!wpa_s->gas_server) {
wpa_printf(MSG_ERROR, "Failed to initialize GAS server");
return -1;
}
#endif /* CONFIG_GAS_SERVER */
#ifdef CONFIG_DPP
if (wpas_dpp_init(wpa_s) < 0)
return -1;
#endif /* CONFIG_DPP */
if (wpa_supplicant_init_eapol(wpa_s) < 0)
return -1;
wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
if (wpa_s->ctrl_iface == NULL) {
wpa_printf(MSG_ERROR,
"Failed to initialize control interface '%s'.\n"
"You may have another wpa_supplicant process "
"already running or the file was\n"
"left by an unclean termination of wpa_supplicant "
"in which case you will need\n"
"to manually remove this file before starting "
"wpa_supplicant again.\n",
wpa_s->conf->ctrl_interface);
return -1;
}
wpa_s->gas = gas_query_init(wpa_s);
if (wpa_s->gas == NULL) {
wpa_printf(MSG_ERROR, "Failed to initialize GAS query");
return -1;
}
if ((!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) ||
wpa_s->p2p_mgmt) &&
wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
return -1;
}
if (wpa_bss_init(wpa_s) < 0)
return -1;
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
#ifdef CONFIG_MESH
dl_list_init(&wpa_s->mesh_external_pmksa_cache);
#endif /* CONFIG_MESH */
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
/*
* Set Wake-on-WLAN triggers, if configured.
* Note: We don't restore/remove the triggers on shutdown (it doesn't
* have effect anyway when the interface is down).
*/
if (capa_res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
return -1;
#ifdef CONFIG_EAP_PROXY
{
size_t len;
wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
wpa_s->imsi, &len);
if (wpa_s->mnc_len > 0) {
wpa_s->imsi[len] = '\0';
wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
wpa_s->imsi, wpa_s->mnc_len);
} else {
wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
}
}
#endif /* CONFIG_EAP_PROXY */
if (pcsc_reader_init(wpa_s) < 0)
return -1;
if (wpas_init_ext_pw(wpa_s) < 0)
return -1;
wpas_rrm_reset(wpa_s);
wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
#ifdef CONFIG_HS20
hs20_init(wpa_s);
#endif /* CONFIG_HS20 */
#ifdef CONFIG_MBO
if (!wpa_s->disable_mbo_oce && 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");
}
}
wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan);
#endif /* CONFIG_MBO */
wpa_supplicant_set_default_scan_ies(wpa_s);
return 0;
}
static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
int notify, int terminate)
{
struct wpa_global *global = wpa_s->global;
struct wpa_supplicant *iface, *prev;
if (wpa_s == wpa_s->parent)
wpas_p2p_group_remove(wpa_s, "*");
iface = global->ifaces;
while (iface) {
if (iface->p2pdev == wpa_s)
iface->p2pdev = iface->parent;
if (iface == wpa_s || iface->parent != wpa_s) {
iface = iface->next;
continue;
}
wpa_printf(MSG_DEBUG,
"Remove remaining child interface %s from parent %s",
iface->ifname, wpa_s->ifname);
prev = iface;
iface = iface->next;
wpa_supplicant_remove_iface(global, prev, terminate);
}
wpa_s->disconnected = 1;
if (wpa_s->drv_priv) {
/*
* Don't deauthenticate if WoWLAN is enable and not explicitly
* been configured to disconnect.
*/
if (!wpa_drv_get_wowlan(wpa_s) ||
wpa_s->conf->wowlan_disconnect_on_deinit) {
wpa_supplicant_deauthenticate(
wpa_s, WLAN_REASON_DEAUTH_LEAVING);
wpa_drv_set_countermeasures(wpa_s, 0);
wpa_clear_keys(wpa_s, NULL);
} else {
wpa_msg(wpa_s, MSG_INFO,
"Do not deauthenticate as part of interface deinit since WoWLAN is enabled");
}
}
wpa_supplicant_cleanup(wpa_s);
wpas_p2p_deinit_iface(wpa_s);
wpas_ctrl_radio_work_flush(wpa_s);
radio_remove_interface(wpa_s);
#ifdef CONFIG_FST
if (wpa_s->fst) {
fst_detach(wpa_s->fst);
wpa_s->fst = NULL;
}
if (wpa_s->received_mb_ies) {
wpabuf_free(wpa_s->received_mb_ies);
wpa_s->received_mb_ies = NULL;
}
#endif /* CONFIG_FST */
if (wpa_s->drv_priv)
wpa_drv_deinit(wpa_s);
if (notify)
wpas_notify_iface_removed(wpa_s);
if (terminate)
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING);
wpa_supplicant_ctrl_iface_deinit(wpa_s, wpa_s->ctrl_iface);
wpa_s->ctrl_iface = NULL;
#ifdef CONFIG_MESH
if (wpa_s->ifmsh) {
wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh, true);
wpa_s->ifmsh = NULL;
}
#endif /* CONFIG_MESH */
if (wpa_s->conf != NULL) {
wpa_config_free(wpa_s->conf);
wpa_s->conf = NULL;
}
os_free(wpa_s->ssids_from_scan_req);
os_free(wpa_s->last_scan_freqs);
os_free(wpa_s);
}
#ifdef CONFIG_MATCH_IFACE
/**
* wpa_supplicant_match_iface - Match an interface description to a name
* @global: Pointer to global data from wpa_supplicant_init()
* @ifname: Name of the interface to match
* Returns: Pointer to the created interface description or %NULL on failure
*/
struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
const char *ifname)
{
int i;
struct wpa_interface *iface, *miface;
for (i = 0; i < global->params.match_iface_count; i++) {
miface = &global->params.match_ifaces[i];
if (!miface->ifname ||
fnmatch(miface->ifname, ifname, 0) == 0) {
iface = os_zalloc(sizeof(*iface));
if (!iface)
return NULL;
*iface = *miface;
if (!miface->ifname)
iface->matched = WPA_IFACE_MATCHED_NULL;
else
iface->matched = WPA_IFACE_MATCHED;
iface->ifname = ifname;
return iface;
}
}
return NULL;
}
/**
* wpa_supplicant_match_existing - Match existing interfaces
* @global: Pointer to global data from wpa_supplicant_init()
* Returns: 0 on success, -1 on failure
*/
static int wpa_supplicant_match_existing(struct wpa_global *global)
{
struct if_nameindex *ifi, *ifp;
struct wpa_supplicant *wpa_s;
struct wpa_interface *iface;
ifp = if_nameindex();
if (!ifp) {
wpa_printf(MSG_ERROR, "if_nameindex: %s", strerror(errno));
return -1;
}
for (ifi = ifp; ifi->if_name; ifi++) {
wpa_s = wpa_supplicant_get_iface(global, ifi->if_name);
if (wpa_s)
continue;
iface = wpa_supplicant_match_iface(global, ifi->if_name);
if (iface) {
wpa_supplicant_add_iface(global, iface, NULL);
os_free(iface);
}
}
if_freenameindex(ifp);
return 0;
}
#endif /* CONFIG_MATCH_IFACE */
/**
* wpa_supplicant_add_iface - Add a new network interface
* @global: Pointer to global data from wpa_supplicant_init()
* @iface: Interface configuration options
* @parent: Parent interface or %NULL to assign new interface as parent
* Returns: Pointer to the created interface or %NULL on failure
*
* This function is used to add new network interfaces for %wpa_supplicant.
* This can be called before wpa_supplicant_run() to add interfaces before the
* main event loop has been started. In addition, new interfaces can be added
* dynamically while %wpa_supplicant is already running. This could happen,
* e.g., when a hotplug network adapter is inserted.
*/
struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
struct wpa_interface *iface,
struct wpa_supplicant *parent)
{
struct wpa_supplicant *wpa_s;
struct wpa_interface t_iface;
struct wpa_ssid *ssid;
if (global == NULL || iface == NULL)
return NULL;
wpa_s = wpa_supplicant_alloc(parent);
if (wpa_s == NULL)
return NULL;
wpa_s->global = global;
t_iface = *iface;
if (global->params.override_driver) {
wpa_printf(MSG_DEBUG, "Override interface parameter: driver "
"('%s' -> '%s')",
iface->driver, global->params.override_driver);
t_iface.driver = global->params.override_driver;
}
if (global->params.override_ctrl_interface) {
wpa_printf(MSG_DEBUG, "Override interface parameter: "
"ctrl_interface ('%s' -> '%s')",
iface->ctrl_interface,
global->params.override_ctrl_interface);
t_iface.ctrl_interface =
global->params.override_ctrl_interface;
}
if (wpa_supplicant_init_iface(wpa_s, &t_iface)) {
wpa_printf(MSG_DEBUG, "Failed to add interface %s",
iface->ifname);
wpa_supplicant_deinit_iface(wpa_s, 0, 0);
return NULL;
}
if (iface->p2p_mgmt == 0) {
/* Notify the control interfaces about new iface */
if (wpas_notify_iface_added(wpa_s)) {
wpa_supplicant_deinit_iface(wpa_s, 1, 0);
return NULL;
}
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
wpas_notify_network_added(wpa_s, ssid);
}
wpa_s->next = global->ifaces;
global->ifaces = wpa_s;
wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
#ifdef CONFIG_P2P
if (wpa_s->global->p2p == NULL &&
!wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
wpas_p2p_add_p2pdev_interface(
wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
wpa_printf(MSG_INFO,
"P2P: Failed to enable P2P Device interface");
/* Try to continue without. P2P will be disabled. */
}
#endif /* CONFIG_P2P */
return wpa_s;
}
/**
* wpa_supplicant_remove_iface - Remove a network interface
* @global: Pointer to global data from wpa_supplicant_init()
* @wpa_s: Pointer to the network interface to be removed
* Returns: 0 if interface was removed, -1 if interface was not found
*
* This function can be used to dynamically remove network interfaces from
* %wpa_supplicant, e.g., when a hotplug network adapter is ejected. In
* addition, this function is used to remove all remaining interfaces when
* %wpa_supplicant is terminated.
*/
int wpa_supplicant_remove_iface(struct wpa_global *global,
struct wpa_supplicant *wpa_s,
int terminate)
{
struct wpa_supplicant *prev;
#ifdef CONFIG_MESH
unsigned int mesh_if_created = wpa_s->mesh_if_created;
char *ifname = NULL;
struct wpa_supplicant *parent = wpa_s->parent;
#endif /* CONFIG_MESH */
/* Remove interface from the global list of interfaces */
prev = global->ifaces;
if (prev == wpa_s) {
global->ifaces = wpa_s->next;
} else {
while (prev && prev->next != wpa_s)
prev = prev->next;
if (prev == NULL)
return -1;
prev->next = wpa_s->next;
}
wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
#ifdef CONFIG_MESH
if (mesh_if_created) {
ifname = os_strdup(wpa_s->ifname);
if (ifname == NULL) {
wpa_dbg(wpa_s, MSG_ERROR,
"mesh: Failed to malloc ifname");
return -1;
}
}
#endif /* CONFIG_MESH */
if (global->p2p_group_formation == wpa_s)
global->p2p_group_formation = NULL;
if (global->p2p_invite_group == wpa_s)
global->p2p_invite_group = NULL;
wpa_supplicant_deinit_iface(wpa_s, 1, terminate);
#ifdef CONFIG_MESH
if (mesh_if_created) {
wpa_drv_if_remove(parent, WPA_IF_MESH, ifname);
os_free(ifname);
}
#endif /* CONFIG_MESH */
return 0;
}
/**
* wpa_supplicant_get_eap_mode - Get the current EAP mode
* @wpa_s: Pointer to the network interface
* Returns: Pointer to the eap mode or the string "UNKNOWN" if not found
*/
const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s)
{
const char *eapol_method;
if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) == 0 &&
wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
return "NO-EAP";
}
eapol_method = eapol_sm_get_method_name(wpa_s->eapol);
if (eapol_method == NULL)
return "UNKNOWN-EAP";
return eapol_method;
}
/**
* wpa_supplicant_get_iface - Get a new network interface
* @global: Pointer to global data from wpa_supplicant_init()
* @ifname: Interface name
* Returns: Pointer to the interface or %NULL if not found
*/
struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
const char *ifname)
{
struct wpa_supplicant *wpa_s;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_strcmp(wpa_s->ifname, ifname) == 0)
return wpa_s;
}
return NULL;
}
#ifndef CONFIG_NO_WPA_MSG
static const char * wpa_supplicant_msg_ifname_cb(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s == NULL)
return NULL;
return wpa_s->ifname;
}
#endif /* CONFIG_NO_WPA_MSG */
#ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL
#define WPA_SUPPLICANT_CLEANUP_INTERVAL 10
#endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */
/* Periodic cleanup tasks */
static void wpas_periodic(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_global *global = eloop_ctx;
struct wpa_supplicant *wpa_s;
eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
wpas_periodic, global, NULL);
#ifdef CONFIG_P2P
if (global->p2p)
p2p_expire_peers(global->p2p);
#endif /* CONFIG_P2P */
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
#ifdef CONFIG_AP
ap_periodic(wpa_s);
#endif /* CONFIG_AP */
}
}
/**
* wpa_supplicant_init - Initialize %wpa_supplicant
* @params: Parameters for %wpa_supplicant
* Returns: Pointer to global %wpa_supplicant data, or %NULL on failure
*
* This function is used to initialize %wpa_supplicant. After successful
* initialization, the returned data pointer can be used to add and remove
* network interfaces, and eventually, to deinitialize %wpa_supplicant.
*/
struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
{
struct wpa_global *global;
int ret, i;
if (params == NULL)
return NULL;
#ifndef CONFIG_NO_WPA_MSG
wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
#endif /* CONFIG_NO_WPA_MSG */
if (params->wpa_debug_file_path)
wpa_debug_open_file(params->wpa_debug_file_path);
if (!params->wpa_debug_file_path && !params->wpa_debug_syslog)
wpa_debug_setup_stdout();
if (params->wpa_debug_syslog)
wpa_debug_open_syslog();
if (params->wpa_debug_tracing) {
ret = wpa_debug_open_linux_tracing();
if (ret) {
wpa_printf(MSG_ERROR,
"Failed to enable trace logging");
return NULL;
}
}
ret = eap_register_methods();
if (ret) {
wpa_printf(MSG_ERROR, "Failed to register EAP methods");
if (ret == -2)
wpa_printf(MSG_ERROR, "Two or more EAP methods used "
"the same EAP type.");
return NULL;
}
global = os_zalloc(sizeof(*global));
if (global == NULL)
return NULL;
dl_list_init(&global->p2p_srv_bonjour);
dl_list_init(&global->p2p_srv_upnp);
global->params.daemonize = params->daemonize;
global->params.wait_for_monitor = params->wait_for_monitor;
global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
if (params->pid_file)
global->params.pid_file = os_strdup(params->pid_file);
if (params->ctrl_interface)
global->params.ctrl_interface =
os_strdup(params->ctrl_interface);
if (params->ctrl_interface_group)
global->params.ctrl_interface_group =
os_strdup(params->ctrl_interface_group);
if (params->override_driver)
global->params.override_driver =
os_strdup(params->override_driver);
if (params->override_ctrl_interface)
global->params.override_ctrl_interface =
os_strdup(params->override_ctrl_interface);
#ifdef CONFIG_MATCH_IFACE
global->params.match_iface_count = params->match_iface_count;
if (params->match_iface_count) {
global->params.match_ifaces =
os_calloc(params->match_iface_count,
sizeof(struct wpa_interface));
os_memcpy(global->params.match_ifaces,
params->match_ifaces,
params->match_iface_count *
sizeof(struct wpa_interface));
}
#endif /* CONFIG_MATCH_IFACE */
#ifdef CONFIG_P2P
if (params->conf_p2p_dev)
global->params.conf_p2p_dev =
os_strdup(params->conf_p2p_dev);
#endif /* CONFIG_P2P */
wpa_debug_level = global->params.wpa_debug_level =
params->wpa_debug_level;
wpa_debug_show_keys = global->params.wpa_debug_show_keys =
params->wpa_debug_show_keys;
wpa_debug_timestamp = global->params.wpa_debug_timestamp =
params->wpa_debug_timestamp;
wpa_printf(MSG_DEBUG, "wpa_supplicant v%s", VERSION_STR);
if (eloop_init()) {
wpa_printf(MSG_ERROR, "Failed to initialize event loop");
wpa_supplicant_deinit(global);
return NULL;
}
random_init(params->entropy_file);
global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
if (global->ctrl_iface == NULL) {
wpa_supplicant_deinit(global);
return NULL;
}
if (wpas_notify_supplicant_initialized(global)) {
wpa_supplicant_deinit(global);
return NULL;
}
for (i = 0; wpa_drivers[i]; i++)
global->drv_count++;
if (global->drv_count == 0) {
wpa_printf(MSG_ERROR, "No drivers enabled");
wpa_supplicant_deinit(global);
return NULL;
}
global->drv_priv = os_calloc(global->drv_count, sizeof(void *));
if (global->drv_priv == NULL) {
wpa_supplicant_deinit(global);
return NULL;
}
#ifdef CONFIG_WIFI_DISPLAY
if (wifi_display_init(global) < 0) {
wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display");
wpa_supplicant_deinit(global);
return NULL;
}
#endif /* CONFIG_WIFI_DISPLAY */
eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
wpas_periodic, global, NULL);
return global;
}
/**
* wpa_supplicant_run - Run the %wpa_supplicant main event loop
* @global: Pointer to global data from wpa_supplicant_init()
* Returns: 0 after successful event loop run, -1 on failure
*
* This function starts the main event loop and continues running as long as
* there are any remaining events. In most cases, this function is running as
* long as the %wpa_supplicant process in still in use.
*/
int wpa_supplicant_run(struct wpa_global *global)
{
struct wpa_supplicant *wpa_s;
if (global->params.daemonize &&
(wpa_supplicant_daemon(global->params.pid_file) ||
eloop_sock_requeue()))
return -1;
#ifdef CONFIG_MATCH_IFACE
if (wpa_supplicant_match_existing(global))
return -1;
#endif
if (global->params.wait_for_monitor) {
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt)
wpa_supplicant_ctrl_iface_wait(
wpa_s->ctrl_iface);
}
eloop_register_signal_terminate(wpa_supplicant_terminate, global);
eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
eloop_run();
return 0;
}
/**
* wpa_supplicant_deinit - Deinitialize %wpa_supplicant
* @global: Pointer to global data from wpa_supplicant_init()
*
* This function is called to deinitialize %wpa_supplicant and to free all
* allocated resources. Remaining network interfaces will also be removed.
*/
void wpa_supplicant_deinit(struct wpa_global *global)
{
int i;
if (global == NULL)
return;
eloop_cancel_timeout(wpas_periodic, global, NULL);
#ifdef CONFIG_WIFI_DISPLAY
wifi_display_deinit(global);
#endif /* CONFIG_WIFI_DISPLAY */
while (global->ifaces)
wpa_supplicant_remove_iface(global, global->ifaces, 1);
if (global->ctrl_iface)
wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface);
wpas_notify_supplicant_deinitialized(global);
eap_peer_unregister_methods();
#ifdef CONFIG_AP
eap_server_unregister_methods();
#endif /* CONFIG_AP */
for (i = 0; wpa_drivers[i] && global->drv_priv; i++) {
if (!global->drv_priv[i])
continue;
wpa_drivers[i]->global_deinit(global->drv_priv[i]);
}
os_free(global->drv_priv);
random_deinit();
eloop_destroy();
if (global->params.pid_file) {
os_daemonize_terminate(global->params.pid_file);
os_free(global->params.pid_file);
}
os_free(global->params.ctrl_interface);
os_free(global->params.ctrl_interface_group);
os_free(global->params.override_driver);
os_free(global->params.override_ctrl_interface);
#ifdef CONFIG_MATCH_IFACE
os_free(global->params.match_ifaces);
#endif /* CONFIG_MATCH_IFACE */
#ifdef CONFIG_P2P
os_free(global->params.conf_p2p_dev);
#endif /* CONFIG_P2P */
os_free(global->p2p_disallow_freq.range);
os_free(global->p2p_go_avoid_freq.range);
os_free(global->add_psk);
os_free(global);
wpa_debug_close_syslog();
wpa_debug_close_file();
wpa_debug_close_linux_tracing();
}
void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
{
if ((wpa_s->conf->changed_parameters & CFG_CHANGED_COUNTRY) &&
wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
char country[3];
country[0] = wpa_s->conf->country[0];
country[1] = wpa_s->conf->country[1];
country[2] = '\0';
if (wpa_drv_set_country(wpa_s, country) < 0) {
wpa_printf(MSG_ERROR, "Failed to set country code "
"'%s'", country);
}
}
if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
wpas_init_ext_pw(wpa_s);
if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS)
wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
if (wpa_s->conf->changed_parameters & CFG_CHANGED_WOWLAN_TRIGGERS) {
struct wpa_driver_capa capa;
int res = wpa_drv_get_capa(wpa_s, &capa);
if (res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
wpa_printf(MSG_ERROR,
"Failed to update wowlan_triggers to '%s'",
wpa_s->conf->wowlan_triggers);
}
if (wpa_s->conf->changed_parameters & CFG_CHANGED_DISABLE_BTM)
wpa_supplicant_set_default_scan_ies(wpa_s);
#ifdef CONFIG_BGSCAN
/*
* We default to global bgscan parameters only when per-network bgscan
* parameters aren't set. Only bother resetting bgscan parameters if
* this is the case.
*/
if ((wpa_s->conf->changed_parameters & CFG_CHANGED_BGSCAN) &&
wpa_s->current_ssid && !wpa_s->current_ssid->bgscan &&
wpa_s->wpa_state == WPA_COMPLETED)
wpa_supplicant_reset_bgscan(wpa_s);
#endif /* CONFIG_BGSCAN */
#ifdef CONFIG_WPS
wpas_wps_update_config(wpa_s);
#endif /* CONFIG_WPS */
wpas_p2p_update_config(wpa_s);
wpa_s->conf->changed_parameters = 0;
}
void add_freq(int *freqs, int *num_freqs, int freq)
{
int i;
for (i = 0; i < *num_freqs; i++) {
if (freqs[i] == freq)
return;
}
freqs[*num_freqs] = freq;
(*num_freqs)++;
}
static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s)
{
struct wpa_bss *bss, *cbss;
const int max_freqs = 10;
int *freqs;
int num_freqs = 0;
freqs = os_calloc(max_freqs + 1, sizeof(int));
if (freqs == NULL)
return NULL;
cbss = wpa_s->current_bss;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (bss == cbss)
continue;
if (bss->ssid_len == cbss->ssid_len &&
os_memcmp(bss->ssid, cbss->ssid, bss->ssid_len) == 0 &&
!wpa_bssid_ignore_is_listed(wpa_s, bss->bssid)) {
add_freq(freqs, &num_freqs, bss->freq);
if (num_freqs == max_freqs)
break;
}
}
if (num_freqs == 0) {
os_free(freqs);
freqs = NULL;
}
return freqs;
}
void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
int timeout;
int count;
int *freqs = NULL;
wpas_connect_work_done(wpa_s);
/*
* Remove possible authentication timeout since the connection failed.
*/
eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
/*
* There is no point in ignoring the AP temporarily if this event is
* generated based on local request to disconnect.
*/
if (wpa_s->own_disconnect_req || wpa_s->own_reconnect_req) {
wpa_s->own_disconnect_req = 0;
wpa_dbg(wpa_s, MSG_DEBUG,
"Ignore connection failure due to local request to disconnect");
return;
}
if (wpa_s->disconnected) {
wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure "
"indication since interface has been put into "
"disconnected state");
return;
}
/*
* Add the failed BSSID into the ignore list and speed up next scan
* attempt if there could be other APs that could accept association.
*/
count = wpa_bssid_ignore_add(wpa_s, bssid);
if (count == 1 && wpa_s->current_bss) {
/*
* This BSS was not in the ignore list before. If there is
* another BSS available for the same ESS, we should try that
* next. Otherwise, we may as well try this one once more
* before allowing other, likely worse, ESSes to be considered.
*/
freqs = get_bss_freqs_in_ess(wpa_s);
if (freqs) {
wpa_dbg(wpa_s, MSG_DEBUG, "Another BSS in this ESS "
"has been seen; try it next");
wpa_bssid_ignore_add(wpa_s, bssid);
/*
* On the next scan, go through only the known channels
* used in this ESS based on previous scans to speed up
* common load balancing use case.
*/
os_free(wpa_s->next_scan_freqs);
wpa_s->next_scan_freqs = freqs;
}
}
wpa_s->consecutive_conn_failures++;
if (wpa_s->consecutive_conn_failures > 3 && wpa_s->current_ssid) {
wpa_printf(MSG_DEBUG, "Continuous association failures - "
"consider temporary network disabling");
wpas_auth_failed(wpa_s, "CONN_FAILED");
}
/*
* Multiple consecutive connection failures mean that other APs are
* either not available or have already been tried, so we can start
* increasing the delay here to avoid constant scanning.
*/
switch (wpa_s->consecutive_conn_failures) {
case 1:
timeout = 100;
break;
case 2:
timeout = 500;
break;
case 3:
timeout = 1000;
break;
case 4:
timeout = 5000;
break;
default:
timeout = 10000;
break;
}
wpa_dbg(wpa_s, MSG_DEBUG,
"Consecutive connection failures: %d --> request scan in %d ms",
wpa_s->consecutive_conn_failures, timeout);
/*
* TODO: if more than one possible AP is available in scan results,
* could try the other ones before requesting a new scan.
*/
/* speed up the connection attempt with normal scan */
wpa_s->normal_scans = 0;
wpa_supplicant_req_scan(wpa_s, timeout / 1000,
1000 * (timeout % 1000));
}
#ifdef CONFIG_FILS
void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
const u8 *realm, *username, *rrk;
size_t realm_len, username_len, rrk_len;
u16 next_seq_num;
/* Clear the PMKSA cache entry if FILS authentication was rejected.
* Check for ERP keys existing to limit when this can be done since
* the rejection response is not protected and such triggers should
* really not allow internal state to be modified unless required to
* avoid significant issues in functionality. In addition, drop
* externally configure PMKSA entries even without ERP keys since it
* is possible for an external component to add PMKSA entries for FILS
* authentication without restoring previously generated ERP keys.
*
* In this case, this is needed to allow recovery from cases where the
* AP or authentication server has dropped PMKSAs and ERP keys. */
if (!ssid || !ssid->eap.erp || !wpa_key_mgmt_fils(ssid->key_mgmt))
return;
if (eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap,
&username, &username_len,
&realm, &realm_len, &next_seq_num,
&rrk, &rrk_len) != 0 ||
!realm) {
wpa_dbg(wpa_s, MSG_DEBUG,
"FILS: Drop external PMKSA cache entry");
wpa_sm_aborted_external_cached(wpa_s->wpa);
wpa_sm_external_pmksa_cache_flush(wpa_s->wpa, ssid);
return;
}
wpa_dbg(wpa_s, MSG_DEBUG, "FILS: Drop PMKSA cache entry");
wpa_sm_aborted_cached(wpa_s->wpa);
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
}
void fils_connection_failure(struct wpa_supplicant *wpa_s)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
const u8 *realm, *username, *rrk;
size_t realm_len, username_len, rrk_len;
u16 next_seq_num;
if (!ssid || !ssid->eap.erp || !wpa_key_mgmt_fils(ssid->key_mgmt) ||
eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap,
&username, &username_len,
&realm, &realm_len, &next_seq_num,
&rrk, &rrk_len) != 0 ||
!realm)
return;
wpa_hexdump_ascii(MSG_DEBUG,
"FILS: Store last connection failure realm",
realm, realm_len);
os_free(wpa_s->last_con_fail_realm);
wpa_s->last_con_fail_realm = os_malloc(realm_len);
if (wpa_s->last_con_fail_realm) {
wpa_s->last_con_fail_realm_len = realm_len;
os_memcpy(wpa_s->last_con_fail_realm, realm, realm_len);
}
}
#endif /* CONFIG_FILS */
int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s)
{
return wpa_s->conf->ap_scan == 2 ||
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION);
}
#if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW)
int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
const char *field,
const char *value)
{
#ifdef IEEE8021X_EAPOL
struct eap_peer_config *eap = &ssid->eap;
wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field);
wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value",
(const u8 *) value, os_strlen(value));
switch (wpa_supplicant_ctrl_req_from_string(field)) {
case WPA_CTRL_REQ_EAP_IDENTITY:
os_free(eap->identity);
eap->identity = (u8 *) os_strdup(value);
eap->identity_len = os_strlen(value);
eap->pending_req_identity = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
break;
case WPA_CTRL_REQ_EAP_PASSWORD:
bin_clear_free(eap->password, eap->password_len);
eap->password = (u8 *) os_strdup(value);
eap->password_len = os_strlen(value);
eap->pending_req_password = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
break;
case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
bin_clear_free(eap->new_password, eap->new_password_len);
eap->new_password = (u8 *) os_strdup(value);
eap->new_password_len = os_strlen(value);
eap->pending_req_new_password = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
break;
case WPA_CTRL_REQ_EAP_PIN:
str_clear_free(eap->cert.pin);
eap->cert.pin = os_strdup(value);
eap->pending_req_pin = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
break;
case WPA_CTRL_REQ_EAP_OTP:
bin_clear_free(eap->otp, eap->otp_len);
eap->otp = (u8 *) os_strdup(value);
eap->otp_len = os_strlen(value);
os_free(eap->pending_req_otp);
eap->pending_req_otp = NULL;
eap->pending_req_otp_len = 0;
break;
case WPA_CTRL_REQ_EAP_PASSPHRASE:
str_clear_free(eap->cert.private_key_passwd);
eap->cert.private_key_passwd = os_strdup(value);
eap->pending_req_passphrase = 0;
if (ssid == wpa_s->current_ssid)
wpa_s->reassociate = 1;
break;
case WPA_CTRL_REQ_SIM:
str_clear_free(eap->external_sim_resp);
eap->external_sim_resp = os_strdup(value);
eap->pending_req_sim = 0;
break;
case WPA_CTRL_REQ_PSK_PASSPHRASE:
if (wpa_config_set(ssid, "psk", value, 0) < 0)
return -1;
ssid->mem_only_psk = 1;
if (ssid->passphrase)
wpa_config_update_psk(ssid);
if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
wpa_supplicant_req_scan(wpa_s, 0, 0);
break;
case WPA_CTRL_REQ_EXT_CERT_CHECK:
if (eap->pending_ext_cert_check != PENDING_CHECK)
return -1;
if (os_strcmp(value, "good") == 0)
eap->pending_ext_cert_check = EXT_CERT_CHECK_GOOD;
else if (os_strcmp(value, "bad") == 0)
eap->pending_ext_cert_check = EXT_CERT_CHECK_BAD;
else
return -1;
break;
default:
wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
return -1;
}
return 0;
#else /* IEEE8021X_EAPOL */
wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included");
return -1;
#endif /* IEEE8021X_EAPOL */
}
#endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW */
int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
#ifdef CONFIG_WEP
int i;
unsigned int drv_enc;
#endif /* CONFIG_WEP */
if (wpa_s->p2p_mgmt)
return 1; /* no normal network profiles on p2p_mgmt interface */
if (ssid == NULL)
return 1;
if (ssid->disabled)
return 1;
#ifdef CONFIG_WEP
if (wpa_s->drv_capa_known)
drv_enc = wpa_s->drv_enc;
else
drv_enc = (unsigned int) -1;
for (i = 0; i < NUM_WEP_KEYS; i++) {
size_t len = ssid->wep_key_len[i];
if (len == 0)
continue;
if (len == 5 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP40))
continue;
if (len == 13 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP104))
continue;
if (len == 16 && (drv_enc & WPA_DRIVER_CAPA_ENC_WEP128))
continue;
return 1; /* invalid WEP key */
}
#endif /* CONFIG_WEP */
if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
(!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
!(wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password) &&
!ssid->mem_only_psk)
return 1;
return 0;
}
int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
{
if (ssid == NULL || ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
if (wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_OPTIONAL &&
!(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) {
/*
* Driver does not support BIP -- ignore pmf=1 default
* since the connection with PMF would fail and the
* configuration does not require PMF to be enabled.
*/
return NO_MGMT_FRAME_PROTECTION;
}
if (ssid &&
(ssid->key_mgmt &
~(WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPS |
WPA_KEY_MGMT_IEEE8021X_NO_WPA)) == 0) {
/*
* Do not use the default PMF value for non-RSN networks
* since PMF is available only with RSN and pmf=2
* configuration would otherwise prevent connections to
* all open networks.
*/
return NO_MGMT_FRAME_PROTECTION;
}
return wpa_s->conf->pmf;
}
return ssid->ieee80211w;
}
+int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+ if (wpa_s->current_ssid == NULL ||
+ wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
+ os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
+ return 0;
+ return wpa_sm_pmf_enabled(wpa_s->wpa);
+}
+
+
int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
{
if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P)
return 1;
if (wpa_s->global->conc_pref == WPA_CONC_PREF_STA)
return 0;
return -1;
}
void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
{
struct wpa_ssid *ssid = wpa_s->current_ssid;
int dur;
struct os_reltime now;
if (ssid == NULL) {
wpa_printf(MSG_DEBUG, "Authentication failure but no known "
"SSID block");
return;
}
if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
return;
ssid->auth_failures++;
#ifdef CONFIG_P2P
if (ssid->p2p_group &&
(wpa_s->p2p_in_provisioning || wpa_s->show_group_started)) {
/*
* Skip the wait time since there is a short timeout on the
* connection to a P2P group.
*/
return;
}
#endif /* CONFIG_P2P */
if (ssid->auth_failures > 50)
dur = 300;
else if (ssid->auth_failures > 10)
dur = 120;
else if (ssid->auth_failures > 5)
dur = 90;
else if (ssid->auth_failures > 3)
dur = 60;
else if (ssid->auth_failures > 2)
dur = 30;
else if (ssid->auth_failures > 1)
dur = 20;
else
dur = 10;
if (ssid->auth_failures > 1 &&
wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt))
dur += os_random() % (ssid->auth_failures * 10);
os_get_reltime(&now);
if (now.sec + dur <= ssid->disabled_until.sec)
return;
ssid->disabled_until.sec = now.sec + dur;
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED
"id=%d ssid=\"%s\" auth_failures=%u duration=%d reason=%s",
ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
ssid->auth_failures, dur, reason);
}
void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int clear_failures)
{
if (ssid == NULL)
return;
if (ssid->disabled_until.sec) {
wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED
"id=%d ssid=\"%s\"",
ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
}
ssid->disabled_until.sec = 0;
ssid->disabled_until.usec = 0;
if (clear_failures)
ssid->auth_failures = 0;
}
int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid)
{
size_t i;
if (wpa_s->disallow_aps_bssid == NULL)
return 0;
for (i = 0; i < wpa_s->disallow_aps_bssid_count; i++) {
if (os_memcmp(wpa_s->disallow_aps_bssid + i * ETH_ALEN,
bssid, ETH_ALEN) == 0)
return 1;
}
return 0;
}
int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
size_t ssid_len)
{
size_t i;
if (wpa_s->disallow_aps_ssid == NULL || ssid == NULL)
return 0;
for (i = 0; i < wpa_s->disallow_aps_ssid_count; i++) {
struct wpa_ssid_value *s = &wpa_s->disallow_aps_ssid[i];
if (ssid_len == s->ssid_len &&
os_memcmp(ssid, s->ssid, ssid_len) == 0)
return 1;
}
return 0;
}
/**
* wpas_request_connection - Request a new connection
* @wpa_s: Pointer to the network interface
*
* This function is used to request a new connection to be found. It will mark
* the interface to allow reassociation and request a new scan to find a
* suitable network to connect to.
*/
void wpas_request_connection(struct wpa_supplicant *wpa_s)
{
wpa_s->normal_scans = 0;
wpa_s->scan_req = NORMAL_SCAN_REQ;
wpa_supplicant_reinit_autoscan(wpa_s);
wpa_s->disconnected = 0;
wpa_s->reassociate = 1;
wpa_s->last_owe_group = 0;
if (wpa_supplicant_fast_associate(wpa_s) != 1)
wpa_supplicant_req_scan(wpa_s, 0, 0);
else
wpa_s->reattach = 0;
}
/**
* wpas_request_disconnection - Request disconnection
* @wpa_s: Pointer to the network interface
*
* This function is used to request disconnection from the currently connected
* network. This will stop any ongoing scans and initiate deauthentication.
*/
void wpas_request_disconnection(struct wpa_supplicant *wpa_s)
{
#ifdef CONFIG_SME
wpa_s->sme.prev_bssid_set = 0;
#endif /* CONFIG_SME */
wpa_s->reassociate = 0;
wpa_s->disconnected = 1;
wpa_supplicant_cancel_sched_scan(wpa_s);
wpa_supplicant_cancel_scan(wpa_s);
wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
radio_remove_works(wpa_s, "connect", 0);
radio_remove_works(wpa_s, "sme-connect", 0);
}
void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
struct wpa_used_freq_data *freqs_data,
unsigned int len)
{
unsigned int i;
wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s",
len, title);
for (i = 0; i < len; i++) {
struct wpa_used_freq_data *cur = &freqs_data[i];
wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d, flags=0x%X",
i, cur->freq, cur->flags);
}
}
/*
* Find the operating frequencies of any of the virtual interfaces that
* are using the same radio as the current interface, and in addition, get
* information about the interface types that are using the frequency.
*/
int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs_data,
unsigned int len)
{
struct wpa_supplicant *ifs;
u8 bssid[ETH_ALEN];
int freq;
unsigned int idx = 0, i;
wpa_dbg(wpa_s, MSG_DEBUG,
"Determining shared radio frequencies (max len %u)", len);
os_memset(freqs_data, 0, sizeof(struct wpa_used_freq_data) * len);
dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
radio_list) {
if (idx == len)
break;
if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
continue;
if (ifs->current_ssid->mode == WPAS_MODE_AP ||
ifs->current_ssid->mode == WPAS_MODE_P2P_GO ||
ifs->current_ssid->mode == WPAS_MODE_MESH)
freq = ifs->current_ssid->frequency;
else if (wpa_drv_get_bssid(ifs, bssid) == 0)
freq = ifs->assoc_freq;
else
continue;
/* Hold only distinct freqs */
for (i = 0; i < idx; i++)
if (freqs_data[i].freq == freq)
break;
if (i == idx)
freqs_data[idx++].freq = freq;
if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
freqs_data[i].flags |= ifs->current_ssid->p2p_group ?
WPA_FREQ_USED_BY_P2P_CLIENT :
WPA_FREQ_USED_BY_INFRA_STATION;
}
}
dump_freq_data(wpa_s, "completed iteration", freqs_data, idx);
return idx;
}
/*
* Find the operating frequencies of any of the virtual interfaces that
* are using the same radio as the current interface.
*/
int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
int *freq_array, unsigned int len)
{
struct wpa_used_freq_data *freqs_data;
int num, i;
os_memset(freq_array, 0, sizeof(int) * len);
freqs_data = os_calloc(len, sizeof(struct wpa_used_freq_data));
if (!freqs_data)
return -1;
num = get_shared_radio_freqs_data(wpa_s, freqs_data, len);
for (i = 0; i < num; i++)
freq_array[i] = freqs_data[i].freq;
os_free(freqs_data);
return num;
}
struct wpa_supplicant *
wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame)
{
switch (frame) {
#ifdef CONFIG_P2P
case VENDOR_ELEM_PROBE_REQ_P2P:
case VENDOR_ELEM_PROBE_RESP_P2P:
case VENDOR_ELEM_PROBE_RESP_P2P_GO:
case VENDOR_ELEM_BEACON_P2P_GO:
case VENDOR_ELEM_P2P_PD_REQ:
case VENDOR_ELEM_P2P_PD_RESP:
case VENDOR_ELEM_P2P_GO_NEG_REQ:
case VENDOR_ELEM_P2P_GO_NEG_RESP:
case VENDOR_ELEM_P2P_GO_NEG_CONF:
case VENDOR_ELEM_P2P_INV_REQ:
case VENDOR_ELEM_P2P_INV_RESP:
case VENDOR_ELEM_P2P_ASSOC_REQ:
case VENDOR_ELEM_P2P_ASSOC_RESP:
return wpa_s->p2pdev;
#endif /* CONFIG_P2P */
default:
return wpa_s;
}
}
void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s)
{
unsigned int i;
char buf[30];
wpa_printf(MSG_DEBUG, "Update vendor elements");
for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
if (wpa_s->vendor_elem[i]) {
int res;
res = os_snprintf(buf, sizeof(buf), "frame[%u]", i);
if (!os_snprintf_error(sizeof(buf), res)) {
wpa_hexdump_buf(MSG_DEBUG, buf,
wpa_s->vendor_elem[i]);
}
}
}
#ifdef CONFIG_P2P
if (wpa_s->parent == wpa_s &&
wpa_s->global->p2p &&
!wpa_s->global->p2p_disabled)
p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem);
#endif /* CONFIG_P2P */
}
int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame,
const u8 *elem, size_t len)
{
u8 *ie, *end;
ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]);
end = ie + wpabuf_len(wpa_s->vendor_elem[frame]);
for (; ie + 1 < end; ie += 2 + ie[1]) {
if (ie + len > end)
break;
if (os_memcmp(ie, elem, len) != 0)
continue;
if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) {
wpabuf_free(wpa_s->vendor_elem[frame]);
wpa_s->vendor_elem[frame] = NULL;
} else {
os_memmove(ie, ie + len, end - (ie + len));
wpa_s->vendor_elem[frame]->used -= len;
}
wpas_vendor_elem_update(wpa_s);
return 0;
}
return -1;
}
struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
u16 num_modes, enum hostapd_hw_mode mode,
bool is_6ghz)
{
u16 i;
+ if (!modes)
+ return NULL;
+
for (i = 0; i < num_modes; i++) {
if (modes[i].mode != mode ||
!modes[i].num_channels || !modes[i].channels)
continue;
if ((!is_6ghz && !is_6ghz_freq(modes[i].channels[0].freq)) ||
(is_6ghz && is_6ghz_freq(modes[i].channels[0].freq)))
return &modes[i];
}
return NULL;
}
struct hostapd_hw_modes * get_mode_with_freq(struct hostapd_hw_modes *modes,
u16 num_modes, int freq)
{
int i, j;
for (i = 0; i < num_modes; i++) {
for (j = 0; j < modes[i].num_channels; j++) {
if (freq == modes[i].channels[j].freq)
return &modes[i];
}
}
return NULL;
}
static struct
wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
const u8 *bssid)
{
struct wpa_bss_tmp_disallowed *bss;
dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
return bss;
}
return NULL;
}
static int wpa_set_driver_tmp_disallow_list(struct wpa_supplicant *wpa_s)
{
struct wpa_bss_tmp_disallowed *tmp;
unsigned int num_bssid = 0;
u8 *bssids;
int ret;
bssids = os_malloc(dl_list_len(&wpa_s->bss_tmp_disallowed) * ETH_ALEN);
if (!bssids)
return -1;
dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
os_memcpy(&bssids[num_bssid * ETH_ALEN], tmp->bssid,
ETH_ALEN);
num_bssid++;
}
ret = wpa_drv_set_bssid_tmp_disallow(wpa_s, num_bssid, bssids);
os_free(bssids);
return ret;
}
static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct wpa_bss_tmp_disallowed *tmp, *bss = timeout_ctx;
/* Make sure the bss is not already freed */
dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
if (bss == tmp) {
remove_bss_tmp_disallowed_entry(wpa_s, tmp);
wpa_set_driver_tmp_disallow_list(wpa_s);
break;
}
}
}
void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
unsigned int sec, int rssi_threshold)
{
struct wpa_bss_tmp_disallowed *bss;
bss = wpas_get_disallowed_bss(wpa_s, bssid);
if (bss) {
eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss);
goto finish;
}
bss = os_malloc(sizeof(*bss));
if (!bss) {
wpa_printf(MSG_DEBUG,
"Failed to allocate memory for temp disallow BSS");
return;
}
os_memcpy(bss->bssid, bssid, ETH_ALEN);
dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
wpa_set_driver_tmp_disallow_list(wpa_s);
finish:
bss->rssi_threshold = rssi_threshold;
eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout,
wpa_s, bss);
}
int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss)
{
struct wpa_bss_tmp_disallowed *disallowed = NULL, *tmp, *prev;
dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
struct wpa_bss_tmp_disallowed, list) {
if (os_memcmp(bss->bssid, tmp->bssid, ETH_ALEN) == 0) {
disallowed = tmp;
break;
}
}
if (!disallowed)
return 0;
if (disallowed->rssi_threshold != 0 &&
bss->level > disallowed->rssi_threshold) {
remove_bss_tmp_disallowed_entry(wpa_s, disallowed);
wpa_set_driver_tmp_disallow_list(wpa_s);
return 0;
}
return 1;
}
int wpas_enable_mac_addr_randomization(struct wpa_supplicant *wpa_s,
unsigned int type, const u8 *addr,
const u8 *mask)
{
if ((addr && !mask) || (!addr && mask)) {
wpa_printf(MSG_INFO,
"MAC_ADDR_RAND_SCAN invalid addr/mask combination");
return -1;
}
if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) {
wpa_printf(MSG_INFO,
"MAC_ADDR_RAND_SCAN cannot allow multicast address");
return -1;
}
if (type & MAC_ADDR_RAND_SCAN) {
if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN,
addr, mask))
return -1;
}
if (type & MAC_ADDR_RAND_SCHED_SCAN) {
if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN,
addr, mask))
return -1;
if (wpa_s->sched_scanning && !wpa_s->pno)
wpas_scan_restart_sched_scan(wpa_s);
}
if (type & MAC_ADDR_RAND_PNO) {
if (wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO,
addr, mask))
return -1;
if (wpa_s->pno) {
wpas_stop_pno(wpa_s);
wpas_start_pno(wpa_s);
}
}
return 0;
}
int wpas_disable_mac_addr_randomization(struct wpa_supplicant *wpa_s,
unsigned int type)
{
wpas_mac_addr_rand_scan_clear(wpa_s, type);
if (wpa_s->pno) {
if (type & MAC_ADDR_RAND_PNO) {
wpas_stop_pno(wpa_s);
wpas_start_pno(wpa_s);
}
} else if (wpa_s->sched_scanning && (type & MAC_ADDR_RAND_SCHED_SCAN)) {
wpas_scan_restart_sched_scan(wpa_s);
}
return 0;
}
int wpa_drv_signal_poll(struct wpa_supplicant *wpa_s,
struct wpa_signal_info *si)
{
int res;
if (!wpa_s->driver->signal_poll)
return -1;
res = wpa_s->driver->signal_poll(wpa_s->drv_priv, si);
#ifdef CONFIG_TESTING_OPTIONS
if (res == 0) {
struct driver_signal_override *dso;
dl_list_for_each(dso, &wpa_s->drv_signal_override,
struct driver_signal_override, list) {
if (os_memcmp(wpa_s->bssid, dso->bssid,
ETH_ALEN) != 0)
continue;
wpa_printf(MSG_DEBUG,
"Override driver signal_poll information: current_signal: %d->%d avg_signal: %d->%d avg_beacon_signal: %d->%d current_noise: %d->%d",
si->current_signal,
dso->si_current_signal,
si->avg_signal,
dso->si_avg_signal,
si->avg_beacon_signal,
dso->si_avg_beacon_signal,
si->current_noise,
dso->si_current_noise);
si->current_signal = dso->si_current_signal;
si->avg_signal = dso->si_avg_signal;
si->avg_beacon_signal = dso->si_avg_beacon_signal;
si->current_noise = dso->si_current_noise;
break;
}
}
#endif /* CONFIG_TESTING_OPTIONS */
return res;
}
struct wpa_scan_results *
wpa_drv_get_scan_results2(struct wpa_supplicant *wpa_s)
{
struct wpa_scan_results *scan_res;
#ifdef CONFIG_TESTING_OPTIONS
size_t idx;
#endif /* CONFIG_TESTING_OPTIONS */
if (!wpa_s->driver->get_scan_results2)
return NULL;
scan_res = wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
#ifdef CONFIG_TESTING_OPTIONS
for (idx = 0; scan_res && idx < scan_res->num; idx++) {
struct driver_signal_override *dso;
struct wpa_scan_res *res = scan_res->res[idx];
dl_list_for_each(dso, &wpa_s->drv_signal_override,
struct driver_signal_override, list) {
if (os_memcmp(res->bssid, dso->bssid, ETH_ALEN) != 0)
continue;
wpa_printf(MSG_DEBUG,
"Override driver scan signal level %d->%d for "
MACSTR,
res->level, dso->scan_level,
MAC2STR(res->bssid));
res->flags |= WPA_SCAN_QUAL_INVALID;
if (dso->scan_level < 0)
res->flags |= WPA_SCAN_LEVEL_DBM;
else
res->flags &= ~WPA_SCAN_LEVEL_DBM;
res->level = dso->scan_level;
break;
}
}
#endif /* CONFIG_TESTING_OPTIONS */
return scan_res;
}
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf
index e3ae77114680..fa257f3dec1b 100644
--- a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf
@@ -1,2068 +1,2068 @@
##### Example wpa_supplicant configuration file ###############################
#
# This file describes configuration file format and lists all available option.
# Please also take a look at simpler configuration examples in 'examples'
# subdirectory.
#
# Empty lines and lines starting with # are ignored
# NOTE! This file may contain password information and should probably be made
# readable only by root user on multiuser systems.
# Note: All file paths in this configuration file should use full (absolute,
# not relative to working directory) path in order to allow working directory
# to be changed. This can happen if wpa_supplicant is run in the background.
# Whether to allow wpa_supplicant to update (overwrite) configuration
#
# This option can be used to allow wpa_supplicant to overwrite configuration
# file whenever configuration is changed (e.g., new network block is added with
# wpa_cli or wpa_gui, or a password is changed). This is required for
# wpa_cli/wpa_gui to be able to store the configuration changes permanently.
# Please note that overwriting configuration file will remove the comments from
# it.
#update_config=1
# global configuration (shared by all network blocks)
#
# Parameters for the control interface. If this is specified, wpa_supplicant
# will open a control interface that is available for external programs to
# manage wpa_supplicant. The meaning of this string depends on which control
# interface mechanism is used. For all cases, the existence of this parameter
# in configuration is used to determine whether the control interface is
# enabled.
#
# For UNIX domain sockets (default on Linux and BSD): This is a directory that
# will be created for UNIX domain sockets for listening to requests from
# external programs (CLI/GUI, etc.) for status information and configuration.
# The socket file will be named based on the interface name, so multiple
# wpa_supplicant processes can be run at the same time if more than one
# interface is used.
# /var/run/wpa_supplicant is the recommended directory for sockets and by
# default, wpa_cli will use it when trying to connect with wpa_supplicant.
#
# Access control for the control interface can be configured by setting the
# directory to allow only members of a group to use sockets. This way, it is
# possible to run wpa_supplicant as root (since it needs to change network
# configuration and open raw sockets) and still allow GUI/CLI components to be
# run as non-root users. However, since the control interface can be used to
# change the network configuration, this access needs to be protected in many
# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you
# want to allow non-root users to use the control interface, add a new group
# and change this value to match with that group. Add users that should have
# control interface access to this group. If this variable is commented out or
# not included in the configuration file, group will not be changed from the
# value it got by default when the directory or socket was created.
#
# When configuring both the directory and group, use following format:
# DIR=/var/run/wpa_supplicant GROUP=wheel
# DIR=/var/run/wpa_supplicant GROUP=0
# (group can be either group name or gid)
#
# For UDP connections (default on Windows): The value will be ignored. This
# variable is just used to select that the control interface is to be created.
# The value can be set to, e.g., udp (ctrl_interface=udp)
#
# For Windows Named Pipe: This value can be used to set the security descriptor
# for controlling access to the control interface. Security descriptor can be
# set using Security Descriptor String Format (see http://msdn.microsoft.com/
# library/default.asp?url=/library/en-us/secauthz/security/
# security_descriptor_string_format.asp). The descriptor string needs to be
# prefixed with SDDL=. For example, ctrl_interface=SDDL=D: would set an empty
# DACL (which will reject all connections). See README-Windows.txt for more
# information about SDDL string format.
#
ctrl_interface=/var/run/wpa_supplicant
# IEEE 802.1X/EAPOL version
# wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines
# EAPOL version 2. However, there are many APs that do not handle the new
# version number correctly (they seem to drop the frames completely). In order
# to make wpa_supplicant interoperate with these APs, the version number is set
# to 1 by default. This configuration value can be used to set it to the new
# version (2).
# Note: When using MACsec, eapol_version shall be set to 3, which is
# defined in IEEE Std 802.1X-2010.
eapol_version=1
# AP scanning/selection
# By default, wpa_supplicant requests driver to perform AP scanning and then
# uses the scan results to select a suitable AP. Another alternative is to
# allow the driver to take care of AP scanning and selection and use
# wpa_supplicant just to process EAPOL frames based on IEEE 802.11 association
# information from the driver.
# 1: wpa_supplicant initiates scanning and AP selection; if no APs matching to
# the currently enabled networks are found, a new network (IBSS or AP mode
# operation) may be initialized (if configured) (default)
# 0: This mode must only be used when using wired Ethernet drivers
# (including MACsec).
# 2: like 0, but associate with APs using security policy and SSID (but not
# BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to
# enable operation with hidden SSIDs and optimized roaming; in this mode,
# the network blocks in the configuration file are tried one by one until
# the driver reports successful association; each network block should have
# explicit security policy (i.e., only one option in the lists) for
# key_mgmt, pairwise, group, proto variables
# Note: ap_scan=0/2 should not be used with the nl80211 driver interface (the
# current Linux interface). ap_scan=1 is the only option working with nl80211.
# For finding networks using hidden SSID, scan_ssid=1 in the network block can
# be used with nl80211.
# When using IBSS or AP mode, ap_scan=2 mode can force the new network to be
# created immediately regardless of scan results. ap_scan=1 mode will first try
# to scan for existing networks and only if no matches with the enabled
# networks are found, a new IBSS or AP mode network is created.
ap_scan=1
# Whether to force passive scan for network connection
#
# By default, scans will send out Probe Request frames on channels that allow
# active scanning. This advertise the local station to the world. Normally this
# is fine, but users may wish to do passive scanning where the radio should only
# listen quietly for Beacon frames and not send any Probe Request frames. Actual
# functionality may be driver dependent.
#
# This parameter can be used to force only passive scanning to be used
# for network connection cases. It should be noted that this will slow
# down scan operations and reduce likelihood of finding the AP. In
# addition, some use cases will override this due to functional
# requirements, e.g., for finding an AP that uses hidden SSID
# (scan_ssid=1) or P2P device discovery.
#
# 0: Do normal scans (allow active scans) (default)
# 1: Do passive scans.
#passive_scan=0
# MPM residency
# By default, wpa_supplicant implements the mesh peering manager (MPM) for an
# open mesh. However, if the driver can implement the MPM, you may set this to
# 0 to use the driver version. When AMPE is enabled, the wpa_supplicant MPM is
# always used.
# 0: MPM lives in the driver
# 1: wpa_supplicant provides an MPM which handles peering (default)
#user_mpm=1
# Maximum number of peer links (0-255; default: 99)
# Maximum number of mesh peering currently maintained by the STA.
#max_peer_links=99
# Timeout in seconds to detect STA inactivity (default: 300 seconds)
#
# This timeout value is used in mesh STA to clean up inactive stations.
#mesh_max_inactivity=300
# cert_in_cb - Whether to include a peer certificate dump in events
# This controls whether peer certificates for authentication server and
# its certificate chain are included in EAP peer certificate events. This is
# enabled by default.
#cert_in_cb=1
# EAP fast re-authentication
# By default, fast re-authentication is enabled for all EAP methods that
# support it. This variable can be used to disable fast re-authentication.
# Normally, there is no need to disable this.
fast_reauth=1
# OpenSSL Engine support
# These options can be used to load OpenSSL engines in special or legacy
# modes.
# The two engines that are supported currently are shown below:
# They are both from the opensc project (http://www.opensc.org/)
# By default the PKCS#11 engine is loaded if the client_cert or
# private_key option appear to be a PKCS#11 URI, and these options
# should not need to be used explicitly.
# make the opensc engine available
#opensc_engine_path=/usr/lib/opensc/engine_opensc.so
# make the pkcs11 engine available
#pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so
# configure the path to the pkcs11 module required by the pkcs11 engine
#pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so
# OpenSSL cipher string
#
# This is an OpenSSL specific configuration option for configuring the default
# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW"
# by default) is used.
# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
# on cipher suite configuration. This is applicable only if wpa_supplicant is
# built to use OpenSSL.
#openssl_ciphers=DEFAULT:!EXP:!LOW
# Dynamic EAP methods
# If EAP methods were built dynamically as shared object files, they need to be
# loaded here before being used in the network blocks. By default, EAP methods
# are included statically in the build, so these lines are not needed
#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_tls.so
#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so
# Driver interface parameters
# This field can be used to configure arbitrary driver interface parameters. The
# format is specific to the selected driver interface. This field is not used
# in most cases.
#driver_param="field=value"
# Country code
# The ISO/IEC alpha2 country code for the country in which this device is
# currently operating.
#country=US
# Maximum lifetime for PMKSA in seconds; default 43200
#dot11RSNAConfigPMKLifetime=43200
# Threshold for reauthentication (percentage of PMK lifetime); default 70
#dot11RSNAConfigPMKReauthThreshold=70
# Timeout for security association negotiation in seconds; default 60
#dot11RSNAConfigSATimeout=60
# Wi-Fi Protected Setup (WPS) parameters
# Universally Unique IDentifier (UUID; see RFC 4122) of the device
# If not configured, UUID will be generated based on the mechanism selected with
# the auto_uuid parameter.
#uuid=12345678-9abc-def0-1234-56789abcdef0
# Automatic UUID behavior
# 0 = generate static value based on the local MAC address (default)
# 1 = generate a random UUID every time wpa_supplicant starts
#auto_uuid=0
# Device Name
# User-friendly description of device; up to 32 octets encoded in UTF-8
#device_name=Wireless Client
# Manufacturer
# The manufacturer of the device (up to 64 ASCII characters)
#manufacturer=Company
# Model Name
# Model of the device (up to 32 ASCII characters)
#model_name=cmodel
# Model Number
# Additional device description (up to 32 ASCII characters)
#model_number=123
# Serial Number
# Serial number of the device (up to 32 characters)
#serial_number=12345
# Primary Device Type
# Used format: <categ>-<OUI>-<subcateg>
# categ = Category as an integer value
# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for
# default WPS OUI
# subcateg = OUI-specific Sub Category as an integer value
# Examples:
# 1-0050F204-1 (Computer / PC)
# 1-0050F204-2 (Computer / Server)
# 5-0050F204-1 (Storage / NAS)
# 6-0050F204-1 (Network Infrastructure / AP)
#device_type=1-0050F204-1
# OS Version
# 4-octet operating system version number (hex string)
#os_version=01020300
# Config Methods
# List of the supported configuration methods
# Available methods: usba ethernet label display ext_nfc_token int_nfc_token
# nfc_interface push_button keypad virtual_display physical_display
# virtual_push_button physical_push_button
# For WSC 1.0:
#config_methods=label display push_button keypad
# For WSC 2.0:
#config_methods=label virtual_display virtual_push_button keypad
# Credential processing
# 0 = process received credentials internally (default)
# 1 = do not process received credentials; just pass them over ctrl_iface to
# external program(s)
# 2 = process received credentials internally and pass them over ctrl_iface
# to external program(s)
#wps_cred_processing=0
# Whether to enable SAE (WPA3-Personal transition mode) automatically for
# WPA2-PSK credentials received using WPS.
# 0 = only add the explicitly listed WPA2-PSK configuration (default)
# 1 = add both the WPA2-PSK and SAE configuration and enable PMF so that the
# station gets configured in WPA3-Personal transition mode (supports both
# WPA2-Personal (PSK) and WPA3-Personal (SAE) APs).
#wps_cred_add_sae=0
# Vendor attribute in WPS M1, e.g., Windows 7 Vertical Pairing
# The vendor attribute contents to be added in M1 (hex string)
#wps_vendor_ext_m1=000137100100020001
# NFC password token for WPS
# These parameters can be used to configure a fixed NFC password token for the
# station. This can be generated, e.g., with nfc_pw_token. When these
# parameters are used, the station is assumed to be deployed with a NFC tag
# that includes the matching NFC password token (e.g., written based on the
# NDEF record from nfc_pw_token).
#
#wps_nfc_dev_pw_id: Device Password ID (16..65535)
#wps_nfc_dh_pubkey: Hexdump of DH Public Key
#wps_nfc_dh_privkey: Hexdump of DH Private Key
#wps_nfc_dev_pw: Hexdump of Device Password
# Priority for the networks added through WPS
# This priority value will be set to each network profile that is added
# by executing the WPS protocol.
#wps_priority=0
# Device Provisioning Protocol (DPP) parameters
#
# How to process DPP configuration
# 0 = report received configuration to an external program for
# processing; do not generate any network profile internally (default)
# 1 = report received configuration to an external program and generate
# a network profile internally, but do not automatically connect
# to the created (disabled) profile; the network profile id is
# reported to external programs
# 2 = report received configuration to an external program, generate
# a network profile internally, try to connect to the created
# profile automatically
#dpp_config_processing=0
#
# Name for Enrollee's DPP Configuration Request
#dpp_name=Test
#
# MUD URL for Enrollee's DPP Configuration Request (optional)
#dpp_mud_url=https://example.com/mud
# Maximum number of BSS entries to keep in memory
# Default: 200
# This can be used to limit memory use on the BSS entries (cached scan
# results). A larger value may be needed in environments that have huge number
# of APs when using ap_scan=1 mode.
#bss_max_count=200
# BSS expiration age in seconds. A BSS will be removed from the local cache
# if it is not in use and has not been seen for this time. Default is 180.
#bss_expiration_age=180
# BSS expiration after number of scans. A BSS will be removed from the local
# cache if it is not seen in this number of scans.
# Default is 2.
#bss_expiration_scan_count=2
# Automatic scan
# This is an optional set of parameters for automatic scanning
# within an interface in following format:
#autoscan=<autoscan module name>:<module parameters>
# autoscan is like bgscan but on disconnected or inactive state.
# For instance, on exponential module parameters would be <base>:<limit>
#autoscan=exponential:3:300
# Which means a delay between scans on a base exponential of 3,
# up to the limit of 300 seconds (3, 9, 27 ... 300)
# For periodic module, parameters would be <fixed interval>
#autoscan=periodic:30
# So a delay of 30 seconds will be applied between each scan.
# Note: If sched_scan_plans are configured and supported by the driver,
# autoscan is ignored.
# filter_ssids - SSID-based scan result filtering
# 0 = do not filter scan results (default)
# 1 = only include configured SSIDs in scan results/BSS table
#filter_ssids=0
# Password (and passphrase, etc.) backend for external storage
# format: <backend name>[:<optional backend parameters>]
# Test backend which stores passwords in memory. Should only be used for
# development purposes.
#ext_password_backend=test:pw1=password|pw2=testing
# File-based backend which reads passwords from a file. The parameter
# identifies the file to read passwords from. The password file follows the
# format of wpa_supplicant.conf and accepts simple `key=passphrase` formatted
# passwords.
#ext_password_backend=file:/path/to/passwords.conf
# Disable P2P functionality
# p2p_disabled=1
# Timeout in seconds to detect STA inactivity (default: 300 seconds)
#
# This timeout value is used in P2P GO mode to clean up
# inactive stations.
#p2p_go_max_inactivity=300
# Passphrase length (8..63) for P2P GO
#
# This parameter controls the length of the random passphrase that is
# generated at the GO. Default: 8.
#p2p_passphrase_len=8
# Extra delay between concurrent P2P search iterations
#
# This value adds extra delay in milliseconds between concurrent search
# iterations to make p2p_find friendlier to concurrent operations by avoiding
# it from taking 100% of radio resources. The default value is 500 ms.
#p2p_search_delay=500
# Opportunistic Key Caching (also known as Proactive Key Caching) default
# This parameter can be used to set the default behavior for the
# proactive_key_caching parameter. By default, OKC is disabled unless enabled
# with the global okc=1 parameter or with the per-network
# proactive_key_caching=1 parameter. With okc=1, OKC is enabled by default, but
# can be disabled with per-network proactive_key_caching=0 parameter.
#okc=0
# Protected Management Frames default
# This parameter can be used to set the default behavior for the ieee80211w
# parameter for RSN networks. By default, PMF is disabled unless enabled with
# the global pmf=1/2 parameter or with the per-network ieee80211w=1/2 parameter.
# With pmf=1/2, PMF is enabled/required by default, but can be disabled with the
# per-network ieee80211w parameter. This global default value does not apply
# for non-RSN networks (key_mgmt=NONE) since PMF is available only when using
# RSN.
#pmf=0
# Enabled SAE finite cyclic groups in preference order
# By default (if this parameter is not set), the mandatory group 19 (ECC group
# defined over a 256-bit prime order field, NIST P-256) is preferred and groups
# 20 (NIST P-384) and 21 (NIST P-521) are also enabled. If this parameter is
# set, the groups will be tried in the indicated order.
# The group values are listed in the IANA registry:
# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
# Note that groups 1, 2, 5, 22, 23, and 24 should not be used in production
# purposes due limited security (see RFC 8247). Groups that are not as strong as
# group 19 (ECC, NIST P-256) are unlikely to be useful for production use cases
# since all implementations are required to support group 19.
#sae_groups=19 20 21
# SAE mechanism for PWE derivation
# 0 = hunting-and-pecking loop only (default without password identifier)
# 1 = hash-to-element only (default with password identifier)
# 2 = both hunting-and-pecking loop and hash-to-element enabled
# Note: The default value is likely to change from 0 to 2 once the new
# hash-to-element mechanism has received more interoperability testing.
# When using SAE password identifier, the hash-to-element mechanism is used
# regardless of the sae_pwe parameter value.
#sae_pwe=0
# Default value for DTIM period (if not overridden in network block)
#dtim_period=2
# Default value for Beacon interval (if not overridden in network block)
#beacon_int=100
# Additional vendor specific elements for Beacon and Probe Response frames
# This parameter can be used to add additional vendor specific element(s) into
# the end of the Beacon and Probe Response frames. The format for these
# element(s) is a hexdump of the raw information elements (id+len+payload for
# one or more elements). This is used in AP and P2P GO modes.
#ap_vendor_elements=dd0411223301
# Ignore scan results older than request
#
# The driver may have a cache of scan results that makes it return
# information that is older than our scan trigger. This parameter can
# be used to configure such old information to be ignored instead of
# allowing it to update the internal BSS table.
#ignore_old_scan_res=0
# scan_cur_freq: Whether to scan only the current frequency
# 0: Scan all available frequencies. (Default)
# 1: Scan current operating frequency if another VIF on the same radio
# is already associated.
# Seconds to consider old scan results valid for association (default: 5)
#scan_res_valid_for_connect=5
# MAC address policy default
# 0 = use permanent MAC address
# 1 = use random MAC address for each ESS connection
# 2 = like 1, but maintain OUI (with local admin bit set)
#
# By default, permanent MAC address is used unless policy is changed by
# the per-network mac_addr parameter. Global mac_addr=1 can be used to
# change this default behavior.
#mac_addr=0
# Lifetime of random MAC address in seconds (default: 60)
#rand_addr_lifetime=60
# MAC address policy for pre-association operations (scanning, ANQP)
# 0 = use permanent MAC address
# 1 = use random MAC address
# 2 = like 1, but maintain OUI (with local admin bit set)
#preassoc_mac_addr=0
# MAC address policy for GAS operations
# 0 = use permanent MAC address
# 1 = use random MAC address
# 2 = like 1, but maintain OUI (with local admin bit set)
# Note that this setting is ignored when a specific MAC address is needed for
# a full protocol exchange that includes GAS, e.g., when going through a DPP
# exchange that exposes the configured interface address as part of the DP
# Public Action frame exchanges before using GAS. That same address is then used
# during the GAS exchange as well to avoid breaking the protocol expectations.
#gas_rand_mac_addr=0
# Lifetime of GAS random MAC address in seconds (default: 60)
#gas_rand_addr_lifetime=60
# Interworking (IEEE 802.11u)
# Enable Interworking
# interworking=1
# Enable P2P GO advertisement of Interworking
# go_interworking=1
# P2P GO Interworking: Access Network Type
# 0 = Private network
# 1 = Private network with guest access
# 2 = Chargeable public network
# 3 = Free public network
# 4 = Personal device network
# 5 = Emergency services only network
# 14 = Test or experimental
# 15 = Wildcard
#go_access_network_type=0
# P2P GO Interworking: Whether the network provides connectivity to the Internet
# 0 = Unspecified
# 1 = Network provides connectivity to the Internet
#go_internet=1
# P2P GO Interworking: Group Venue Info (optional)
# The available values are defined in IEEE Std 802.11-2016, 9.4.1.35.
# Example values (group,type):
# 0,0 = Unspecified
# 1,7 = Convention Center
# 1,13 = Coffee Shop
# 2,0 = Unspecified Business
# 7,1 Private Residence
#go_venue_group=7
#go_venue_type=1
# Homogeneous ESS identifier
# If this is set, scans will be used to request response only from BSSes
# belonging to the specified Homogeneous ESS. This is used only if interworking
# is enabled.
# hessid=00:11:22:33:44:55
# Automatic network selection behavior
# 0 = do not automatically go through Interworking network selection
# (i.e., require explicit interworking_select command for this; default)
# 1 = perform Interworking network selection if one or more
# credentials have been configured and scan did not find a
# matching network block
#auto_interworking=0
# GAS Address3 field behavior
# 0 = P2P specification (Address3 = AP BSSID); default
# 1 = IEEE 802.11 standard compliant (Address3 = Wildcard BSSID when
# sent to not-associated AP; if associated, AP BSSID)
#gas_address3=0
# Publish fine timing measurement (FTM) responder functionality in
# the Extended Capabilities element bit 70.
# Controls whether FTM responder functionality will be published by AP/STA.
# Note that actual FTM responder operation is managed outside wpa_supplicant.
# 0 = Do not publish; default
# 1 = Publish
#ftm_responder=0
# Publish fine timing measurement (FTM) initiator functionality in
# the Extended Capabilities element bit 71.
# Controls whether FTM initiator functionality will be published by AP/STA.
# Note that actual FTM initiator operation is managed outside wpa_supplicant.
# 0 = Do not publish; default
# 1 = Publish
#ftm_initiator=0
# credential block
#
# Each credential used for automatic network selection is configured as a set
# of parameters that are compared to the information advertised by the APs when
# interworking_select and interworking_connect commands are used.
#
# credential fields:
#
# temporary: Whether this credential is temporary and not to be saved
#
# priority: Priority group
# By default, all networks and credentials get the same priority group
# (0). This field can be used to give higher priority for credentials
# (and similarly in struct wpa_ssid for network blocks) to change the
# Interworking automatic networking selection behavior. The matching
# network (based on either an enabled network block or a credential)
# with the highest priority value will be selected.
#
# pcsc: Use PC/SC and SIM/USIM card
#
# realm: Home Realm for Interworking
#
# username: Username for Interworking network selection
#
# password: Password for Interworking network selection
#
# ca_cert: CA certificate for Interworking network selection
#
# client_cert: File path to client certificate file (PEM/DER)
# This field is used with Interworking networking selection for a case
# where client certificate/private key is used for authentication
# (EAP-TLS). Full path to the file should be used since working
# directory may change when wpa_supplicant is run in the background.
#
# Certificates from PKCS#11 tokens can be referenced by a PKCS#11 URI.
#
# For example: private_key="pkcs11:manufacturer=piv_II;id=%01"
#
# Alternatively, a named configuration blob can be used by setting
# this to blob://blob_name.
#
# private_key: File path to client private key file (PEM/DER/PFX)
# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
# commented out. Both the private key and certificate will be read
# from the PKCS#12 file in this case. Full path to the file should be
# used since working directory may change when wpa_supplicant is run
# in the background.
#
# Keys in PKCS#11 tokens can be referenced by a PKCS#11 URI.
# For example: private_key="pkcs11:manufacturer=piv_II;id=%01"
#
# Windows certificate store can be used by leaving client_cert out and
# configuring private_key in one of the following formats:
#
# cert://substring_to_match
#
# hash://certificate_thumbprint_in_hex
#
# For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
#
# Note that when running wpa_supplicant as an application, the user
# certificate store (My user account) is used, whereas computer store
# (Computer account) is used when running wpasvc as a service.
#
# Alternatively, a named configuration blob can be used by setting
# this to blob://blob_name.
#
# private_key_passwd: Password for private key file
#
# imsi: IMSI in <MCC> | <MNC> | '-' | <MSIN> format
#
# milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
# format
#
# domain: Home service provider FQDN(s)
# This is used to compare against the Domain Name List to figure out
# whether the AP is operated by the Home SP. Multiple domain entries can
# be used to configure alternative FQDNs that will be considered home
# networks.
#
# roaming_consortium: Roaming Consortium OI
# If roaming_consortium_len is non-zero, this field contains the
# Roaming Consortium OI that can be used to determine which access
# points support authentication with this credential. This is an
# alternative to the use of the realm parameter. When using Roaming
# Consortium to match the network, the EAP parameters need to be
# pre-configured with the credential since the NAI Realm information
# may not be available or fetched.
#
# required_roaming_consortium: Required Roaming Consortium OI
# If required_roaming_consortium_len is non-zero, this field contains the
# Roaming Consortium OI that is required to be advertised by the AP for
# the credential to be considered matching.
#
# roaming_consortiums: Roaming Consortium OI(s) memberships
# This string field contains one or more comma delimited OIs (hexdump)
# identifying the roaming consortiums of which the provider is a member.
# The list is sorted from the most preferred one to the least preferred
# one. A match between the Roaming Consortium OIs advertised by an AP and
# the OIs in this list indicates that successful authentication is
# possible.
# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/RoamingConsortiumOI)
#
# eap: Pre-configured EAP method
# This optional field can be used to specify which EAP method will be
# used with this credential. If not set, the EAP method is selected
# automatically based on ANQP information (e.g., NAI Realm).
#
# phase1: Pre-configure Phase 1 (outer authentication) parameters
# This optional field is used with like the 'eap' parameter.
#
# phase2: Pre-configure Phase 2 (inner authentication) parameters
# This optional field is used with like the 'eap' parameter.
#
# excluded_ssid: Excluded SSID
# This optional field can be used to excluded specific SSID(s) from
# matching with the network. Multiple entries can be used to specify more
# than one SSID.
#
# roaming_partner: Roaming partner information
# This optional field can be used to configure preferences between roaming
# partners. The field is a string in following format:
# <FQDN>,<0/1 exact match>,<priority>,<* or country code>
# (non-exact match means any subdomain matches the entry; priority is in
# 0..255 range with 0 being the highest priority)
#
# update_identifier: PPS MO ID
# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
#
# provisioning_sp: FQDN of the SP that provisioned the credential
# This optional field can be used to keep track of the SP that provisioned
# the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
#
# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
# These fields can be used to specify minimum download/upload backhaul
# bandwidth that is preferred for the credential. This constraint is
# ignored if the AP does not advertise WAN Metrics information or if the
# limit would prevent any connection. Values are in kilobits per second.
# min_dl_bandwidth_home
# min_ul_bandwidth_home
# min_dl_bandwidth_roaming
# min_ul_bandwidth_roaming
#
# max_bss_load: Maximum BSS Load Channel Utilization (1..255)
# (PPS/<X+>/Policy/MaximumBSSLoadValue)
# This value is used as the maximum channel utilization for network
# selection purposes for home networks. If the AP does not advertise
# BSS Load or if the limit would prevent any connection, this constraint
# will be ignored.
#
# req_conn_capab: Required connection capability
# (PPS/<X+>/Policy/RequiredProtoPortTuple)
# This value is used to configure set of required protocol/port pairs that
# a roaming network shall support (include explicitly in Connection
# Capability ANQP element). This constraint is ignored if the AP does not
# advertise Connection Capability or if this constraint would prevent any
# network connection. This policy is not used in home networks.
# Format: <protocol>[:<comma-separated list of ports]
# Multiple entries can be used to list multiple requirements.
# For example, number of common TCP protocols:
# req_conn_capab=6,22,80,443
# For example, IPSec/IKE:
# req_conn_capab=17:500
# req_conn_capab=50
#
# ocsp: Whether to use/require OCSP to check server certificate
# 0 = do not use OCSP stapling (TLS certificate status extension)
# 1 = try to use OCSP stapling, but not require response
# 2 = require valid OCSP stapling response
# 3 = require valid OCSP stapling response for all not-trusted
# certificates in the server certificate chain
#
# sim_num: Identifier for which SIM to use in multi-SIM devices
#
# for example:
#
#cred={
# realm="example.com"
# username="user@example.com"
# password="password"
# ca_cert="/etc/wpa_supplicant/ca.pem"
# domain="example.com"
#}
#
#cred={
# imsi="310026-000000000"
# milenage="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82"
#}
#
#cred={
# realm="example.com"
# username="user"
# password="password"
# ca_cert="/etc/wpa_supplicant/ca.pem"
# domain="example.com"
# roaming_consortium=223344
# eap=TTLS
# phase2="auth=MSCHAPV2"
#}
# Hotspot 2.0
# hs20=1
# Scheduled scan plans
#
# A space delimited list of scan plans. Each scan plan specifies the scan
# interval and number of iterations, delimited by a colon. The last scan plan
# will run infinitely and thus must specify only the interval and not the number
# of iterations.
#
# The driver advertises the maximum number of scan plans supported. If more scan
# plans than supported are configured, only the first ones are set (up to the
# maximum supported). The last scan plan that specifies only the interval is
# always set as the last plan.
#
# If the scan interval or the number of iterations for a scan plan exceeds the
# maximum supported, it will be set to the maximum supported value.
#
# Format:
# sched_scan_plans=<interval:iterations> <interval:iterations> ... <interval>
#
# Example:
# sched_scan_plans=10:100 20:200 30
# Multi Band Operation (MBO) non-preferred channels
# A space delimited list of non-preferred channels where each channel is a colon
# delimited list of values.
# Format:
# non_pref_chan=<oper_class>:<chan>:<preference>:<reason>
# Example:
# non_pref_chan=81:5:10:2 81:1:0:2 81:9:0:2
# MBO Cellular Data Capabilities
# 1 = Cellular data connection available
# 2 = Cellular data connection not available
# 3 = Not cellular capable (default)
#mbo_cell_capa=3
# Optimized Connectivity Experience (OCE)
# oce: Enable OCE features (bitmap)
# Set BIT(0) to Enable OCE in non-AP STA mode (default; disabled if the driver
# does not indicate support for OCE in STA mode)
# Set BIT(1) to Enable OCE in STA-CFON mode
#oce=1
# Extended Key ID support for Individually Addressed frames
# 0 = force off: Do not use Extended Key ID (default)
# 1 = auto: Activate Extended Key ID support if the driver supports it
#extended_key_id=0
# network block
#
# Each network (usually AP's sharing the same SSID) is configured as a separate
# block in this configuration file. The network blocks are in preference order
# (the first match is used).
#
# network block fields:
#
# disabled:
# 0 = this network can be used (default)
# 1 = this network block is disabled (can be enabled through ctrl_iface,
# e.g., with wpa_cli or wpa_gui)
#
# id_str: Network identifier string for external scripts. This value is passed
# to external action script through wpa_cli as WPA_ID_STR environment
# variable to make it easier to do network specific configuration.
#
# ssid: SSID (mandatory); network name in one of the optional formats:
# - an ASCII string with double quotation
# - a hex string (two characters per octet of SSID)
# - a printf-escaped ASCII string P"<escaped string>"
#
# scan_ssid:
# 0 = do not scan this SSID with specific Probe Request frames (default)
# 1 = scan with SSID-specific Probe Request frames (this can be used to
# find APs that do not accept broadcast SSID or use multiple SSIDs;
# this will add latency to scanning, so enable this only when needed)
#
# bssid: BSSID (optional); if set, this network block is used only when
# associating with the AP using the configured BSSID
#
# ignore_broadcast_ssid: SSID broadcast behavior
# Send empty SSID in beacons and ignore probe request frames that do not
# specify full SSID, i.e., require stations to know SSID.
# default: disabled (0)
# 1 = send empty (length=0) SSID in beacon and ignore probe request for
# broadcast SSID
# 2 = clear SSID (ASCII 0), but keep the original length (this may be required
# with some clients that do not support empty SSID) and ignore probe
# requests for broadcast SSID
#
# priority: priority group (integer)
# By default, all networks will get same priority group (0). If some of the
# networks are more desirable, this field can be used to change the order in
# which wpa_supplicant goes through the networks when selecting a BSS. The
# priority groups will be iterated in decreasing priority (i.e., the larger the
# priority value, the sooner the network is matched against the scan results).
# Within each priority group, networks will be selected based on security
# policy, signal strength, etc.
# Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are not
# using this priority to select the order for scanning. Instead, they try the
# networks in the order that used in the configuration file.
#
# mode: IEEE 802.11 operation mode
# 0 = infrastructure (Managed) mode, i.e., associate with an AP (default)
# 1 = IBSS (ad-hoc, peer-to-peer)
# 2 = AP (access point)
# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) and
# WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE (fixed group key
# TKIP/CCMP) is available for backwards compatibility, but its use is
# deprecated. WPA-None requires following network block options:
# proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not
# both), and psk must also be set.
#
# frequency: Channel frequency in megahertz (MHz) for IBSS, e.g.,
# 2412 = IEEE 802.11b/g channel 1. This value is used to configure the initial
# channel for IBSS (adhoc) networks. It is ignored in the infrastructure mode.
# In addition, this value is only used by the station that creates the IBSS. If
# an IBSS network with the configured SSID is already present, the frequency of
# the network will be used instead of this configured value.
#
# pbss: Whether to use PBSS. Relevant to IEEE 802.11ad networks only.
# 0 = do not use PBSS
# 1 = use PBSS
# 2 = don't care (not allowed in AP mode)
# Used together with mode configuration. When mode is AP, it means to start a
# PCP instead of a regular AP. When mode is infrastructure it means connect
# to a PCP instead of AP. In this mode you can also specify 2 (don't care)
# which means connect to either PCP or AP.
# P2P_GO and P2P_GROUP_FORMATION modes must use PBSS in IEEE 802.11ad network.
# For more details, see IEEE Std 802.11ad-2012.
#
# scan_freq: List of frequencies to scan
# Space-separated list of frequencies in MHz to scan when searching for this
# BSS. If the subset of channels used by the network is known, this option can
# be used to optimize scanning to not occur on channels that the network does
# not use. Example: scan_freq=2412 2437 2462
#
# freq_list: Array of allowed frequencies
# Space-separated list of frequencies in MHz to allow for selecting the BSS. If
# set, scan results that do not match any of the specified frequencies are not
# considered when selecting a BSS.
#
# This can also be set on the outside of the network block. In this case,
# it limits the frequencies that will be scanned.
#
# bgscan: Background scanning
# wpa_supplicant behavior for background scanning can be specified by
# configuring a bgscan module. These modules are responsible for requesting
# background scans for the purpose of roaming within an ESS (i.e., within a
# single network block with all the APs using the same SSID). The bgscan
# parameter uses following format: "<bgscan module name>:<module parameters>"
# Following bgscan modules are available:
# simple - Periodic background scans based on signal strength
# bgscan="simple:<short bgscan interval in seconds>:<signal strength threshold>:
# <long interval>"
# bgscan="simple:30:-45:300"
# learn - Learn channels used by the network and try to avoid bgscans on other
# channels (experimental)
# bgscan="learn:<short bgscan interval in seconds>:<signal strength threshold>:
# <long interval>[:<database file name>]"
# bgscan="learn:30:-45:300:/etc/wpa_supplicant/network1.bgscan"
# Explicitly disable bgscan by setting
# bgscan=""
#
# This option can also be set outside of all network blocks for the bgscan
# parameter to apply for all the networks that have no specific bgscan
# parameter.
#
# proto: list of accepted protocols
# WPA = WPA/IEEE 802.11i/D3.0
# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN)
# Note that RSN is used also for WPA3.
# If not set, this defaults to: WPA RSN
#
# key_mgmt: list of accepted authenticated key management protocols
# WPA-PSK = WPA pre-shared key (this requires 'psk' field)
# WPA-EAP = WPA using EAP authentication
# IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically
# generated WEP keys
# NONE = WPA is not used; plaintext or static WEP could be used
# WPA-NONE = WPA-None for IBSS (deprecated; use proto=RSN key_mgmt=WPA-PSK
# instead)
# FT-PSK = Fast BSS Transition (IEEE 802.11r) with pre-shared key
# FT-EAP = Fast BSS Transition (IEEE 802.11r) with EAP authentication
# FT-EAP-SHA384 = Fast BSS Transition (IEEE 802.11r) with EAP authentication
# and using SHA384
# WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms
# WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms
# SAE = Simultaneous authentication of equals; pre-shared key/password -based
# authentication with stronger security than WPA-PSK especially when using
# not that strong password; a.k.a. WPA3-Personal
# FT-SAE = SAE with FT
# WPA-EAP-SUITE-B = Suite B 128-bit level
# WPA-EAP-SUITE-B-192 = Suite B 192-bit level
# OSEN = Hotspot 2.0 Rel 2 online signup connection
# FILS-SHA256 = Fast Initial Link Setup with SHA256
# FILS-SHA384 = Fast Initial Link Setup with SHA384
# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256
# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
# OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open)
# DPP = Device Provisioning Protocol
# If not set, this defaults to: WPA-PSK WPA-EAP
#
# ieee80211w: whether management frame protection is enabled
# 0 = disabled (default unless changed with the global pmf parameter)
# 1 = optional
# 2 = required
# The most common configuration options for this based on the PMF (protected
# management frames) certification program are:
# PMF enabled: ieee80211w=1 and key_mgmt=WPA-EAP WPA-EAP-SHA256
# PMF required: ieee80211w=2 and key_mgmt=WPA-EAP-SHA256
# (and similarly for WPA-PSK and WPA-PSK-SHA256 if WPA2-Personal is used)
# WPA3-Personal-only mode: ieee80211w=2 and key_mgmt=SAE
#
# ocv: whether operating channel validation is enabled
-# This is a countermeasure against multi-channel man-in-the-middle attacks.
+# This is a countermeasure against multi-channel on-path attacks.
# Enabling this automatically also enables ieee80211w, if not yet enabled.
# 0 = disabled (default)
# 1 = enabled if wpa_supplicant's SME in use. Otherwise enabled only when the
# driver indicates support for operating channel validation.
#ocv=1
#
# auth_alg: list of allowed IEEE 802.11 authentication algorithms
# OPEN = Open System authentication (required for WPA/WPA2)
# SHARED = Shared Key authentication (requires static WEP keys)
# LEAP = LEAP/Network EAP (only used with LEAP)
# If not set, automatic selection is used (Open System with LEAP enabled if
# LEAP is allowed as one of the EAP methods).
#
# pairwise: list of accepted pairwise (unicast) ciphers for WPA
# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
# NONE = Use only Group Keys (deprecated, should not be included if APs support
# pairwise keys)
# If not set, this defaults to: CCMP TKIP
#
# group: list of accepted group (broadcast/multicast) ciphers for WPA
# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
# WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key
# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11]
# If not set, this defaults to: CCMP TKIP WEP104 WEP40
#
# group_mgmt: list of accepted group management ciphers for RSN (PMF)
# AES-128-CMAC = BIP-CMAC-128
# BIP-GMAC-128
# BIP-GMAC-256
# BIP-CMAC-256
# If not set, no constraint on the cipher, i.e., accept whichever cipher the AP
# indicates.
#
# psk: WPA preshared key; 256-bit pre-shared key
# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
# generated using the passphrase and SSID). ASCII passphrase must be between
# 8 and 63 characters (inclusive). ext:<name of external PSK field> format can
# be used to indicate that the PSK/passphrase is stored in external storage.
# This field is not needed, if WPA-EAP is used.
# Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys
# from ASCII passphrase. This process uses lot of CPU and wpa_supplicant
# startup and reconfiguration time can be optimized by generating the PSK only
# only when the passphrase or SSID has actually changed.
#
# mem_only_psk: Whether to keep PSK/passphrase only in memory
# 0 = allow psk/passphrase to be stored to the configuration file
# 1 = do not store psk/passphrase to the configuration file
#mem_only_psk=0
#
# sae_password: SAE password
# This parameter can be used to set a password for SAE. By default, the
# passphrase from the psk parameter is used if this separate parameter is not
# used, but psk follows the WPA-PSK constraints (8..63 characters) even though
# SAE passwords do not have such constraints.
#
# sae_password_id: SAE password identifier
# This parameter can be used to set an identifier for the SAE password. By
# default, no such identifier is used. If set, the specified identifier value
# is used by the other peer to select which password to use for authentication.
#
# eapol_flags: IEEE 802.1X/EAPOL options (bit field)
# Dynamic WEP key required for non-WPA mode
# bit0 (1): require dynamically generated unicast WEP key
# bit1 (2): require dynamically generated broadcast WEP key
# (3 = require both keys; default)
# Note: When using wired authentication (including MACsec drivers),
# eapol_flags must be set to 0 for the authentication to be completed
# successfully.
#
# macsec_policy: IEEE 802.1X/MACsec options
# This determines how sessions are secured with MACsec (only for MACsec
# drivers).
# 0: MACsec not in use (default)
# 1: MACsec enabled - Should secure, accept key server's advice to
# determine whether to use a secure session or not.
#
# macsec_integ_only: IEEE 802.1X/MACsec transmit mode
# This setting applies only when MACsec is in use, i.e.,
# - macsec_policy is enabled
# - the key server has decided to enable MACsec
# 0: Encrypt traffic (default)
# 1: Integrity only
#
# macsec_replay_protect: IEEE 802.1X/MACsec replay protection
# This setting applies only when MACsec is in use, i.e.,
# - macsec_policy is enabled
# - the key server has decided to enable MACsec
# 0: Replay protection disabled (default)
# 1: Replay protection enabled
#
# macsec_replay_window: IEEE 802.1X/MACsec replay protection window
# This determines a window in which replay is tolerated, to allow receipt
# of frames that have been misordered by the network.
# This setting applies only when MACsec replay protection active, i.e.,
# - macsec_replay_protect is enabled
# - the key server has decided to enable MACsec
# 0: No replay window, strict check (default)
# 1..2^32-1: number of packets that could be misordered
#
# macsec_port: IEEE 802.1X/MACsec port
# Port component of the SCI
# Range: 1-65534 (default: 1)
#
# mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode
# This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair.
# In this mode, instances of wpa_supplicant can act as MACsec peers. The peer
# with lower priority will become the key server and start distributing SAKs.
# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-byte (128-bit)
# hex-string (32 hex-digits) or a 32-byte (256-bit) hex-string (64 hex-digits)
# mka_ckn (CKN = CAK Name) takes a 1..32-bytes (8..256 bit) hex-string
# (2..64 hex-digits)
# mka_priority (Priority of MKA Actor) is in 0..255 range with 255 being
# default priority
#
# mixed_cell: This option can be used to configure whether so called mixed
# cells, i.e., networks that use both plaintext and encryption in the same
# SSID, are allowed when selecting a BSS from scan results.
# 0 = disabled (default)
# 1 = enabled
#
# proactive_key_caching:
# Enable/disable opportunistic PMKSA caching for WPA2.
# 0 = disabled (default unless changed with the global okc parameter)
# 1 = enabled
#
# ft_eap_pmksa_caching:
# Whether FT-EAP PMKSA caching is allowed
# 0 = do not try to use PMKSA caching with FT-EAP (default)
# 1 = try to use PMKSA caching with FT-EAP
# This controls whether to try to use PMKSA caching with FT-EAP for the
# FT initial mobility domain association.
#ft_eap_pmksa_caching=0
#
# wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or
# hex without quotation, e.g., 0102030405)
# wep_tx_keyidx: Default WEP key index (TX) (0..3)
#
# wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
# enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
#
# wpa_deny_ptk0_rekey: Workaround for PTK rekey issues
# PTK0 rekeys (using only one Key ID value for pairwise keys) can degrade the
# security and stability with some cards.
# To avoid the issues wpa_supplicant can replace those PTK rekeys (including
# EAP reauthentications) with fast reconnects.
#
# Available options:
# 0 = always rekey when configured/instructed (default)
# 1 = only rekey when the local driver is explicitly indicating it can perform
# this operation without issues
# 2 = never allow problematic PTK0 rekeys
#
# group_rekey: Group rekeying time in seconds. This value, if non-zero, is used
# as the dot11RSNAConfigGroupRekeyTime parameter when operating in
# Authenticator role in IBSS, or in AP and mesh modes.
#
# Following fields are only used with internal EAP implementation.
# eap: space-separated list of accepted EAP methods
# MD5 = EAP-MD5 (insecure and does not generate keying material ->
# cannot be used with WPA; to be used as a Phase 2 method
# with EAP-PEAP or EAP-TTLS)
# MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used
# as a Phase 2 method with EAP-PEAP or EAP-TTLS)
# OTP = EAP-OTP (cannot be used separately with WPA; to be used
# as a Phase 2 method with EAP-PEAP or EAP-TTLS)
# GTC = EAP-GTC (cannot be used separately with WPA; to be used
# as a Phase 2 method with EAP-PEAP or EAP-TTLS)
# TLS = EAP-TLS (client and server certificate)
# PEAP = EAP-PEAP (with tunnelled EAP authentication)
# TTLS = EAP-TTLS (with tunnelled EAP or PAP/CHAP/MSCHAP/MSCHAPV2
# authentication)
# If not set, all compiled in methods are allowed.
#
# identity: Identity string for EAP
# This field is also used to configure user NAI for
# EAP-PSK/PAX/SAKE/GPSK.
# anonymous_identity: Anonymous identity string for EAP (to be used as the
# unencrypted identity with EAP types that support different tunnelled
# identity, e.g., EAP-TTLS). This field can also be used with
# EAP-SIM/AKA/AKA' to store the pseudonym identity.
# password: Password string for EAP. This field can include either the
# plaintext password (using ASCII or hex string) or a NtPasswordHash
# (16-byte MD4 hash of password) in hash:<32 hex digits> format.
# NtPasswordHash can only be used when the password is for MSCHAPv2 or
# MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
# EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit
# PSK) is also configured using this field. For EAP-GPSK, this is a
# variable length PSK. ext:<name of external password field> format can
# be used to indicate that the password is stored in external storage.
# ca_cert: File path to CA certificate file (PEM/DER). This file can have one
# or more trusted CA certificates. If ca_cert and ca_path are not
# included, server certificate will not be verified. This is insecure and
# a trusted CA certificate should always be configured when using
# EAP-TLS/TTLS/PEAP. Full path should be used since working directory may
# change when wpa_supplicant is run in the background.
#
# Alternatively, this can be used to only perform matching of the server
# certificate (SHA-256 hash of the DER encoded X.509 certificate). In
# this case, the possible CA certificates in the server certificate chain
# are ignored and only the server certificate is verified. This is
# configured with the following format:
# hash:://server/sha256/cert_hash_in_hex
# For example: "hash://server/sha256/
# 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a"
#
# On Windows, trusted CA certificates can be loaded from the system
# certificate store by setting this to cert_store://<name>, e.g.,
# ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
# Note that when running wpa_supplicant as an application, the user
# certificate store (My user account) is used, whereas computer store
# (Computer account) is used when running wpasvc as a service.
# ca_path: Directory path for CA certificate files (PEM). This path may
# contain multiple CA certificates in OpenSSL format. Common use for this
# is to point to system trusted CA list which is often installed into
# directory like /etc/ssl/certs. If configured, these certificates are
# added to the list of trusted CAs. ca_cert may also be included in that
# case, but it is not required.
# client_cert: File path to client certificate file (PEM/DER)
# Full path should be used since working directory may change when
# wpa_supplicant is run in the background.
# Alternatively, a named configuration blob can be used by setting this
# to blob://<blob name>.
# private_key: File path to client private key file (PEM/DER/PFX)
# When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
# commented out. Both the private key and certificate will be read from
# the PKCS#12 file in this case. Full path should be used since working
# directory may change when wpa_supplicant is run in the background.
# Windows certificate store can be used by leaving client_cert out and
# configuring private_key in one of the following formats:
# cert://substring_to_match
# hash://certificate_thumbprint_in_hex
# for example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
# Note that when running wpa_supplicant as an application, the user
# certificate store (My user account) is used, whereas computer store
# (Computer account) is used when running wpasvc as a service.
# Alternatively, a named configuration blob can be used by setting this
# to blob://<blob name>.
# private_key_passwd: Password for private key file (if left out, this will be
# asked through control interface)
# dh_file: File path to DH/DSA parameters file (in PEM format)
# This is an optional configuration file for setting parameters for an
# ephemeral DH key exchange. In most cases, the default RSA
# authentication does not use this configuration. However, it is possible
# setup RSA to use ephemeral DH key exchange. In addition, ciphers with
# DSA keys always use ephemeral DH keys. This can be used to achieve
# forward secrecy. If the file is in DSA parameters format, it will be
# automatically converted into DH params.
# subject_match: Substring to be matched against the subject of the
# authentication server certificate. If this string is set, the server
# certificate is only accepted if it contains this string in the subject.
# The subject string is in following format:
# /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com
# Note: Since this is a substring match, this cannot be used securely to
# do a suffix match against a possible domain name in the CN entry. For
# such a use case, domain_suffix_match or domain_match should be used
# instead.
# altsubject_match: Semicolon separated string of entries to be matched against
# the alternative subject name of the authentication server certificate.
# If this string is set, the server certificate is only accepted if it
# contains one of the entries in an alternative subject name extension.
# altSubjectName string is in following format: TYPE:VALUE
# Example: EMAIL:server@example.com
# Example: DNS:server.example.com;DNS:server2.example.com
# Following types are supported: EMAIL, DNS, URI
# domain_suffix_match: Constraint for server domain name. If set, this FQDN is
# used as a suffix match requirement for the AAA server certificate in
# SubjectAltName dNSName element(s). If a matching dNSName is found, this
# constraint is met. If no dNSName values are present, this constraint is
# matched against SubjectName CN using same suffix match comparison.
#
# Suffix match here means that the host/domain name is compared one label
# at a time starting from the top-level domain and all the labels in
# domain_suffix_match shall be included in the certificate. The
# certificate may include additional sub-level labels in addition to the
# required labels.
#
# More than one match string can be provided by using semicolons to
# separate the strings (e.g., example.org;example.com). When multiple
# strings are specified, a match with any one of the values is considered
# a sufficient match for the certificate, i.e., the conditions are ORed
# together.
#
# For example, domain_suffix_match=example.com would match
# test.example.com but would not match test-example.com.
# domain_match: Constraint for server domain name
# If set, this FQDN is used as a full match requirement for the
# server certificate in SubjectAltName dNSName element(s). If a
# matching dNSName is found, this constraint is met. If no dNSName
# values are present, this constraint is matched against SubjectName CN
# using same full match comparison. This behavior is similar to
# domain_suffix_match, but has the requirement of a full match, i.e.,
# no subdomains or wildcard matches are allowed. Case-insensitive
# comparison is used, so "Example.com" matches "example.com", but would
# not match "test.Example.com".
#
# More than one match string can be provided by using semicolons to
# separate the strings (e.g., example.org;example.com). When multiple
# strings are specified, a match with any one of the values is considered
# a sufficient match for the certificate, i.e., the conditions are ORed
# together.
# phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters
# (string with field-value pairs, e.g., "peapver=0" or
# "peapver=1 peaplabel=1")
# 'peapver' can be used to force which PEAP version (0 or 1) is used.
# 'peaplabel=1' can be used to force new label, "client PEAP encryption",
# to be used during key derivation when PEAPv1 or newer. Most existing
# PEAPv1 implementation seem to be using the old label, "client EAP
# encryption", and wpa_supplicant is now using that as the default value.
# Some servers, e.g., Radiator, may require peaplabel=1 configuration to
# interoperate with PEAPv1; see eap_testing.txt for more details.
# 'peap_outer_success=0' can be used to terminate PEAP authentication on
# tunneled EAP-Success. This is required with some RADIUS servers that
# implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
# Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode)
# include_tls_length=1 can be used to force wpa_supplicant to include
# TLS Message Length field in all TLS messages even if they are not
# fragmented.
# sim_min_num_chal=3 can be used to configure EAP-SIM to require three
# challenges (by default, it accepts 2 or 3)
# result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
# protected result indication.
# 'crypto_binding' option can be used to control PEAPv0 cryptobinding
# behavior:
# * 0 = do not use cryptobinding (default)
# * 1 = use cryptobinding if server supports it
# * 2 = require cryptobinding
# EAP-WSC (WPS) uses following options: pin=<Device Password> or
# pbc=1.
#
# For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
# used to configure a mode that allows EAP-Success (and EAP-Failure)
# without going through authentication step. Some switches use such
# sequence when forcing the port to be authorized/unauthorized or as a
# fallback option if the authentication server is unreachable. By default,
# wpa_supplicant discards such frames to protect against potential attacks
# by rogue devices, but this option can be used to disable that protection
# for cases where the server/authenticator does not need to be
# authenticated.
# phase2: Phase2 (inner authentication with TLS tunnel) parameters
# (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS). "mschapv2_retry=0" can be
# used to disable MSCHAPv2 password retry in authentication failure cases.
#
# TLS-based methods can use the following parameters to control TLS behavior
# (these are normally in the phase1 parameter, but can be used also in the
# phase2 parameter when EAP-TLS is used within the inner tunnel):
# tls_allow_md5=1 - allow MD5-based certificate signatures (depending on the
# TLS library, these may be disabled by default to enforce stronger
# security)
# tls_disable_time_checks=1 - ignore certificate validity time (this requests
# the TLS library to accept certificates even if they are not currently
# valid, i.e., have expired or have not yet become valid; this should be
# used only for testing purposes)
# tls_disable_session_ticket=1 - disable TLS Session Ticket extension
# tls_disable_session_ticket=0 - allow TLS Session Ticket extension to be used
# Note: If not set, this is automatically set to 1 for EAP-TLS/PEAP/TTLS
# as a workaround for broken authentication server implementations unless
# EAP workarounds are disabled with eap_workaround=0.
# For EAP-FAST, this must be set to 0 (or left unconfigured for the
# default value to be used automatically).
# tls_disable_tlsv1_0=1 - disable use of TLSv1.0
# tls_disable_tlsv1_0=0 - explicitly enable use of TLSv1.0 (this allows
# systemwide TLS policies to be overridden)
# tls_disable_tlsv1_1=1 - disable use of TLSv1.1 (a workaround for AAA servers
# that have issues interoperating with updated TLS version)
# tls_disable_tlsv1_1=0 - explicitly enable use of TLSv1.1 (this allows
# systemwide TLS policies to be overridden)
# tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers
# that have issues interoperating with updated TLS version)
# tls_disable_tlsv1_2=0 - explicitly enable use of TLSv1.2 (this allows
# systemwide TLS policies to be overridden)
# tls_disable_tlsv1_3=1 - disable use of TLSv1.3 (a workaround for AAA servers
# that have issues interoperating with updated TLS version)
# tls_disable_tlsv1_3=0 - enable TLSv1.3 (experimental - disabled by default)
# tls_ext_cert_check=0 - No external server certificate validation (default)
# tls_ext_cert_check=1 - External server certificate validation enabled; this
# requires an external program doing validation of server certificate
# chain when receiving CTRL-RSP-EXT_CERT_CHECK event from the control
# interface and report the result of the validation with
# CTRL-RSP_EXT_CERT_CHECK.
# tls_suiteb=0 - do not apply Suite B 192-bit constraints on TLS (default)
# tls_suiteb=1 - apply Suite B 192-bit constraints on TLS; this is used in
# particular when using Suite B with RSA keys of >= 3K (3072) bits
#
# Following certificate/private key fields are used in inner Phase2
# authentication when using EAP-TTLS or EAP-PEAP.
# ca_cert2: File path to CA certificate file. This file can have one or more
# trusted CA certificates. If ca_cert2 and ca_path2 are not included,
# server certificate will not be verified. This is insecure and a trusted
# CA certificate should always be configured.
# ca_path2: Directory path for CA certificate files (PEM)
# client_cert2: File path to client certificate file
# private_key2: File path to client private key file
# private_key2_passwd: Password for private key file
# dh_file2: File path to DH/DSA parameters file (in PEM format)
# subject_match2: Substring to be matched against the subject of the
# authentication server certificate. See subject_match for more details.
# altsubject_match2: Semicolon separated string of entries to be matched
# against the alternative subject name of the authentication server
# certificate. See altsubject_match documentation for more details.
# domain_suffix_match2: Constraint for server domain name. See
# domain_suffix_match for more details.
# ocsp2: See ocsp for more details.
#
# Separate machine credentials can be configured for EAP-TEAP Phase 2 with
# "machine_" prefix (e.g., "machine_identity") in the configuration parameters.
# See the parameters without that prefix for more details on the meaning and
# format of each such parameter.
#
# fragment_size: Maximum EAP fragment size in bytes (default 1398).
# This value limits the fragment size for EAP methods that support
# fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
# small enough to make the EAP messages fit in MTU of the network
# interface used for EAPOL. The default value is suitable for most
# cases.
#
# ocsp: Whether to use/require OCSP to check server certificate
# 0 = do not use OCSP stapling (TLS certificate status extension)
# 1 = try to use OCSP stapling, but not require response
# 2 = require valid OCSP stapling response
# 3 = require valid OCSP stapling response for all not-trusted
# certificates in the server certificate chain
#
# openssl_ciphers: OpenSSL specific cipher configuration
# This can be used to override the global openssl_ciphers configuration
# parameter (see above).
#
# erp: Whether EAP Re-authentication Protocol (ERP) is enabled
#
# EAP-FAST variables:
# pac_file: File path for the PAC entries. wpa_supplicant will need to be able
# to create this file and write updates to it when PAC is being
# provisioned or refreshed. Full path to the file should be used since
# working directory may change when wpa_supplicant is run in the
# background. Alternatively, a named configuration blob can be used by
# setting this to blob://<blob name>
# phase1: fast_provisioning option can be used to enable in-line provisioning
# of EAP-FAST credentials (PAC):
# 0 = disabled,
# 1 = allow unauthenticated provisioning,
# 2 = allow authenticated provisioning,
# 3 = allow both unauthenticated and authenticated provisioning
# fast_max_pac_list_len=<num> option can be used to set the maximum
# number of PAC entries to store in a PAC list (default: 10)
# fast_pac_format=binary option can be used to select binary format for
# storing PAC entries in order to save some space (the default
# text format uses about 2.5 times the size of minimal binary
# format)
#
# wpa_supplicant supports number of "EAP workarounds" to work around
# interoperability issues with incorrectly behaving authentication servers.
# These are enabled by default because some of the issues are present in large
# number of authentication servers. Strict EAP conformance mode can be
# configured by disabling workarounds with eap_workaround=0.
# update_identifier: PPS MO ID
# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
#
# roaming_consortium_selection: Roaming Consortium Selection
# The matching Roaming Consortium OI that was used to generate this
# network profile.
# Station inactivity limit
#
# If a station does not send anything in ap_max_inactivity seconds, an
# empty data frame is sent to it in order to verify whether it is
# still in range. If this frame is not ACKed, the station will be
# disassociated and then deauthenticated. This feature is used to
# clear station table of old entries when the STAs move out of the
# range.
#
# The station can associate again with the AP if it is still in range;
# this inactivity poll is just used as a nicer way of verifying
# inactivity; i.e., client will not report broken connection because
# disassociation frame is not sent immediately without first polling
# the STA with a data frame.
# default: 300 (i.e., 5 minutes)
#ap_max_inactivity=300
# DTIM period in Beacon intervals for AP mode (default: 2)
#dtim_period=2
# Beacon interval (default: 100 TU)
#beacon_int=100
# WPS in AP mode
# 0 = WPS enabled and configured (default)
# 1 = WPS disabled
#wps_disabled=0
# FILS DH Group
# 0 = PFS disabled with FILS shared key authentication (default)
# 1-65535 = DH Group to use for FILS PFS
#fils_dh_group=0
# DPP PFS
# 0: allow PFS to be used or not used (default)
# 1: require PFS to be used (note: not compatible with DPP R1)
# 2: do not allow PFS to be used
#dpp_pfs=0
# Whether beacon protection is enabled
# This depends on management frame protection (ieee80211w) being enabled and
# beacon protection support indication from the driver.
# 0 = disabled (default)
# 1 = enabled
#beacon_prot=0
# OWE DH Group
# 0: use default (19) first and then try all supported groups one by one if AP
# rejects the selected group
# 1-65535: DH Group to use for OWE
# Groups 19 (NIST P-256), 20 (NIST P-384), and 21 (NIST P-521) are
# currently supported.
#owe_group=0
# OWE-only mode (disable transition mode)
# 0: enable transition mode (allow connection to either OWE or open BSS)
# 1 = disable transition mode (allow connection only with OWE)
#owe_only=0
# OWE PTK derivation workaround
# Initial OWE implementation used SHA256 when deriving the PTK for all
# OWE groups. This was supposed to change to SHA384 for group 20 and
# SHA512 for group 21. This parameter can be used to enable older
# behavior mainly for testing purposes. There is no impact to group 19
# behavior, but if enabled, this will make group 20 and 21 cases use
# SHA256-based PTK derivation which will not work with the updated
# OWE implementation on the AP side.
#owe_ptk_workaround=0
# Transition Disable indication
# The AP can notify authenticated stations to disable transition mode
# in their network profiles when the network has completed transition
# steps, i.e., once sufficiently large number of APs in the ESS have
# been updated to support the more secure alternative. When this
# indication is used, the stations are expected to automatically
# disable transition mode and less secure security options. This
# includes use of WEP, TKIP (including use of TKIP as the group
# cipher), and connections without PMF.
# Bitmap bits:
# bit 0 (0x01): WPA3-Personal (i.e., disable WPA2-Personal = WPA-PSK
# and only allow SAE to be used)
# bit 1 (0x02): SAE-PK (disable SAE without use of SAE-PK)
# bit 2 (0x04): WPA3-Enterprise (move to requiring PMF)
# bit 3 (0x08): Enhanced Open (disable use of open network; require
# OWE)
# SAE-PK mode
# 0: automatic SAE/SAE-PK selection based on password; enable
# transition mode (allow SAE authentication without SAE-PK)
# 1: SAE-PK only (disable transition mode; allow SAE authentication
# only with SAE-PK)
# 2: disable SAE-PK (allow SAE authentication only without SAE-PK)
#sae_pk=0
# MAC address policy
# 0 = use permanent MAC address
# 1 = use random MAC address for each ESS connection
# 2 = like 1, but maintain OUI (with local admin bit set)
#mac_addr=0
# disable_ht: Whether HT (802.11n) should be disabled.
# 0 = HT enabled (if AP supports it)
# 1 = HT disabled
#
# disable_ht40: Whether HT-40 (802.11n) should be disabled.
# 0 = HT-40 enabled (if AP supports it)
# 1 = HT-40 disabled
#
# disable_sgi: Whether SGI (short guard interval) should be disabled.
# 0 = SGI enabled (if AP supports it)
# 1 = SGI disabled
#
# disable_ldpc: Whether LDPC should be disabled.
# 0 = LDPC enabled (if AP supports it)
# 1 = LDPC disabled
#
# ht40_intolerant: Whether 40 MHz intolerant should be indicated.
# 0 = 40 MHz tolerant (default)
# 1 = 40 MHz intolerant
#
# ht_mcs: Configure allowed MCS rates.
# Parsed as an array of bytes, in base-16 (ascii-hex)
# ht_mcs="" // Use all available (default)
# ht_mcs="0xff 00 00 00 00 00 00 00 00 00 " // Use MCS 0-7 only
# ht_mcs="0xff ff 00 00 00 00 00 00 00 00 " // Use MCS 0-15 only
#
# disable_max_amsdu: Whether MAX_AMSDU should be disabled.
# -1 = Do not make any changes.
# 0 = Enable MAX-AMSDU if hardware supports it.
# 1 = Disable AMSDU
#
# ampdu_factor: Maximum A-MPDU Length Exponent
# Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
#
# ampdu_density: Allow overriding AMPDU density configuration.
# Treated as hint by the kernel.
# -1 = Do not make any changes.
# 0-3 = Set AMPDU density (aka factor) to specified value.
#
# tx_stbc: Allow overriding STBC support for TX streams
# Value: 0-1, see IEEE Std 802.11-2016, 9.4.2.56.2.
# -1 = Do not make any changes (default)
# 0 = Set if not supported
# 1 = Set if supported
#
# rx_stbc: Allow overriding STBC support for RX streams
# Value: 0-3, see IEEE Std 802.11-2016, 9.4.2.56.2.
# -1 = Do not make any changes (default)
# 0 = Set if not supported
# 1 = Set for support of one spatial stream
# 2 = Set for support of one and two spatial streams
# 3 = Set for support of one, two and three spatial streams
# disable_vht: Whether VHT should be disabled.
# 0 = VHT enabled (if AP supports it)
# 1 = VHT disabled
#
# vht_capa: VHT capabilities to set in the override
# vht_capa_mask: mask of VHT capabilities
#
# vht_rx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for RX NSS 1-8
# vht_tx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for TX NSS 1-8
# 0: MCS 0-7
# 1: MCS 0-8
# 2: MCS 0-9
# 3: not supported
# multi_ap_backhaul_sta: Multi-AP backhaul STA functionality
# 0 = normal STA (default)
# 1 = backhaul STA
# A backhaul STA sends the Multi-AP IE, fails to associate if the AP does not
# support Multi-AP, and sets 4-address mode if it does. Thus, the netdev can be
# added to a bridge to allow forwarding frames over this backhaul link.
##### Fast Session Transfer (FST) support #####################################
#
# The options in this section are only available when the build configuration
# option CONFIG_FST is set while compiling wpa_supplicant. They allow this
# interface to be a part of FST setup.
#
# FST is the transfer of a session from a channel to another channel, in the
# same or different frequency bands.
#
# For details, see IEEE Std 802.11ad-2012.
# Identifier of an FST Group the interface belongs to.
#fst_group_id=bond0
# Interface priority within the FST Group.
# Announcing a higher priority for an interface means declaring it more
# preferable for FST switch.
# fst_priority is in 1..255 range with 1 being the lowest priority.
#fst_priority=100
# Default LLT value for this interface in milliseconds. The value used in case
# no value provided during session setup. Default is 50 msec.
# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2
# Transitioning between states).
#fst_llt=100
# BSS Transition Management
# disable_btm - Disable BSS transition management in STA
# Set to 0 to enable BSS transition management (default behavior)
# Set to 1 to disable BSS transition management
#disable_btm=0
# Enable EDMG capability in STA/AP mode, default value is false
#enable_edmg=1
# This value is used to configure the channel bonding feature.
# Default value is 0.
# Relevant only if enable_edmg is true
# In AP mode it defines the EDMG channel to use for AP operation.
# In STA mode it defines the EDMG channel for connection (if supported by AP).
#edmg_channel=9
# Example blocks:
# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
network={
ssid="simple"
psk="very secret passphrase"
priority=5
}
# Same as previous, but request SSID-specific scanning (for APs that reject
# broadcast SSID)
network={
ssid="second ssid"
scan_ssid=1
psk="very secret passphrase"
priority=2
}
# Only WPA-PSK is used. Any valid cipher combination is accepted.
network={
ssid="example"
proto=WPA
key_mgmt=WPA-PSK
pairwise=CCMP TKIP
group=CCMP TKIP WEP104 WEP40
psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
priority=2
}
# WPA-Personal(PSK) with TKIP and enforcement for frequent PTK rekeying
network={
ssid="example"
proto=WPA
key_mgmt=WPA-PSK
pairwise=TKIP
group=TKIP
psk="not so secure passphrase"
wpa_ptk_rekey=600
}
# Only WPA-EAP is used. Both CCMP and TKIP is accepted. An AP that used WEP104
# or WEP40 as the group cipher will not be accepted.
network={
ssid="example"
proto=RSN
key_mgmt=WPA-EAP
pairwise=CCMP TKIP
group=CCMP TKIP
eap=TLS
identity="user@example.com"
ca_cert="/etc/cert/ca.pem"
client_cert="/etc/cert/user.pem"
private_key="/etc/cert/user.prv"
private_key_passwd="password"
priority=1
}
# EAP-PEAP/MSCHAPv2 configuration for RADIUS servers that use the new peaplabel
# (e.g., Radiator)
network={
ssid="example"
key_mgmt=WPA-EAP
eap=PEAP
identity="user@example.com"
password="foobar"
ca_cert="/etc/cert/ca.pem"
phase1="peaplabel=1"
phase2="auth=MSCHAPV2"
priority=10
}
# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
# unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
network={
ssid="example"
key_mgmt=WPA-EAP
eap=TTLS
identity="user@example.com"
anonymous_identity="anonymous@example.com"
password="foobar"
ca_cert="/etc/cert/ca.pem"
priority=2
}
# EAP-TTLS/MSCHAPv2 configuration with anonymous identity for the unencrypted
# use. Real identity is sent only within an encrypted TLS tunnel.
network={
ssid="example"
key_mgmt=WPA-EAP
eap=TTLS
identity="user@example.com"
anonymous_identity="anonymous@example.com"
password="foobar"
ca_cert="/etc/cert/ca.pem"
phase2="auth=MSCHAPV2"
}
# WPA-EAP, EAP-TTLS with different CA certificate used for outer and inner
# authentication.
network={
ssid="example"
key_mgmt=WPA-EAP
eap=TTLS
# Phase1 / outer authentication
anonymous_identity="anonymous@example.com"
ca_cert="/etc/cert/ca.pem"
# Phase 2 / inner authentication
phase2="autheap=TLS"
ca_cert2="/etc/cert/ca2.pem"
client_cert2="/etc/cer/user.pem"
private_key2="/etc/cer/user.prv"
private_key2_passwd="password"
priority=2
}
# Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and
# group cipher.
network={
ssid="example"
bssid=00:11:22:33:44:55
proto=WPA RSN
key_mgmt=WPA-PSK WPA-EAP
pairwise=CCMP
group=CCMP
psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
}
# Special characters in SSID, so use hex string. Default to WPA-PSK, WPA-EAP
# and all valid ciphers.
network={
ssid=00010203
psk=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
}
# EAP-SIM with a GSM SIM or USIM
network={
ssid="eap-sim-test"
key_mgmt=WPA-EAP
eap=SIM
pin="1234"
pcsc=""
}
# EAP-PSK
network={
ssid="eap-psk-test"
key_mgmt=WPA-EAP
eap=PSK
anonymous_identity="eap_psk_user"
password=06b4be19da289f475aa46a33cb793029
identity="eap_psk_user@example.com"
}
# IEEE 802.1X/EAPOL with dynamically generated WEP keys (i.e., no WPA) using
# EAP-TLS for authentication and key generation; require both unicast and
# broadcast WEP keys.
network={
ssid="1x-test"
key_mgmt=IEEE8021X
eap=TLS
identity="user@example.com"
ca_cert="/etc/cert/ca.pem"
client_cert="/etc/cert/user.pem"
private_key="/etc/cert/user.prv"
private_key_passwd="password"
eapol_flags=3
}
# LEAP with dynamic WEP keys
network={
ssid="leap-example"
key_mgmt=IEEE8021X
eap=LEAP
identity="user"
password="foobar"
}
# EAP-IKEv2 using shared secrets for both server and peer authentication
network={
ssid="ikev2-example"
key_mgmt=WPA-EAP
eap=IKEV2
identity="user"
password="foobar"
}
# EAP-FAST with WPA (WPA or WPA2)
network={
ssid="eap-fast-test"
key_mgmt=WPA-EAP
eap=FAST
anonymous_identity="FAST-000102030405"
identity="username"
password="password"
phase1="fast_provisioning=1"
pac_file="/etc/wpa_supplicant.eap-fast-pac"
}
network={
ssid="eap-fast-test"
key_mgmt=WPA-EAP
eap=FAST
anonymous_identity="FAST-000102030405"
identity="username"
password="password"
phase1="fast_provisioning=1"
pac_file="blob://eap-fast-pac"
}
# Plaintext connection (no WPA, no IEEE 802.1X)
network={
ssid="plaintext-test"
key_mgmt=NONE
}
# Shared WEP key connection (no WPA, no IEEE 802.1X)
network={
ssid="static-wep-test"
key_mgmt=NONE
wep_key0="abcde"
wep_key1=0102030405
wep_key2="1234567890123"
wep_tx_keyidx=0
priority=5
}
# Shared WEP key connection (no WPA, no IEEE 802.1X) using Shared Key
# IEEE 802.11 authentication
network={
ssid="static-wep-test2"
key_mgmt=NONE
wep_key0="abcde"
wep_key1=0102030405
wep_key2="1234567890123"
wep_tx_keyidx=0
priority=5
auth_alg=SHARED
}
# IBSS/ad-hoc network with RSN
network={
ssid="ibss-rsn"
key_mgmt=WPA-PSK
proto=RSN
psk="12345678"
mode=1
frequency=2412
pairwise=CCMP
group=CCMP
}
# IBSS/ad-hoc network with WPA-None/TKIP (deprecated)
network={
ssid="test adhoc"
mode=1
frequency=2412
proto=WPA
key_mgmt=WPA-NONE
pairwise=NONE
group=TKIP
psk="secret passphrase"
}
# open mesh network
network={
ssid="test mesh"
mode=5
frequency=2437
key_mgmt=NONE
}
# secure (SAE + AMPE) network
network={
ssid="secure mesh"
mode=5
frequency=2437
key_mgmt=SAE
psk="very secret passphrase"
}
# Catch all example that allows more or less all configuration modes
network={
ssid="example"
scan_ssid=1
key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
pairwise=CCMP TKIP
group=CCMP TKIP WEP104 WEP40
psk="very secret passphrase"
eap=TTLS PEAP TLS
identity="user@example.com"
password="foobar"
ca_cert="/etc/cert/ca.pem"
client_cert="/etc/cert/user.pem"
private_key="/etc/cert/user.prv"
private_key_passwd="password"
phase1="peaplabel=0"
}
# Example of EAP-TLS with smartcard (openssl engine)
network={
ssid="example"
key_mgmt=WPA-EAP
eap=TLS
proto=RSN
pairwise=CCMP TKIP
group=CCMP TKIP
identity="user@example.com"
ca_cert="/etc/cert/ca.pem"
# Certificate and/or key identified by PKCS#11 URI (RFC7512)
client_cert="pkcs11:manufacturer=piv_II;id=%01"
private_key="pkcs11:manufacturer=piv_II;id=%01"
# Optional PIN configuration; this can be left out and PIN will be
# asked through the control interface
pin="1234"
}
# Example configuration showing how to use an inlined blob as a CA certificate
# data instead of using external file
network={
ssid="example"
key_mgmt=WPA-EAP
eap=TTLS
identity="user@example.com"
anonymous_identity="anonymous@example.com"
password="foobar"
ca_cert="blob://exampleblob"
priority=20
}
blob-base64-exampleblob={
SGVsbG8gV29ybGQhCg==
}
# Wildcard match for SSID (plaintext APs only). This example select any
# open AP regardless of its SSID.
network={
key_mgmt=NONE
}
# Example configuration ignoring two APs - these will be ignored
# for this network.
network={
ssid="example"
psk="very secret passphrase"
bssid_ignore=02:11:22:33:44:55 02:22:aa:44:55:66
}
# Example configuration limiting AP selection to a specific set of APs;
# any other AP not matching the masked address will be ignored.
network={
ssid="example"
psk="very secret passphrase"
bssid_accept=02:55:ae:bc:00:00/ff:ff:ff:ff:00:00 00:00:77:66:55:44/00:00:ff:ff:ff:ff
}
# Example config file that will only scan on channel 36.
freq_list=5180
network={
key_mgmt=NONE
}
# Example configuration using EAP-TTLS for authentication and key
# generation for MACsec
network={
key_mgmt=IEEE8021X
eap=TTLS
phase2="auth=PAP"
anonymous_identity="anonymous@example.com"
identity="user@example.com"
password="secretr"
ca_cert="/etc/cert/ca.pem"
eapol_flags=0
macsec_policy=1
}
# Example configuration for MACsec with preshared key
network={
key_mgmt=NONE
eapol_flags=0
macsec_policy=1
mka_cak=0123456789ABCDEF0123456789ABCDEF
mka_ckn=6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435
mka_priority=128
}
diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
index 60acb53c5c38..cbc955159bbe 100644
--- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
+++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h
@@ -1,1750 +1,1886 @@
/*
* wpa_supplicant - Internal definitions
* Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef WPA_SUPPLICANT_I_H
#define WPA_SUPPLICANT_I_H
#include "utils/bitfield.h"
#include "utils/list.h"
#include "common/defs.h"
#include "common/sae.h"
#include "common/wpa_ctrl.h"
#include "crypto/sha384.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "wps/wps_defs.h"
#include "config_ssid.h"
#include "wmm_ac.h"
extern const char *const wpa_supplicant_version;
extern const char *const wpa_supplicant_license;
#ifndef CONFIG_NO_STDOUT_DEBUG
extern const char *const wpa_supplicant_full_license1;
extern const char *const wpa_supplicant_full_license2;
extern const char *const wpa_supplicant_full_license3;
extern const char *const wpa_supplicant_full_license4;
extern const char *const wpa_supplicant_full_license5;
#endif /* CONFIG_NO_STDOUT_DEBUG */
struct wpa_sm;
struct wpa_supplicant;
struct ibss_rsn;
struct scan_info;
struct wpa_bss;
struct wpa_scan_results;
struct hostapd_hw_modes;
struct wpa_driver_associate_params;
/*
* Forward declarations of private structures used within the ctrl_iface
* backends. Other parts of wpa_supplicant do not have access to data stored in
* these structures.
*/
struct ctrl_iface_priv;
struct ctrl_iface_global_priv;
struct wpas_dbus_priv;
struct wpas_binder_priv;
/**
* struct wpa_interface - Parameters for wpa_supplicant_add_iface()
*/
struct wpa_interface {
/**
* confname - Configuration name (file or profile) name
*
* This can also be %NULL when a configuration file is not used. In
* that case, ctrl_interface must be set to allow the interface to be
* configured.
*/
const char *confname;
/**
* confanother - Additional configuration name (file or profile) name
*
* This can also be %NULL when the additional configuration file is not
* used.
*/
const char *confanother;
/**
* ctrl_interface - Control interface parameter
*
* If a configuration file is not used, this variable can be used to
* set the ctrl_interface parameter that would have otherwise been read
* from the configuration file. If both confname and ctrl_interface are
* set, ctrl_interface is used to override the value from configuration
* file.
*/
const char *ctrl_interface;
/**
* driver - Driver interface name, or %NULL to use the default driver
*/
const char *driver;
/**
* driver_param - Driver interface parameters
*
* If a configuration file is not used, this variable can be used to
* set the driver_param parameters that would have otherwise been read
* from the configuration file. If both confname and driver_param are
* set, driver_param is used to override the value from configuration
* file.
*/
const char *driver_param;
/**
* ifname - Interface name
*/
const char *ifname;
/**
* bridge_ifname - Optional bridge interface name
*
* If the driver interface (ifname) is included in a Linux bridge
* device, the bridge interface may need to be used for receiving EAPOL
* frames. This can be enabled by setting this variable to enable
* receiving of EAPOL frames from an additional interface.
*/
const char *bridge_ifname;
/**
* p2p_mgmt - Interface used for P2P management (P2P Device operations)
*
* Indicates whether wpas_p2p_init() must be called for this interface.
* This is used only when the driver supports a dedicated P2P Device
* interface that is not a network interface.
*/
int p2p_mgmt;
#ifdef CONFIG_MATCH_IFACE
/**
* matched - Interface was matched rather than specified
*
*/
enum {
WPA_IFACE_NOT_MATCHED,
WPA_IFACE_MATCHED_NULL,
WPA_IFACE_MATCHED
} matched;
#endif /* CONFIG_MATCH_IFACE */
};
/**
* struct wpa_params - Parameters for wpa_supplicant_init()
*/
struct wpa_params {
/**
* daemonize - Run %wpa_supplicant in the background
*/
int daemonize;
/**
* wait_for_monitor - Wait for a monitor program before starting
*/
int wait_for_monitor;
/**
* pid_file - Path to a PID (process ID) file
*
* If this and daemonize are set, process ID of the background process
* will be written to the specified file.
*/
char *pid_file;
/**
* wpa_debug_level - Debugging verbosity level (e.g., MSG_INFO)
*/
int wpa_debug_level;
/**
* wpa_debug_show_keys - Whether keying material is included in debug
*
* This parameter can be used to allow keying material to be included
* in debug messages. This is a security risk and this option should
* not be enabled in normal configuration. If needed during
* development or while troubleshooting, this option can provide more
* details for figuring out what is happening.
*/
int wpa_debug_show_keys;
/**
* wpa_debug_timestamp - Whether to include timestamp in debug messages
*/
int wpa_debug_timestamp;
/**
* ctrl_interface - Global ctrl_iface path/parameter
*/
char *ctrl_interface;
/**
* ctrl_interface_group - Global ctrl_iface group
*/
char *ctrl_interface_group;
/**
* dbus_ctrl_interface - Enable the DBus control interface
*/
int dbus_ctrl_interface;
/**
* wpa_debug_file_path - Path of debug file or %NULL to use stdout
*/
const char *wpa_debug_file_path;
/**
* wpa_debug_syslog - Enable log output through syslog
*/
int wpa_debug_syslog;
/**
* wpa_debug_tracing - Enable log output through Linux tracing
*/
int wpa_debug_tracing;
/**
* override_driver - Optional driver parameter override
*
* This parameter can be used to override the driver parameter in
* dynamic interface addition to force a specific driver wrapper to be
* used instead.
*/
char *override_driver;
/**
* override_ctrl_interface - Optional ctrl_interface override
*
* This parameter can be used to override the ctrl_interface parameter
* in dynamic interface addition to force a control interface to be
* created.
*/
char *override_ctrl_interface;
/**
* entropy_file - Optional entropy file
*
* This parameter can be used to configure wpa_supplicant to maintain
* its internal entropy store over restarts.
*/
char *entropy_file;
#ifdef CONFIG_P2P
/**
* conf_p2p_dev - Configuration file used to hold the
* P2P Device configuration parameters.
*
* This can also be %NULL. In such a case, if a P2P Device dedicated
* interfaces is created, the main configuration file will be used.
*/
char *conf_p2p_dev;
#endif /* CONFIG_P2P */
#ifdef CONFIG_MATCH_IFACE
/**
* match_ifaces - Interface descriptions to match
*/
struct wpa_interface *match_ifaces;
/**
* match_iface_count - Number of defined matching interfaces
*/
int match_iface_count;
#endif /* CONFIG_MATCH_IFACE */
};
struct p2p_srv_bonjour {
struct dl_list list;
struct wpabuf *query;
struct wpabuf *resp;
};
struct p2p_srv_upnp {
struct dl_list list;
u8 version;
char *service;
};
/**
* struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
*
* This structure is initialized by calling wpa_supplicant_init() when starting
* %wpa_supplicant.
*/
struct wpa_global {
struct wpa_supplicant *ifaces;
struct wpa_params params;
struct ctrl_iface_global_priv *ctrl_iface;
struct wpas_dbus_priv *dbus;
struct wpas_binder_priv *binder;
void **drv_priv;
size_t drv_count;
struct os_time suspend_time;
struct p2p_data *p2p;
struct wpa_supplicant *p2p_init_wpa_s;
struct wpa_supplicant *p2p_group_formation;
struct wpa_supplicant *p2p_invite_group;
u8 p2p_dev_addr[ETH_ALEN];
struct os_reltime p2p_go_wait_client;
struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */
struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */
int p2p_disabled;
int cross_connection;
int p2p_long_listen; /* remaining time in long Listen state in ms */
struct wpa_freq_range_list p2p_disallow_freq;
struct wpa_freq_range_list p2p_go_avoid_freq;
enum wpa_conc_pref {
WPA_CONC_PREF_NOT_SET,
WPA_CONC_PREF_STA,
WPA_CONC_PREF_P2P
} conc_pref;
unsigned int p2p_per_sta_psk:1;
unsigned int p2p_fail_on_wps_complete:1;
unsigned int p2p_24ghz_social_channels:1;
unsigned int pending_p2ps_group:1;
unsigned int pending_group_iface_for_p2ps:1;
unsigned int pending_p2ps_group_freq;
#ifdef CONFIG_WIFI_DISPLAY
int wifi_display;
#define MAX_WFD_SUBELEMS 12
struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
#endif /* CONFIG_WIFI_DISPLAY */
struct psk_list_entry *add_psk; /* From group formation */
};
/**
* struct wpa_radio - Internal data for per-radio information
*
* This structure is used to share data about configured interfaces
* (struct wpa_supplicant) that share the same physical radio, e.g., to allow
* better coordination of offchannel operations.
*/
struct wpa_radio {
char name[16]; /* from driver_ops get_radio_name() or empty if not
* available */
/** NULL if no external scan running. */
struct wpa_supplicant *external_scan_req_interface;
unsigned int num_active_works;
struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
struct dl_list work; /* struct wpa_radio_work::list entries */
};
/**
* Checks whether an external scan is running on a given radio.
* @radio: Pointer to radio struct
* Returns: true if an external scan is running, false otherwise.
*/
static inline bool external_scan_running(struct wpa_radio *radio)
{
return radio && radio->external_scan_req_interface;
}
#define MAX_ACTIVE_WORKS 2
/**
* struct wpa_radio_work - Radio work item
*/
struct wpa_radio_work {
struct dl_list list;
unsigned int freq; /* known frequency (MHz) or 0 for multiple/unknown */
const char *type;
struct wpa_supplicant *wpa_s;
void (*cb)(struct wpa_radio_work *work, int deinit);
void *ctx;
unsigned int started:1;
struct os_reltime time;
unsigned int bands;
};
int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
const char *type, int next,
void (*cb)(struct wpa_radio_work *work, int deinit),
void *ctx);
void radio_work_done(struct wpa_radio_work *work);
void radio_remove_works(struct wpa_supplicant *wpa_s,
const char *type, int remove_all);
void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx);
void radio_work_check_next(struct wpa_supplicant *wpa_s);
struct wpa_radio_work *
radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);
struct wpa_connect_work {
unsigned int sme:1;
unsigned int bss_removed:1;
struct wpa_bss *bss;
struct wpa_ssid *ssid;
};
int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
struct wpa_ssid *test_ssid);
void wpas_connect_work_free(struct wpa_connect_work *cwork);
void wpas_connect_work_done(struct wpa_supplicant *wpa_s);
struct wpa_external_work {
unsigned int id;
char type[100];
unsigned int timeout;
};
enum wpa_radio_work_band wpas_freq_to_band(int freq);
unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs);
/**
* offchannel_send_action_result - Result of offchannel send Action frame
*/
enum offchannel_send_action_result {
OFFCHANNEL_SEND_ACTION_SUCCESS /**< Frame was send and acknowledged */,
OFFCHANNEL_SEND_ACTION_NO_ACK /**< Frame was sent, but not acknowledged
*/,
OFFCHANNEL_SEND_ACTION_FAILED /**< Frame was not sent due to a failure
*/
};
struct wps_ap_info {
u8 bssid[ETH_ALEN];
enum wps_ap_info_type {
WPS_AP_NOT_SEL_REG,
WPS_AP_SEL_REG,
WPS_AP_SEL_REG_OUR
} type;
unsigned int tries;
struct os_reltime last_attempt;
unsigned int pbc_active;
u8 uuid[WPS_UUID_LEN];
};
#define WPA_FREQ_USED_BY_INFRA_STATION BIT(0)
#define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1)
struct wpa_used_freq_data {
int freq;
unsigned int flags;
};
#define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */
/*
* struct rrm_data - Data used for managing RRM features
*/
struct rrm_data {
/* rrm_used - indication regarding the current connection */
unsigned int rrm_used:1;
/*
* notify_neighbor_rep - Callback for notifying report requester
*/
void (*notify_neighbor_rep)(void *ctx, struct wpabuf *neighbor_rep);
/*
* neighbor_rep_cb_ctx - Callback context
* Received in the callback registration, and sent to the callback
* function as a parameter.
*/
void *neighbor_rep_cb_ctx;
/* next_neighbor_rep_token - Next request's dialog token */
u8 next_neighbor_rep_token;
/* token - Dialog token of the current radio measurement */
u8 token;
/* destination address of the current radio measurement request */
u8 dst_addr[ETH_ALEN];
};
enum wpa_supplicant_test_failure {
WPAS_TEST_FAILURE_NONE,
WPAS_TEST_FAILURE_SCAN_TRIGGER,
};
struct icon_entry {
struct dl_list list;
u8 bssid[ETH_ALEN];
u8 dialog_token;
char *file_name;
u8 *image;
size_t image_len;
};
struct wpa_bss_tmp_disallowed {
struct dl_list list;
u8 bssid[ETH_ALEN];
int rssi_threshold;
};
struct beacon_rep_data {
u8 token;
u8 last_indication;
struct wpa_driver_scan_params scan_params;
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
u8 bssid[ETH_ALEN];
enum beacon_report_detail report_detail;
struct bitfield *eids;
};
struct external_pmksa_cache {
struct dl_list list;
void *pmksa_cache;
};
struct fils_hlp_req {
struct dl_list list;
u8 dst[ETH_ALEN];
struct wpabuf *pkt;
};
struct driver_signal_override {
struct dl_list list;
u8 bssid[ETH_ALEN];
int si_current_signal;
int si_avg_signal;
int si_avg_beacon_signal;
int si_current_noise;
int scan_level;
};
struct robust_av_data {
u8 dialog_token;
enum scs_request_type request_type;
u8 up_bitmap;
u8 up_limit;
u32 stream_timeout;
u8 frame_classifier[48];
size_t frame_classifier_len;
bool valid_config;
};
+struct dscp_policy_status {
+ u8 id;
+ u8 status;
+};
+
+struct dscp_resp_data {
+ bool more;
+ bool reset;
+ bool solicited;
+ struct dscp_policy_status *policy;
+ int num_policies;
+};
+
#ifdef CONFIG_PASN
struct pasn_fils {
u8 nonce[FILS_NONCE_LEN];
u8 anonce[FILS_NONCE_LEN];
u8 session[FILS_SESSION_LEN];
u8 erp_pmkid[PMKID_LEN];
bool completed;
};
struct wpas_pasn {
int akmp;
int cipher;
u16 group;
int freq;
size_t kdk_len;
u8 trans_seq;
u8 status;
u8 bssid[ETH_ALEN];
size_t pmk_len;
u8 pmk[PMK_LEN_MAX];
bool using_pmksa;
u8 hash[SHA384_MAC_LEN];
struct wpabuf *beacon_rsne_rsnxe;
struct wpa_ptk ptk;
struct crypto_ecdh *ecdh;
struct wpabuf *comeback;
u16 comeback_after;
#ifdef CONFIG_SAE
struct sae_data sae;
#endif /* CONFIG_SAE */
struct wpa_ssid *ssid;
#ifdef CONFIG_FILS
struct pasn_fils fils;
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
u8 pmk_r1[PMK_LEN_MAX];
size_t pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
#endif /* CONFIG_IEEE80211R */
};
#endif /* CONFIG_PASN */
+
+enum ip_version {
+ IPV4 = 4,
+ IPV6 = 6,
+};
+
+
+struct ipv4_params {
+ struct in_addr src_ip;
+ struct in_addr dst_ip;
+ u16 src_port;
+ u16 dst_port;
+ u8 dscp;
+ u8 protocol;
+ u8 param_mask;
+};
+
+
+struct ipv6_params {
+ struct in6_addr src_ip;
+ struct in6_addr dst_ip;
+ u16 src_port;
+ u16 dst_port;
+ u8 dscp;
+ u8 next_header;
+ u8 flow_label[3];
+ u8 param_mask;
+};
+
+
+struct type4_params {
+ u8 classifier_mask;
+ enum ip_version ip_version;
+ union {
+ struct ipv4_params v4;
+ struct ipv6_params v6;
+ } ip_params;
+};
+
+
+struct type10_params {
+ u8 prot_instance;
+ u8 prot_number;
+ u8 *filter_value;
+ u8 *filter_mask;
+ size_t filter_len;
+};
+
+
+struct tclas_element {
+ u8 user_priority;
+ u8 classifier_type;
+ union {
+ struct type4_params type4_param;
+ struct type10_params type10_param;
+ } frame_classifier;
+};
+
+
+struct scs_desc_elem {
+ u8 scs_id;
+ enum scs_request_type request_type;
+ u8 intra_access_priority;
+ bool scs_up_avail;
+ struct tclas_element *tclas_elems;
+ unsigned int num_tclas_elem;
+ u8 tclas_processing;
+};
+
+
+struct scs_robust_av_data {
+ struct scs_desc_elem *scs_desc_elems;
+ unsigned int num_scs_desc;
+};
+
+
+enum scs_response_status {
+ SCS_DESC_SENT = 0,
+ SCS_DESC_SUCCESS = 1,
+};
+
+
+struct active_scs_elem {
+ struct dl_list list;
+ u8 scs_id;
+ enum scs_response_status status;
+};
+
+
/**
* struct wpa_supplicant - Internal data for wpa_supplicant interface
*
* This structure contains the internal data for core wpa_supplicant code. This
* should be only used directly from the core code. However, a pointer to this
* data is used from other files as an arbitrary context pointer in calls to
* core functions.
*/
struct wpa_supplicant {
struct wpa_global *global;
struct wpa_radio *radio; /* shared radio context */
struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */
struct wpa_supplicant *parent;
struct wpa_supplicant *p2pdev;
struct wpa_supplicant *next;
struct l2_packet_data *l2;
struct l2_packet_data *l2_br;
struct os_reltime roam_start;
struct os_reltime roam_time;
struct os_reltime session_start;
struct os_reltime session_length;
unsigned char own_addr[ETH_ALEN];
unsigned char perm_addr[ETH_ALEN];
char ifname[100];
#ifdef CONFIG_MATCH_IFACE
int matched;
#endif /* CONFIG_MATCH_IFACE */
#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
char *dbus_new_path;
char *dbus_groupobj_path;
#ifdef CONFIG_AP
char *preq_notify_peer;
#endif /* CONFIG_AP */
#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
#ifdef CONFIG_CTRL_IFACE_BINDER
const void *binder_object_key;
#endif /* CONFIG_CTRL_IFACE_BINDER */
char bridge_ifname[16];
char *confname;
char *confanother;
struct wpa_config *conf;
int countermeasures;
struct os_reltime last_michael_mic_error;
u8 bssid[ETH_ALEN];
u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
* field contains the target BSSID. */
int reassociate; /* reassociation requested */
bool roam_in_progress; /* roam in progress */
unsigned int reassoc_same_bss:1; /* reassociating to the same BSS */
unsigned int reassoc_same_ess:1; /* reassociating to the same ESS */
int disconnected; /* all connections disabled; i.e., do no reassociate
* before this has been cleared */
struct wpa_ssid *current_ssid;
struct wpa_ssid *last_ssid;
struct wpa_bss *current_bss;
int ap_ies_from_associnfo;
unsigned int assoc_freq;
u8 *last_con_fail_realm;
size_t last_con_fail_realm_len;
/* Selected configuration (based on Beacon/ProbeResp WPA IE) */
int pairwise_cipher;
int deny_ptk0_rekey;
int group_cipher;
int key_mgmt;
int wpa_proto;
int mgmt_group_cipher;
void *drv_priv; /* private data used by driver_ops */
void *global_drv_priv;
u8 *bssid_filter;
size_t bssid_filter_count;
u8 *disallow_aps_bssid;
size_t disallow_aps_bssid_count;
struct wpa_ssid_value *disallow_aps_ssid;
size_t disallow_aps_ssid_count;
u32 setband_mask;
/* Preferred network for the next connection attempt */
struct wpa_ssid *next_ssid;
/* previous scan was wildcard when interleaving between
* wildcard scans and specific SSID scan when max_ssids=1 */
int prev_scan_wildcard;
struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID;
* NULL = not yet initialized (start
* with wildcard SSID)
* WILDCARD_SSID_SCAN = wildcard
* SSID was used in the previous scan
*/
#define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1)
struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
int sched_scan_timeout;
int first_sched_scan;
int sched_scan_timed_out;
struct sched_scan_plan *sched_scan_plans;
size_t sched_scan_plans_num;
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
void (*scan_res_fail_handler)(struct wpa_supplicant *wpa_s);
struct dl_list bss; /* struct wpa_bss::list */
struct dl_list bss_id; /* struct wpa_bss::list_id */
size_t num_bss;
unsigned int bss_update_idx;
unsigned int bss_next_id;
/*
* Pointers to BSS entries in the order they were in the last scan
* results.
*/
struct wpa_bss **last_scan_res;
size_t last_scan_res_used;
size_t last_scan_res_size;
struct os_reltime last_scan;
const struct wpa_driver_ops *driver;
int interface_removed; /* whether the network interface has been
* removed */
struct wpa_sm *wpa;
struct ptksa_cache *ptksa;
struct eapol_sm *eapol;
struct ctrl_iface_priv *ctrl_iface;
enum wpa_states wpa_state;
struct wpa_radio_work *scan_work;
int scanning;
int sched_scanning;
unsigned int sched_scan_stop_req:1;
int new_connection;
int eapol_received; /* number of EAPOL packets received after the
* previous association event */
u8 rsnxe[20];
size_t rsnxe_len;
struct scard_data *scard;
char imsi[20];
int mnc_len;
unsigned char last_eapol_src[ETH_ALEN];
unsigned int keys_cleared; /* bitfield of key indexes that the driver is
* known not to be configured with a key */
struct wpa_bssid_ignore *bssid_ignore;
/* Number of connection failures since last successful connection */
unsigned int consecutive_conn_failures;
/**
* scan_req - Type of the scan request
*/
enum scan_req_type {
/**
* NORMAL_SCAN_REQ - Normal scan request
*
* This is used for scans initiated by wpa_supplicant to find an
* AP for a connection.
*/
NORMAL_SCAN_REQ,
/**
* INITIAL_SCAN_REQ - Initial scan request
*
* This is used for the first scan on an interface to force at
* least one scan to be run even if the configuration does not
* include any enabled networks.
*/
INITIAL_SCAN_REQ,
/**
* MANUAL_SCAN_REQ - Manual scan request
*
* This is used for scans where the user request a scan or
* a specific wpa_supplicant operation (e.g., WPS) requires scan
* to be run.
*/
MANUAL_SCAN_REQ
} scan_req, last_scan_req;
enum wpa_states scan_prev_wpa_state;
struct os_reltime scan_trigger_time, scan_start_time;
/* Minimum freshness requirement for connection purposes */
struct os_reltime scan_min_time;
int scan_runs; /* number of scan runs since WPS was started */
int *next_scan_freqs;
int *select_network_scan_freqs;
int *manual_scan_freqs;
int *manual_sched_scan_freqs;
unsigned int manual_scan_passive:1;
unsigned int manual_scan_use_id:1;
unsigned int manual_scan_only_new:1;
unsigned int own_scan_requested:1;
unsigned int own_scan_running:1;
unsigned int clear_driver_scan_cache:1;
unsigned int manual_scan_id;
int scan_interval; /* time in sec between scans to find suitable AP */
int normal_scans; /* normal scans run before sched_scan */
int scan_for_connection; /* whether the scan request was triggered for
* finding a connection */
/*
* A unique cookie representing the vendor scan request. This cookie is
* returned from the driver interface. 0 indicates that there is no
* pending vendor scan request.
*/
u64 curr_scan_cookie;
#define MAX_SCAN_ID 16
int scan_id[MAX_SCAN_ID];
unsigned int scan_id_count;
u8 next_scan_bssid[ETH_ALEN];
unsigned int next_scan_bssid_wildcard_ssid:1;
struct wpa_ssid_value *ssids_from_scan_req;
unsigned int num_ssids_from_scan_req;
int *last_scan_freqs;
unsigned int num_last_scan_freqs;
unsigned int suitable_network;
unsigned int no_suitable_network;
u64 drv_flags;
u64 drv_flags2;
unsigned int drv_enc;
unsigned int drv_rrm_flags;
/*
* A bitmap of supported protocols for probe response offload. See
* struct wpa_driver_capa in driver.h
*/
unsigned int probe_resp_offloads;
/* extended capabilities supported by the driver */
const u8 *extended_capa, *extended_capa_mask;
unsigned int extended_capa_len;
int max_scan_ssids;
int max_sched_scan_ssids;
unsigned int max_sched_scan_plans;
unsigned int max_sched_scan_plan_interval;
unsigned int max_sched_scan_plan_iterations;
int sched_scan_supported;
unsigned int max_match_sets;
unsigned int max_remain_on_chan;
unsigned int max_stations;
int pending_mic_error_report;
int pending_mic_error_pairwise;
int mic_errors_seen; /* Michael MIC errors with the current PTK */
struct wps_context *wps;
int wps_success; /* WPS success event received */
struct wps_er *wps_er;
unsigned int wps_run;
struct os_reltime wps_pin_start_time;
bool bssid_ignore_cleared;
struct wpabuf *pending_eapol_rx;
struct os_reltime pending_eapol_rx_time;
u8 pending_eapol_rx_src[ETH_ALEN];
unsigned int last_eapol_matches_bssid:1;
unsigned int eap_expected_failure:1;
unsigned int reattach:1; /* reassociation to the same BSS requested */
unsigned int mac_addr_changed:1;
unsigned int added_vif:1;
unsigned int wnmsleep_used:1;
unsigned int owe_transition_select:1;
unsigned int owe_transition_search:1;
unsigned int connection_set:1;
unsigned int connection_ht:1;
unsigned int connection_vht:1;
unsigned int connection_he:1;
unsigned int disable_mbo_oce:1;
struct os_reltime last_mac_addr_change;
int last_mac_addr_style;
struct ibss_rsn *ibss_rsn;
int set_sta_uapsd;
int sta_uapsd;
int set_ap_uapsd;
int ap_uapsd;
int auth_alg;
u16 last_owe_group;
#ifdef CONFIG_SME
struct {
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
int freq;
u8 assoc_req_ie[1500];
size_t assoc_req_ie_len;
int mfp;
int ft_used;
u8 mobility_domain[2];
u8 *ft_ies;
size_t ft_ies_len;
u8 prev_bssid[ETH_ALEN];
int prev_bssid_set;
int auth_alg;
int proto;
int sa_query_count; /* number of pending SA Query requests;
* 0 = no SA Query in progress */
int sa_query_timed_out;
u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
* sa_query_count octets of pending
* SA Query transaction identifiers */
struct os_reltime sa_query_start;
struct os_reltime last_unprot_disconnect;
enum { HT_SEC_CHAN_UNKNOWN,
HT_SEC_CHAN_ABOVE,
HT_SEC_CHAN_BELOW } ht_sec_chan;
u8 sched_obss_scan;
u16 obss_scan_int;
u16 bss_max_idle_period;
#ifdef CONFIG_SAE
struct sae_data sae;
struct wpabuf *sae_token;
int sae_group_index;
unsigned int sae_pmksa_caching:1;
u16 seq_num;
u8 ext_auth_bssid[ETH_ALEN];
u8 ext_auth_ssid[SSID_MAX_LEN];
size_t ext_auth_ssid_len;
int *sae_rejected_groups;
#endif /* CONFIG_SAE */
} sme;
#endif /* CONFIG_SME */
#ifdef CONFIG_AP
struct hostapd_iface *ap_iface;
void (*ap_configured_cb)(void *ctx, void *data);
void *ap_configured_cb_ctx;
void *ap_configured_cb_data;
#endif /* CONFIG_AP */
struct hostapd_iface *ifmsh;
#ifdef CONFIG_MESH
struct mesh_rsn *mesh_rsn;
int mesh_if_idx;
unsigned int mesh_if_created:1;
unsigned int mesh_ht_enabled:1;
unsigned int mesh_vht_enabled:1;
unsigned int mesh_he_enabled:1;
struct wpa_driver_mesh_join_params *mesh_params;
#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
/* struct external_pmksa_cache::list */
struct dl_list mesh_external_pmksa_cache;
#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
#endif /* CONFIG_MESH */
unsigned int off_channel_freq;
struct wpabuf *pending_action_tx;
u8 pending_action_src[ETH_ALEN];
u8 pending_action_dst[ETH_ALEN];
u8 pending_action_bssid[ETH_ALEN];
unsigned int pending_action_freq;
int pending_action_no_cck;
int pending_action_without_roc;
unsigned int pending_action_tx_done:1;
void (*pending_action_tx_status_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);
unsigned int roc_waiting_drv_freq;
int action_tx_wait_time;
int action_tx_wait_time_used;
int p2p_mgmt;
#ifdef CONFIG_P2P
struct p2p_go_neg_results *go_params;
int create_p2p_iface;
u8 pending_interface_addr[ETH_ALEN];
char pending_interface_name[100];
int pending_interface_type;
int p2p_group_idx;
unsigned int pending_listen_freq;
unsigned int pending_listen_duration;
enum {
NOT_P2P_GROUP_INTERFACE,
P2P_GROUP_INTERFACE_PENDING,
P2P_GROUP_INTERFACE_GO,
P2P_GROUP_INTERFACE_CLIENT
} p2p_group_interface;
struct p2p_group *p2p_group;
char p2p_pin[10];
int p2p_wps_method;
u8 p2p_auth_invite[ETH_ALEN];
int p2p_sd_over_ctrl_iface;
int p2p_in_provisioning;
int p2p_in_invitation;
int p2p_invite_go_freq;
int pending_invite_ssid_id;
int show_group_started;
u8 go_dev_addr[ETH_ALEN];
int pending_pd_before_join;
u8 pending_join_iface_addr[ETH_ALEN];
u8 pending_join_dev_addr[ETH_ALEN];
int pending_join_wps_method;
u8 p2p_join_ssid[SSID_MAX_LEN];
size_t p2p_join_ssid_len;
int p2p_join_scan_count;
int auto_pd_scan_retry;
int force_long_sd;
u16 pending_pd_config_methods;
enum {
NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP
} pending_pd_use;
/*
* Whether cross connection is disallowed by the AP to which this
* interface is associated (only valid if there is an association).
*/
int cross_connect_disallowed;
/*
* Whether this P2P group is configured to use cross connection (only
* valid if this is P2P GO interface). The actual cross connect packet
* forwarding may not be configured depending on the uplink status.
*/
int cross_connect_enabled;
/* Whether cross connection forwarding is in use at the moment. */
int cross_connect_in_use;
/*
* Uplink interface name for cross connection
*/
char cross_connect_uplink[100];
unsigned int p2p_auto_join:1;
unsigned int p2p_auto_pd:1;
unsigned int p2p_go_do_acs:1;
unsigned int p2p_persistent_group:1;
unsigned int p2p_fallback_to_go_neg:1;
unsigned int p2p_pd_before_go_neg:1;
unsigned int p2p_go_ht40:1;
unsigned int p2p_go_vht:1;
unsigned int p2p_go_edmg:1;
unsigned int p2p_go_he:1;
unsigned int user_initiated_pd:1;
unsigned int p2p_go_group_formation_completed:1;
unsigned int group_formation_reported:1;
unsigned int waiting_presence_resp;
int p2p_first_connection_timeout;
unsigned int p2p_nfc_tag_enabled:1;
unsigned int p2p_peer_oob_pk_hash_known:1;
unsigned int p2p_disable_ip_addr_req:1;
unsigned int p2ps_method_config_any:1;
unsigned int p2p_cli_probe:1;
+ unsigned int p2p_go_allow_dfs:1;
enum hostapd_hw_mode p2p_go_acs_band;
int p2p_persistent_go_freq;
int p2p_persistent_id;
int p2p_go_intent;
int p2p_connect_freq;
struct os_reltime p2p_auto_started;
struct wpa_ssid *p2p_last_4way_hs_fail;
struct wpa_radio_work *p2p_scan_work;
struct wpa_radio_work *p2p_listen_work;
struct wpa_radio_work *p2p_send_action_work;
u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */
struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group
* formation */
u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
u8 p2p_ip_addr_info[3 * 4];
/* group common frequencies */
int *p2p_group_common_freqs;
unsigned int p2p_group_common_freqs_num;
u8 p2ps_join_addr[ETH_ALEN];
unsigned int p2p_go_max_oper_chwidth;
unsigned int p2p_go_vht_center_freq2;
int p2p_lo_started;
#endif /* CONFIG_P2P */
struct wpa_ssid *bgscan_ssid;
const struct bgscan_ops *bgscan;
void *bgscan_priv;
const struct autoscan_ops *autoscan;
struct wpa_driver_scan_params *autoscan_params;
void *autoscan_priv;
struct wpa_ssid *connect_without_scan;
struct wps_ap_info *wps_ap;
size_t num_wps_ap;
int wps_ap_iter;
int after_wps;
int known_wps_freq;
unsigned int wps_freq;
int wps_fragment_size;
int auto_reconnect_disabled;
/* Channel preferences for AP/P2P GO use */
int best_24_freq;
int best_5_freq;
int best_overall_freq;
struct gas_query *gas;
struct gas_server *gas_server;
#ifdef CONFIG_INTERWORKING
unsigned int fetch_anqp_in_progress:1;
unsigned int network_select:1;
unsigned int auto_select:1;
unsigned int auto_network_select:1;
unsigned int interworking_fast_assoc_tried:1;
unsigned int fetch_all_anqp:1;
unsigned int fetch_osu_info:1;
unsigned int fetch_osu_waiting_scan:1;
unsigned int fetch_osu_icon_in_progress:1;
struct wpa_bss *interworking_gas_bss;
unsigned int osu_icon_id;
struct dl_list icon_head; /* struct icon_entry */
struct osu_provider *osu_prov;
size_t osu_prov_count;
struct os_reltime osu_icon_fetch_start;
unsigned int num_osu_scans;
unsigned int num_prov_found;
#endif /* CONFIG_INTERWORKING */
unsigned int drv_capa_known;
struct {
struct hostapd_hw_modes *modes;
u16 num_modes;
u16 flags;
} hw;
enum local_hw_capab {
CAPAB_NO_HT_VHT,
CAPAB_HT,
CAPAB_HT40,
CAPAB_VHT,
} hw_capab;
#ifdef CONFIG_MACSEC
struct ieee802_1x_kay *kay;
#endif /* CONFIG_MACSEC */
int pno;
int pno_sched_pending;
/* WLAN_REASON_* reason codes. Negative if locally generated. */
int disconnect_reason;
/* WLAN_STATUS_* status codes from last received Authentication frame
* from the AP. */
u16 auth_status_code;
/* WLAN_STATUS_* status codes from (Re)Association Response frame. */
u16 assoc_status_code;
struct ext_password_data *ext_pw;
struct wpabuf *last_gas_resp, *prev_gas_resp;
u8 last_gas_addr[ETH_ALEN], prev_gas_addr[ETH_ALEN];
u8 last_gas_dialog_token, prev_gas_dialog_token;
unsigned int no_keep_alive:1;
unsigned int ext_mgmt_frame_handling:1;
unsigned int ext_eapol_frame_io:1;
unsigned int wmm_ac_supported:1;
unsigned int ext_work_in_progress:1;
unsigned int own_disconnect_req:1;
unsigned int own_reconnect_req:1;
unsigned int ignore_post_flush_scan_res:1;
#define MAC_ADDR_RAND_SCAN BIT(0)
#define MAC_ADDR_RAND_SCHED_SCAN BIT(1)
#define MAC_ADDR_RAND_PNO BIT(2)
#define MAC_ADDR_RAND_ALL (MAC_ADDR_RAND_SCAN | \
MAC_ADDR_RAND_SCHED_SCAN | \
MAC_ADDR_RAND_PNO)
unsigned int mac_addr_rand_supported;
unsigned int mac_addr_rand_enable;
/* MAC Address followed by mask (2 * ETH_ALEN) */
u8 *mac_addr_scan;
u8 *mac_addr_sched_scan;
u8 *mac_addr_pno;
#ifdef CONFIG_WNM
u8 wnm_dialog_token;
u8 wnm_reply;
u8 wnm_num_neighbor_report;
u8 wnm_mode;
u16 wnm_dissoc_timer;
u8 wnm_bss_termination_duration[12];
struct neighbor_report *wnm_neighbor_report_elements;
struct os_reltime wnm_cand_valid_until;
u8 wnm_cand_from_bss[ETH_ALEN];
enum bss_trans_mgmt_status_code bss_tm_status;
struct wpabuf *coloc_intf_elems;
u8 coloc_intf_dialog_token;
u8 coloc_intf_auto_report;
u8 coloc_intf_timeout;
#ifdef CONFIG_MBO
unsigned int wnm_mbo_trans_reason_present:1;
u8 wnm_mbo_transition_reason;
#endif /* CONFIG_MBO */
#endif /* CONFIG_WNM */
#ifdef CONFIG_TESTING_GET_GTK
u8 last_gtk[32];
size_t last_gtk_len;
#endif /* CONFIG_TESTING_GET_GTK */
unsigned int num_multichan_concurrent;
struct wpa_radio_work *connect_work;
unsigned int ext_work_id;
struct wpabuf *vendor_elem[NUM_VENDOR_ELEM_FRAMES];
#ifdef CONFIG_TESTING_OPTIONS
struct l2_packet_data *l2_test;
unsigned int extra_roc_dur;
enum wpa_supplicant_test_failure test_failure;
char *get_pref_freq_list_override;
unsigned int reject_btm_req_reason;
unsigned int p2p_go_csa_on_inv:1;
unsigned int ignore_auth_resp:1;
unsigned int ignore_assoc_disallow:1;
unsigned int disable_sa_query:1;
unsigned int testing_resend_assoc:1;
unsigned int ignore_sae_h2e_only:1;
int ft_rsnxe_used;
struct wpabuf *sae_commit_override;
enum wpa_alg last_tk_alg;
u8 last_tk_addr[ETH_ALEN];
int last_tk_key_idx;
u8 last_tk[WPA_TK_MAX_LEN];
size_t last_tk_len;
struct wpabuf *last_assoc_req_wpa_ie;
int *extra_sae_rejected_groups;
struct wpabuf *rsne_override_eapol;
struct wpabuf *rsnxe_override_assoc;
struct wpabuf *rsnxe_override_eapol;
struct dl_list drv_signal_override;
unsigned int oci_freq_override_eapol;
unsigned int oci_freq_override_saquery_req;
unsigned int oci_freq_override_saquery_resp;
unsigned int oci_freq_override_eapol_g2;
unsigned int oci_freq_override_ft_assoc;
unsigned int oci_freq_override_fils_assoc;
unsigned int oci_freq_override_wnm_sleep;
#endif /* CONFIG_TESTING_OPTIONS */
struct wmm_ac_assoc_data *wmm_ac_assoc_info;
struct wmm_tspec_element *tspecs[WMM_AC_NUM][TS_DIR_IDX_COUNT];
struct wmm_ac_addts_request *addts_request;
u8 wmm_ac_last_dialog_token;
struct wmm_tspec_element *last_tspecs;
u8 last_tspecs_count;
struct rrm_data rrm;
struct beacon_rep_data beacon_rep_data;
#ifdef CONFIG_FST
struct fst_iface *fst;
const struct wpabuf *fst_ies;
struct wpabuf *received_mb_ies;
#endif /* CONFIG_FST */
#ifdef CONFIG_MBO
/* Multiband operation non-preferred channel */
struct wpa_mbo_non_pref_channel {
enum mbo_non_pref_chan_reason reason;
u8 oper_class;
u8 chan;
u8 preference;
} *non_pref_chan;
size_t non_pref_chan_num;
u8 mbo_wnm_token;
/**
* enable_oce - Enable OCE if it is enabled by user and device also
* supports OCE.
* User can enable OCE with wpa_config's 'oce' parameter as follows -
* - Set BIT(0) to enable OCE in non-AP STA mode.
* - Set BIT(1) to enable OCE in STA-CFON mode.
*/
u8 enable_oce;
#endif /* CONFIG_MBO */
/*
* This should be under CONFIG_MBO, but it is left out to allow using
* the bss_temp_disallowed list for other purposes as well.
*/
struct dl_list bss_tmp_disallowed;
/*
* Content of a measurement report element with type 8 (LCI),
* own location.
*/
struct wpabuf *lci;
struct os_reltime lci_time;
struct os_reltime beacon_rep_scan;
/* FILS HLP requests (struct fils_hlp_req) */
struct dl_list fils_hlp_req;
struct sched_scan_relative_params {
/**
* relative_rssi_set - Enable relatively preferred BSS reporting
*
* 0 = Disable reporting relatively preferred BSSs
* 1 = Enable reporting relatively preferred BSSs
*/
int relative_rssi_set;
/**
* relative_rssi - Relative RSSI for reporting better BSSs
*
* Amount of RSSI by which a BSS should be better than the
* current connected BSS so that the new BSS can be reported
* to user space. This applies to sched_scan operations.
*/
int relative_rssi;
/**
* relative_adjust_band - Band in which RSSI is to be adjusted
*/
enum set_band relative_adjust_band;
/**
* relative_adjust_rssi - RSSI adjustment
*
* An amount of relative_adjust_rssi should be added to the
* BSSs that belong to the relative_adjust_band while comparing
* with other bands for BSS reporting.
*/
int relative_adjust_rssi;
} srp;
/* RIC elements for FT protocol */
struct wpabuf *ric_ies;
int last_auth_timeout_sec;
#ifdef CONFIG_DPP
struct dpp_global *dpp;
struct dpp_authentication *dpp_auth;
struct wpa_radio_work *dpp_listen_work;
unsigned int dpp_pending_listen_freq;
unsigned int dpp_listen_freq;
struct os_reltime dpp_listen_end;
u8 dpp_allowed_roles;
int dpp_qr_mutual;
int dpp_netrole;
int dpp_auth_ok_on_ack;
int dpp_in_response_listen;
int dpp_gas_client;
int dpp_gas_dialog_token;
u8 dpp_intro_bssid[ETH_ALEN];
void *dpp_intro_network;
struct dpp_pkex *dpp_pkex;
struct dpp_bootstrap_info *dpp_pkex_bi;
char *dpp_pkex_code;
char *dpp_pkex_identifier;
char *dpp_pkex_auth_cmd;
char *dpp_configurator_params;
struct os_reltime dpp_last_init;
struct os_reltime dpp_init_iter_start;
unsigned int dpp_init_max_tries;
unsigned int dpp_init_retry_time;
unsigned int dpp_resp_wait_time;
unsigned int dpp_resp_max_tries;
unsigned int dpp_resp_retry_time;
u8 dpp_last_ssid[SSID_MAX_LEN];
size_t dpp_last_ssid_len;
bool dpp_conf_backup_received;
#ifdef CONFIG_DPP2
struct dpp_pfs *dpp_pfs;
int dpp_pfs_fallback;
struct wpabuf *dpp_presence_announcement;
struct dpp_bootstrap_info *dpp_chirp_bi;
int dpp_chirp_freq;
int *dpp_chirp_freqs;
int dpp_chirp_iter;
int dpp_chirp_round;
int dpp_chirp_scan_done;
int dpp_chirp_listen;
struct wpa_ssid *dpp_reconfig_ssid;
int dpp_reconfig_ssid_id;
struct dpp_reconfig_id *dpp_reconfig_id;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
char *dpp_config_obj_override;
char *dpp_discovery_override;
char *dpp_groups_override;
unsigned int dpp_ignore_netaccesskey_mismatch:1;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_DPP */
#ifdef CONFIG_FILS
unsigned int disable_fils:1;
#endif /* CONFIG_FILS */
unsigned int ieee80211ac:1;
unsigned int enabled_4addr_mode:1;
unsigned int multi_bss_support:1;
unsigned int drv_authorized_port:1;
unsigned int multi_ap_ie:1;
unsigned int multi_ap_backhaul:1;
unsigned int multi_ap_fronthaul:1;
struct robust_av_data robust_av;
bool mscs_setup_done;
#ifdef CONFIG_PASN
struct wpas_pasn pasn;
struct wpa_radio_work *pasn_auth_work;
#endif /* CONFIG_PASN */
+ struct scs_robust_av_data scs_robust_av_req;
+ u8 scs_dialog_token;
+#ifdef CONFIG_TESTING_OPTIONS
+ unsigned int disable_scs_support:1;
+ unsigned int disable_mscs_support:1;
+#endif /* CONFIG_TESTING_OPTIONS */
+ struct dl_list active_scs_ids;
+ bool ongoing_scs_req;
+ u8 dscp_req_dialog_token;
+ u8 dscp_query_dialog_token;
+ unsigned int enable_dscp_policy_capa:1;
+ unsigned int connection_dscp:1;
+ unsigned int wait_for_dscp_req:1;
};
/* wpa_supplicant.c */
void wpa_supplicant_apply_ht_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params);
void wpa_supplicant_apply_vht_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params);
void wpa_supplicant_apply_he_overrides(
struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
struct wpa_driver_associate_params *params);
int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);
const char * wpa_supplicant_state_txt(enum wpa_states state);
int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s);
int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s);
int wpa_supplicant_update_bridge_ifname(struct wpa_supplicant *wpa_s,
const char *bridge_ifname);
+void wpas_set_mgmt_group_cipher(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, struct wpa_ie_data *ie);
int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, struct wpa_ssid *ssid,
u8 *wpa_ie, size_t *wpa_ie_len);
int wpas_restore_permanent_mac_addr(struct wpa_supplicant *wpa_s);
void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss,
struct wpa_ssid *ssid);
void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s);
void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr);
void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
int sec, int usec);
void wpas_auth_timeout_restart(struct wpa_supplicant *wpa_s, int sec_diff);
void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s);
void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
enum wpa_states state);
struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s);
const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s);
void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s);
void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
u16 reason_code);
void wpa_supplicant_reconnect(struct wpa_supplicant *wpa_s);
struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s);
int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id);
int wpa_supplicant_remove_all_networks(struct wpa_supplicant *wpa_s);
void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
const char *pkcs11_engine_path,
const char *pkcs11_module_path);
int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s,
int ap_scan);
int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
unsigned int expire_age);
int wpa_supplicant_set_bss_expiration_count(struct wpa_supplicant *wpa_s,
unsigned int expire_count);
int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s,
int scan_interval);
int wpa_supplicant_set_debug_params(struct wpa_global *global,
int debug_level, int debug_timestamp,
int debug_show_keys);
void free_hw_features(struct wpa_supplicant *wpa_s);
void wpa_show_license(void);
struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
const char *ifname);
struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
struct wpa_interface *iface,
struct wpa_supplicant *parent);
int wpa_supplicant_remove_iface(struct wpa_global *global,
struct wpa_supplicant *wpa_s,
int terminate);
struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
const char *ifname);
struct wpa_global * wpa_supplicant_init(struct wpa_params *params);
int wpa_supplicant_run(struct wpa_global *global);
void wpa_supplicant_deinit(struct wpa_global *global);
int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpa_supplicant_terminate_proc(struct wpa_global *global);
void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
const u8 *buf, size_t len);
void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
void fils_connection_failure(struct wpa_supplicant *wpa_s);
void fils_pmksa_cache_flush(struct wpa_supplicant *wpa_s);
int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason);
void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int clear_failures);
int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid);
int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
size_t ssid_len);
void wpas_request_connection(struct wpa_supplicant *wpa_s);
void wpas_request_disconnection(struct wpa_supplicant *wpa_s);
int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
void add_freq(int *freqs, int *num_freqs, int freq);
int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len,
u8 *op_class, u8 *chan, u8 *phy_type);
int wpas_twt_send_setup(struct wpa_supplicant *wpa_s, u8 dtok, int exponent,
int mantissa, u8 min_twt, int setup_cmd, u64 twt,
bool requestor, bool trigger, bool implicit,
bool flow_type, u8 flow_id, bool protection,
u8 twt_channel, u8 control);
int wpas_twt_send_teardown(struct wpa_supplicant *wpa_s, u8 flags);
void wpas_rrm_reset(struct wpa_supplicant *wpa_s);
void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
const u8 *report, size_t report_len);
int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
const struct wpa_ssid_value *ssid,
int lci, int civic,
void (*cb)(void *ctx,
struct wpabuf *neighbor_rep),
void *cb_ctx);
void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
const u8 *src, const u8 *dst,
const u8 *frame, size_t len);
void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
const u8 *src,
const u8 *frame, size_t len,
int rssi);
void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s);
int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res,
struct scan_info *info);
void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s);
void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s);
void wpas_clear_disabled_interface(void *eloop_ctx, void *timeout_ctx);
void wpa_supplicant_reset_bgscan(struct wpa_supplicant *wpa_s);
/* MBO functions */
int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len,
int add_oce_capa);
const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr);
const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr);
void wpas_mbo_check_pmf(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
struct wpa_ssid *ssid);
const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len,
enum mbo_attr_id attr);
int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
const char *non_pref_chan);
void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie);
void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie,
size_t len);
size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
size_t len,
enum mbo_transition_reject_reason reason);
void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa);
struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, u32 mbo_subtypes);
void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, const u8 *sa,
const u8 *data, size_t slen);
void wpas_update_mbo_connect_params(struct wpa_supplicant *wpa_s);
/* op_classes.c */
enum chan_allowed {
- NOT_ALLOWED, NO_IR, ALLOWED
+ NOT_ALLOWED, NO_IR, RADAR, ALLOWED
};
enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class,
u8 channel, u8 bw);
size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
struct wpa_bss *bss, u8 *pos, size_t len);
int * wpas_supp_op_classes(struct wpa_supplicant *wpa_s);
int wpas_enable_mac_addr_randomization(struct wpa_supplicant *wpa_s,
unsigned int type, const u8 *addr,
const u8 *mask);
int wpas_disable_mac_addr_randomization(struct wpa_supplicant *wpa_s,
unsigned int type);
/**
* wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
* @wpa_s: Pointer to wpa_supplicant data
* @ssid: Pointer to the network block the reply is for
* @field: field the response is a reply for
* @value: value (ie, password, etc) for @field
* Returns: 0 on success, non-zero on error
*
* Helper function to handle replies to control interface requests.
*/
int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid,
const char *field,
const char *value);
void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
const struct wpa_ssid *ssid,
struct hostapd_freq_params *freq);
/* events.c */
void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
struct wpa_bss *selected,
struct wpa_ssid *ssid);
void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx);
void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx);
void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s);
int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid **selected_ssid);
int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
void wpa_supplicant_update_channel_list(struct wpa_supplicant *wpa_s,
struct channel_list_changed *info);
int wpa_supplicant_need_to_roam_within_ess(struct wpa_supplicant *wpa_s,
struct wpa_bss *current_bss,
struct wpa_bss *seleceted);
/* eap_register.c */
int eap_register_methods(void);
/**
* Utility method to tell if a given network is for persistent group storage
* @ssid: Network object
* Returns: 1 if network is a persistent group, 0 otherwise
*/
static inline int network_is_persistent_group(struct wpa_ssid *ssid)
{
return ssid->disabled == 2 && ssid->p2p_persistent_group;
}
static inline int wpas_mode_to_ieee80211_mode(enum wpas_mode mode)
{
switch (mode) {
default:
case WPAS_MODE_INFRA:
return IEEE80211_MODE_INFRA;
case WPAS_MODE_IBSS:
return IEEE80211_MODE_IBSS;
case WPAS_MODE_AP:
case WPAS_MODE_P2P_GO:
case WPAS_MODE_P2P_GROUP_FORMATION:
return IEEE80211_MODE_AP;
case WPAS_MODE_MESH:
return IEEE80211_MODE_MESH;
}
}
int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr);
int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
struct wpa_used_freq_data *freqs_data,
unsigned int len);
int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
struct wpa_used_freq_data *freqs_data,
unsigned int len);
int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
int *freq_array, unsigned int len);
void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s);
struct wpa_supplicant * wpas_vendor_elem(struct wpa_supplicant *wpa_s,
enum wpa_vendor_elem_frame frame);
int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame,
const u8 *elem, size_t len);
#ifdef CONFIG_FST
struct fst_wpa_obj;
void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
struct fst_wpa_obj *iface_obj);
#endif /* CONFIG_FST */
int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd);
struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
u16 num_modes, enum hostapd_hw_mode mode,
bool is_6ghz);
struct hostapd_hw_modes * get_mode_with_freq(struct hostapd_hw_modes *modes,
u16 num_modes, int freq);
void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
unsigned int sec, int rssi_threshold);
int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss);
void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s);
struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int i, struct wpa_bss *bss,
struct wpa_ssid *group,
int only_first_ssid, int debug_print);
int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
enum wpa_driver_if_type if_type,
unsigned int *num,
unsigned int *freq_list);
int wpa_is_fils_supported(struct wpa_supplicant *wpa_s);
int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s);
void wpas_clear_driver_signal_override(struct wpa_supplicant *wpa_s);
int wpas_send_mscs_req(struct wpa_supplicant *wpa_s);
void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
struct wpabuf *buf);
void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
const u8 *src, const u8 *buf,
size_t len);
void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
const u8 *ies, size_t ies_len);
+int wpas_send_scs_req(struct wpa_supplicant *wpa_s);
+void free_up_tclas_elem(struct scs_desc_elem *elem);
+void free_up_scs_desc(struct scs_robust_av_data *data);
+void wpas_handle_robust_av_scs_recv_action(struct wpa_supplicant *wpa_s,
+ const u8 *src, const u8 *buf,
+ size_t len);
+void wpas_scs_deinit(struct wpa_supplicant *wpa_s);
+void wpas_handle_qos_mgmt_recv_action(struct wpa_supplicant *wpa_s,
+ const u8 *src,
+ const u8 *buf, size_t len);
+void wpas_dscp_deinit(struct wpa_supplicant *wpa_s);
+int wpas_send_dscp_response(struct wpa_supplicant *wpa_s,
+ struct dscp_resp_data *resp_data);
+void wpas_handle_assoc_resp_qos_mgmt(struct wpa_supplicant *wpa_s,
+ const u8 *ies, size_t ies_len);
+int wpas_send_dscp_query(struct wpa_supplicant *wpa_s, const char *domain_name,
+ size_t domain_name_length);
int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
const u8 *bssid, int akmp, int cipher,
u16 group, int network_id,
const u8 *comeback, size_t comeback_len);
void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s);
int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s,
const u8 *data, size_t data_len, u8 acked);
int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
const struct ieee80211_mgmt *mgmt, size_t len);
int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *bssid);
#endif /* WPA_SUPPLICANT_I_H */
diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.c b/contrib/wpa/wpa_supplicant/wpas_glue.c
index 96818697882f..17fc05bcbdab 100644
--- a/contrib/wpa/wpa_supplicant/wpas_glue.c
+++ b/contrib/wpa/wpa_supplicant/wpas_glue.c
@@ -1,1492 +1,1494 @@
/*
* WPA Supplicant - Glue code to setup EAPOL and RSN modules
* Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
#include "common.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "eap_peer/eap.h"
#include "rsn_supp/wpa.h"
#include "eloop.h"
#include "config.h"
#include "l2_packet/l2_packet.h"
#include "common/wpa_common.h"
#include "common/ptksa_cache.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "rsn_supp/pmksa_cache.h"
#include "sme.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "wpas_glue.h"
#include "wps_supplicant.h"
#include "bss.h"
#include "scan.h"
#include "notify.h"
#include "wpas_kay.h"
#ifndef CONFIG_NO_CONFIG_BLOBS
#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA)
static void wpa_supplicant_set_config_blob(void *ctx,
struct wpa_config_blob *blob)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_config_set_blob(wpa_s->conf, blob);
if (wpa_s->conf->update_config) {
int ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
if (ret) {
wpa_printf(MSG_DEBUG, "Failed to update config after "
"blob set");
}
}
}
static const struct wpa_config_blob *
wpa_supplicant_get_config_blob(void *ctx, const char *name)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_config_get_blob(wpa_s->conf, name);
}
#endif /* defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) */
#endif /* CONFIG_NO_CONFIG_BLOBS */
#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA)
static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
const void *data, u16 data_len,
size_t *msg_len, void **data_pos)
{
struct ieee802_1x_hdr *hdr;
*msg_len = sizeof(*hdr) + data_len;
hdr = os_malloc(*msg_len);
if (hdr == NULL)
return NULL;
hdr->version = wpa_s->conf->eapol_version;
hdr->type = type;
hdr->length = host_to_be16(data_len);
if (data)
os_memcpy(hdr + 1, data, data_len);
else
os_memset(hdr + 1, 0, data_len);
if (data_pos)
*data_pos = hdr + 1;
return (u8 *) hdr;
}
/**
* wpa_ether_send - Send Ethernet frame
* @wpa_s: Pointer to wpa_supplicant data
* @dest: Destination MAC address
* @proto: Ethertype in host byte order
* @buf: Frame payload starting from IEEE 802.1X header
* @len: Frame payload length
* Returns: >=0 on success, <0 on failure
*/
int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest,
u16 proto, const u8 *buf, size_t len)
{
#ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
size_t hex_len = 2 * len + 1;
char *hex = os_malloc(hex_len);
if (hex == NULL)
return -1;
wpa_snprintf_hex(hex, hex_len, buf, len);
wpa_msg(wpa_s, MSG_INFO, "EAPOL-TX " MACSTR " %s",
MAC2STR(dest), hex);
os_free(hex);
return 0;
}
#endif /* CONFIG_TESTING_OPTIONS */
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_CONTROL_PORT) {
int encrypt = wpa_s->wpa &&
wpa_sm_has_ptk_installed(wpa_s->wpa);
return wpa_drv_tx_control_port(wpa_s, dest, proto, buf, len,
!encrypt);
}
if (wpa_s->l2) {
return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
}
return -1;
}
#endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */
#ifdef IEEE8021X_EAPOL
/**
* wpa_supplicant_eapol_send - Send IEEE 802.1X EAPOL packet to Authenticator
* @ctx: Pointer to wpa_supplicant data (wpa_s)
* @type: IEEE 802.1X packet type (IEEE802_1X_TYPE_*)
* @buf: EAPOL payload (after IEEE 802.1X header)
* @len: EAPOL payload length
* Returns: >=0 on success, <0 on failure
*
* This function adds Ethernet and IEEE 802.1X header and sends the EAPOL frame
* to the current Authenticator.
*/
static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf,
size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
u8 *msg, *dst, bssid[ETH_ALEN];
size_t msglen;
int res;
/* TODO: could add l2_packet_sendmsg that allows fragments to avoid
* extra copy here */
if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
wpa_s->key_mgmt == WPA_KEY_MGMT_OWE ||
wpa_s->key_mgmt == WPA_KEY_MGMT_DPP ||
wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
/* Current SSID is not using IEEE 802.1X/EAP, so drop possible
* EAPOL frames (mainly, EAPOL-Start) from EAPOL state
* machines. */
wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X "
"mode (type=%d len=%lu)", type,
(unsigned long) len);
return -1;
}
if (pmksa_cache_get_current(wpa_s->wpa) &&
type == IEEE802_1X_TYPE_EAPOL_START) {
/*
* We were trying to use PMKSA caching and sending EAPOL-Start
* would abort that and trigger full EAPOL authentication.
* However, we've already waited for the AP/Authenticator to
* start 4-way handshake or EAP authentication, and apparently
* it has not done so since the startWhen timer has reached zero
* to get the state machine sending EAPOL-Start. This is not
* really supposed to happen, but an interoperability issue with
* a deployed AP has been identified where the connection fails
* due to that AP failing to operate correctly if PMKID is
* included in the Association Request frame. To work around
* this, assume PMKSA caching failed and try to initiate full
* EAP authentication.
*/
if (!wpa_s->current_ssid ||
wpa_s->current_ssid->eap_workaround) {
wpa_printf(MSG_DEBUG,
"RSN: Timeout on waiting for the AP to initiate 4-way handshake for PMKSA caching or EAP authentication - try to force it to start EAP authentication");
} else {
wpa_printf(MSG_DEBUG,
"RSN: PMKSA caching - do not send EAPOL-Start");
return -1;
}
}
if (is_zero_ether_addr(wpa_s->bssid)) {
wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an "
"EAPOL frame");
if (wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
!is_zero_ether_addr(bssid)) {
dst = bssid;
wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR
" from the driver as the EAPOL destination",
MAC2STR(dst));
} else {
dst = wpa_s->last_eapol_src;
wpa_printf(MSG_DEBUG, "Using the source address of the"
" last received EAPOL frame " MACSTR " as "
"the EAPOL destination",
MAC2STR(dst));
}
} else {
/* BSSID was already set (from (Re)Assoc event, so use it as
* the EAPOL destination. */
dst = wpa_s->bssid;
}
msg = wpa_alloc_eapol(wpa_s, type, buf, len, &msglen, NULL);
if (msg == NULL)
return -1;
wpa_printf(MSG_DEBUG, "TX EAPOL: dst=" MACSTR, MAC2STR(dst));
wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen);
res = wpa_ether_send(wpa_s, dst, ETH_P_EAPOL, msg, msglen);
os_free(msg);
return res;
}
#ifdef CONFIG_WEP
/**
* wpa_eapol_set_wep_key - set WEP key for the driver
* @ctx: Pointer to wpa_supplicant data (wpa_s)
* @unicast: 1 = individual unicast key, 0 = broadcast key
* @keyidx: WEP key index (0..3)
* @key: Pointer to key data
* @keylen: Key length in bytes
* Returns: 0 on success or < 0 on error.
*/
static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx,
const u8 *key, size_t keylen)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
int cipher = (keylen == 5) ? WPA_CIPHER_WEP40 :
WPA_CIPHER_WEP104;
if (unicast)
wpa_s->pairwise_cipher = cipher;
else
wpa_s->group_cipher = cipher;
}
return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
unicast ? wpa_s->bssid : NULL,
keyidx, unicast, NULL, 0, key, keylen,
unicast ? KEY_FLAG_PAIRWISE_RX_TX :
KEY_FLAG_GROUP_RX_TX_DEFAULT);
}
#endif /* CONFIG_WEP */
static void wpa_supplicant_aborted_cached(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_sm_aborted_cached(wpa_s->wpa);
}
static const char * result_str(enum eapol_supp_result result)
{
switch (result) {
case EAPOL_SUPP_RESULT_FAILURE:
return "FAILURE";
case EAPOL_SUPP_RESULT_SUCCESS:
return "SUCCESS";
case EAPOL_SUPP_RESULT_EXPECTED_FAILURE:
return "EXPECTED_FAILURE";
}
return "?";
}
static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol,
enum eapol_supp_result result,
void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
int res, pmk_len;
u8 pmk[PMK_LEN];
wpa_printf(MSG_DEBUG, "EAPOL authentication completed - result=%s",
result_str(result));
if (wpas_wps_eapol_cb(wpa_s) > 0)
return;
wpa_s->eap_expected_failure = result ==
EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
if (result != EAPOL_SUPP_RESULT_SUCCESS) {
/*
* Make sure we do not get stuck here waiting for long EAPOL
* timeout if the AP does not disconnect in case of
* authentication failure.
*/
wpa_supplicant_req_auth_timeout(wpa_s, 2, 0);
} else {
ieee802_1x_notify_create_actor(wpa_s, wpa_s->last_eapol_src);
}
if (result != EAPOL_SUPP_RESULT_SUCCESS ||
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X))
return;
if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt))
return;
wpa_printf(MSG_DEBUG, "Configure PMK for driver-based RSN 4-way "
"handshake");
pmk_len = PMK_LEN;
if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) {
#ifdef CONFIG_IEEE80211R
u8 buf[2 * PMK_LEN];
wpa_printf(MSG_DEBUG, "RSN: Use FT XXKey as PMK for "
"driver-based 4-way hs and FT");
res = eapol_sm_get_key(eapol, buf, 2 * PMK_LEN);
if (res == 0) {
os_memcpy(pmk, buf + PMK_LEN, PMK_LEN);
os_memset(buf, 0, sizeof(buf));
}
#else /* CONFIG_IEEE80211R */
res = -1;
#endif /* CONFIG_IEEE80211R */
} else {
res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
if (res) {
/*
* EAP-LEAP is an exception from other EAP methods: it
* uses only 16-byte PMK.
*/
res = eapol_sm_get_key(eapol, pmk, 16);
pmk_len = 16;
}
}
if (res) {
wpa_printf(MSG_DEBUG, "Failed to get PMK from EAPOL state "
"machines");
return;
}
wpa_hexdump_key(MSG_DEBUG, "RSN: Configure PMK for driver-based 4-way "
"handshake", pmk, pmk_len);
if (wpa_drv_set_key(wpa_s, 0, NULL, 0, 0, NULL, 0, pmk,
pmk_len, KEY_FLAG_PMK)) {
wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
}
wpa_supplicant_cancel_scan(wpa_s);
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
}
static void wpa_supplicant_notify_eapol_done(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete");
if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
wpa_supplicant_set_state(wpa_s, WPA_4WAY_HANDSHAKE);
} else {
wpa_supplicant_cancel_auth_timeout(wpa_s);
wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
}
}
#endif /* IEEE8021X_EAPOL */
#ifndef CONFIG_NO_WPA
static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s)
{
int ret = 0;
struct wpa_bss *curr = NULL, *bss;
struct wpa_ssid *ssid = wpa_s->current_ssid;
const u8 *ie;
dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
if (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) != 0)
continue;
if (ssid == NULL ||
((bss->ssid_len == ssid->ssid_len &&
os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) == 0) ||
ssid->ssid_len == 0)) {
curr = bss;
break;
}
#ifdef CONFIG_OWE
if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
(bss->flags & WPA_BSS_OWE_TRANSITION)) {
curr = bss;
break;
}
#endif /* CONFIG_OWE */
}
if (curr) {
ie = wpa_bss_get_vendor_ie(curr, WPA_IE_VENDOR_TYPE);
if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
ret = -1;
ie = wpa_bss_get_ie(curr, WLAN_EID_RSN);
if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
ret = -1;
ie = wpa_bss_get_ie(curr, WLAN_EID_RSNX);
if (wpa_sm_set_ap_rsnxe(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
ret = -1;
} else {
ret = -1;
}
return ret;
}
static int wpa_supplicant_get_beacon_ie(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_get_beacon_ie(wpa_s) == 0) {
return 0;
}
/* No WPA/RSN IE found in the cached scan results. Try to get updated
* scan results from the driver. */
if (wpa_supplicant_update_scan_results(wpa_s) < 0)
return -1;
return wpa_get_beacon_ie(wpa_s);
}
static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type,
const void *data, u16 data_len,
size_t *msg_len, void **data_pos)
{
return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos);
}
static int _wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto,
const u8 *buf, size_t len)
{
return wpa_ether_send(wpa_s, dest, proto, buf, len);
}
static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s)
{
wpa_supplicant_cancel_auth_timeout(wpa_s);
}
static void _wpa_supplicant_set_state(void *wpa_s, enum wpa_states state)
{
wpa_supplicant_set_state(wpa_s, state);
}
/**
* wpa_supplicant_get_state - Get the connection state
* @wpa_s: Pointer to wpa_supplicant data
* Returns: The current connection state (WPA_*)
*/
static enum wpa_states wpa_supplicant_get_state(struct wpa_supplicant *wpa_s)
{
return wpa_s->wpa_state;
}
static enum wpa_states _wpa_supplicant_get_state(void *wpa_s)
{
return wpa_supplicant_get_state(wpa_s);
}
static void _wpa_supplicant_deauthenticate(void *wpa_s, u16 reason_code)
{
wpa_supplicant_deauthenticate(wpa_s, reason_code);
/* Schedule a scan to make sure we continue looking for networks */
wpa_supplicant_req_scan(wpa_s, 5, 0);
}
static void _wpa_supplicant_reconnect(void *wpa_s)
{
wpa_supplicant_reconnect(wpa_s);
}
static void * wpa_supplicant_get_network_ctx(void *wpa_s)
{
return wpa_supplicant_get_ssid(wpa_s);
}
static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_drv_get_bssid(wpa_s, bssid);
}
static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
const u8 *key, size_t key_len,
enum key_flag key_flag)
{
struct wpa_supplicant *wpa_s = _wpa_s;
if (alg == WPA_ALG_TKIP && key_idx == 0 && key_len == 32) {
/* Clear the MIC error counter when setting a new PTK. */
wpa_s->mic_errors_seen = 0;
}
#ifdef CONFIG_TESTING_GET_GTK
if (key_idx > 0 && addr && is_broadcast_ether_addr(addr) &&
alg != WPA_ALG_NONE && key_len <= sizeof(wpa_s->last_gtk)) {
os_memcpy(wpa_s->last_gtk, key, key_len);
wpa_s->last_gtk_len = key_len;
}
#endif /* CONFIG_TESTING_GET_GTK */
#ifdef CONFIG_TESTING_OPTIONS
if (addr && !is_broadcast_ether_addr(addr) &&
!(key_flag & KEY_FLAG_MODIFY)) {
wpa_s->last_tk_alg = alg;
os_memcpy(wpa_s->last_tk_addr, addr, ETH_ALEN);
wpa_s->last_tk_key_idx = key_idx;
if (key)
os_memcpy(wpa_s->last_tk, key, key_len);
wpa_s->last_tk_len = key_len;
}
#endif /* CONFIG_TESTING_OPTIONS */
return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
key, key_len, key_flag);
}
static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
int protection_type,
int key_type)
{
return wpa_drv_mlme_setprotection(wpa_s, addr, protection_type,
key_type);
}
static struct wpa_ssid * wpas_get_network_ctx(struct wpa_supplicant *wpa_s,
void *network_ctx)
{
struct wpa_ssid *ssid;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
if (network_ctx == ssid)
return ssid;
}
return NULL;
}
static int wpa_supplicant_add_pmkid(void *_wpa_s, void *network_ctx,
const u8 *bssid, const u8 *pmkid,
const u8 *fils_cache_id,
const u8 *pmk, size_t pmk_len,
u32 pmk_lifetime, u8 pmk_reauth_threshold,
int akmp)
{
struct wpa_supplicant *wpa_s = _wpa_s;
struct wpa_ssid *ssid;
struct wpa_pmkid_params params;
os_memset(&params, 0, sizeof(params));
ssid = wpas_get_network_ctx(wpa_s, network_ctx);
if (ssid) {
wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_ADDED MACSTR " %d",
MAC2STR(bssid), ssid->id);
if ((akmp == WPA_KEY_MGMT_FT_IEEE8021X ||
akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) &&
!ssid->ft_eap_pmksa_caching) {
/* Since we will not be using PMKSA caching for FT-EAP
* within wpa_supplicant to avoid known interop issues
* with APs, do not add this PMKID to the driver either
* so that we won't be hitting those interop issues
* with driver-based RSNE generation. */
wpa_printf(MSG_DEBUG,
"FT: Do not add PMKID entry to the driver since FT-EAP PMKSA caching is not enabled in configuration");
return 0;
}
}
if (ssid && fils_cache_id) {
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
params.fils_cache_id = fils_cache_id;
} else {
params.bssid = bssid;
}
params.pmkid = pmkid;
params.pmk = pmk;
params.pmk_len = pmk_len;
params.pmk_lifetime = pmk_lifetime;
params.pmk_reauth_threshold = pmk_reauth_threshold;
return wpa_drv_add_pmkid(wpa_s, &params);
}
static int wpa_supplicant_remove_pmkid(void *_wpa_s, void *network_ctx,
const u8 *bssid, const u8 *pmkid,
const u8 *fils_cache_id)
{
struct wpa_supplicant *wpa_s = _wpa_s;
struct wpa_ssid *ssid;
struct wpa_pmkid_params params;
os_memset(&params, 0, sizeof(params));
ssid = wpas_get_network_ctx(wpa_s, network_ctx);
if (ssid)
wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_REMOVED MACSTR " %d",
MAC2STR(bssid), ssid->id);
if (ssid && fils_cache_id) {
params.ssid = ssid->ssid;
params.ssid_len = ssid->ssid_len;
params.fils_cache_id = fils_cache_id;
} else {
params.bssid = bssid;
}
params.pmkid = pmkid;
return wpa_drv_remove_pmkid(wpa_s, &params);
}
#ifdef CONFIG_IEEE80211R
static int wpa_supplicant_update_ft_ies(void *ctx, const u8 *md,
const u8 *ies, size_t ies_len)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
return sme_update_ft_ies(wpa_s, md, ies, ies_len);
return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len);
}
static int wpa_supplicant_send_ft_action(void *ctx, u8 action,
const u8 *target_ap,
const u8 *ies, size_t ies_len)
{
struct wpa_supplicant *wpa_s = ctx;
int ret;
u8 *data, *pos;
size_t data_len;
if (action != 1) {
wpa_printf(MSG_ERROR, "Unsupported send_ft_action action %d",
action);
return -1;
}
/*
* Action frame payload:
* Category[1] = 6 (Fast BSS Transition)
* Action[1] = 1 (Fast BSS Transition Request)
* STA Address
* Target AP Address
* FT IEs
*/
data_len = 2 + 2 * ETH_ALEN + ies_len;
data = os_malloc(data_len);
if (data == NULL)
return -1;
pos = data;
*pos++ = 0x06; /* FT Action category */
*pos++ = action;
os_memcpy(pos, wpa_s->own_addr, ETH_ALEN);
pos += ETH_ALEN;
os_memcpy(pos, target_ap, ETH_ALEN);
pos += ETH_ALEN;
os_memcpy(pos, ies, ies_len);
ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0,
wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid,
data, data_len, 0);
os_free(data);
return ret;
}
static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_driver_auth_params params;
struct wpa_bss *bss;
bss = wpa_bss_get_bssid(wpa_s, target_ap);
if (bss == NULL)
return -1;
os_memset(&params, 0, sizeof(params));
params.bssid = target_ap;
params.freq = bss->freq;
params.ssid = bss->ssid;
params.ssid_len = bss->ssid_len;
params.auth_alg = WPA_AUTH_ALG_FT;
params.local_state_change = 1;
return wpa_drv_authenticate(wpa_s, &params);
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_TDLS
static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
int *tdls_ext_setup,
int *tdls_chan_switch)
{
struct wpa_supplicant *wpa_s = ctx;
*tdls_supported = 0;
*tdls_ext_setup = 0;
*tdls_chan_switch = 0;
if (!wpa_s->drv_capa_known)
return -1;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)
*tdls_supported = 1;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP)
*tdls_ext_setup = 1;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH)
*tdls_chan_switch = 1;
return 0;
}
static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst,
u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capab,
int initiator, const u8 *buf,
size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token,
status_code, peer_capab, initiator, buf,
len);
}
static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *peer)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_drv_tdls_oper(wpa_s, oper, peer);
}
static int wpa_supplicant_tdls_peer_addset(
void *ctx, const u8 *peer, int add, u16 aid, u16 capability,
const u8 *supp_rates, size_t supp_rates_len,
const struct ieee80211_ht_capabilities *ht_capab,
const struct ieee80211_vht_capabilities *vht_capab,
const struct ieee80211_he_capabilities *he_capab,
size_t he_capab_len,
+ const struct ieee80211_he_6ghz_band_cap *he_6ghz_he_capab,
u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len,
const u8 *supp_channels, size_t supp_channels_len,
const u8 *supp_oper_classes, size_t supp_oper_classes_len)
{
struct wpa_supplicant *wpa_s = ctx;
struct hostapd_sta_add_params params;
os_memset(&params, 0, sizeof(params));
params.addr = peer;
params.aid = aid;
params.capability = capability;
params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED;
/*
* Don't rely only on qosinfo for WMM capability. It may be 0 even when
* present. Allow the WMM IE to also indicate QoS support.
*/
if (wmm || qosinfo)
params.flags |= WPA_STA_WMM;
params.ht_capabilities = ht_capab;
params.vht_capabilities = vht_capab;
params.he_capab = he_capab;
params.he_capab_len = he_capab_len;
+ params.he_6ghz_capab = he_6ghz_he_capab;
params.qosinfo = qosinfo;
params.listen_interval = 0;
params.supp_rates = supp_rates;
params.supp_rates_len = supp_rates_len;
params.set = !add;
params.ext_capab = ext_capab;
params.ext_capab_len = ext_capab_len;
params.supp_channels = supp_channels;
params.supp_channels_len = supp_channels_len;
params.supp_oper_classes = supp_oper_classes;
params.supp_oper_classes_len = supp_oper_classes_len;
return wpa_drv_sta_add(wpa_s, &params);
}
static int wpa_supplicant_tdls_enable_channel_switch(
void *ctx, const u8 *addr, u8 oper_class,
const struct hostapd_freq_params *params)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_drv_tdls_enable_channel_switch(wpa_s, addr, oper_class,
params);
}
static int wpa_supplicant_tdls_disable_channel_switch(void *ctx, const u8 *addr)
{
struct wpa_supplicant *wpa_s = ctx;
return wpa_drv_tdls_disable_channel_switch(wpa_s, addr);
}
#endif /* CONFIG_TDLS */
#endif /* CONFIG_NO_WPA */
enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field)
{
if (os_strcmp(field, "IDENTITY") == 0)
return WPA_CTRL_REQ_EAP_IDENTITY;
else if (os_strcmp(field, "PASSWORD") == 0)
return WPA_CTRL_REQ_EAP_PASSWORD;
else if (os_strcmp(field, "NEW_PASSWORD") == 0)
return WPA_CTRL_REQ_EAP_NEW_PASSWORD;
else if (os_strcmp(field, "PIN") == 0)
return WPA_CTRL_REQ_EAP_PIN;
else if (os_strcmp(field, "OTP") == 0)
return WPA_CTRL_REQ_EAP_OTP;
else if (os_strcmp(field, "PASSPHRASE") == 0)
return WPA_CTRL_REQ_EAP_PASSPHRASE;
else if (os_strcmp(field, "SIM") == 0)
return WPA_CTRL_REQ_SIM;
else if (os_strcmp(field, "PSK_PASSPHRASE") == 0)
return WPA_CTRL_REQ_PSK_PASSPHRASE;
else if (os_strcmp(field, "EXT_CERT_CHECK") == 0)
return WPA_CTRL_REQ_EXT_CERT_CHECK;
return WPA_CTRL_REQ_UNKNOWN;
}
const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
const char *default_txt,
const char **txt)
{
const char *ret = NULL;
*txt = default_txt;
switch (field) {
case WPA_CTRL_REQ_EAP_IDENTITY:
*txt = "Identity";
ret = "IDENTITY";
break;
case WPA_CTRL_REQ_EAP_PASSWORD:
*txt = "Password";
ret = "PASSWORD";
break;
case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
*txt = "New Password";
ret = "NEW_PASSWORD";
break;
case WPA_CTRL_REQ_EAP_PIN:
*txt = "PIN";
ret = "PIN";
break;
case WPA_CTRL_REQ_EAP_OTP:
ret = "OTP";
break;
case WPA_CTRL_REQ_EAP_PASSPHRASE:
*txt = "Private key passphrase";
ret = "PASSPHRASE";
break;
case WPA_CTRL_REQ_SIM:
ret = "SIM";
break;
case WPA_CTRL_REQ_PSK_PASSPHRASE:
*txt = "PSK or passphrase";
ret = "PSK_PASSPHRASE";
break;
case WPA_CTRL_REQ_EXT_CERT_CHECK:
*txt = "External server certificate validation";
ret = "EXT_CERT_CHECK";
break;
default:
break;
}
/* txt needs to be something */
if (*txt == NULL) {
wpa_printf(MSG_WARNING, "No message for request %d", field);
ret = NULL;
}
return ret;
}
void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
const char *field_name, const char *txt)
{
char *buf;
size_t buflen;
int len;
buflen = 100 + os_strlen(txt) + ssid->ssid_len;
buf = os_malloc(buflen);
if (buf == NULL)
return;
len = os_snprintf(buf, buflen, "%s-%d:%s needed for SSID ",
field_name, ssid->id, txt);
if (os_snprintf_error(buflen, len)) {
os_free(buf);
return;
}
if (ssid->ssid && buflen > len + ssid->ssid_len) {
os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
len += ssid->ssid_len;
buf[len] = '\0';
}
buf[buflen - 1] = '\0';
wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s", buf);
os_free(buf);
}
#ifdef IEEE8021X_EAPOL
#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
static void wpa_supplicant_eap_param_needed(void *ctx,
enum wpa_ctrl_req_type field,
const char *default_txt)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *ssid = wpa_s->current_ssid;
const char *field_name, *txt = NULL;
if (ssid == NULL)
return;
if (field == WPA_CTRL_REQ_EXT_CERT_CHECK)
ssid->eap.pending_ext_cert_check = PENDING_CHECK;
wpas_notify_network_request(wpa_s, ssid, field, default_txt);
field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt,
&txt);
if (field_name == NULL) {
wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed",
field);
return;
}
wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name);
wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
}
#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
#define wpa_supplicant_eap_param_needed NULL
#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
#ifdef CONFIG_EAP_PROXY
static void wpa_supplicant_eap_proxy_cb(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
size_t len;
wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1,
wpa_s->imsi, &len);
if (wpa_s->mnc_len > 0) {
wpa_s->imsi[len] = '\0';
wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
wpa_s->imsi, wpa_s->mnc_len);
} else {
wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
}
}
static void wpa_sm_sim_state_error_handler(struct wpa_supplicant *wpa_s)
{
int i;
struct wpa_ssid *ssid;
const struct eap_method_type *eap_methods;
if (!wpa_s->conf)
return;
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
eap_methods = ssid->eap.eap_methods;
if (!eap_methods)
continue;
for (i = 0; eap_methods[i].method != EAP_TYPE_NONE; i++) {
if (eap_methods[i].vendor == EAP_VENDOR_IETF &&
(eap_methods[i].method == EAP_TYPE_SIM ||
eap_methods[i].method == EAP_TYPE_AKA ||
eap_methods[i].method == EAP_TYPE_AKA_PRIME)) {
wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
break;
}
}
}
}
static void
wpa_supplicant_eap_proxy_notify_sim_status(void *ctx,
enum eap_proxy_sim_state sim_state)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_printf(MSG_DEBUG, "eap_proxy: SIM card status %u", sim_state);
switch (sim_state) {
case SIM_STATE_ERROR:
wpa_sm_sim_state_error_handler(wpa_s);
break;
default:
wpa_printf(MSG_DEBUG, "eap_proxy: SIM card status unknown");
break;
}
}
#endif /* CONFIG_EAP_PROXY */
static void wpa_supplicant_port_cb(void *ctx, int authorized)
{
struct wpa_supplicant *wpa_s = ctx;
#ifdef CONFIG_AP
if (wpa_s->ap_iface) {
wpa_printf(MSG_DEBUG, "AP mode active - skip EAPOL Supplicant "
"port status: %s",
authorized ? "Authorized" : "Unauthorized");
return;
}
#endif /* CONFIG_AP */
wpa_printf(MSG_DEBUG, "EAPOL: Supplicant port status: %s",
authorized ? "Authorized" : "Unauthorized");
wpa_drv_set_supp_port(wpa_s, authorized);
}
static void wpa_supplicant_cert_cb(void *ctx, struct tls_cert_data *cert,
const char *cert_hash)
{
struct wpa_supplicant *wpa_s = ctx;
wpas_notify_certification(wpa_s, cert, cert_hash);
}
static void wpa_supplicant_status_cb(void *ctx, const char *status,
const char *parameter)
{
struct wpa_supplicant *wpa_s = ctx;
wpas_notify_eap_status(wpa_s, status, parameter);
}
static void wpa_supplicant_eap_error_cb(void *ctx, int error_code)
{
struct wpa_supplicant *wpa_s = ctx;
wpas_notify_eap_error(wpa_s, error_code);
}
static int wpa_supplicant_eap_auth_start_cb(void *ctx)
{
struct wpa_supplicant *wpa_s = ctx;
if (!wpa_s->new_connection && wpa_s->deny_ptk0_rekey &&
!wpa_sm_ext_key_id_active(wpa_s->wpa)) {
wpa_msg(wpa_s, MSG_INFO,
"WPA: PTK0 rekey not allowed, reconnecting");
wpa_supplicant_reconnect(wpa_s);
return -1;
}
return 0;
}
static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
char *str;
int res;
wpa_hexdump_ascii(MSG_DEBUG, "EAP method updated anonymous_identity",
id, len);
if (wpa_s->current_ssid == NULL)
return;
if (id == NULL) {
if (wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
"NULL", 0) < 0)
return;
} else {
str = os_malloc(len * 2 + 1);
if (str == NULL)
return;
wpa_snprintf_hex(str, len * 2 + 1, id, len);
res = wpa_config_set(wpa_s->current_ssid, "anonymous_identity",
str, 0);
os_free(str);
if (res < 0)
return;
}
if (wpa_s->conf->update_config) {
res = wpa_config_write(wpa_s->confname, wpa_s->conf);
if (res) {
wpa_printf(MSG_DEBUG, "Failed to update config after "
"anonymous_id update");
}
}
}
#endif /* IEEE8021X_EAPOL */
int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
{
#ifdef IEEE8021X_EAPOL
struct eapol_ctx *ctx;
ctx = os_zalloc(sizeof(*ctx));
if (ctx == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate EAPOL context.");
return -1;
}
ctx->ctx = wpa_s;
ctx->msg_ctx = wpa_s;
ctx->eapol_send_ctx = wpa_s;
ctx->preauth = 0;
ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done;
ctx->eapol_send = wpa_supplicant_eapol_send;
#ifdef CONFIG_WEP
ctx->set_wep_key = wpa_eapol_set_wep_key;
#endif /* CONFIG_WEP */
#ifndef CONFIG_NO_CONFIG_BLOBS
ctx->set_config_blob = wpa_supplicant_set_config_blob;
ctx->get_config_blob = wpa_supplicant_get_config_blob;
#endif /* CONFIG_NO_CONFIG_BLOBS */
ctx->aborted_cached = wpa_supplicant_aborted_cached;
ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
ctx->openssl_ciphers = wpa_s->conf->openssl_ciphers;
ctx->wps = wpa_s->wps;
ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
#ifdef CONFIG_EAP_PROXY
ctx->eap_proxy_cb = wpa_supplicant_eap_proxy_cb;
ctx->eap_proxy_notify_sim_status =
wpa_supplicant_eap_proxy_notify_sim_status;
#endif /* CONFIG_EAP_PROXY */
ctx->port_cb = wpa_supplicant_port_cb;
ctx->cb = wpa_supplicant_eapol_cb;
ctx->cert_cb = wpa_supplicant_cert_cb;
ctx->cert_in_cb = wpa_s->conf->cert_in_cb;
ctx->status_cb = wpa_supplicant_status_cb;
ctx->eap_error_cb = wpa_supplicant_eap_error_cb;
ctx->confirm_auth_cb = wpa_supplicant_eap_auth_start_cb;
ctx->set_anon_id = wpa_supplicant_set_anon_id;
ctx->cb_ctx = wpa_s;
wpa_s->eapol = eapol_sm_init(ctx);
if (wpa_s->eapol == NULL) {
os_free(ctx);
wpa_printf(MSG_ERROR, "Failed to initialize EAPOL state "
"machines.");
return -1;
}
#endif /* IEEE8021X_EAPOL */
return 0;
}
#ifndef CONFIG_NO_WPA
static void wpa_supplicant_set_rekey_offload(void *ctx,
const u8 *kek, size_t kek_len,
const u8 *kck, size_t kck_len,
const u8 *replay_ctr)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_drv_set_rekey_info(wpa_s, kek, kek_len, kck, kck_len, replay_ctr);
}
static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
size_t pmk_len)
{
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s->conf->key_mgmt_offload &&
(wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
return wpa_drv_set_key(wpa_s, 0, NULL, 0, 0,
NULL, 0, pmk, pmk_len, KEY_FLAG_PMK);
else
return 0;
}
static void wpa_supplicant_fils_hlp_rx(void *ctx, const u8 *dst, const u8 *src,
const u8 *pkt, size_t pkt_len)
{
struct wpa_supplicant *wpa_s = ctx;
char *hex;
size_t hexlen;
hexlen = pkt_len * 2 + 1;
hex = os_malloc(hexlen);
if (!hex)
return;
wpa_snprintf_hex(hex, hexlen, pkt, pkt_len);
wpa_msg(wpa_s, MSG_INFO, FILS_HLP_RX "dst=" MACSTR " src=" MACSTR
" frame=%s", MAC2STR(dst), MAC2STR(src), hex);
os_free(hex);
}
static int wpa_supplicant_channel_info(void *_wpa_s,
struct wpa_channel_info *ci)
{
struct wpa_supplicant *wpa_s = _wpa_s;
return wpa_drv_channel_info(wpa_s, ci);
}
static void disable_wpa_wpa2(struct wpa_ssid *ssid)
{
ssid->proto &= ~WPA_PROTO_WPA;
ssid->proto |= WPA_PROTO_RSN;
ssid->key_mgmt &= ~(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK |
WPA_KEY_MGMT_PSK_SHA256);
ssid->group_cipher &= ~WPA_CIPHER_TKIP;
if (!(ssid->group_cipher & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)))
ssid->group_cipher |= WPA_CIPHER_CCMP;
ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
}
static void wpa_supplicant_transition_disable(void *_wpa_s, u8 bitmap)
{
struct wpa_supplicant *wpa_s = _wpa_s;
struct wpa_ssid *ssid;
int changed = 0;
wpa_msg(wpa_s, MSG_INFO, TRANSITION_DISABLE "%02x", bitmap);
ssid = wpa_s->current_ssid;
if (!ssid)
return;
#ifdef CONFIG_SAE
if ((bitmap & TRANSITION_DISABLE_WPA3_PERSONAL) &&
wpa_key_mgmt_sae(wpa_s->key_mgmt) &&
(ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) &&
(ssid->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED ||
(ssid->group_cipher & WPA_CIPHER_TKIP))) {
wpa_printf(MSG_DEBUG,
"WPA3-Personal transition mode disabled based on AP notification");
disable_wpa_wpa2(ssid);
changed = 1;
}
if ((bitmap & TRANSITION_DISABLE_SAE_PK) &&
wpa_key_mgmt_sae(wpa_s->key_mgmt) &&
#ifdef CONFIG_SME
wpa_s->sme.sae.state == SAE_ACCEPTED &&
wpa_s->sme.sae.pk &&
#endif /* CONFIG_SME */
(ssid->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) &&
(ssid->sae_pk != SAE_PK_MODE_ONLY ||
ssid->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED ||
(ssid->group_cipher & WPA_CIPHER_TKIP))) {
wpa_printf(MSG_DEBUG,
"SAE-PK: SAE authentication without PK disabled based on AP notification");
disable_wpa_wpa2(ssid);
ssid->sae_pk = SAE_PK_MODE_ONLY;
changed = 1;
}
#endif /* CONFIG_SAE */
if ((bitmap & TRANSITION_DISABLE_WPA3_ENTERPRISE) &&
wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
(ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X |
WPA_KEY_MGMT_FT_IEEE8021X |
WPA_KEY_MGMT_IEEE8021X_SHA256)) &&
(ssid->ieee80211w != MGMT_FRAME_PROTECTION_REQUIRED ||
(ssid->group_cipher & WPA_CIPHER_TKIP))) {
disable_wpa_wpa2(ssid);
changed = 1;
}
if ((bitmap & TRANSITION_DISABLE_ENHANCED_OPEN) &&
wpa_s->key_mgmt == WPA_KEY_MGMT_OWE &&
(ssid->key_mgmt & WPA_KEY_MGMT_OWE) &&
!ssid->owe_only) {
ssid->owe_only = 1;
changed = 1;
}
if (!changed)
return;
#ifndef CONFIG_NO_CONFIG_WRITE
if (wpa_s->conf->update_config &&
wpa_config_write(wpa_s->confname, wpa_s->conf))
wpa_printf(MSG_DEBUG, "Failed to update configuration");
#endif /* CONFIG_NO_CONFIG_WRITE */
}
static void wpa_supplicant_store_ptk(void *ctx, u8 *addr, int cipher,
u32 life_time, const struct wpa_ptk *ptk)
{
struct wpa_supplicant *wpa_s = ctx;
ptksa_cache_add(wpa_s->ptksa, addr, cipher, life_time, ptk);
}
#endif /* CONFIG_NO_WPA */
int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
{
#ifndef CONFIG_NO_WPA
struct wpa_sm_ctx *ctx;
wpa_s->ptksa = ptksa_cache_init();
if (!wpa_s->ptksa) {
wpa_printf(MSG_ERROR, "Failed to allocate PTKSA");
return -1;
}
ctx = os_zalloc(sizeof(*ctx));
if (ctx == NULL) {
wpa_printf(MSG_ERROR, "Failed to allocate WPA context.");
ptksa_cache_deinit(wpa_s->ptksa);
wpa_s->ptksa = NULL;
return -1;
}
ctx->ctx = wpa_s;
ctx->msg_ctx = wpa_s;
ctx->set_state = _wpa_supplicant_set_state;
ctx->get_state = _wpa_supplicant_get_state;
ctx->deauthenticate = _wpa_supplicant_deauthenticate;
ctx->reconnect = _wpa_supplicant_reconnect;
ctx->set_key = wpa_supplicant_set_key;
ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
ctx->get_bssid = wpa_supplicant_get_bssid;
ctx->ether_send = _wpa_ether_send;
ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie;
ctx->alloc_eapol = _wpa_alloc_eapol;
ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout;
ctx->add_pmkid = wpa_supplicant_add_pmkid;
ctx->remove_pmkid = wpa_supplicant_remove_pmkid;
#ifndef CONFIG_NO_CONFIG_BLOBS
ctx->set_config_blob = wpa_supplicant_set_config_blob;
ctx->get_config_blob = wpa_supplicant_get_config_blob;
#endif /* CONFIG_NO_CONFIG_BLOBS */
ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection;
#ifdef CONFIG_IEEE80211R
ctx->update_ft_ies = wpa_supplicant_update_ft_ies;
ctx->send_ft_action = wpa_supplicant_send_ft_action;
ctx->mark_authenticated = wpa_supplicant_mark_authenticated;
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_TDLS
ctx->tdls_get_capa = wpa_supplicant_tdls_get_capa;
ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt;
ctx->tdls_oper = wpa_supplicant_tdls_oper;
ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset;
ctx->tdls_enable_channel_switch =
wpa_supplicant_tdls_enable_channel_switch;
ctx->tdls_disable_channel_switch =
wpa_supplicant_tdls_disable_channel_switch;
#endif /* CONFIG_TDLS */
ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;
ctx->fils_hlp_rx = wpa_supplicant_fils_hlp_rx;
ctx->channel_info = wpa_supplicant_channel_info;
ctx->transition_disable = wpa_supplicant_transition_disable;
ctx->store_ptk = wpa_supplicant_store_ptk;
wpa_s->wpa = wpa_sm_init(ctx);
if (wpa_s->wpa == NULL) {
wpa_printf(MSG_ERROR,
"Failed to initialize WPA state machine");
os_free(ctx);
ptksa_cache_deinit(wpa_s->ptksa);
wpa_s->ptksa = NULL;
return -1;
}
#endif /* CONFIG_NO_WPA */
return 0;
}
void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
struct rsn_supp_config conf;
if (ssid) {
os_memset(&conf, 0, sizeof(conf));
conf.network_ctx = ssid;
conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
#ifdef IEEE8021X_EAPOL
conf.proactive_key_caching = ssid->proactive_key_caching < 0 ?
wpa_s->conf->okc : ssid->proactive_key_caching;
conf.eap_workaround = ssid->eap_workaround;
conf.eap_conf_ctx = &ssid->eap;
#endif /* IEEE8021X_EAPOL */
conf.ssid = ssid->ssid;
conf.ssid_len = ssid->ssid_len;
conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey;
conf.wpa_deny_ptk0_rekey = ssid->wpa_deny_ptk0_rekey;
conf.owe_ptk_workaround = ssid->owe_ptk_workaround;
#ifdef CONFIG_P2P
if (ssid->p2p_group && wpa_s->current_bss &&
!wpa_s->p2p_disable_ip_addr_req) {
struct wpabuf *p2p;
p2p = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
P2P_IE_VENDOR_TYPE);
if (p2p) {
u8 group_capab;
group_capab = p2p_get_group_capab(p2p);
if (group_capab &
P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION)
conf.p2p = 1;
wpabuf_free(p2p);
}
}
#endif /* CONFIG_P2P */
conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation;
#ifdef CONFIG_FILS
if (wpa_key_mgmt_fils(wpa_s->key_mgmt))
conf.fils_cache_id =
wpa_bss_get_fils_cache_id(wpa_s->current_bss);
#endif /* CONFIG_FILS */
if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_BEACON_PROTECTION) ||
(wpa_s->drv_flags2 &
WPA_DRIVER_FLAGS2_BEACON_PROTECTION_CLIENT))
conf.beacon_prot = ssid->beacon_prot;
#ifdef CONFIG_PASN
#ifdef CONFIG_TESTING_OPTIONS
conf.force_kdk_derivation = wpa_s->conf->force_kdk_derivation;
#endif /* CONFIG_TESTING_OPTIONS */
#endif /* CONFIG_PASN */
}
wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
}

File Metadata

Mime Type
application/octet-stream
Expires
Mon, Apr 29, 6:55 PM (2 d)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
l2eK3eh1ys48
Default Alt Text
(7 MB)

Event Timeline